mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[amberelectric] Initial contribution (#16850)
Signed-off-by: Paul Smedley <paul@smedley.id.au>
This commit is contained in:
parent
643c9e457c
commit
30fbc999e2
@ -22,6 +22,7 @@
|
||||
/bundles/org.openhab.binding.allplay/ @dominicdesu
|
||||
/bundles/org.openhab.binding.amazondashbutton/ @openhab/add-ons-maintainers
|
||||
/bundles/org.openhab.binding.amazonechocontrol/ @mgeramb
|
||||
/bundles/org.openhab.binding.amberelectric/ @psmedley
|
||||
/bundles/org.openhab.binding.ambientweather/ @mhilbush
|
||||
/bundles/org.openhab.binding.amplipi/ @kaikreuzer
|
||||
/bundles/org.openhab.binding.androiddebugbridge/ @GiviMAD
|
||||
|
@ -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>
|
||||
|
13
bundles/org.openhab.binding.amberelectric/NOTICE
Normal file
13
bundles/org.openhab.binding.amberelectric/NOTICE
Normal file
@ -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
bundles/org.openhab.binding.amberelectric/README.md
Normal file
69
bundles/org.openhab.binding.amberelectric/README.md
Normal file
@ -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.
|
||||
|
||||
## Supported Things
|
||||
|
||||
- `service` Amber Electric API
|
||||
|
||||
## Discovery
|
||||
|
||||
The binding does not support auto discovery.
|
||||
|
||||
## 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 |
|
||||
|------------------------|----------------------|---------------------------------------------------------------------------------|
|
||||
| electricity-price | Number:EnergyPrice | Current price to import power from the grid
|
||||
| controlled-load-price | Number:EnergyPrice | Current price to import power for Controlled Load
|
||||
| feed-in-price | Number:EnergyPrice | Current price to export power to the grid
|
||||
| electricity-status | String | Current price status of grid import
|
||||
| controlled-load-status | String | Current price status of controlled load import
|
||||
| feed-in-status | String | Current price status of Feed-In
|
||||
| nem-time | String | NEM time of last pricing update
|
||||
| renewables | Number:Dimensionless | Current level of renewables in the grid
|
||||
| spike | Switch | Report if the grid has a current price spike
|
||||
|
||||
## Full Example
|
||||
|
||||
### `amberelectric.things`:
|
||||
|
||||
```java
|
||||
amberelectric:service:AmberElectric [ apiKey="psk_xxxxxxxxxxxxxxxxxxxx" ]
|
||||
```
|
||||
|
||||
### `amberelectric.items`:
|
||||
|
||||
```java
|
||||
Number:EnergyPrice AmberElectric_ElectricityPrice { channel="amberelectric:service:AmberElectric:electricity-price" }
|
||||
Number:EnergyPrice AmberElectric_ControlledLoadPrice { channel="amberelectric:service:AmberElectric:controlled-load-price" }
|
||||
Number:EnergyPrice AmberElectric_FeedInPrice { channel="amberelectric:service:AmberElectric:feed-in-price" }
|
||||
String AmberElectric_ElectricityStatus { channel="amberelectric:service:AmberElectric:electricity-status" }
|
||||
String AmberElectric_ControlledLoadStatus { channel="amberelectric:service:AmberElectric:controlled-load-status" }
|
||||
String AmberElectric_FeedInStatus { channel="amberelectric:service:AmberElectric:feed-in-status" }
|
||||
String AmberElectric_nemtime { channel="amberelectric:service:AmberElectric:nem-time" }
|
||||
Number AmberElectric_Renewables { channel="amberelectric:service:AmberElectric:renewables" }
|
||||
Switch AmberElectric_Spike { channel="amberelectric:service:AmberElectric:spike" }
|
||||
```
|
||||
|
||||
### `amberelectric.sitemap`:
|
||||
|
||||
```perl
|
||||
Text item=AmberElectric_ElectricityPrice label="Electricity Price"
|
||||
Text item=AmberElectric_ControlledLoadPrice label="Controlled Load Price"
|
||||
Text item=AmberElectric_FeedInPrice label="Feed-In Price"
|
||||
Text item=AmberElectric_ElectricityStatus label="Electricity Price Status"
|
||||
Text item=AmberElectric_ControlledLoadStatus 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
bundles/org.openhab.binding.amberelectric/pom.xml
Normal file
17
bundles/org.openhab.binding.amberelectric/pom.xml
Normal file
@ -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>
|
@ -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>
|
@ -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.amberelectric.internal;
|
||||
|
||||
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, "service");
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String CHANNEL_ELECTRICITY_PRICE = "electricity-price";
|
||||
public static final String CHANNEL_CONTROLLED_LOAD_PRICE = "controlled-load-price";
|
||||
public static final String CHANNEL_FEED_IN_PRICE = "feed-in-price";
|
||||
public static final String CHANNEL_ELECTRICITY_STATUS = "electricity-status";
|
||||
public static final String CHANNEL_CONTROLLED_LOAD_STATUS = "controlled-load-status";
|
||||
public static final String CHANNEL_FEED_IN_STATUS = "feed-in-status";
|
||||
public static final String CHANNEL_NEM_TIME = "nem-time";
|
||||
public static final String CHANNEL_RENEWABLES = "renewables";
|
||||
public static final String CHANNEL_SPIKE = "spike";
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(AMBERELECTRIC_THING);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
/**
|
||||
* 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 javax.measure.Unit;
|
||||
|
||||
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.config.core.Configuration;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.CurrencyUnits;
|
||||
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");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
config = getConfigAs(AmberElectricConfiguration.class);
|
||||
if (config.apiKey.isBlank()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/offline.conf-error.no-api-key");
|
||||
return;
|
||||
}
|
||||
|
||||
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() {
|
||||
logger.debug("Scheduling poll every {} s", refreshInterval);
|
||||
this.pollFuture = scheduler.scheduleWithFixedDelay(this::poll, 0, refreshInterval, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
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.cancel(true);
|
||||
pollFuture = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void pollStatus() throws IOException {
|
||||
try {
|
||||
if (siteID.isEmpty()) {
|
||||
Sites sites = webTargets.getSites(apiKey, nmi);
|
||||
// add error handling
|
||||
siteID = sites.siteid;
|
||||
Configuration configuration = editConfiguration();
|
||||
configuration.put("nmi", sites.nmi);
|
||||
updateConfiguration(configuration);
|
||||
logger.debug("Detected amber siteid is {}, for nmi {}", sites.siteid, sites.nmi);
|
||||
}
|
||||
|
||||
CurrentPrices currentPrices = webTargets.getCurrentPrices(siteID, apiKey);
|
||||
final String electricityUnit = " AUD/kWh";
|
||||
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
Unit<?> unit = CurrencyUnits.getInstance().getUnit("AUD");
|
||||
if (unit == null) {
|
||||
logger.trace("Currency AUD is unknown, falling back to DecimalType");
|
||||
updateState(AmberElectricBindingConstants.CHANNEL_ELECTRICITY_PRICE,
|
||||
new DecimalType(currentPrices.elecPerKwh / 100));
|
||||
updateState(AmberElectricBindingConstants.CHANNEL_CONTROLLED_LOAD_PRICE,
|
||||
new DecimalType(currentPrices.clPerKwh / 100));
|
||||
updateState(AmberElectricBindingConstants.CHANNEL_FEED_IN_PRICE,
|
||||
new DecimalType(currentPrices.feedInPerKwh / 100));
|
||||
} else {
|
||||
updateState(AmberElectricBindingConstants.CHANNEL_ELECTRICITY_PRICE,
|
||||
new QuantityType<>(currentPrices.elecPerKwh / 100 + " " + electricityUnit));
|
||||
updateState(AmberElectricBindingConstants.CHANNEL_CONTROLLED_LOAD_PRICE,
|
||||
new QuantityType<>(currentPrices.clPerKwh / 100 + " " + electricityUnit));
|
||||
updateState(AmberElectricBindingConstants.CHANNEL_FEED_IN_PRICE,
|
||||
new QuantityType<>(currentPrices.feedInPerKwh / 100 + " " + electricityUnit));
|
||||
}
|
||||
updateState(AmberElectricBindingConstants.CHANNEL_CONTROLLED_LOAD_STATUS,
|
||||
new StringType(currentPrices.clStatus));
|
||||
updateState(AmberElectricBindingConstants.CHANNEL_ELECTRICITY_STATUS,
|
||||
new StringType(currentPrices.elecStatus));
|
||||
updateState(AmberElectricBindingConstants.CHANNEL_FEED_IN_STATUS,
|
||||
new StringType(currentPrices.feedInStatus));
|
||||
updateState(AmberElectricBindingConstants.CHANNEL_NEM_TIME, new StringType(currentPrices.nemTime));
|
||||
updateState(AmberElectricBindingConstants.CHANNEL_RENEWABLES, new DecimalType(currentPrices.renewables));
|
||||
updateState(AmberElectricBindingConstants.CHANNEL_SPIKE,
|
||||
OnOffType.from(!"none".equals(currentPrices.spikeStatus)));
|
||||
} catch (AmberElectricCommunicationException e) {
|
||||
logger.debug("Unexpected error connecting to Amber Electric API", e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
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.Component;
|
||||
|
||||
/**
|
||||
* The {@link AmberElectricHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Paul Smedley - Initial contribution
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.amberelectric")
|
||||
@NonNullByDefault
|
||||
public class AmberElectricHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return AmberElectricBindingConstants.SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (thingTypeUID.equals(AmberElectricBindingConstants.AMBERELECTRIC_THING)) {
|
||||
return new AmberElectricHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* 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.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
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.io.net.http.HttpUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Handles performing the actual HTTP requests for communicating with the AmberAPI.
|
||||
*
|
||||
* @author Paul Smedley - Initial Contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AmberElectricWebTargets {
|
||||
private static final int TIMEOUT_MS = 30000;
|
||||
private static final String BASE_URI = "https://api.amber.com.au/v1/";
|
||||
private final Logger logger = LoggerFactory.getLogger(AmberElectricWebTargets.class);
|
||||
|
||||
public AmberElectricWebTargets() {
|
||||
}
|
||||
|
||||
public Sites getSites(String apiKey, String nmi) throws AmberElectricCommunicationException {
|
||||
String getSitesUri = BASE_URI + "sites";
|
||||
String response = invoke("GET", getSitesUri, apiKey);
|
||||
logger.trace("Received response: \"{}\"", response);
|
||||
return Sites.parse(response, nmi);
|
||||
}
|
||||
|
||||
public CurrentPrices getCurrentPrices(String siteid, String apiKey) throws AmberElectricCommunicationException {
|
||||
String getCurrentPricesUri = BASE_URI + "sites/" + siteid + "/prices/current";
|
||||
String response = invoke("GET", getCurrentPricesUri, apiKey);
|
||||
logger.trace("Received response: \"{}\"", response);
|
||||
return CurrentPrices.parse(response);
|
||||
}
|
||||
|
||||
protected Properties getHttpHeaders(String accessToken) {
|
||||
Properties httpHeaders = new Properties();
|
||||
httpHeaders.put("Authorization", "Bearer " + accessToken);
|
||||
httpHeaders.put("Content-Type", "application/json");
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
private String invoke(String httpMethod, String uri, String accessToken)
|
||||
throws AmberElectricCommunicationException {
|
||||
return invoke(httpMethod, uri, accessToken, null, null);
|
||||
}
|
||||
|
||||
private String invoke(String httpMethod, String uri, String apiKey, @Nullable InputStream content,
|
||||
@Nullable String contentType) throws AmberElectricCommunicationException {
|
||||
logger.debug("Calling url: {}", uri);
|
||||
@Nullable
|
||||
String response;
|
||||
try {
|
||||
response = HttpUtil.executeUrl(httpMethod, uri, getHttpHeaders(apiKey), content, contentType, TIMEOUT_MS);
|
||||
} catch (IOException ex) {
|
||||
logger.debug("{}", ex.getLocalizedMessage(), ex);
|
||||
// Response will also be set to null if parsing in executeUrl fails so we use null here to make the
|
||||
// error check below consistent.
|
||||
response = null;
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
throw new AmberElectricCommunicationException(
|
||||
String.format("AmberElectric returned no response while invoking %s", uri));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* 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.api;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
* Container class for Current Pricing, related to amberelectric
|
||||
*
|
||||
* @author Paul Smedley <paul@smedley.id.au> - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class CurrentPrices {
|
||||
public double elecPerKwh;
|
||||
public double clPerKwh;
|
||||
public double feedInPerKwh;
|
||||
public String elecStatus = "";
|
||||
public String clStatus = "";
|
||||
public String feedInStatus = "";
|
||||
public double renewables;
|
||||
public String spikeStatus = "";
|
||||
public String nemTime = "";
|
||||
|
||||
private CurrentPrices() {
|
||||
}
|
||||
|
||||
public static CurrentPrices parse(String response) {
|
||||
/* parse json string */
|
||||
JsonArray jsonArray = JsonParser.parseString(response).getAsJsonArray();
|
||||
JsonObject jsonObject = jsonArray.get(0).getAsJsonObject();
|
||||
CurrentPrices currentprices = new CurrentPrices();
|
||||
currentprices.nemTime = jsonObject.get("nemTime").getAsString();
|
||||
currentprices.renewables = jsonObject.get("renewables").getAsDouble();
|
||||
currentprices.spikeStatus = jsonObject.get("spikeStatus").getAsString();
|
||||
for (int i = 0; i < jsonArray.size(); i++) {
|
||||
jsonObject = jsonArray.get(i).getAsJsonObject();
|
||||
if ("general".equals(jsonObject.get("channelType").getAsString())) {
|
||||
currentprices.elecPerKwh = jsonObject.get("perKwh").getAsDouble();
|
||||
currentprices.elecStatus = jsonObject.get("descriptor").getAsString();
|
||||
}
|
||||
if ("feedIn".equals(jsonObject.get("channelType").getAsString())) {
|
||||
// Multiple value from API by -1 to make the value match the app
|
||||
currentprices.feedInPerKwh = -1 * jsonObject.get("perKwh").getAsDouble();
|
||||
currentprices.feedInStatus = jsonObject.get("descriptor").getAsString();
|
||||
}
|
||||
if ("controlledLoad".equals(jsonObject.get("channelType").getAsString())) {
|
||||
currentprices.clPerKwh = jsonObject.get("perKwh").getAsDouble();
|
||||
currentprices.clStatus = jsonObject.get("descriptor").getAsString();
|
||||
}
|
||||
}
|
||||
return currentprices;
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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.api;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
* Class for holding the set of parameters used to read the controller variables.
|
||||
*
|
||||
* @author Paul Smedley - Initial Contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Sites {
|
||||
public String siteid = "";
|
||||
public String nmi = "";
|
||||
|
||||
private Sites() {
|
||||
}
|
||||
|
||||
public static Sites parse(String response, String nem) {
|
||||
/* parse json string */
|
||||
JsonArray jsonArray = JsonParser.parseString(response).getAsJsonArray();
|
||||
Sites sites = new Sites();
|
||||
for (int i = 0; i < jsonArray.size(); i++) {
|
||||
JsonObject jsonObject = jsonArray.get(i).getAsJsonObject();
|
||||
if (nem.equals(jsonObject.get("nmi").getAsString())) {
|
||||
sites.siteid = jsonObject.get("id").getAsString();
|
||||
sites.nmi = jsonObject.get("nmi").getAsString();
|
||||
}
|
||||
}
|
||||
if ((nem.isEmpty()) || (sites.siteid.isEmpty())) { // nem not specified, or not found so we take the first
|
||||
// siteid
|
||||
// found
|
||||
JsonObject jsonObject = jsonArray.get(0).getAsJsonObject();
|
||||
sites.siteid = jsonObject.get("id").getAsString();
|
||||
sites.nmi = jsonObject.get("nmi").getAsString();
|
||||
}
|
||||
return sites;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<addon:addon id="amberelectric" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
|
||||
|
||||
<type>binding</type>
|
||||
<name>Amber Electric Binding</name>
|
||||
<description>This is the binding for Amber Electric.</description>
|
||||
<connection>cloud</connection>
|
||||
|
||||
</addon:addon>
|
@ -0,0 +1,43 @@
|
||||
# add-on
|
||||
|
||||
addon.amberelectric.name = Amber Electric Binding
|
||||
addon.amberelectric.description = This is the binding for Amber Electric.
|
||||
|
||||
# thing types
|
||||
|
||||
thing-type.amberelectric.service.label = Amber Electric
|
||||
thing-type.amberelectric.service.description = Amber Electric - wholesale access to power prices
|
||||
thing-type.amberelectric.service.channel.controlled-load-price.label = Current Controlled Load Price
|
||||
thing-type.amberelectric.service.channel.controlled-load-price.description = Current price to import power for Controlled Load
|
||||
thing-type.amberelectric.service.channel.controlled-load-status.label = Current Controlled Load Status
|
||||
thing-type.amberelectric.service.channel.controlled-load-status.description = Current price status of Controlled Load
|
||||
thing-type.amberelectric.service.channel.feed-in-price.label = Current Feed-In Price
|
||||
thing-type.amberelectric.service.channel.feed-in-price.description = Current price to export power to the grid
|
||||
thing-type.amberelectric.service.channel.feed-in-status.label = Current Feed-In Status
|
||||
thing-type.amberelectric.service.channel.feed-in-status.description = Current price status of Feed-In
|
||||
|
||||
# thing types config
|
||||
|
||||
thing-type.config.amberelectric.service.apiKey.label = API Key
|
||||
thing-type.config.amberelectric.service.apiKey.description = API key from the Amber website
|
||||
thing-type.config.amberelectric.service.nmi.label = NMI
|
||||
thing-type.config.amberelectric.service.nmi.description = NMI for your address (Optional)
|
||||
thing-type.config.amberelectric.service.refresh.label = Refresh Interval
|
||||
thing-type.config.amberelectric.service.refresh.description = Specifies the refresh interval in seconds
|
||||
|
||||
# channel types
|
||||
|
||||
channel-type.amberelectric.electricity-price.label = Current Electricity Price
|
||||
channel-type.amberelectric.electricity-price.description = Current price to import power from the grid
|
||||
channel-type.amberelectric.electricity-status.label = Current Electricity Status
|
||||
channel-type.amberelectric.electricity-status.description = Current price status of grid import
|
||||
channel-type.amberelectric.nem-time.label = NEM Time
|
||||
channel-type.amberelectric.nem-time.description = NEM time of last pricing update
|
||||
channel-type.amberelectric.renewables.label = Current Renewables
|
||||
channel-type.amberelectric.renewables.description = Current level of renewables in the grid
|
||||
channel-type.amberelectric.spike.label = Energy Price Spike
|
||||
channel-type.amberelectric.spike.description = Report if the grid has a current price spike
|
||||
|
||||
# thing status descriptions
|
||||
|
||||
offline.conf-error.no-api-key = API key must be set
|
@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="amberelectric"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="service">
|
||||
<label>Amber Electric</label>
|
||||
<description>Amber Electric - wholesale access to power prices</description>
|
||||
|
||||
<channels>
|
||||
<channel id="electricity-price" typeId="electricity-price"/>
|
||||
<channel id="controlled-load-price" typeId="electricity-price">
|
||||
<label>Current Controlled Load Price</label>
|
||||
<description>Current price to import power for Controlled Load</description>
|
||||
</channel>
|
||||
<channel id="feed-in-price" typeId="electricity-price">
|
||||
<label>Current Feed-In Price</label>
|
||||
<description>Current price to export power to the grid</description>
|
||||
</channel>
|
||||
<channel id="electricity-status" typeId="electricity-status"/>
|
||||
<channel id="controlled-load-status" typeId="electricity-status">
|
||||
<label>Current Controlled Load Status</label>
|
||||
<description>Current price status of Controlled Load</description>
|
||||
</channel>
|
||||
<channel id="feed-in-status" typeId="electricity-status">
|
||||
<label>Current Feed-In Status</label>
|
||||
<description>Current price status of Feed-In</description>
|
||||
</channel>
|
||||
<channel id="nem-time" typeId="nemtime"/>
|
||||
<channel id="spike" typeId="spike"/>
|
||||
<channel id="renewables" typeId="renewables"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="apiKey" type="text" required="true">
|
||||
<label>API Key</label>
|
||||
<description>API key from the Amber website</description>
|
||||
</parameter>
|
||||
<parameter name="nmi" type="text">
|
||||
<label>NMI</label>
|
||||
<description>NMI for your address (Optional)</description>
|
||||
</parameter>
|
||||
<parameter name="refresh" type="integer" min="60" unit="s">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Specifies the refresh interval in seconds</description>
|
||||
<default>60</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="electricity-price">
|
||||
<item-type>Number:EnergyPrice</item-type>
|
||||
<label>Current Electricity Price</label>
|
||||
<description>Current price to import power from the grid</description>
|
||||
<category>Price</category>
|
||||
<state readOnly="true" pattern="%.3f %unit%"/>
|
||||
</channel-type>
|
||||
<channel-type id="electricity-status">
|
||||
<item-type>String</item-type>
|
||||
<label>Current Electricity Status</label>
|
||||
<description>Current price status of grid import</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
<channel-type id="nem-time">
|
||||
<item-type>String</item-type>
|
||||
<label>NEM Time</label>
|
||||
<description>NEM time of last pricing update</description>
|
||||
<state readOnly="true" pattern="%s"/>
|
||||
</channel-type>
|
||||
<channel-type id="renewables">
|
||||
<item-type unitHint="%">Number:Dimensionless</item-type>
|
||||
<label>Current Renewables</label>
|
||||
<description>Current level of renewables in the grid</description>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
<channel-type id="spike">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Energy Price Spike</label>
|
||||
<description>Report if the grid has a current price spike</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
@ -55,6 +55,7 @@
|
||||
<module>org.openhab.binding.allplay</module>
|
||||
<module>org.openhab.binding.amazondashbutton</module>
|
||||
<module>org.openhab.binding.amazonechocontrol</module>
|
||||
<module>org.openhab.binding.amberelectric</module>
|
||||
<module>org.openhab.binding.ambientweather</module>
|
||||
<module>org.openhab.binding.amplipi</module>
|
||||
<module>org.openhab.binding.androiddebugbridge</module>
|
||||
|
Loading…
Reference in New Issue
Block a user