mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[awattar] Initial contribution (#11976)
* First alpha version of the awattar binding Signed-off-by: wolfii <wolfgang.klimt@consol.de> Signed-off-by: Wolfgang Klimt <github@klimt.de> * Corrected typos Signed-off-by: wolfii <wolfgang.klimt@consol.de> Signed-off-by: Wolfgang Klimt <github@klimt.de> * More typos Signed-off-by: wolfii <wolfgang.klimt@consol.de> Signed-off-by: Wolfgang Klimt <github@klimt.de> * Improved time handling to consider time zone. Signed-off-by: wolfii <wolfgang.klimt@consol.de> Signed-off-by: Wolfgang Klimt <github@klimt.de> * Corrected logical time problem, start adding nextprice thing Signed-off-by: wolfii <wolfgang.klimt@consol.de> Signed-off-by: Wolfgang Klimt <github@klimt.de> * Added support for Austria Signed-off-by: wolfii <wolfgang.klimt@consol.de> Signed-off-by: Wolfgang Klimt <github@klimt.de> * Use List instead of Set Signed-off-by: wolfii <wolfgang.klimt@consol.de> Signed-off-by: Wolfgang Klimt <github@klimt.de> * Minor corrections Signed-off-by: Wolfgang Klimt <github@klimt.de> * Removed unneeded handler, corrected fetching of prices Signed-off-by: Wolfgang Klimt <github@klimt.de> * Added i18n, updated documentation Signed-off-by: Wolfgang Klimt <github@klimt.de> * Corrected pom.xml after rebase Signed-off-by: Wolfgang Klimt <github@klimt.de> * Updated version to 3.3.0-SNAPSHOT Signed-off-by: Wolfgang Klimt <github@klimt.de> * Corrected findings of Code analysis tool Signed-off-by: Wolfgang Klimt <github@klimt.de> * Updated copyright notice Signed-off-by: Wolfgang Klimt <github@klimt.de> * Updates to get rid of compiler warnings Signed-off-by: Wolfgang Klimt <github@klimt.de> * Worked on review comments from @fwolter Obeyed most of the review comments. Exceptions: * binding is already added to bom/openhab-addons/pom.xml (at least I found it there and there was a commit notice in git log) * mvn license:format brought back 2021, so I manually set everything to 2022. Should I try to rebase my whole branch? * In two places the binding needs to adjust to minute boundaries, hence scheduleWithFixedDelay will not work. * I removed empty trailing lines, but mvn spotless:apply brought them back * The OhInfXmlUsageCheck seems to be wrong. * The ConstantNameCheck in AwattarUtil seems to be mislead by the fact that all members of the class are static, including the logger. From my point of view it is not a real "constant". Signed-off-by: Wolfgang Klimt <github@klimt.de> * Updated Readme to match code changes Signed-off-by: Wolfgang Klimt <github@klimt.de> * Further work on review comments from @fwolter Signed-off-by: Wolfgang Klimt <github@klimt.de> * Corrected config definition Signed-off-by: Wolfgang Klimt <github@klimt.de> * Changed Copyright to 2022 Signed-off-by: Wolfgang Klimt <github@klimt.de> * Review comments from @fwolter. Improved timezone handling Signed-off-by: Wolfgang Klimt <github@klimt.de> Co-authored-by: wolfii <wolfgang.klimt@consol.de>
This commit is contained in:
parent
3cac11b16b
commit
61de1a5387
@ -29,6 +29,7 @@
|
||||
/bundles/org.openhab.binding.autelis/ @digitaldan
|
||||
/bundles/org.openhab.binding.automower/ @maxpg
|
||||
/bundles/org.openhab.binding.avmfritz/ @cweitkamp
|
||||
/bundles/org.openhab.binding.awattar/ @Wolfgang1966
|
||||
/bundles/org.openhab.binding.benqprojector/ @mlobstein
|
||||
/bundles/org.openhab.binding.bigassfan/ @mhilbush
|
||||
/bundles/org.openhab.binding.bluetooth/ @cdjackson @cpmeister
|
||||
|
@ -136,6 +136,11 @@
|
||||
<artifactId>org.openhab.binding.avmfritz</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.awattar</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.benqprojector</artifactId>
|
||||
|
13
bundles/org.openhab.binding.awattar/NOTICE
Normal file
13
bundles/org.openhab.binding.awattar/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
|
263
bundles/org.openhab.binding.awattar/README.md
Normal file
263
bundles/org.openhab.binding.awattar/README.md
Normal file
@ -0,0 +1,263 @@
|
||||
# aWATTar Binding
|
||||
|
||||
This binding provides access to the hourly prices for electricity for the German and Austrian provider aWATTar.
|
||||
|
||||
|
||||
## Supported Things
|
||||
|
||||
There are three supported things.
|
||||
|
||||
### aWATTar Bridge
|
||||
|
||||
The `bridge` reads price data from the aWATTar API and stores the (optional) config values for VAT and energy base price.
|
||||
|
||||
### Prices Thing
|
||||
|
||||
The `prices` Thing provides todays and (after 14:00) tomorrows net and gross prices.
|
||||
|
||||
### Bestprice Thing
|
||||
|
||||
The `bestprice` Thing identifies the hours with the cheapest prices based on the given parameters.
|
||||
|
||||
## Discovery
|
||||
|
||||
Auto discovery is not supported.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
### aWATTar Bridge
|
||||
|
||||
|
||||
| Parameter | Description |
|
||||
|-------------|-------------------------------------------------------------------------------------------------------------------------------|
|
||||
| vatPercent | Percentage of the value added tax to apply to net prices. Optional, defaults to 19. |
|
||||
| basePrice | The net(!) base price you have to pay for every kWh. Optional, but you most probably want to set it based on you delivery contract. |
|
||||
| timeZone | The time zone the hour definitions of the things below refer to. Default is `CET`, as it corresponds to the aWATTar API. It is strongly recommended not to change this. However, if you do so, be aware that the prices delivered by the API will not cover a whole calendar day in this timezone. **Advanced** |
|
||||
| country | The country prices should be received for. Use `DE` for Germany or `AT` for Austria. `DE` is the default. |
|
||||
|
||||
### Prices Thing
|
||||
|
||||
The prices thing does not need any configuration.
|
||||
|
||||
### Bestprice Thing
|
||||
|
||||
| Parameter | Description |
|
||||
|-------------|-------------------------------------------------------------------------------------------------------------------------------|
|
||||
| rangeStart | First hour of the time range the binding should search for the best prices. Default: `0` |
|
||||
| rangeDuration | The duration of the time range the binding should search for best prices. Default: `24` |
|
||||
| length | number of best price hours to find within the range. This value has to be at least `1` and below `rangeDuration` Default: `1` |
|
||||
| consecutive | if `true`, the thing identifies the cheapest consecutive range of `length` hours within the lookup range. Otherwise, the thing contains the cheapest `length` hours within the lookup range. Default: `true` |
|
||||
|
||||
#### Limitations
|
||||
|
||||
The channels of a bestprice thing are only defined when the binding has enough data to compute them.
|
||||
The thing is recomputed after the end of the candidate time range for the next day, but only as soon as data for the next day is available from the aWATTar API, which is around 14:00.
|
||||
So for a bestprice thing with `[ rangeStart=5, rangeDuration=5 ]` all channels will be undefined from 10:00 to 14:00.
|
||||
Also, due to the time the aWATTar API delivers the data for the next day, it doesn't make sense to define a thing with `[ rangeStart=12, rangeDuration=20 ]` as the binding will be able to compute the channels only after 14:00.
|
||||
|
||||
## Channels
|
||||
|
||||
### Prices Thing
|
||||
|
||||
For every hour, the `prices` thing provides the following prices:
|
||||
|
||||
| channel | type | description |
|
||||
|----------|--------|------------------------------|
|
||||
| market-net | Number | This net market price per kWh. This is directly taken from the price the aWATTar API delivers. |
|
||||
| market-gross | Number | The market price including VAT, using the defined VAT percentage. |
|
||||
| total-net | Number | Sum of net market price and configured base price |
|
||||
| total-gross | Number | Sum of market and base price with VAT applied. Most probably this is the final price you will have to pay for one kWh in a certain hour |
|
||||
|
||||
|
||||
All prices are available in each of the following channel groups:
|
||||
|
||||
|
||||
| channel group | description |
|
||||
|----------|--------------------------------|
|
||||
| current | The prices for the current hour |
|
||||
| today00, today01, today02 ... today23 | Hourly prices for today. `today00` provides the price from 0:00 to 1:00, `today01` from 1:00 to 02:00 and so on. As long as the API is working, this data should always be available |
|
||||
| tomorrow00, tomorrow01, ... tomorrow23 | Hourly prices for the next day. They should be available starting at 14:00. |
|
||||
|
||||
|
||||
### Bestprice Thing
|
||||
|
||||
| channel | type | description |
|
||||
|----------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| active | Switch | `ON` if the current time is within the bestprice period, `OFF` otherwise. If `consecutive` was set to `false`, this channel may change between `ON` and `OFF` multiple times within the bestprice period. |
|
||||
| start | DateTime | The exact start time of the bestprice range. If `consecutive` was `false`, it is the start time of the first hour found. |
|
||||
| end | DateTime | The exact end time of the bestprice range. If `consecutive` was `false`, it is the end time of the last hour found. |
|
||||
| countdown | Number:Time | The time in minutes until start of the bestprice range. If start time passed. the channel will be set to `UNDEFINED` until the values for the next day are available. |
|
||||
| remaining | Number:Time | The time in minutes until end of the bestprice range. If start time passed. the channel will be set to `UNDEFINED` until the values for the next day are available. |
|
||||
| hours | String | A comma separated list of hours this bestprice period contains. |
|
||||
|
||||
|
||||
|
||||
|
||||
## Full Example
|
||||
|
||||
### Things
|
||||
|
||||
awattar.things:
|
||||
|
||||
```
|
||||
Bridge awattar:bridge:bridge1 "aWATTar Bridge" [ country="DE", vatPercent="19", basePrice="17.22"] {
|
||||
Thing prices price1 "aWATTar Price" []
|
||||
// The car should be loaded for 4 hours during the night
|
||||
Thing bestprice carloader "Car Loader" [ rangeStart="22", rangeDuration="8", length="4", consecutive="true" ]
|
||||
// In the cheapest hour of the night the garden should be watered
|
||||
Thing bestprice water "Water timer" [ rangeStart="19", rangeDuration="12", length="1" ]
|
||||
// The heatpump should run the 12 cheapest hours per day
|
||||
Thing bestprice heatpump "Heat pump" [ length="12", consecutive="false" ]
|
||||
}
|
||||
```
|
||||
|
||||
### Items
|
||||
|
||||
awattar.items:
|
||||
|
||||
```
|
||||
Number:Dimensionless currentnet "Current price [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:current#market-net" }
|
||||
Number:Dimensionless currentgross "Current price [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:current#market-gross" }
|
||||
Number:Dimensionless totalnet "Current price [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:current#total-net" }
|
||||
Number:Dimensionless totalgross "Current price [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:current#total-gross" }
|
||||
Number:Dimensionless totalgross "Current price [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:current#total-gross" }
|
||||
|
||||
Number:Dimensionless today00 "Today 00-01 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today00#total-gross" }
|
||||
Number:Dimensionless today01 "Today 01-02 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today01#total-gross" }
|
||||
Number:Dimensionless today02 "Today 02-03 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today02#total-gross" }
|
||||
Number:Dimensionless today03 "Today 03-04 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today03#total-gross" }
|
||||
Number:Dimensionless today04 "Today 04-05 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today04#total-gross" }
|
||||
Number:Dimensionless today05 "Today 05-06 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today05#total-gross" }
|
||||
Number:Dimensionless today06 "Today 06-07 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today06#total-gross" }
|
||||
Number:Dimensionless today07 "Today 07-08 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today07#total-gross" }
|
||||
Number:Dimensionless today08 "Today 08-09 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today08#total-gross" }
|
||||
Number:Dimensionless today09 "Today 09-10 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today09#total-gross" }
|
||||
Number:Dimensionless today10 "Today 10-11 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today10#total-gross" }
|
||||
Number:Dimensionless today11 "Today 11-12 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today11#total-gross" }
|
||||
Number:Dimensionless today12 "Today 12-13 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today12#total-gross" }
|
||||
Number:Dimensionless today13 "Today 13-14 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today13#total-gross" }
|
||||
Number:Dimensionless today14 "Today 14-15 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today14#total-gross" }
|
||||
Number:Dimensionless today15 "Today 15-16 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today15#total-gross" }
|
||||
Number:Dimensionless today16 "Today 16-17 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today16#total-gross" }
|
||||
Number:Dimensionless today17 "Today 17-18 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today17#total-gross" }
|
||||
Number:Dimensionless today18 "Today 18-19 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today18#total-gross" }
|
||||
Number:Dimensionless today19 "Today 19-20 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today19#total-gross" }
|
||||
Number:Dimensionless today20 "Today 20-21 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today20#total-gross" }
|
||||
Number:Dimensionless today21 "Today 21-22 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today21#total-gross" }
|
||||
Number:Dimensionless today22 "Today 22-23 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today22#total-gross" }
|
||||
Number:Dimensionless today23 "Today 23-00 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:today23#total-gross" }
|
||||
|
||||
Number:Dimensionless tomorrow00 "Tomorrow 00-01 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow00#total-gross" }
|
||||
Number:Dimensionless tomorrow01 "Tomorrow 01-02 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow01#total-gross" }
|
||||
Number:Dimensionless tomorrow02 "Tomorrow 02-03 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow02#total-gross" }
|
||||
Number:Dimensionless tomorrow03 "Tomorrow 03-04 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow03#total-gross" }
|
||||
Number:Dimensionless tomorrow04 "Tomorrow 04-05 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow04#total-gross" }
|
||||
Number:Dimensionless tomorrow05 "Tomorrow 05-06 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow05#total-gross" }
|
||||
Number:Dimensionless tomorrow06 "Tomorrow 06-07 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow06#total-gross" }
|
||||
Number:Dimensionless tomorrow07 "Tomorrow 07-08 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow07#total-gross" }
|
||||
Number:Dimensionless tomorrow08 "Tomorrow 08-09 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow08#total-gross" }
|
||||
Number:Dimensionless tomorrow09 "Tomorrow 09-10 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow09#total-gross" }
|
||||
Number:Dimensionless tomorrow10 "Tomorrow 10-11 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow10#total-gross" }
|
||||
Number:Dimensionless tomorrow11 "Tomorrow 11-12 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow11#total-gross" }
|
||||
Number:Dimensionless tomorrow12 "Tomorrow 12-13 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow12#total-gross" }
|
||||
Number:Dimensionless tomorrow13 "Tomorrow 13-14 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow13#total-gross" }
|
||||
Number:Dimensionless tomorrow14 "Tomorrow 14-15 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow14#total-gross" }
|
||||
Number:Dimensionless tomorrow15 "Tomorrow 15-16 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow15#total-gross" }
|
||||
Number:Dimensionless tomorrow16 "Tomorrow 16-17 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow16#total-gross" }
|
||||
Number:Dimensionless tomorrow17 "Tomorrow 17-18 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow17#total-gross" }
|
||||
Number:Dimensionless tomorrow18 "Tomorrow 18-19 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow18#total-gross" }
|
||||
Number:Dimensionless tomorrow19 "Tomorrow 19-20 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow19#total-gross" }
|
||||
Number:Dimensionless tomorrow20 "Tomorrow 20-21 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow20#total-gross" }
|
||||
Number:Dimensionless tomorrow21 "Tomorrow 21-22 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow21#total-gross" }
|
||||
Number:Dimensionless tomorrow22 "Tomorrow 22-23 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow22#total-gross" }
|
||||
Number:Dimensionless tomorrow23 "Tomorrow 23-00 [%2.2f ct/kWh]" { channel="awattar:prices:bridge1:price1:tomorrow23#total-gross" }
|
||||
|
||||
DateTime CarStart "Start car loader [%1$tH:%1$tM]" { channel="awattar:bestprice:bridge1:carloader:start" }
|
||||
DateTime CarEnd "End car loader [%1$tH:%1$tM]" { channel="awattar:bestprice:bridge1:carloader:end" }
|
||||
String CarCountdown "Countdown for car loader [%s]" { channel="awattar:bestprice:bridge1:carloader:countdown" }
|
||||
String CarHours "Hours for car loader [%s]" { channel="awattar:bestprice:bridge1:carloader:hours" }
|
||||
Switch CarActive { channel="awattar:bestprice:bridge1:carloader:active" }
|
||||
|
||||
Switch WaterActive { channel="awattar:bestprice:bridge1:water:active" }
|
||||
Switch HeatpumpActive { channel="awattar:bestprice:bridge1:heatpump:active" }
|
||||
```
|
||||
|
||||
### Sitemap
|
||||
|
||||
```
|
||||
sitemap default label="aWATTar Sitemap"
|
||||
{
|
||||
Frame label="Car Loader" {
|
||||
Switch item=CarActive
|
||||
Text item=CarCountdown
|
||||
Text item=CarStart
|
||||
Text item=CarEnd
|
||||
Text item=CarHours
|
||||
}
|
||||
Frame label="Current Prices" {
|
||||
Text label="Current Net" item=currentnet
|
||||
Text label="Current Gross" item=currentgross
|
||||
Text label="Total Net" item=totalnet
|
||||
Text label="Total Gross" item=totalgross
|
||||
}
|
||||
Frame label="Todays Prices (total gross)" {
|
||||
Text item=today00
|
||||
Text item=today01
|
||||
Text item=today02
|
||||
Text item=today03
|
||||
Text item=today04
|
||||
Text item=today05
|
||||
Text item=today06
|
||||
Text item=today07
|
||||
Text item=today08
|
||||
Text item=today09
|
||||
Text item=today10
|
||||
Text item=today11
|
||||
Text item=today12
|
||||
Text item=today13
|
||||
Text item=today14
|
||||
Text item=today15
|
||||
Text item=today16
|
||||
Text item=today17
|
||||
Text item=today18
|
||||
Text item=today19
|
||||
Text item=today20
|
||||
Text item=today21
|
||||
Text item=today22
|
||||
Text item=today23
|
||||
}
|
||||
Frame label="Tomorrows Prices (total gross)" {
|
||||
Text item=tomorrow00
|
||||
Text item=tomorrow01
|
||||
Text item=tomorrow02
|
||||
Text item=tomorrow03
|
||||
Text item=tomorrow04
|
||||
Text item=tomorrow05
|
||||
Text item=tomorrow06
|
||||
Text item=tomorrow07
|
||||
Text item=tomorrow08
|
||||
Text item=tomorrow09
|
||||
Text item=tomorrow10
|
||||
Text item=tomorrow11
|
||||
Text item=tomorrow12
|
||||
Text item=tomorrow13
|
||||
Text item=tomorrow14
|
||||
Text item=tomorrow15
|
||||
Text item=tomorrow16
|
||||
Text item=tomorrow17
|
||||
Text item=tomorrow18
|
||||
Text item=tomorrow19
|
||||
Text item=tomorrow20
|
||||
Text item=tomorrow21
|
||||
Text item=tomorrow22
|
||||
Text item=tomorrow23
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Usage hints
|
||||
|
||||
The idea of this binding is to support both automated and non automated components of your home.
|
||||
For automated components, just decide when and how long you want to power them on and use the `active` switch of the bestprice thing to do so.
|
||||
Many non automated components still allow some kind of locally programmed start and end times, e.g. washing machines or dishwashers.
|
||||
So if you know your dishwasher needs less than 3 hour for one run and you want it to be done the next morning, use either the `countdown` or the `remaining` channel of a bestprice thing to determine the best start or end time to select.
|
17
bundles/org.openhab.binding.awattar/pom.xml
Normal file
17
bundles/org.openhab.binding.awattar/pom.xml
Normal file
@ -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.awattar</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: aWATTar Binding</name>
|
||||
|
||||
</project>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.awattar-${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-awattar" description="aWATTar Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.awattar/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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.awattar.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Base class for results
|
||||
*
|
||||
* @author Wolfgang Klimt - initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class AwattarBestPriceResult {
|
||||
|
||||
private long start;
|
||||
private long end;
|
||||
|
||||
public AwattarBestPriceResult() {
|
||||
}
|
||||
|
||||
public long getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public void updateStart(long start) {
|
||||
if (this.start == 0 || this.start > start) {
|
||||
this.start = start;
|
||||
}
|
||||
}
|
||||
|
||||
public long getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public void updateEnd(long end) {
|
||||
if (this.end == 0 || this.end < end) {
|
||||
this.end = end;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract boolean isActive();
|
||||
|
||||
public abstract String getHours();
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.awattar.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Stores the bestprice config
|
||||
*
|
||||
* @author Wolfgang Klimt - initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AwattarBestpriceConfiguration {
|
||||
|
||||
public int rangeStart;
|
||||
public int rangeDuration;
|
||||
public int length;
|
||||
public boolean consecutive;
|
||||
|
||||
public String toString() {
|
||||
return String.format("{ s: %d, d: %d, l: %d, c: %b )", rangeStart, rangeDuration, length, consecutive);
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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.awattar.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.type.ChannelGroupTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link AwattarBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Wolfgang Klimt - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AwattarBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "awattar";
|
||||
public static final String API = "api";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_BRIDGE = new ThingTypeUID(BINDING_ID, "bridge");
|
||||
public static final ThingTypeUID THING_TYPE_PRICE = new ThingTypeUID(BINDING_ID, "prices");
|
||||
public static final ThingTypeUID THING_TYPE_BESTPRICE = new ThingTypeUID(BINDING_ID, "bestprice");
|
||||
public static final ThingTypeUID THING_TYPE_BESTNEXT = new ThingTypeUID(BINDING_ID, "bestnext");
|
||||
|
||||
public static final ChannelGroupTypeUID CHANNEL_GROUP_TYPE_HOURLY_PRICES = new ChannelGroupTypeUID(BINDING_ID,
|
||||
"hourly-prices");
|
||||
|
||||
public static final String CHANNEL_GROUP_CURRENT = "current";
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String CHANNEL_TOTAL_NET = "total-net";
|
||||
public static final String CHANNEL_TOTAL_GROSS = "total-gross";
|
||||
public static final String CHANNEL_MARKET_NET = "market-net";
|
||||
public static final String CHANNEL_MARKET_GROSS = "market-gross";
|
||||
|
||||
public static final String CHANNEL_ACTIVE = "active";
|
||||
public static final String CHANNEL_START = "start";
|
||||
public static final String CHANNEL_END = "end";
|
||||
public static final String CHANNEL_COUNTDOWN = "countdown";
|
||||
public static final String CHANNEL_REMAINING = "remaining";
|
||||
public static final String CHANNEL_HOURS = "hours";
|
||||
public static final String CHANNEL_DURATION = "rangeDuration";
|
||||
public static final String CHANNEL_LOOKUP_HOURS = "lookupHours";
|
||||
public static final String CHANNEL_CONSECUTIVE = "consecutive";
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.awattar.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Stores the bridge configuration
|
||||
*
|
||||
* @author Wolfgang Klimt - initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AwattarBridgeConfiguration {
|
||||
|
||||
public double basePrice;
|
||||
public double vatPercent;
|
||||
public String country = "";
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* 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.awattar.internal;
|
||||
|
||||
import static org.openhab.binding.awattar.internal.AwattarUtil.formatDate;
|
||||
import static org.openhab.binding.awattar.internal.AwattarUtil.getHourFrom;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Stores a consecutive bestprice result
|
||||
*
|
||||
* @author Wolfgang Klimt - initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AwattarConsecutiveBestPriceResult extends AwattarBestPriceResult {
|
||||
|
||||
private double priceSum = 0;
|
||||
private int length = 0;
|
||||
private String hours;
|
||||
private ZoneId zoneId;
|
||||
|
||||
public AwattarConsecutiveBestPriceResult(List<AwattarPrice> prices, ZoneId zoneId) {
|
||||
super();
|
||||
this.zoneId = zoneId;
|
||||
StringBuilder hours = new StringBuilder();
|
||||
boolean second = false;
|
||||
for (AwattarPrice price : prices) {
|
||||
priceSum += price.getPrice();
|
||||
length++;
|
||||
updateStart(price.getStartTimestamp());
|
||||
updateEnd(price.getEndTimestamp());
|
||||
if (second) {
|
||||
hours.append(',');
|
||||
}
|
||||
hours.append(getHourFrom(price.getStartTimestamp(), zoneId));
|
||||
second = true;
|
||||
}
|
||||
this.hours = hours.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return contains(Instant.now().toEpochMilli());
|
||||
}
|
||||
|
||||
public boolean contains(long timestamp) {
|
||||
return timestamp >= getStart() && timestamp < getEnd();
|
||||
}
|
||||
|
||||
public double getPriceSum() {
|
||||
return priceSum;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("{%s, %s, %.2f}", formatDate(getStart(), zoneId), formatDate(getEnd(), zoneId),
|
||||
priceSum / length);
|
||||
}
|
||||
|
||||
public String getHours() {
|
||||
return hours;
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* 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.awattar.internal;
|
||||
|
||||
import static org.openhab.binding.awattar.internal.AwattarUtil.*;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Stores a non consecutive bestprice result
|
||||
*
|
||||
* @author Wolfgang Klimt - initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AwattarNonConsecutiveBestPriceResult extends AwattarBestPriceResult {
|
||||
|
||||
private List<AwattarPrice> members;
|
||||
private ZoneId zoneId;
|
||||
private boolean sorted = true;
|
||||
|
||||
public AwattarNonConsecutiveBestPriceResult(int size, ZoneId zoneId) {
|
||||
super();
|
||||
this.zoneId = zoneId;
|
||||
members = new ArrayList<AwattarPrice>();
|
||||
}
|
||||
|
||||
public void addMember(AwattarPrice member) {
|
||||
sorted = false;
|
||||
members.add(member);
|
||||
updateStart(member.getStartTimestamp());
|
||||
updateEnd(member.getEndTimestamp());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return members.stream().anyMatch(x -> x.contains(Instant.now().toEpochMilli()));
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("NonConsecutiveBestpriceResult with %s", members.toString());
|
||||
}
|
||||
|
||||
private void sort() {
|
||||
if (!sorted) {
|
||||
members.sort(new Comparator<AwattarPrice>() {
|
||||
@Override
|
||||
public int compare(AwattarPrice o1, AwattarPrice o2) {
|
||||
return Long.compare(o1.getStartTimestamp(), o2.getStartTimestamp());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public String getHours() {
|
||||
boolean second = false;
|
||||
sort();
|
||||
StringBuilder res = new StringBuilder();
|
||||
for (AwattarPrice price : members) {
|
||||
if (second) {
|
||||
res.append(',');
|
||||
}
|
||||
res.append(getHourFrom(price.getStartTimestamp(), zoneId));
|
||||
second = true;
|
||||
}
|
||||
return res.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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.awattar.internal;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* Class to store hourly price data.
|
||||
*
|
||||
* @author Wolfgang Klimt - initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AwattarPrice implements Comparable<AwattarPrice> {
|
||||
private final Double price;
|
||||
private final long endTimestamp;
|
||||
private final long startTimestamp;
|
||||
|
||||
private final int hour;
|
||||
|
||||
public AwattarPrice(double price, long startTimestamp, long endTimestamp, ZoneId zoneId) {
|
||||
this.price = price;
|
||||
this.endTimestamp = endTimestamp;
|
||||
this.startTimestamp = startTimestamp;
|
||||
this.hour = ZonedDateTime.ofInstant(Instant.ofEpochMilli(startTimestamp), zoneId).getHour();
|
||||
}
|
||||
|
||||
public long getStartTimestamp() {
|
||||
return startTimestamp;
|
||||
}
|
||||
|
||||
public long getEndTimestamp() {
|
||||
return endTimestamp;
|
||||
}
|
||||
|
||||
public double getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("(%1$tF %1$tR - %2$tR: %3$.3f)", startTimestamp, endTimestamp, getPrice());
|
||||
}
|
||||
|
||||
public int getHour() {
|
||||
return hour;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(AwattarPrice o) {
|
||||
return price.compareTo(o.price);
|
||||
}
|
||||
|
||||
public boolean isBetween(long start, long end) {
|
||||
return startTimestamp >= start && endTimestamp <= end;
|
||||
}
|
||||
|
||||
public boolean contains(long timestamp) {
|
||||
return startTimestamp <= timestamp && endTimestamp > timestamp;
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 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.awattar.internal;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
import javax.measure.quantity.Time;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
|
||||
/**
|
||||
* Some utility methods
|
||||
*
|
||||
* @author Wolfgang Klimt - initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AwattarUtil {
|
||||
|
||||
public static long getMillisToNextMinute(int mod, TimeZoneProvider timeZoneProvider) {
|
||||
long now = Instant.now().toEpochMilli();
|
||||
ZonedDateTime dt = ZonedDateTime.now(timeZoneProvider.getTimeZone()).truncatedTo(ChronoUnit.MINUTES);
|
||||
int min = dt.getMinute();
|
||||
int offset = min % mod;
|
||||
offset = offset == 0 ? mod : offset;
|
||||
dt = dt.plusMinutes(offset);
|
||||
long result = dt.toInstant().toEpochMilli() - now;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ZonedDateTime getCalendarForHour(int hour, ZoneId zone) {
|
||||
return ZonedDateTime.now(zone).truncatedTo(ChronoUnit.DAYS).plus(hour, ChronoUnit.HOURS);
|
||||
}
|
||||
|
||||
public static DateTimeType getDateTimeType(long time, TimeZoneProvider tz) {
|
||||
return new DateTimeType(ZonedDateTime.ofInstant(Instant.ofEpochMilli(time), tz.getTimeZone()));
|
||||
}
|
||||
|
||||
public static QuantityType<Time> getDuration(long millis) {
|
||||
long minutes = millis / 60000;
|
||||
return QuantityType.valueOf(minutes, Units.MINUTE);
|
||||
}
|
||||
|
||||
public static String formatDate(long date, ZoneId zoneId) {
|
||||
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(date), zoneId).toString();
|
||||
}
|
||||
|
||||
public static String getHourFrom(long timestamp, ZoneId zoneId) {
|
||||
ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.ofEpochMilli(timestamp), zoneId);
|
||||
return String.format("%02d", zdt.getHour());
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* 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.awattar.internal.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Represents data from aWATTar API
|
||||
*
|
||||
* @author Wolfgang Klimt - initial contribution
|
||||
*/
|
||||
public class AwattarApiData {
|
||||
|
||||
@SerializedName("data")
|
||||
@Expose
|
||||
public List<Datum> data = null;
|
||||
@SerializedName("object")
|
||||
@Expose
|
||||
public String object;
|
||||
@SerializedName("url")
|
||||
@Expose
|
||||
public String url;
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.awattar.internal.dto;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Represents a Datum
|
||||
*
|
||||
* @author Wolfgang Klimt - initial contribution
|
||||
*/
|
||||
public class Datum {
|
||||
|
||||
@SerializedName("end_timestamp")
|
||||
@Expose
|
||||
public long endTimestamp;
|
||||
@SerializedName("marketprice")
|
||||
@Expose
|
||||
public double marketprice;
|
||||
@SerializedName("start_timestamp")
|
||||
@Expose
|
||||
public long startTimestamp;
|
||||
@SerializedName("unit")
|
||||
@Expose
|
||||
public String unit;
|
||||
}
|
@ -0,0 +1,266 @@
|
||||
/**
|
||||
* 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.awattar.internal.handler;
|
||||
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.BINDING_ID;
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_ACTIVE;
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_COUNTDOWN;
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_END;
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_HOURS;
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_REMAINING;
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_START;
|
||||
import static org.openhab.binding.awattar.internal.AwattarUtil.getCalendarForHour;
|
||||
import static org.openhab.binding.awattar.internal.AwattarUtil.getDateTimeType;
|
||||
import static org.openhab.binding.awattar.internal.AwattarUtil.getDuration;
|
||||
import static org.openhab.binding.awattar.internal.AwattarUtil.getMillisToNextMinute;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.SortedMap;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.awattar.internal.AwattarBestPriceResult;
|
||||
import org.openhab.binding.awattar.internal.AwattarBestpriceConfiguration;
|
||||
import org.openhab.binding.awattar.internal.AwattarConsecutiveBestPriceResult;
|
||||
import org.openhab.binding.awattar.internal.AwattarNonConsecutiveBestPriceResult;
|
||||
import org.openhab.binding.awattar.internal.AwattarPrice;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Channel;
|
||||
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.thing.type.ChannelKind;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link AwattarBestpriceHandler} is responsible for computing the best prices for a given configuration.
|
||||
*
|
||||
* @author Wolfgang Klimt - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AwattarBestpriceHandler extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AwattarBestpriceHandler.class);
|
||||
|
||||
private final int thingRefreshInterval = 60;
|
||||
@Nullable
|
||||
private ScheduledFuture<?> thingRefresher;
|
||||
|
||||
private final TimeZoneProvider timeZoneProvider;
|
||||
|
||||
public AwattarBestpriceHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
|
||||
super(thing);
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
AwattarBestpriceConfiguration config = getConfigAs(AwattarBestpriceConfiguration.class);
|
||||
|
||||
boolean configValid = true;
|
||||
|
||||
if (config.length >= config.rangeDuration) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/error.length.value");
|
||||
configValid = false;
|
||||
}
|
||||
|
||||
if (!configValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
ScheduledFuture<?> localRefresher = thingRefresher;
|
||||
if (localRefresher == null || localRefresher.isCancelled()) {
|
||||
/*
|
||||
* The scheduler is required to run exactly at minute borders, hence we can't use scheduleWithFixedDelay
|
||||
* here
|
||||
*/
|
||||
thingRefresher = scheduler.scheduleAtFixedRate(this::refreshChannels,
|
||||
getMillisToNextMinute(1, timeZoneProvider), thingRefreshInterval * 1000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
ScheduledFuture<?> localRefresher = thingRefresher;
|
||||
if (localRefresher != null) {
|
||||
localRefresher.cancel(true);
|
||||
thingRefresher = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshChannels() {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
for (Channel channel : getThing().getChannels()) {
|
||||
ChannelUID channelUID = channel.getUID();
|
||||
if (ChannelKind.STATE.equals(channel.getKind()) && isLinked(channelUID)) {
|
||||
refreshChannel(channel.getUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshChannel(ChannelUID channelUID) {
|
||||
State state = UnDefType.UNDEF;
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/error.bridge.missing");
|
||||
updateState(channelUID, state);
|
||||
return;
|
||||
}
|
||||
AwattarBridgeHandler bridgeHandler = (AwattarBridgeHandler) bridge.getHandler();
|
||||
if (bridgeHandler == null || bridgeHandler.getPriceMap() == null) {
|
||||
logger.debug("No prices available, so can't refresh channel.");
|
||||
// no prices available, can't continue
|
||||
updateState(channelUID, state);
|
||||
return;
|
||||
}
|
||||
AwattarBestpriceConfiguration config = getConfigAs(AwattarBestpriceConfiguration.class);
|
||||
Timerange timerange = getRange(config.rangeStart, config.rangeDuration, bridgeHandler.getTimeZone());
|
||||
if (!(bridgeHandler.containsPriceFor(timerange.start) && bridgeHandler.containsPriceFor(timerange.end))) {
|
||||
updateState(channelUID, state);
|
||||
return;
|
||||
}
|
||||
|
||||
AwattarBestPriceResult result;
|
||||
if (config.consecutive) {
|
||||
ArrayList<AwattarPrice> range = new ArrayList<AwattarPrice>(config.rangeDuration);
|
||||
range.addAll(getPriceRange(bridgeHandler, timerange,
|
||||
(o1, o2) -> Long.compare(o1.getStartTimestamp(), o2.getStartTimestamp())));
|
||||
AwattarConsecutiveBestPriceResult res = new AwattarConsecutiveBestPriceResult(
|
||||
range.subList(0, config.length), bridgeHandler.getTimeZone());
|
||||
|
||||
for (int i = 1; i <= range.size() - config.length; i++) {
|
||||
AwattarConsecutiveBestPriceResult res2 = new AwattarConsecutiveBestPriceResult(
|
||||
range.subList(i, i + config.length), bridgeHandler.getTimeZone());
|
||||
if (res2.getPriceSum() < res.getPriceSum()) {
|
||||
res = res2;
|
||||
}
|
||||
}
|
||||
result = res;
|
||||
} else {
|
||||
List<AwattarPrice> range = getPriceRange(bridgeHandler, timerange,
|
||||
(o1, o2) -> Double.compare(o1.getPrice(), o2.getPrice()));
|
||||
AwattarNonConsecutiveBestPriceResult res = new AwattarNonConsecutiveBestPriceResult(config.length,
|
||||
bridgeHandler.getTimeZone());
|
||||
int ct = 0;
|
||||
for (AwattarPrice price : range) {
|
||||
res.addMember(price);
|
||||
if (++ct >= config.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
result = res;
|
||||
}
|
||||
String channelId = channelUID.getIdWithoutGroup();
|
||||
long diff;
|
||||
switch (channelId) {
|
||||
case CHANNEL_ACTIVE:
|
||||
state = OnOffType.from(result.isActive());
|
||||
break;
|
||||
case CHANNEL_START:
|
||||
state = getDateTimeType(result.getStart(), timeZoneProvider);
|
||||
break;
|
||||
case CHANNEL_END:
|
||||
state = getDateTimeType(result.getEnd(), timeZoneProvider);
|
||||
break;
|
||||
case CHANNEL_COUNTDOWN:
|
||||
diff = result.getStart() - Instant.now().toEpochMilli();
|
||||
if (diff >= 0) {
|
||||
state = getDuration(diff);
|
||||
}
|
||||
break;
|
||||
case CHANNEL_REMAINING:
|
||||
diff = result.getEnd() - Instant.now().toEpochMilli();
|
||||
if (result.isActive()) {
|
||||
state = getDuration(diff);
|
||||
}
|
||||
break;
|
||||
case CHANNEL_HOURS:
|
||||
state = new StringType(result.getHours());
|
||||
break;
|
||||
default:
|
||||
logger.warn("Unknown channel id {} for Thing type {}", channelUID, getThing().getThingTypeUID());
|
||||
}
|
||||
updateState(channelUID, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
refreshChannel(channelUID);
|
||||
} else {
|
||||
logger.debug("Binding {} only supports refresh command", BINDING_ID);
|
||||
}
|
||||
}
|
||||
|
||||
private List<AwattarPrice> getPriceRange(AwattarBridgeHandler bridgeHandler, Timerange range,
|
||||
Comparator<AwattarPrice> comparator) {
|
||||
ArrayList<AwattarPrice> result = new ArrayList<>();
|
||||
SortedMap<Long, AwattarPrice> priceMap = bridgeHandler.getPriceMap();
|
||||
if (priceMap == null) {
|
||||
logger.debug("No prices available, can't compute ranges");
|
||||
return result;
|
||||
}
|
||||
result.addAll(priceMap.values().stream().filter(x -> x.isBetween(range.start, range.end))
|
||||
.collect(Collectors.toSet()));
|
||||
result.sort(comparator);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Timerange getRange(int start, int duration, ZoneId zoneId) {
|
||||
ZonedDateTime startCal = getCalendarForHour(start, zoneId);
|
||||
ZonedDateTime endCal = startCal.plusHours(duration);
|
||||
ZonedDateTime now = ZonedDateTime.now(zoneId);
|
||||
if (now.getHour() < start) {
|
||||
// we are before the range, so we might be still within the last range
|
||||
startCal = startCal.minusDays(1);
|
||||
endCal = endCal.minusDays(1);
|
||||
}
|
||||
if (endCal.toInstant().toEpochMilli() < Instant.now().toEpochMilli()) {
|
||||
// span is in the past, add one day
|
||||
startCal = startCal.plusDays(1);
|
||||
endCal = endCal.plusDays(1);
|
||||
}
|
||||
return new Timerange(startCal.toInstant().toEpochMilli(), endCal.toInstant().toEpochMilli());
|
||||
}
|
||||
|
||||
private class Timerange {
|
||||
long start;
|
||||
long end;
|
||||
|
||||
Timerange(long start, long end) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,268 @@
|
||||
/**
|
||||
* 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.awattar.internal.handler;
|
||||
|
||||
import static org.eclipse.jetty.http.HttpMethod.GET;
|
||||
import static org.eclipse.jetty.http.HttpStatus.OK_200;
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.BINDING_ID;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
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.openhab.binding.awattar.internal.AwattarBridgeConfiguration;
|
||||
import org.openhab.binding.awattar.internal.AwattarPrice;
|
||||
import org.openhab.binding.awattar.internal.dto.AwattarApiData;
|
||||
import org.openhab.binding.awattar.internal.dto.Datum;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
* The {@link AwattarBridgeHandler} is responsible for retrieving data from the aWATTar API.
|
||||
*
|
||||
* The API provides hourly prices for the current day and, starting from 14:00, hourly prices for the next day.
|
||||
* Check the documentation at https://www.awattar.de/services/api
|
||||
*
|
||||
*
|
||||
*
|
||||
* @author Wolfgang Klimt - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AwattarBridgeHandler extends BaseBridgeHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(AwattarBridgeHandler.class);
|
||||
private final HttpClient httpClient;
|
||||
@Nullable
|
||||
private ScheduledFuture<?> dataRefresher;
|
||||
|
||||
private static final String URLDE = "https://api.awattar.de/v1/marketdata";
|
||||
private static final String URLAT = "https://api.awattar.at/v1/marketdata";
|
||||
private String url;
|
||||
|
||||
// This cache stores price data for up to two days
|
||||
@Nullable
|
||||
private SortedMap<Long, AwattarPrice> priceMap;
|
||||
private final int dataRefreshInterval = 60;
|
||||
private double vatFactor = 0;
|
||||
private long lastUpdated = 0;
|
||||
private double basePrice = 0;
|
||||
private long minTimestamp = 0;
|
||||
private long maxTimestamp = 0;
|
||||
private ZoneId zone;
|
||||
private TimeZoneProvider timeZoneProvider;
|
||||
|
||||
public AwattarBridgeHandler(Bridge thing, HttpClient httpClient, TimeZoneProvider timeZoneProvider) {
|
||||
super(thing);
|
||||
this.httpClient = httpClient;
|
||||
url = URLDE;
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
zone = timeZoneProvider.getTimeZone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
AwattarBridgeConfiguration config = getConfigAs(AwattarBridgeConfiguration.class);
|
||||
vatFactor = 1 + (config.vatPercent / 100);
|
||||
basePrice = config.basePrice;
|
||||
zone = timeZoneProvider.getTimeZone();
|
||||
switch (config.country) {
|
||||
case "DE":
|
||||
url = URLDE;
|
||||
break;
|
||||
case "AT":
|
||||
url = URLAT;
|
||||
break;
|
||||
default:
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/error.unsupported.country");
|
||||
return;
|
||||
}
|
||||
|
||||
dataRefresher = scheduler.scheduleWithFixedDelay(this::refreshIfNeeded, 0, dataRefreshInterval * 1000,
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
ScheduledFuture<?> localRefresher = dataRefresher;
|
||||
if (localRefresher != null) {
|
||||
localRefresher.cancel(true);
|
||||
}
|
||||
dataRefresher = null;
|
||||
priceMap = null;
|
||||
lastUpdated = 0;
|
||||
}
|
||||
|
||||
public void refreshIfNeeded() {
|
||||
if (needRefresh()) {
|
||||
refresh();
|
||||
}
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
private void getPrices() {
|
||||
try {
|
||||
// we start one day in the past to cover ranges that already started yesterday
|
||||
ZonedDateTime zdt = LocalDate.now(zone).atStartOfDay(zone).minusDays(1);
|
||||
long start = zdt.toInstant().toEpochMilli();
|
||||
// Starting from midnight yesterday we add three days so that the range covers the whole next day.
|
||||
zdt = zdt.plusDays(3);
|
||||
long end = zdt.toInstant().toEpochMilli();
|
||||
|
||||
StringBuilder request = new StringBuilder(url);
|
||||
request.append("?start=").append(start).append("&end=").append(end);
|
||||
|
||||
logger.trace("aWATTar API request: = '{}'", request);
|
||||
ContentResponse contentResponse = httpClient.newRequest(request.toString()).method(GET)
|
||||
.timeout(10, TimeUnit.SECONDS).send();
|
||||
int httpStatus = contentResponse.getStatus();
|
||||
String content = contentResponse.getContentAsString();
|
||||
logger.trace("aWATTar API response: status = {}, content = '{}'", httpStatus, content);
|
||||
|
||||
switch (httpStatus) {
|
||||
case OK_200:
|
||||
Gson gson = new Gson();
|
||||
SortedMap<Long, AwattarPrice> result = new TreeMap<>();
|
||||
minTimestamp = 0;
|
||||
maxTimestamp = 0;
|
||||
AwattarApiData apiData = gson.fromJson(content, AwattarApiData.class);
|
||||
if (apiData != null) {
|
||||
for (Datum d : apiData.data) {
|
||||
result.put(d.startTimestamp,
|
||||
new AwattarPrice(d.marketprice / 10.0, d.startTimestamp, d.endTimestamp, zone));
|
||||
updateMin(d.startTimestamp);
|
||||
updateMax(d.endTimestamp);
|
||||
}
|
||||
priceMap = result;
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
lastUpdated = Instant.now().toEpochMilli();
|
||||
} else {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"@text/error.invalid.data");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"@text/warn.awattar.statuscode");
|
||||
}
|
||||
} catch (JsonSyntaxException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/error.json");
|
||||
} catch (InterruptedException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/error.interrupted");
|
||||
} catch (ExecutionException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/error.execution");
|
||||
} catch (TimeoutException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/error.timeout");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean needRefresh() {
|
||||
if (getThing().getStatus() != ThingStatus.ONLINE) {
|
||||
return true;
|
||||
}
|
||||
SortedMap<Long, AwattarPrice> localMap = priceMap;
|
||||
if (localMap == null) {
|
||||
return true;
|
||||
}
|
||||
return localMap.lastKey() < Instant.now().toEpochMilli() + 9 * 3600 * 1000;
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
getPrices();
|
||||
}
|
||||
|
||||
public double getVatFactor() {
|
||||
return vatFactor;
|
||||
}
|
||||
|
||||
public double getBasePrice() {
|
||||
return basePrice;
|
||||
}
|
||||
|
||||
public long getLastUpdated() {
|
||||
return lastUpdated;
|
||||
}
|
||||
|
||||
public ZoneId getTimeZone() {
|
||||
return zone;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public synchronized SortedMap<Long, AwattarPrice> getPriceMap() {
|
||||
if (priceMap == null) {
|
||||
refresh();
|
||||
}
|
||||
return priceMap;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AwattarPrice getPriceFor(long timestamp) {
|
||||
SortedMap<Long, AwattarPrice> priceMap = getPriceMap();
|
||||
if (priceMap == null) {
|
||||
return null;
|
||||
}
|
||||
if (!containsPriceFor(timestamp)) {
|
||||
return null;
|
||||
}
|
||||
for (AwattarPrice price : priceMap.values()) {
|
||||
if (timestamp >= price.getStartTimestamp() && timestamp < price.getEndTimestamp()) {
|
||||
return price;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean containsPriceFor(long timestamp) {
|
||||
return minTimestamp <= timestamp && maxTimestamp >= timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
refresh();
|
||||
} else {
|
||||
logger.debug("Binding {} only supports refresh command", BINDING_ID);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMin(long ts) {
|
||||
minTimestamp = (minTimestamp == 0) ? ts : Math.min(minTimestamp, ts);
|
||||
}
|
||||
|
||||
private void updateMax(long ts) {
|
||||
maxTimestamp = (maxTimestamp == 0) ? ts : Math.max(ts, maxTimestamp);
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* 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.awattar.internal.handler;
|
||||
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.THING_TYPE_BESTPRICE;
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.THING_TYPE_BRIDGE;
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.THING_TYPE_PRICE;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
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;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link AwattarHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Wolfgang Klimt - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.awattar", service = ThingHandlerFactory.class)
|
||||
public class AwattarHandlerFactory extends BaseThingHandlerFactory {
|
||||
private Logger logger = LoggerFactory.getLogger(AwattarHandlerFactory.class);
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_PRICE, THING_TYPE_BESTPRICE,
|
||||
THING_TYPE_BRIDGE);
|
||||
private final HttpClient httpClient;
|
||||
private final TimeZoneProvider timeZoneProvider;
|
||||
|
||||
@Activate
|
||||
public AwattarHandlerFactory(final @Reference HttpClientFactory httpClientFactory,
|
||||
final @Reference TimeZoneProvider timeZoneProvider) {
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@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_BRIDGE.equals(thingTypeUID)) {
|
||||
return new AwattarBridgeHandler((Bridge) thing, httpClient, timeZoneProvider);
|
||||
}
|
||||
if (THING_TYPE_PRICE.equals(thingTypeUID)) {
|
||||
return new AwattarPriceHandler(thing, timeZoneProvider);
|
||||
}
|
||||
if (THING_TYPE_BESTPRICE.equals(thingTypeUID)) {
|
||||
return new AwattarBestpriceHandler(thing, timeZoneProvider);
|
||||
}
|
||||
|
||||
logger.warn("Unknown thing type {}, not creating handler!", thingTypeUID);
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
/**
|
||||
* 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.awattar.internal.handler;
|
||||
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.BINDING_ID;
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_GROUP_CURRENT;
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_MARKET_GROSS;
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_MARKET_NET;
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_TOTAL_GROSS;
|
||||
import static org.openhab.binding.awattar.internal.AwattarBindingConstants.CHANNEL_TOTAL_NET;
|
||||
import static org.openhab.binding.awattar.internal.AwattarUtil.getCalendarForHour;
|
||||
import static org.openhab.binding.awattar.internal.AwattarUtil.getMillisToNextMinute;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.ZonedDateTime;
|
||||
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.awattar.internal.AwattarPrice;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Channel;
|
||||
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.thing.type.ChannelKind;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link AwattarPriceHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Wolfgang Klimt - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class AwattarPriceHandler extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(AwattarPriceHandler.class);
|
||||
|
||||
private int thingRefreshInterval = 60;
|
||||
private TimeZoneProvider timeZoneProvider;
|
||||
private @Nullable ScheduledFuture<?> thingRefresher;
|
||||
|
||||
public AwattarPriceHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
|
||||
super(thing);
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (command instanceof RefreshType) {
|
||||
refreshChannel(channelUID);
|
||||
} else {
|
||||
logger.debug("Binding {} only supports refresh command", BINDING_ID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the binding and start the refresh job.
|
||||
* The refresh job runs once after initialization and afterwards every hour.
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
synchronized (this) {
|
||||
ScheduledFuture<?> localRefresher = thingRefresher;
|
||||
if (localRefresher == null || localRefresher.isCancelled()) {
|
||||
/*
|
||||
* The scheduler is required to run exactly at minute borders, hence we can't use scheduleWithFixedDelay
|
||||
* here
|
||||
*/
|
||||
thingRefresher = scheduler.scheduleAtFixedRate(this::refreshChannels,
|
||||
getMillisToNextMinute(1, timeZoneProvider), thingRefreshInterval * 1000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
ScheduledFuture<?> localRefresher = thingRefresher;
|
||||
if (localRefresher != null) {
|
||||
localRefresher.cancel(true);
|
||||
thingRefresher = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshChannels() {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
for (Channel channel : getThing().getChannels()) {
|
||||
ChannelUID channelUID = channel.getUID();
|
||||
if (ChannelKind.STATE.equals(channel.getKind()) && channelUID.isInGroup() && channelUID.getGroupId() != null
|
||||
&& isLinked(channelUID)) {
|
||||
refreshChannel(channel.getUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshChannel(ChannelUID channelUID) {
|
||||
State state = UnDefType.UNDEF;
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/error.bridge.missing");
|
||||
return;
|
||||
}
|
||||
AwattarBridgeHandler bridgeHandler = (AwattarBridgeHandler) bridge.getHandler();
|
||||
if (bridgeHandler == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/error.bridge.missing");
|
||||
return;
|
||||
}
|
||||
String group = channelUID.getGroupId();
|
||||
if (group == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"@text/error.channelgroup.missing");
|
||||
return;
|
||||
}
|
||||
|
||||
ZonedDateTime target;
|
||||
|
||||
if (group.equals(CHANNEL_GROUP_CURRENT)) {
|
||||
target = ZonedDateTime.now(bridgeHandler.getTimeZone());
|
||||
} else if (group.startsWith("today")) {
|
||||
target = getCalendarForHour(Integer.valueOf(group.substring(5)), bridgeHandler.getTimeZone());
|
||||
} else if (group.startsWith("tomorrow")) {
|
||||
target = getCalendarForHour(Integer.valueOf(group.substring(8)), bridgeHandler.getTimeZone()).plusDays(1);
|
||||
} else {
|
||||
logger.warn("Unsupported channel group {}", group);
|
||||
updateState(channelUID, state);
|
||||
return;
|
||||
}
|
||||
|
||||
AwattarPrice price = bridgeHandler.getPriceFor(target.toInstant().toEpochMilli());
|
||||
|
||||
if (price == null) {
|
||||
logger.trace("No price found for hour {}", target.toString());
|
||||
updateState(channelUID, state);
|
||||
return;
|
||||
}
|
||||
double currentprice = price.getPrice();
|
||||
|
||||
String channelId = channelUID.getIdWithoutGroup();
|
||||
switch (channelId) {
|
||||
case CHANNEL_MARKET_NET:
|
||||
state = toDecimalType(currentprice);
|
||||
break;
|
||||
case CHANNEL_MARKET_GROSS:
|
||||
state = toDecimalType(currentprice * bridgeHandler.getVatFactor());
|
||||
break;
|
||||
case CHANNEL_TOTAL_NET:
|
||||
state = toDecimalType(currentprice + bridgeHandler.getBasePrice());
|
||||
break;
|
||||
case CHANNEL_TOTAL_GROSS:
|
||||
state = toDecimalType((currentprice + bridgeHandler.getBasePrice()) * bridgeHandler.getVatFactor());
|
||||
break;
|
||||
default:
|
||||
logger.warn("Unknown channel id {} for Thing type {}", channelUID, getThing().getThingTypeUID());
|
||||
}
|
||||
updateState(channelUID, state);
|
||||
}
|
||||
|
||||
private DecimalType toDecimalType(Double value) {
|
||||
BigDecimal bd = BigDecimal.valueOf(value);
|
||||
return new DecimalType(bd.setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="awattar" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>aWATTar Binding</name>
|
||||
<description>Hourly Electricity Prices for Germany and Austria.</description>
|
||||
|
||||
</binding:binding>
|
@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="bridge-type:awattar:bridge">
|
||||
<parameter name="country" type="text" pattern="DE|AT">
|
||||
<label>Country</label>
|
||||
<default>DE</default>
|
||||
<description>Country to get prices for. Only DE (Germany) and AT (Austria) are supported.</description>
|
||||
<options>
|
||||
<option value="DE">DE</option>
|
||||
<option value="AT">AT</option>
|
||||
</options>
|
||||
</parameter>
|
||||
<parameter name="vatPercent" type="decimal">
|
||||
<label>VAT Percent</label>
|
||||
<description>Specifies the value added tax percentage</description>
|
||||
<default>19</default>
|
||||
</parameter>
|
||||
<parameter name="basePrice" type="decimal">
|
||||
<label>Base Price</label>
|
||||
<description>Specifies the net base price per kWh</description>
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<config-description uri="thing-type:awattar:bestprice">
|
||||
<parameter name="rangeStart" type="integer" min="0" max="23">
|
||||
<label>Range Start</label>
|
||||
<description>Earliest possible hour of bestprice period.</description>
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
<parameter name="rangeDuration" type="integer" min="1" max="24">
|
||||
<label>Range Duration</label>
|
||||
<description>Duration of bestprice candidate range</description>
|
||||
<default>24</default>
|
||||
</parameter>
|
||||
<parameter name="length" type="integer" min="1" max="23">
|
||||
<label>Length</label>
|
||||
<description>The number of hours the bestprice period should last</description>
|
||||
<default>1</default>
|
||||
</parameter>
|
||||
<parameter name="consecutive" type="boolean">
|
||||
<label>Consecutive</label>
|
||||
<description>Do the hours need to be consecutive?</description>
|
||||
<default>true</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,171 @@
|
||||
binding.awattar.name = aWATTar Binding
|
||||
binding.awattar.description = Hourly market electricity prices for Germany and Austria
|
||||
|
||||
bridge-type.awattar.bridge.label = aWATTar Bridge
|
||||
bridge-type.awattar.bridge.description = Provides price data from the aWATTar API.
|
||||
|
||||
bridge-type.config.awattar.bridge.country.label = Country
|
||||
bridge-type.config.awattar.bridge.country.description = Country to get prices for. Only DE (Germany) and AT (Austria) are supported
|
||||
bridge-type.config.awattar.bridge.vatPercent.label = VAT Percent
|
||||
bridge-type.config.awattar.bridge.vatPercent.description = Specifies the value added tax percentage
|
||||
bridge-type.config.awattar.bridge.basePrice.label = Base price
|
||||
bridge-type.config.awattar.bridge.basePrice.description = Specifies the net base price per kWh
|
||||
bridge-type.config.awattar.bridge.timeZone.label = Time zone
|
||||
bridge-type.config.awattar.bridge.timeZone.description = Time zone to apply to the hour definitions. Default CET aligns to the aWATTar API
|
||||
|
||||
# thing types
|
||||
thing-type.awattar.prices.label = aWATTar Hourly Prices
|
||||
thing-type.awattar.prices.description = Prices for one kilowatt-hour at the given hour in Cent
|
||||
thing-type.awattar.bestprice.label = Best price
|
||||
thing-type.awattar.bestprice.description = Evaluates the lowest price period for the given settings
|
||||
|
||||
# thing type config description
|
||||
thing-type.config.awattar.bestprice.rangeStart.label = Range Start
|
||||
thing-type.config.awattar.bestprice.rangeStart.description = Earliest possible hour of bestprice period.
|
||||
thing-type.config.awattar.bestprice.rangeDuration.label = Range duration
|
||||
thing-type.config.awattar.bestprice.rangeDuration.description = Duration of bestprice candidate range
|
||||
thing-type.config.awattar.bestprice.length.label = Length
|
||||
thing-type.config.awattar.bestprice.length.description = The number of hours the bestprice period should last
|
||||
thing-type.config.awattar.bestprice.consecutive.label = Consecutive
|
||||
thing-type.config.awattar.bestprice.consecutive.description = Do the hours need to be consecutive?
|
||||
|
||||
# channel types
|
||||
channel-type.awattar.price.label = ct/kWh
|
||||
channel-type.awattar.price.description = Price in ct/kWh
|
||||
channel-type.awattar.input-duration.label = Duration
|
||||
channel-type.awattar.input-duration.description = Length of the bestprice period to search for (hours)
|
||||
channel-type.awattar.input-hours.label = Lookup time
|
||||
channel-type.awattar.input-hours.description = How many hours from now should be checked?
|
||||
channel-type.awattar.input-switch.label = Consecutive
|
||||
channel-type.awattar.input-switch.description = Consecutive range needed?
|
||||
channel-type.awattar.switch-type.label = Active
|
||||
channel-type.awattar.switch-type.description = Currently activated
|
||||
channel-type.awattar.start-time-stamp.label = Starttime
|
||||
channel-type.awattar.start-time-stamp.description = Starting time of period.
|
||||
channel-type.awattar.end-time-stamp.label = end-time-stamp
|
||||
channel-type.awattar.end-time-stamp.description = End time of period.
|
||||
channel-type.awattar.countdown-type.label = Countdown
|
||||
channel-type.awattar.countdown-type.description = Time until start of period.
|
||||
channel-type.awattar.remaining-type.label = Remaining
|
||||
channel-type.awattar.remaining-type.description = Time until end of period.
|
||||
channel-type.awattar.hours-type.label = Hours
|
||||
channel-type.awattar.hours-type.description = A list of all hours within this bestprice range
|
||||
|
||||
# channel group types
|
||||
channel-group-type.awattar.hourly-prices.label = Hourly prices
|
||||
channel-group-type.awattar.hourly-prices.description = Hourly net and gross prices
|
||||
channel-group-type.awattar.current.label = Current prices
|
||||
channel-group-type.awattar.current.description = The prices of the current hour
|
||||
|
||||
channel-group-type.awattar.today00.label = Today 00:00
|
||||
channel-group-type.awattar.today00.description = Todays prices from 00:00 to 01:00
|
||||
channel-group-type.awattar.today01.label = Today 01:00
|
||||
channel-group-type.awattar.today01.description = Todays prices from 01:00 to 02:00
|
||||
channel-group-type.awattar.today02.label = Today 02:00
|
||||
channel-group-type.awattar.today02.description = Todays prices from 02:00 to 03:00
|
||||
channel-group-type.awattar.today03.label = Today 03:00
|
||||
channel-group-type.awattar.today03.description = Todays prices from 03:00 to 04:00
|
||||
channel-group-type.awattar.today04.label = Today 04:00
|
||||
channel-group-type.awattar.today04.description = Todays prices from 04:00 to 05:00
|
||||
channel-group-type.awattar.today05.label = Today 05:00
|
||||
channel-group-type.awattar.today05.description = Todays prices from 05:00 to 06:00
|
||||
channel-group-type.awattar.today06.label = Today 06:00
|
||||
channel-group-type.awattar.today06.description = Todays prices from 06:00 to 07:00
|
||||
channel-group-type.awattar.today07.label = Today 07:00
|
||||
channel-group-type.awattar.today07.description = Todays prices from 07:00 to 08:00
|
||||
channel-group-type.awattar.today08.label = Today 08:00
|
||||
channel-group-type.awattar.today08.description = Todays prices from 08:00 to 09:00
|
||||
channel-group-type.awattar.today09.label = Today 09:00
|
||||
channel-group-type.awattar.today09.description = Todays prices from 09:00 to 10:00
|
||||
channel-group-type.awattar.today10.label = Today 10:00
|
||||
channel-group-type.awattar.today10.description = Todays prices from 10:00 to 11:00
|
||||
channel-group-type.awattar.today11.label = Today 11:00
|
||||
channel-group-type.awattar.today11.description = Todays prices from 11:00 to 12:00
|
||||
channel-group-type.awattar.today12.label = Today 12:00
|
||||
channel-group-type.awattar.today12.description = Todays prices from 12:00 to 13:00
|
||||
channel-group-type.awattar.today13.label = Today 13:00
|
||||
channel-group-type.awattar.today13.description = Todays prices from 13:00 to 14:00
|
||||
channel-group-type.awattar.today14.label = Today 14:00
|
||||
channel-group-type.awattar.today14.description = Todays prices from 14:00 to 15:00
|
||||
channel-group-type.awattar.today15.label = Today 15:00
|
||||
channel-group-type.awattar.today15.description = Todays prices from 15:00 to 16:00
|
||||
channel-group-type.awattar.today16.label = Today 16:00
|
||||
channel-group-type.awattar.today16.description = Todays prices from 16:00 to 17:00
|
||||
channel-group-type.awattar.today17.label = Today 17:00
|
||||
channel-group-type.awattar.today17.description = Todays prices from 17:00 to 18:00
|
||||
channel-group-type.awattar.today18.label = Today 18:00
|
||||
channel-group-type.awattar.today18.description = Todays prices from 18:00 to 19:00
|
||||
channel-group-type.awattar.today19.label = Today 19:00
|
||||
channel-group-type.awattar.today19.description = Todays prices from 19:00 to 20:00
|
||||
channel-group-type.awattar.today20.label = Today 20:00
|
||||
channel-group-type.awattar.today20.description = Todays prices from 20:00 to 21:00
|
||||
channel-group-type.awattar.today21.label = Today 21:00
|
||||
channel-group-type.awattar.today21.description = Todays prices from 21:00 to 22:00
|
||||
channel-group-type.awattar.today22.label = Today 22:00
|
||||
channel-group-type.awattar.today22.description = Todays prices from 22:00 to 23:00
|
||||
channel-group-type.awattar.today23.label = Today 23:00
|
||||
channel-group-type.awattar.today23.description = Todays prices from 23:00 to 00:00
|
||||
|
||||
channel-group-type.awattar.tomorrow00.label = Tomorrow 00:00
|
||||
channel-group-type.awattar.tomorrow00.description = Tomorrows prices from 00:00 to 01:00
|
||||
channel-group-type.awattar.tomorrow01.label = Tomorrow 01:00
|
||||
channel-group-type.awattar.tomorrow01.description = Tomorrows prices from 01:00 to 02:00
|
||||
channel-group-type.awattar.tomorrow02.label = Tomorrow 02:00
|
||||
channel-group-type.awattar.tomorrow02.description = Tomorrows prices from 02:00 to 03:00
|
||||
channel-group-type.awattar.tomorrow03.label = Tomorrow 03:00
|
||||
channel-group-type.awattar.tomorrow03.description = Tomorrows prices from 03:00 to 04:00
|
||||
channel-group-type.awattar.tomorrow04.label = Tomorrow 04:00
|
||||
channel-group-type.awattar.tomorrow04.description = Tomorrows prices from 04:00 to 05:00
|
||||
channel-group-type.awattar.tomorrow05.label = Tomorrow 05:00
|
||||
channel-group-type.awattar.tomorrow05.description = Tomorrows prices from 05:00 to 06:00
|
||||
channel-group-type.awattar.tomorrow06.label = Tomorrow 06:00
|
||||
channel-group-type.awattar.tomorrow06.description = Tomorrows prices from 06:00 to 07:00
|
||||
channel-group-type.awattar.tomorrow07.label = Tomorrow 07:00
|
||||
channel-group-type.awattar.tomorrow07.description = Tomorrows prices from 07:00 to 08:00
|
||||
channel-group-type.awattar.tomorrow08.label = Tomorrow 08:00
|
||||
channel-group-type.awattar.tomorrow08.description = Tomorrows prices from 08:00 to 09:00
|
||||
channel-group-type.awattar.tomorrow09.label = Tomorrow 09:00
|
||||
channel-group-type.awattar.tomorrow09.description = Tomorrows prices from 09:00 to 10:00
|
||||
channel-group-type.awattar.tomorrow10.label = Tomorrow 10:00
|
||||
channel-group-type.awattar.tomorrow10.description = Tomorrows prices from 10:00 to 11:00
|
||||
channel-group-type.awattar.tomorrow11.label = Tomorrow 11:00
|
||||
channel-group-type.awattar.tomorrow11.description = Tomorrows prices from 11:00 to 12:00
|
||||
channel-group-type.awattar.tomorrow12.label = Tomorrow 12:00
|
||||
channel-group-type.awattar.tomorrow12.description = Tomorrows prices from 12:00 to 13:00
|
||||
channel-group-type.awattar.tomorrow13.label = Tomorrow 13:00
|
||||
channel-group-type.awattar.tomorrow13.description = Tomorrows prices from 13:00 to 14:00
|
||||
channel-group-type.awattar.tomorrow14.label = Tomorrow 14:00
|
||||
channel-group-type.awattar.tomorrow14.description = Tomorrows prices from 14:00 to 15:00
|
||||
channel-group-type.awattar.tomorrow15.label = Tomorrow 15:00
|
||||
channel-group-type.awattar.tomorrow15.description = Tomorrows prices from 15:00 to 16:00
|
||||
channel-group-type.awattar.tomorrow16.label = Tomorrow 16:00
|
||||
channel-group-type.awattar.tomorrow16.description = Tomorrows prices from 16:00 to 17:00
|
||||
channel-group-type.awattar.tomorrow17.label = Tomorrow 17:00
|
||||
channel-group-type.awattar.tomorrow17.description = Tomorrows prices from 17:00 to 18:00
|
||||
channel-group-type.awattar.tomorrow18.label = Tomorrow 18:00
|
||||
channel-group-type.awattar.tomorrow18.description = Tomorrows prices from 18:00 to 19:00
|
||||
channel-group-type.awattar.tomorrow19.label = Tomorrow 19:00
|
||||
channel-group-type.awattar.tomorrow19.description = Tomorrows prices from 19:00 to 20:00
|
||||
channel-group-type.awattar.tomorrow20.label = Tomorrow 20:00
|
||||
channel-group-type.awattar.tomorrow20.description = Tomorrows prices from 20:00 to 21:00
|
||||
channel-group-type.awattar.tomorrow21.label = Tomorrow 21:00
|
||||
channel-group-type.awattar.tomorrow21.description = Tomorrows prices from 21:00 to 22:00
|
||||
channel-group-type.awattar.tomorrow22.label = Tomorrow 22:00
|
||||
channel-group-type.awattar.tomorrow22.description = Tomorrows prices from 22:00 to 23:00
|
||||
channel-group-type.awattar.tomorrow23.label = Tomorrow 23:00
|
||||
channel-group-type.awattar.tomorrow23.description = Tomorrows prices from 23:00 to 00:00
|
||||
|
||||
|
||||
error.config.missing=Configuration missing!
|
||||
error.bridge.missing=Bridge is missing!
|
||||
error.channelgroup.missing=Channelgroup missing!
|
||||
error.unsupported.country=Unsupported country, only DE and AT are supported
|
||||
error.duration.value=Invalid duration value
|
||||
error.json=Invalid JSON response from aWATTar API
|
||||
error.interrupted=Communication interrupted
|
||||
error.execution=Execution error
|
||||
error.timeout=Timeout retrieving prices from aWATTar API
|
||||
error.invalid.data=No or invalid data received from aWATTar API
|
||||
error.length.value=length needs to be > 0 and < duration.
|
||||
warn.awattar.statuscode=aWATTar server did not respond with status code 200
|
||||
error.start.value=Invalid start value
|
@ -0,0 +1,171 @@
|
||||
binding.awattar.name = aWATTar Binding
|
||||
binding.awattar.description = Stündlich wechselnde Strompreise für Deutschland und Österreich
|
||||
|
||||
bridge-type.awattar.bridge.label = aWATTar Bridge
|
||||
bridge-type.awattar.bridge.description = Ermittelt Strompreise von der aWATTar API.
|
||||
|
||||
bridge-type.config.awattar.bridge.country.label = Land
|
||||
bridge-type.config.awattar.bridge.country.description = Land, für das Preise ermittelt werden sollen. Nur Deutschland (DE) und Österreich (AT) werden unterstützt
|
||||
bridge-type.config.awattar.bridge.vatPercent.label = USt-Satz
|
||||
bridge-type.config.awattar.bridge.vatPercent.description = Umsatzsteuer in Prozent
|
||||
bridge-type.config.awattar.bridge.basePrice.label = Basispreis
|
||||
bridge-type.config.awattar.bridge.basePrice.description = Der Netto-Grundpreis pro Kilowattstunde
|
||||
bridge-type.config.awattar.bridge.timeZone.label = Zeitzone
|
||||
bridge-type.config.awattar.bridge.timeZone.description = Zeitzone für die Stundenangaben. Default ist CET, passend zur aWATTar API
|
||||
|
||||
# thing types
|
||||
thing-type.awattar.prices.label = aWATTar Stundenpreise
|
||||
thing-type.awattar.prices.description = Preise pro Kilowattstunde für die jeweilige Stunde in Cent
|
||||
thing-type.awattar.bestprice.label = Bester Preis
|
||||
thing-type.awattar.bestprice.description = Ermittelt die Stunden mit den günstigsten Preisen im angegebenen Zeitraum
|
||||
|
||||
# thing type config description
|
||||
thing-type.config.awattar.bestprice.rangeStart.label = Startzeit
|
||||
thing-type.config.awattar.bestprice.rangeStart.description = Erste Stunde des zu durchsuchenden Zeitraums.
|
||||
thing-type.config.awattar.bestprice.rangeDuration.label = Dauer
|
||||
thing-type.config.awattar.bestprice.rangeDuration.description = Dauer des zu durchsuchenden Zeitraums
|
||||
thing-type.config.awattar.bestprice.length.label = Länge
|
||||
thing-type.config.awattar.bestprice.length.description = Die Anzahl der zu findenden günstigen Stunden
|
||||
thing-type.config.awattar.bestprice.consecutive.label = Durchgehend
|
||||
thing-type.config.awattar.bestprice.consecutive.description = Wird ein einzelner durchgehender Zeitraum gesucht?
|
||||
|
||||
# channel types
|
||||
channel-type.awattar.price.label = ct/kWh
|
||||
channel-type.awattar.price.description = Preis in ct/kWh
|
||||
channel-type.awattar.input-duration.label = Dauer
|
||||
channel-type.awattar.input-duration.description = Die Anzahl der zu findenden günstigen Stunden
|
||||
channel-type.awattar.input-hours.label = Suchzeitraum
|
||||
channel-type.awattar.input-hours.description = Wie viele Stunden sollen durchsucht werden?
|
||||
channel-type.awattar.input-switch.label = Durchgehend
|
||||
channel-type.awattar.input-switch.description = Wird ein durchgehender Zeitraum gesucht?
|
||||
channel-type.awattar.switch-type.label = Aktiv
|
||||
channel-type.awattar.switch-type.description = Kennzeichnet Zeiträume mit günstigen Preisen
|
||||
channel-type.awattar.start-time-stamp.label = Startzeit
|
||||
channel-type.awattar.start-time-stamp.description = Start der gefundenen Periode
|
||||
channel-type.awattar.end-time-stamp.label = Endzeit
|
||||
channel-type.awattar.end-time-stamp.description = Ende der gefundenen Periode
|
||||
channel-type.awattar.countdown-type.label = Countdown
|
||||
channel-type.awattar.countdown-type.description = Zeit bis zum Beginn der gefundenen Periode
|
||||
channel-type.awattar.remaining-type.label = Verbleibend
|
||||
channel-type.awattar.remaining-type.description = Zeit bis zum Ende der gefundenen Periode
|
||||
channel-type.awattar.hours-type.label = Stunden
|
||||
channel-type.awattar.hours-type.description = Eine Liste aller gefundenen Stunden mit günstigen Preisen
|
||||
|
||||
|
||||
# channel group types
|
||||
channel-group-type.awattar.hourly-prices.label = Preise
|
||||
channel-group-type.awattar.hourly-prices.description = Stündliche Netto- und Bruttopreise
|
||||
channel-group-type.awattar.current.label = Aktuelle Preise
|
||||
channel-group-type.awattar.current.description = Die aktuellen Netto- und Bruttopreise
|
||||
|
||||
channel-group-type.awattar.today00.label = Heute 00:00
|
||||
channel-group-type.awattar.today00.description = Heutige Preise von 00:00 bis 01:00
|
||||
channel-group-type.awattar.today01.label = Heute 01:00
|
||||
channel-group-type.awattar.today01.description = Heutige Preise von 01:00 bis 02:00
|
||||
channel-group-type.awattar.today02.label = Heute 02:00
|
||||
channel-group-type.awattar.today02.description = Heutige Preise von 02:00 bis 03:00
|
||||
channel-group-type.awattar.today03.label = Heute 03:00
|
||||
channel-group-type.awattar.today03.description = Heutige Preise von 03:00 bis 04:00
|
||||
channel-group-type.awattar.today04.label = Heute 04:00
|
||||
channel-group-type.awattar.today04.description = Heutige Preise von 04:00 bis 05:00
|
||||
channel-group-type.awattar.today05.label = Heute 05:00
|
||||
channel-group-type.awattar.today05.description = Heutige Preise von 05:00 bis 06:00
|
||||
channel-group-type.awattar.today06.label = Heute 06:00
|
||||
channel-group-type.awattar.today06.description = Heutige Preise von 06:00 bis 07:00
|
||||
channel-group-type.awattar.today07.label = Heute 07:00
|
||||
channel-group-type.awattar.today07.description = Heutige Preise von 07:00 bis 08:00
|
||||
channel-group-type.awattar.today08.label = Heute 08:00
|
||||
channel-group-type.awattar.today08.description = Heutige Preise von 08:00 bis 09:00
|
||||
channel-group-type.awattar.today09.label = Heute 09:00
|
||||
channel-group-type.awattar.today09.description = Heutige Preise von 09:00 bis 10:00
|
||||
channel-group-type.awattar.today10.label = Heute 10:00
|
||||
channel-group-type.awattar.today10.description = Heutige Preise von 10:00 bis 11:00
|
||||
channel-group-type.awattar.today11.label = Heute 11:00
|
||||
channel-group-type.awattar.today11.description = Heutige Preise von 11:00 bis 12:00
|
||||
channel-group-type.awattar.today12.label = Heute 12:00
|
||||
channel-group-type.awattar.today12.description = Heutige Preise von 12:00 bis 13:00
|
||||
channel-group-type.awattar.today13.label = Heute 13:00
|
||||
channel-group-type.awattar.today13.description = Heutige Preise von 13:00 bis 14:00
|
||||
channel-group-type.awattar.today14.label = Heute 14:00
|
||||
channel-group-type.awattar.today14.description = Heutige Preise von 14:00 bis 15:00
|
||||
channel-group-type.awattar.today15.label = Heute 15:00
|
||||
channel-group-type.awattar.today15.description = Heutige Preise von 15:00 bis 16:00
|
||||
channel-group-type.awattar.today16.label = Heute 16:00
|
||||
channel-group-type.awattar.today16.description = Heutige Preise von 16:00 bis 17:00
|
||||
channel-group-type.awattar.today17.label = Heute 17:00
|
||||
channel-group-type.awattar.today17.description = Heutige Preise von 17:00 bis 18:00
|
||||
channel-group-type.awattar.today18.label = Heute 18:00
|
||||
channel-group-type.awattar.today18.description = Heutige Preise von 18:00 bis 19:00
|
||||
channel-group-type.awattar.today19.label = Heute 19:00
|
||||
channel-group-type.awattar.today19.description = Heutige Preise von 19:00 bis 20:00
|
||||
channel-group-type.awattar.today20.label = Heute 20:00
|
||||
channel-group-type.awattar.today20.description = Heutige Preise von 20:00 bis 21:00
|
||||
channel-group-type.awattar.today21.label = Heute 21:00
|
||||
channel-group-type.awattar.today21.description = Heutige Preise von 21:00 bis 22:00
|
||||
channel-group-type.awattar.today22.label = Heute 22:00
|
||||
channel-group-type.awattar.today22.description = Heutige Preise von 22:00 bis 23:00
|
||||
channel-group-type.awattar.today23.label = Heute 23:00
|
||||
channel-group-type.awattar.today23.description = Heutige Preise von 23:00 bis 00:00
|
||||
|
||||
channel-group-type.awattar.tomorrow00.label = Morgen 00:00
|
||||
channel-group-type.awattar.tomorrow00.description = Morgige Preise von 00:00 bis 01:00
|
||||
channel-group-type.awattar.tomorrow01.label = Morgen 01:00
|
||||
channel-group-type.awattar.tomorrow01.description = Morgige Preise von 01:00 bis 02:00
|
||||
channel-group-type.awattar.tomorrow02.label = Morgen 02:00
|
||||
channel-group-type.awattar.tomorrow02.description = Morgige Preise von 02:00 bis 03:00
|
||||
channel-group-type.awattar.tomorrow03.label = Morgen 03:00
|
||||
channel-group-type.awattar.tomorrow03.description = Morgige Preise von 03:00 bis 04:00
|
||||
channel-group-type.awattar.tomorrow04.label = Morgen 04:00
|
||||
channel-group-type.awattar.tomorrow04.description = Morgige Preise von 04:00 bis 05:00
|
||||
channel-group-type.awattar.tomorrow05.label = Morgen 05:00
|
||||
channel-group-type.awattar.tomorrow05.description = Morgige Preise von 05:00 bis 06:00
|
||||
channel-group-type.awattar.tomorrow06.label = Morgen 06:00
|
||||
channel-group-type.awattar.tomorrow06.description = Morgige Preise von 06:00 bis 07:00
|
||||
channel-group-type.awattar.tomorrow07.label = Morgen 07:00
|
||||
channel-group-type.awattar.tomorrow07.description = Morgige Preise von 07:00 bis 08:00
|
||||
channel-group-type.awattar.tomorrow08.label = Morgen 08:00
|
||||
channel-group-type.awattar.tomorrow08.description = Morgige Preise von 08:00 bis 09:00
|
||||
channel-group-type.awattar.tomorrow09.label = Morgen 09:00
|
||||
channel-group-type.awattar.tomorrow09.description = Morgige Preise von 09:00 bis 10:00
|
||||
channel-group-type.awattar.tomorrow10.label = Morgen 10:00
|
||||
channel-group-type.awattar.tomorrow10.description = Morgige Preise von 10:00 bis 11:00
|
||||
channel-group-type.awattar.tomorrow11.label = Morgen 11:00
|
||||
channel-group-type.awattar.tomorrow11.description = Morgige Preise von 11:00 bis 12:00
|
||||
channel-group-type.awattar.tomorrow12.label = Morgen 12:00
|
||||
channel-group-type.awattar.tomorrow12.description = Morgige Preise von 12:00 bis 13:00
|
||||
channel-group-type.awattar.tomorrow13.label = Morgen 13:00
|
||||
channel-group-type.awattar.tomorrow13.description = Morgige Preise von 13:00 bis 14:00
|
||||
channel-group-type.awattar.tomorrow14.label = Morgen 14:00
|
||||
channel-group-type.awattar.tomorrow14.description = Morgige Preise von 14:00 bis 15:00
|
||||
channel-group-type.awattar.tomorrow15.label = Morgen 15:00
|
||||
channel-group-type.awattar.tomorrow15.description = Morgige Preise von 15:00 bis 16:00
|
||||
channel-group-type.awattar.tomorrow16.label = Morgen 16:00
|
||||
channel-group-type.awattar.tomorrow16.description = Morgige Preise von 16:00 bis 17:00
|
||||
channel-group-type.awattar.tomorrow17.label = Morgen 17:00
|
||||
channel-group-type.awattar.tomorrow17.description = Morgige Preise von 17:00 bis 18:00
|
||||
channel-group-type.awattar.tomorrow18.label = Morgen 18:00
|
||||
channel-group-type.awattar.tomorrow18.description = Morgige Preise von 18:00 bis 19:00
|
||||
channel-group-type.awattar.tomorrow19.label = Morgen 19:00
|
||||
channel-group-type.awattar.tomorrow19.description = Morgige Preise von 19:00 bis 20:00
|
||||
channel-group-type.awattar.tomorrow20.label = Morgen 20:00
|
||||
channel-group-type.awattar.tomorrow20.description = Morgige Preise von 20:00 bis 21:00
|
||||
channel-group-type.awattar.tomorrow21.label = Morgen 21:00
|
||||
channel-group-type.awattar.tomorrow21.description = Morgige Preise von 21:00 bis 22:00
|
||||
channel-group-type.awattar.tomorrow22.label = Morgen 22:00
|
||||
channel-group-type.awattar.tomorrow22.description = Morgige Preise von 22:00 bis 23:00
|
||||
channel-group-type.awattar.tomorrow23.label = Morgen 23:00
|
||||
channel-group-type.awattar.tomorrow23.description = Morgige Preise von 23:00 bis 00:00
|
||||
|
||||
error.config.missing=Konfiguration fehlt!
|
||||
error.bridge.missing=Bridge fehlt!
|
||||
error.channelgroup.missing=Channelgroup fehlt!
|
||||
error.unsupported.country=Land wird nicht unterstützt, bitte DE oder AT verwenden
|
||||
error.duration.value=Ungültiger Wert für Dauer
|
||||
error.json=Ungültiges JSON von aWATTar
|
||||
error.interrupted=Kommunikation unterbrochen
|
||||
error.execution=Ausführungsfehler
|
||||
error.timeout=Timeout beim Abrufen der Preise von aWATTar
|
||||
error.invalid.data=Keine oder ungültige Daten von der aWATTar API erhalten
|
||||
error.length.value=Length muss größer als 0 und kleiner als duration sein.
|
||||
warn.awattar.statuscode=Der aWATTar Server antwortete nicht mit Statuscode 200
|
||||
error.start.value=Ungültiger Startwert
|
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="openweathermap"
|
||||
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">
|
||||
|
||||
|
||||
<bridge-type id="bridge">
|
||||
<label>aWATTar Bridge</label>
|
||||
<description>Provides price data from the aWATTar API.</description>
|
||||
<config-description-ref uri="bridge-type:awattar:bridge"/>
|
||||
</bridge-type>
|
||||
|
||||
</thing:thing-descriptions>
|
@ -0,0 +1,345 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="awattar"
|
||||
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="prices">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bridge"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Hourly Price</label>
|
||||
<description>Prices for one kilowatt-hour at the given hour in Cent</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="current" typeId="hourly-prices">
|
||||
<label>Current Prices</label>
|
||||
<description>The prices of the current hour</description>
|
||||
</channel-group>
|
||||
|
||||
|
||||
<channel-group id="today00" typeId="hourly-prices">
|
||||
<label>Today 00:00</label>
|
||||
<description>Todays prices from 00:00 to 01:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today01" typeId="hourly-prices">
|
||||
<label>Today 01:00</label>
|
||||
<description>Todays prices from 01:00 to 02:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today02" typeId="hourly-prices">
|
||||
<label>Today 02:00</label>
|
||||
<description>Todays prices from 02:00 to 03:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today03" typeId="hourly-prices">
|
||||
<label>Today 03:00</label>
|
||||
<description>Todays prices from 03:00 to 04:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today04" typeId="hourly-prices">
|
||||
<label>Today 04:00</label>
|
||||
<description>Todays prices from 04:00 to 05:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today05" typeId="hourly-prices">
|
||||
<label>Today 05:00</label>
|
||||
<description>Todays prices from 05:00 to 06:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today06" typeId="hourly-prices">
|
||||
<label>Today 06:00</label>
|
||||
<description>Todays prices from 06:00 to 07:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today07" typeId="hourly-prices">
|
||||
<label>Today 07:00</label>
|
||||
<description>Todays prices from 07:00 to 08:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today08" typeId="hourly-prices">
|
||||
<label>Today 08:00</label>
|
||||
<description>Todays prices from 08:00 to 09:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today09" typeId="hourly-prices">
|
||||
<label>Today 09:00</label>
|
||||
<description>Todays prices from 09:00 to 10:00</description>
|
||||
</channel-group>
|
||||
|
||||
<channel-group id="today10" typeId="hourly-prices">
|
||||
<label>Today 10:00</label>
|
||||
<description>Todays prices from 10:00 to 11:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today11" typeId="hourly-prices">
|
||||
<label>Today 11:00</label>
|
||||
<description>Todays prices from 11:00 to 12:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today12" typeId="hourly-prices">
|
||||
<label>Today 12:00</label>
|
||||
<description>Todays prices from 12:00 to 13:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today13" typeId="hourly-prices">
|
||||
<label>Today 13:00</label>
|
||||
<description>Todays prices from 13:00 to 14:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today14" typeId="hourly-prices">
|
||||
<label>Today 14:00</label>
|
||||
<description>Todays prices from 14:00 to 15:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today15" typeId="hourly-prices">
|
||||
<label>Today 15:00</label>
|
||||
<description>Todays prices from 15:00 to 16:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today16" typeId="hourly-prices">
|
||||
<label>Today 16:00</label>
|
||||
<description>Todays prices from 16:00 to 17:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today17" typeId="hourly-prices">
|
||||
<label>Today 17:00</label>
|
||||
<description>Todays prices from 17:00 to 18:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today18" typeId="hourly-prices">
|
||||
<label>Today 18:00</label>
|
||||
<description>Todays prices from 18:00 to 19:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today19" typeId="hourly-prices">
|
||||
<label>Today 19:00</label>
|
||||
<description>Todays prices from 19:00 to 10:00</description>
|
||||
</channel-group>
|
||||
|
||||
<channel-group id="today20" typeId="hourly-prices">
|
||||
<label>Today 20:00</label>
|
||||
<description>Todays prices from 20:00 to 21:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today21" typeId="hourly-prices">
|
||||
<label>Today 21:00</label>
|
||||
<description>Todays prices from 21:00 to 22:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today22" typeId="hourly-prices">
|
||||
<label>Today 22:00</label>
|
||||
<description>Todays prices from 22:00 to 23:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="today23" typeId="hourly-prices">
|
||||
<label>Today 23:00</label>
|
||||
<description>Todays prices from 23:00 to 24:00</description>
|
||||
</channel-group>
|
||||
|
||||
<!-- Tomorrow -->
|
||||
<channel-group id="tomorrow00" typeId="hourly-prices">
|
||||
<label>Tomorrow 00:00</label>
|
||||
<description>Tomorrows prices from 00:00 to 01:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow01" typeId="hourly-prices">
|
||||
<label>Tomorrow 01:00</label>
|
||||
<description>Tomorrows prices from 01:00 to 02:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow02" typeId="hourly-prices">
|
||||
<label>Tomorrow 02:00</label>
|
||||
<description>Tomorrows prices from 02:00 to 03:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow03" typeId="hourly-prices">
|
||||
<label>Tomorrow 03:00</label>
|
||||
<description>Tomorrows prices from 03:00 to 04:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow04" typeId="hourly-prices">
|
||||
<label>Tomorrow 04:00</label>
|
||||
<description>Tomorrows prices from 04:00 to 05:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow05" typeId="hourly-prices">
|
||||
<label>Tomorrow 05:00</label>
|
||||
<description>Tomorrows prices from 05:00 to 06:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow06" typeId="hourly-prices">
|
||||
<label>Tomorrow 06:00</label>
|
||||
<description>Tomorrows prices from 06:00 to 07:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow07" typeId="hourly-prices">
|
||||
<label>Tomorrow 07:00</label>
|
||||
<description>Tomorrows prices from 07:00 to 08:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow08" typeId="hourly-prices">
|
||||
<label>Tomorrow 08:00</label>
|
||||
<description>Tomorrows prices from 08:00 to 09:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow09" typeId="hourly-prices">
|
||||
<label>Tomorrow 09:00</label>
|
||||
<description>Tomorrows prices from 09:00 to 10:00</description>
|
||||
</channel-group>
|
||||
|
||||
<channel-group id="tomorrow10" typeId="hourly-prices">
|
||||
<label>Tomorrow 10:00</label>
|
||||
<description>Tomorrows prices from 10:00 to 11:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow11" typeId="hourly-prices">
|
||||
<label>Tomorrow 11:00</label>
|
||||
<description>Tomorrows prices from 11:00 to 12:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow12" typeId="hourly-prices">
|
||||
<label>Tomorrow 12:00</label>
|
||||
<description>Tomorrows prices from 12:00 to 13:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow13" typeId="hourly-prices">
|
||||
<label>Tomorrow 13:00</label>
|
||||
<description>Tomorrows prices from 13:00 to 14:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow14" typeId="hourly-prices">
|
||||
<label>Tomorrow 14:00</label>
|
||||
<description>Tomorrows prices from 14:00 to 15:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow15" typeId="hourly-prices">
|
||||
<label>Tomorrow 15:00</label>
|
||||
<description>Tomorrows prices from 15:00 to 16:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow16" typeId="hourly-prices">
|
||||
<label>Tomorrow 16:00</label>
|
||||
<description>Tomorrows prices from 16:00 to 17:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow17" typeId="hourly-prices">
|
||||
<label>Tomorrow 17:00</label>
|
||||
<description>Tomorrows prices from 17:00 to 18:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow18" typeId="hourly-prices">
|
||||
<label>Tomorrow 18:00</label>
|
||||
<description>Tomorrows prices from 18:00 to 19:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow19" typeId="hourly-prices">
|
||||
<label>Tomorrow 19:00</label>
|
||||
<description>Tomorrows prices from 19:00 to 10:00</description>
|
||||
</channel-group>
|
||||
|
||||
<channel-group id="tomorrow20" typeId="hourly-prices">
|
||||
<label>Tomorrow 20:00</label>
|
||||
<description>Tomorrows prices from 20:00 to 21:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow21" typeId="hourly-prices">
|
||||
<label>Tomorrow 21:00</label>
|
||||
<description>Tomorrows prices from 21:00 to 22:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow22" typeId="hourly-prices">
|
||||
<label>Tomorrow 22:00</label>
|
||||
<description>Tomorrows prices from 22:00 to 23:00</description>
|
||||
</channel-group>
|
||||
<channel-group id="tomorrow23" typeId="hourly-prices">
|
||||
<label>Tomorrow 23:00</label>
|
||||
<description>Tomorrows prices from 23:00 to 24:00</description>
|
||||
</channel-group>
|
||||
</channel-groups>
|
||||
</thing-type>
|
||||
|
||||
|
||||
<thing-type id="bestprice">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="bridge"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>
|
||||
Best Price
|
||||
</label>
|
||||
|
||||
<description>Evaluates the lowest price period for the given settings</description>
|
||||
<channels>
|
||||
<channel id="active" typeId="switch-type">
|
||||
<label>Active</label>
|
||||
</channel>
|
||||
<channel id="start" typeId="start-time-stamp"/>
|
||||
<channel id="end" typeId="end-time-stamp"/>
|
||||
<channel id="countdown" typeId="countdown-type"/>
|
||||
<channel id="remaining" typeId="remaining-type"/>
|
||||
<channel id="hours" typeId="hours-type"/>
|
||||
</channels>
|
||||
<config-description-ref uri="thing-type:awattar:bestprice"/>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="price">
|
||||
<item-type>Number</item-type>
|
||||
<label>Price</label>
|
||||
<description>Price in ct/kWh</description>
|
||||
<state readOnly="true" pattern="%.3f ct"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="input-duration">
|
||||
<item-type>Number</item-type>
|
||||
<label>Duration</label>
|
||||
<description>Length of the bestprice period to search for (hours)</description>
|
||||
<state readOnly="false" pattern="%d"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="input-hours">
|
||||
<item-type>Number</item-type>
|
||||
<label>Lookup Time</label>
|
||||
<description>How many hours from now should be checked?</description>
|
||||
<state readOnly="false" pattern="%d"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="input-switch">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Consecutive</label>
|
||||
<description>Consecutive range needed?</description>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
|
||||
<channel-type id="switch-type">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Active</label>
|
||||
<description>Currently activated</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="start-time-stamp">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Starttime</label>
|
||||
<description>Starting time of period.</description>
|
||||
<category>Time</category>
|
||||
<state readOnly="true" pattern="%1$tH:%1$tM"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="end-time-stamp">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Endtime</label>
|
||||
<description>End time of period.</description>
|
||||
<category>Time</category>
|
||||
<state readOnly="true" pattern="%1$tH:%1$tM"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="countdown-type">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Countdown</label>
|
||||
<description>Time until start of period.</description>
|
||||
<category>Time</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="remaining-type">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Remaining</label>
|
||||
<description>Time until end of period.</description>
|
||||
<category>Time</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="hours-type">
|
||||
<item-type>String</item-type>
|
||||
<label>Hours</label>
|
||||
<description>A list of all hours within this bestprice range.</description>
|
||||
<category>Time</category>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-group-type id="hourly-prices">
|
||||
<label>Hourly Prices</label>
|
||||
<description>Hourly net and gross prices</description>
|
||||
<channels>
|
||||
<channel id="market-net" typeId="price">
|
||||
<label>Net Marketprice</label>
|
||||
</channel>
|
||||
|
||||
<channel id="market-gross" typeId="price">
|
||||
<label>Gross Marketprice</label>
|
||||
</channel>
|
||||
<channel id="total-net" typeId="price">
|
||||
<label>Net Total</label>
|
||||
</channel>
|
||||
<channel id="total-gross" typeId="price">
|
||||
<label>Gross Total</label>
|
||||
</channel>
|
||||
</channels>
|
||||
|
||||
</channel-group-type>
|
||||
|
||||
|
||||
</thing:thing-descriptions>
|
@ -61,6 +61,7 @@
|
||||
<module>org.openhab.binding.autelis</module>
|
||||
<module>org.openhab.binding.automower</module>
|
||||
<module>org.openhab.binding.avmfritz</module>
|
||||
<module>org.openhab.binding.awattar</module>
|
||||
<module>org.openhab.binding.benqprojector</module>
|
||||
<module>org.openhab.binding.bigassfan</module>
|
||||
<module>org.openhab.binding.bluetooth</module>
|
||||
|
Loading…
Reference in New Issue
Block a user