mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[freecurrency] Initial contribution (#16194)
Signed-off-by: Jan N. Klug <github@klug.nrw>
This commit is contained in:
parent
84d4dd3ef4
commit
9400ced25f
@ -115,6 +115,7 @@
|
|||||||
/bundles/org.openhab.binding.foobot/ @airboxlab @Hilbrand
|
/bundles/org.openhab.binding.foobot/ @airboxlab @Hilbrand
|
||||||
/bundles/org.openhab.binding.freebox/ @lolodomo
|
/bundles/org.openhab.binding.freebox/ @lolodomo
|
||||||
/bundles/org.openhab.binding.freeboxos/ @clinique
|
/bundles/org.openhab.binding.freeboxos/ @clinique
|
||||||
|
/bundles/org.openhab.binding.freecurrency/ @J-N-K
|
||||||
/bundles/org.openhab.binding.fronius/ @trokohl
|
/bundles/org.openhab.binding.fronius/ @trokohl
|
||||||
/bundles/org.openhab.binding.fsinternetradio/ @paphko
|
/bundles/org.openhab.binding.fsinternetradio/ @paphko
|
||||||
/bundles/org.openhab.binding.ftpupload/ @paulianttila
|
/bundles/org.openhab.binding.ftpupload/ @paulianttila
|
||||||
|
@ -566,6 +566,11 @@
|
|||||||
<artifactId>org.openhab.binding.freeboxos</artifactId>
|
<artifactId>org.openhab.binding.freeboxos</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
|
<artifactId>org.openhab.binding.freecurrency</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openhab.addons.bundles</groupId>
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
<artifactId>org.openhab.binding.fronius</artifactId>
|
<artifactId>org.openhab.binding.fronius</artifactId>
|
||||||
|
13
bundles/org.openhab.binding.freecurrency/NOTICE
Normal file
13
bundles/org.openhab.binding.freecurrency/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
|
40
bundles/org.openhab.binding.freecurrency/README.md
Normal file
40
bundles/org.openhab.binding.freecurrency/README.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Freecurrency Binding
|
||||||
|
|
||||||
|
The Freecurrency binding connects [Freecurrency API](https://freecurrencyapi.com) to openHAB.
|
||||||
|
It allows to get exchange rates between supported currencies and acts as a currency provider for openHAB's UoM support.
|
||||||
|
|
||||||
|
The binding automatically updates the exchange rates at 00:01 UTC.
|
||||||
|
There is a limit of 10 (5.000) free request per minute (month), so a daily refresh (and even some restarts per day) will not get you into trouble.
|
||||||
|
|
||||||
|
## Supported Things
|
||||||
|
|
||||||
|
There is only one thing: `info` which is extensible with exchange rate channels.
|
||||||
|
You can add as many of these things as you like, but in general one should be sufficient for most use-cases.
|
||||||
|
|
||||||
|
## Binding Configuration
|
||||||
|
|
||||||
|
The binding has two configuration parameters: `apiKey` and `baseCurrency`.
|
||||||
|
|
||||||
|
The `apiKey` is mandatory and can be retrieved from your dashboard after creating a free account at [Freecurrency API website](https://app.freecurrencyapi.com/login).
|
||||||
|
|
||||||
|
The `baseCurrency` defaults to US dollars (`USD`), but can be configured to any other supported currency.
|
||||||
|
Available currencies are provided as configuration options.
|
||||||
|
Please note that misconfiguration will result in no exchanges rates being provided.
|
||||||
|
|
||||||
|
## Thing Configuration
|
||||||
|
|
||||||
|
### `info` Thing Configuration
|
||||||
|
|
||||||
|
The thing has no configuration options and is automatically attached to the currency provider.
|
||||||
|
|
||||||
|
## Channels
|
||||||
|
|
||||||
|
| Channel | Channel Type | Item Type | Read/Write | Description |
|
||||||
|
|----------------|---------------|-----------|------------|----------------------------------------------------------------------------------------------|
|
||||||
|
| lastUpdate | last-update | DateTime | R/O | The timestamp of the last exchange rate refresh |
|
||||||
|
| <user defined> | exchange-rate | Number | R/O | The exchange rate between the configured currency and the base currency (or second currency) |
|
||||||
|
|
||||||
|
The `exchange-rate` channels have two configuration parameters: `currency1` and `currency2`.
|
||||||
|
Any currency code can be configured for both parameters, but only `currency1` is mandatory.
|
||||||
|
If you omit `currency2`, the configured base-currency will be used as reference.
|
||||||
|
Available currencies are provided as configuration options.
|
17
bundles/org.openhab.binding.freecurrency/pom.xml
Normal file
17
bundles/org.openhab.binding.freecurrency/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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.openhab.addons.bundles</groupId>
|
||||||
|
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||||
|
<version>4.2.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>org.openhab.binding.freecurrency</artifactId>
|
||||||
|
|
||||||
|
<name>openHAB Add-ons :: Bundles :: Freecurrency Binding</name>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<features name="org.openhab.binding.freecurrency-${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-freecurrency" description="Freecurrency Binding" version="${project.version}">
|
||||||
|
<feature>openhab-runtime-base</feature>
|
||||||
|
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.freecurrency/${project.version}</bundle>
|
||||||
|
</feature>
|
||||||
|
</features>
|
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.freecurrency.internal;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link ExchangeRateListener} interface can be implemented to receive a notification when exchange rates have been
|
||||||
|
* updated.
|
||||||
|
*
|
||||||
|
* @author Jan N. Klug - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public interface ExchangeRateListener {
|
||||||
|
|
||||||
|
void onExchangeRatesChanged();
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.freecurrency.internal;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link FreecurrencyBindingConstants} class defines common constants, which are
|
||||||
|
* used across the whole binding.
|
||||||
|
*
|
||||||
|
* @author Jan N. Klug - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class FreecurrencyBindingConstants {
|
||||||
|
|
||||||
|
private static final String BINDING_ID = "freecurrency";
|
||||||
|
|
||||||
|
// List of all Thing Type UIDs
|
||||||
|
public static final ThingTypeUID THING_TYPE_INFO = new ThingTypeUID(BINDING_ID, "info");
|
||||||
|
|
||||||
|
public static final ChannelTypeUID CHANNEL_TYPE_EXCHANGE_RATE = new ChannelTypeUID(BINDING_ID, "exchange-rate");
|
||||||
|
public static final ChannelTypeUID CHANNEL_TYPE_LAST_UPDATE = new ChannelTypeUID(BINDING_ID, "last-update");
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.freecurrency.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.freecurrency.internal.FreecurrencyBindingConstants.CHANNEL_TYPE_EXCHANGE_RATE;
|
||||||
|
import static org.openhab.binding.freecurrency.internal.FreecurrencyBindingConstants.CHANNEL_TYPE_LAST_UPDATE;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.openhab.binding.freecurrency.internal.config.FreecurrencyExhangeRateChannelConfig;
|
||||||
|
import org.openhab.core.library.types.DateTimeType;
|
||||||
|
import org.openhab.core.library.types.DecimalType;
|
||||||
|
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.binding.BaseThingHandler;
|
||||||
|
import org.openhab.core.types.Command;
|
||||||
|
import org.openhab.core.types.RefreshType;
|
||||||
|
import org.openhab.core.types.UnDefType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link FreecurrencyHandler} is responsible for handling commands, which are
|
||||||
|
* sent to one of the channels.
|
||||||
|
*
|
||||||
|
* @author Jan N. Klug - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class FreecurrencyHandler extends BaseThingHandler implements ExchangeRateListener {
|
||||||
|
private final FreecurrencyProvider freecurrencyProvider;
|
||||||
|
|
||||||
|
public FreecurrencyHandler(Thing thing, FreecurrencyProvider freecurrencyProvider) {
|
||||||
|
super(thing);
|
||||||
|
this.freecurrencyProvider = freecurrencyProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||||
|
Channel channel = thing.getChannel(channelUID);
|
||||||
|
if (RefreshType.REFRESH.equals(command) && channel != null) {
|
||||||
|
refreshChannel(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
updateStatus(ThingStatus.ONLINE);
|
||||||
|
freecurrencyProvider.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
freecurrencyProvider.removeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshChannel(Channel channel) {
|
||||||
|
if (CHANNEL_TYPE_EXCHANGE_RATE.equals(channel.getChannelTypeUID())) {
|
||||||
|
FreecurrencyExhangeRateChannelConfig config = channel.getConfiguration()
|
||||||
|
.as(FreecurrencyExhangeRateChannelConfig.class);
|
||||||
|
BigDecimal val = freecurrencyProvider.getExchangeRate(config.currency1, config.currency2);
|
||||||
|
updateState(channel.getUID(), val != null ? new DecimalType(val) : UnDefType.UNDEF);
|
||||||
|
} else if (CHANNEL_TYPE_LAST_UPDATE.equals(channel.getChannelTypeUID())) {
|
||||||
|
ZonedDateTime lastUpdated = freecurrencyProvider.getLastUpdated();
|
||||||
|
updateState(channel.getUID(), lastUpdated == null ? UnDefType.UNDEF : new DateTimeType(lastUpdated));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onExchangeRatesChanged() {
|
||||||
|
thing.getChannels().forEach(this::refreshChannel);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.freecurrency.internal;
|
||||||
|
|
||||||
|
import static org.openhab.binding.freecurrency.internal.FreecurrencyBindingConstants.*;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.thing.Thing;
|
||||||
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
|
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||||
|
import org.osgi.service.component.annotations.Activate;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link FreecurrencyHandlerFactory} is responsible for creating things and thing
|
||||||
|
* handlers.
|
||||||
|
*
|
||||||
|
* @author Jan N. Klug - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
@Component(configurationPid = "binding.freecurrency", service = ThingHandlerFactory.class)
|
||||||
|
public class FreecurrencyHandlerFactory extends BaseThingHandlerFactory {
|
||||||
|
|
||||||
|
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_INFO);
|
||||||
|
private final FreecurrencyProvider freecurrencyProvider;
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public FreecurrencyHandlerFactory(@Reference FreecurrencyProvider freecurrencyProvider) {
|
||||||
|
this.freecurrencyProvider = freecurrencyProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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_INFO.equals(thingTypeUID)) {
|
||||||
|
return new FreecurrencyHandler(thing, freecurrencyProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,231 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.freecurrency.internal;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.MathContext;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.measure.Unit;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.openhab.binding.freecurrency.internal.config.FreecurrencyServiceConfig;
|
||||||
|
import org.openhab.binding.freecurrency.internal.dto.CurrenciesDTO;
|
||||||
|
import org.openhab.binding.freecurrency.internal.dto.ExchangeRatesDTO;
|
||||||
|
import org.openhab.core.config.core.ConfigOptionProvider;
|
||||||
|
import org.openhab.core.config.core.Configuration;
|
||||||
|
import org.openhab.core.config.core.ParameterOption;
|
||||||
|
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||||
|
import org.openhab.core.library.dimension.Currency;
|
||||||
|
import org.openhab.core.library.unit.CurrencyProvider;
|
||||||
|
import org.openhab.core.library.unit.CurrencyUnit;
|
||||||
|
import org.openhab.core.scheduler.Scheduler;
|
||||||
|
import org.osgi.service.component.annotations.Activate;
|
||||||
|
import org.osgi.service.component.annotations.Component;
|
||||||
|
import org.osgi.service.component.annotations.ConfigurationPolicy;
|
||||||
|
import org.osgi.service.component.annotations.Deactivate;
|
||||||
|
import org.osgi.service.component.annotations.Modified;
|
||||||
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link FreecurrencyProvider} class implements a {@link CurrencyProvider} based on currencies and dynamic exchange
|
||||||
|
* rates from <a href="https://freecurrencyapi.com">Freecurrency API</a>. It also allows to register
|
||||||
|
* {@link ExchangeRateListener}s for classes that want to be notified about changed exchange rates.
|
||||||
|
*
|
||||||
|
* @author Jan N. Klug - Initial contribution
|
||||||
|
*/
|
||||||
|
@Component(immediate = true, configurationPid = "binding.freecurrency", configurationPolicy = ConfigurationPolicy.REQUIRE, service = {
|
||||||
|
CurrencyProvider.class, ConfigOptionProvider.class, FreecurrencyProvider.class })
|
||||||
|
@NonNullByDefault
|
||||||
|
public class FreecurrencyProvider implements CurrencyProvider, ConfigOptionProvider {
|
||||||
|
private static final CurrencyInformation DEFAULT_CURRENCY_USD = new CurrencyInformation("USD",
|
||||||
|
new CurrencyUnit("USD", null), "US Dollar");
|
||||||
|
private static final Duration REFRESH_OFFSET = Duration.parse("P1DT1M");
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(FreecurrencyProvider.class);
|
||||||
|
private final HttpClient httpClient;
|
||||||
|
private final Scheduler scheduler;
|
||||||
|
private final Gson gson = new Gson();
|
||||||
|
private @NonNullByDefault({}) FreecurrencyServiceConfig config;
|
||||||
|
private @Nullable ScheduledFuture<?> refreshJob;
|
||||||
|
private Map<String, CurrencyInformation> currencies = Map.of();
|
||||||
|
private Map<Unit<Currency>, BigDecimal> exchangeRates = Map.of();
|
||||||
|
private @Nullable ZonedDateTime lastUpdated = null;
|
||||||
|
private Set<ExchangeRateListener> exchangeRateListeners = new HashSet<>();
|
||||||
|
|
||||||
|
@Activate
|
||||||
|
public FreecurrencyProvider(@Reference HttpClientFactory httpClientFactory, @Reference Scheduler scheduler,
|
||||||
|
Map<String, Object> config) {
|
||||||
|
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||||
|
this.scheduler = scheduler;
|
||||||
|
modified(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Modified
|
||||||
|
public void modified(Map<String, Object> config) {
|
||||||
|
stopRefresh();
|
||||||
|
this.config = new Configuration(config).as(FreecurrencyServiceConfig.class);
|
||||||
|
|
||||||
|
if (this.config.apiKey.isBlank()) {
|
||||||
|
logger.warn("Configuration error: API key must not be blank.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrencies();
|
||||||
|
getExchangeRates();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deactivate
|
||||||
|
public void deactivate() {
|
||||||
|
stopRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(ExchangeRateListener listener) {
|
||||||
|
exchangeRateListeners.add(listener);
|
||||||
|
listener.onExchangeRatesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeListener(ExchangeRateListener listener) {
|
||||||
|
exchangeRateListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable BigDecimal getExchangeRate(String currency1, String currency2) {
|
||||||
|
CurrencyInformation info1 = currencies.get(currency1);
|
||||||
|
CurrencyInformation info2 = currencies.get(currency2.isBlank() ? config.baseCurrency : currency2);
|
||||||
|
if (info1 == null || info2 == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BigDecimal rate1 = exchangeRates.get(info1.unit());
|
||||||
|
BigDecimal rate2 = exchangeRates.get(info2.unit());
|
||||||
|
if (rate1 == null || rate2 == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return rate2.divide(rate1, MathContext.DECIMAL128);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable ZonedDateTime getLastUpdated() {
|
||||||
|
return lastUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopRefresh() {
|
||||||
|
ScheduledFuture<?> localJob = this.refreshJob;
|
||||||
|
if (localJob != null) {
|
||||||
|
localJob.cancel(false);
|
||||||
|
refreshJob = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getCurrencies() {
|
||||||
|
String uri = "https://api.freecurrencyapi.com/v1/currencies?apikey=" + config.apiKey;
|
||||||
|
try {
|
||||||
|
String currenciesJson = httpClient.GET(uri).getContentAsString();
|
||||||
|
CurrenciesDTO currenciesDTO = gson.fromJson(currenciesJson, CurrenciesDTO.class);
|
||||||
|
currencies = currenciesDTO.data.values().stream()
|
||||||
|
.map(c -> new CurrencyInformation(c.code, new CurrencyUnit(c.code, null), c.name))
|
||||||
|
.collect(Collectors.toMap(CurrencyInformation::code, u -> u));
|
||||||
|
logger.debug("Retrieved {} currencies", currencies.size());
|
||||||
|
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||||
|
logger.debug("Failed to request currencies", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getExchangeRates() {
|
||||||
|
if (!currencies.containsKey(config.baseCurrency)) {
|
||||||
|
logger.warn("Configuration error: Base currency '{}' is not in list of available currencies {}.",
|
||||||
|
config.baseCurrency, currencies.keySet());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String uri = "https://api.freecurrencyapi.com/v1/latest?apikey=" + config.apiKey + "&base_currency="
|
||||||
|
+ config.baseCurrency;
|
||||||
|
try {
|
||||||
|
String currenciesJson = httpClient.GET(uri).getContentAsString();
|
||||||
|
ExchangeRatesDTO exchangeRatesDTO = gson.fromJson(currenciesJson, ExchangeRatesDTO.class);
|
||||||
|
Map<Unit<Currency>, BigDecimal> newExchangeRates = new HashMap<>();
|
||||||
|
exchangeRatesDTO.data.forEach((k, v) -> {
|
||||||
|
CurrencyInformation currencyInfo = currencies.get(k);
|
||||||
|
if (currencyInfo == null) {
|
||||||
|
logger.debug("Not considering exchange rate for '{}' because it is not supported.", k);
|
||||||
|
} else {
|
||||||
|
newExchangeRates.put(currencyInfo.unit, v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
exchangeRates = newExchangeRates;
|
||||||
|
logger.debug("Retrieved exchange rates for {} currencies", newExchangeRates.size());
|
||||||
|
lastUpdated = ZonedDateTime.now();
|
||||||
|
|
||||||
|
// exchange rates are refreshed every day at midnight UTC
|
||||||
|
// we refresh one minute later to be sure we are not too early.
|
||||||
|
Instant nextRefresh = Instant.now().truncatedTo(ChronoUnit.DAYS).plus(REFRESH_OFFSET);
|
||||||
|
refreshJob = scheduler.at(this::getExchangeRates, nextRefresh);
|
||||||
|
|
||||||
|
// notify listeners about changed exchange rates
|
||||||
|
exchangeRateListeners.forEach(ExchangeRateListener::onExchangeRatesChanged);
|
||||||
|
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||||
|
logger.debug("Failed to request currencies", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Freecurrency API";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Unit<Currency> getBaseCurrency() {
|
||||||
|
return currencies.getOrDefault(config.baseCurrency, DEFAULT_CURRENCY_USD).unit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Unit<Currency>> getAdditionalCurrencies() {
|
||||||
|
return exchangeRates.keySet().stream().filter(c -> !config.baseCurrency.equals(c.getName())).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Function<Unit<Currency>, @Nullable BigDecimal> getExchangeRateFunction() {
|
||||||
|
return c -> exchangeRates.get(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Collection<ParameterOption> getParameterOptions(URI uri, String param, @Nullable String context,
|
||||||
|
@Nullable Locale locale) {
|
||||||
|
if (("binding:freecurrency".equals(uri.toString()) && "baseCurrency".equals(param))
|
||||||
|
|| ("channel-type:freecurrency:exchange-rate".equals(uri.toString()) && param.startsWith("currency"))) {
|
||||||
|
return currencies.values().stream().map(c -> new ParameterOption(c.code(), c.name() + " (" + c.code + ")"))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record CurrencyInformation(String code, Unit<Currency> unit, String name) {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.freecurrency.internal.config;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link FreecurrencyExhangeRateChannelConfig} class defines
|
||||||
|
*
|
||||||
|
* @author Jan N. Klug - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class FreecurrencyExhangeRateChannelConfig {
|
||||||
|
public String currency1 = "";
|
||||||
|
public String currency2 = "";
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.freecurrency.internal.config;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link FreecurrencyServiceConfig} class holds the service configuration
|
||||||
|
*
|
||||||
|
* @author Jan N. Klug - Initial contribution
|
||||||
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
|
public class FreecurrencyServiceConfig {
|
||||||
|
public String apiKey = "";
|
||||||
|
public String baseCurrency = "USD";
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.freecurrency.internal.dto;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link CurrenciesDTO} class is used to retrieve the available currencies
|
||||||
|
*
|
||||||
|
* @author Jan N. Klug - Initial contribution
|
||||||
|
*/
|
||||||
|
public class CurrenciesDTO {
|
||||||
|
public Map<String, CurrencyDTO> data;
|
||||||
|
|
||||||
|
public static class CurrencyDTO {
|
||||||
|
public String symbol;
|
||||||
|
public String name;
|
||||||
|
@SerializedName("symbol_native")
|
||||||
|
public String symbolNative;
|
||||||
|
@SerializedName("decimal_digits")
|
||||||
|
public int decimalDigits;
|
||||||
|
public int rounding;
|
||||||
|
public String code;
|
||||||
|
@SerializedName("name_plural")
|
||||||
|
public String namePlural;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||||
|
*
|
||||||
|
* See the NOTICE file(s) distributed with this work for additional
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made available under the
|
||||||
|
* terms of the Eclipse Public License 2.0 which is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-2.0
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.openhab.binding.freecurrency.internal.dto;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link ExchangeRatesDTO} class is used to retrieve the exchange-rates for all currencies
|
||||||
|
*
|
||||||
|
* @author Jan N. Klug - Initial contribution
|
||||||
|
*/
|
||||||
|
public class ExchangeRatesDTO {
|
||||||
|
public Map<String, String> meta;
|
||||||
|
public Map<String, BigDecimal> data;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<addon:addon id="freecurrency" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
|
||||||
|
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
|
||||||
|
|
||||||
|
<type>binding</type>
|
||||||
|
<name>Freecurrency Binding</name>
|
||||||
|
<description>This is the binding to integrate Freecurrency API as currency provider.</description>
|
||||||
|
<connection>cloud</connection>
|
||||||
|
|
||||||
|
<config-description>
|
||||||
|
<parameter name="apiKey" type="text" required="true">
|
||||||
|
<label>API Key</label>
|
||||||
|
<description>The API key retrieved from freecurrencyapi.com</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="baseCurrency" type="text">
|
||||||
|
<label>Base Currency</label>
|
||||||
|
<description>The base currency for this provider.</description>
|
||||||
|
<default>USD</default>
|
||||||
|
</parameter>
|
||||||
|
</config-description>
|
||||||
|
|
||||||
|
</addon:addon>
|
@ -0,0 +1,29 @@
|
|||||||
|
# add-on
|
||||||
|
|
||||||
|
addon.freecurrency.name = Freecurrency Binding
|
||||||
|
addon.freecurrency.description = This is the binding to integrate Freecurrency API as currency provider.
|
||||||
|
|
||||||
|
# add-on config
|
||||||
|
|
||||||
|
addon.config.freecurrency.apiKey.label = API Key
|
||||||
|
addon.config.freecurrency.apiKey.description = The API key retrieved from freecurrencyapi.com
|
||||||
|
addon.config.freecurrency.baseCurrency.label = Base Currency
|
||||||
|
addon.config.freecurrency.baseCurrency.description = The base currency for this provider.
|
||||||
|
|
||||||
|
# thing types
|
||||||
|
|
||||||
|
thing-type.freecurrency.info.label = Currency Information
|
||||||
|
thing-type.freecurrency.info.description = Provide exchanges rates between currencies and service status information.
|
||||||
|
|
||||||
|
# channel types
|
||||||
|
|
||||||
|
channel-type.freecurrency.exchange-rate.label = Exchange Rate
|
||||||
|
channel-type.freecurrency.exchange-rate.description = Exchange rate between two currencies
|
||||||
|
channel-type.freecurrency.last-update.label = Last Update
|
||||||
|
channel-type.freecurrency.last-update.description = The timestamp of the last retrieved set of exchange rates.
|
||||||
|
|
||||||
|
# channel types config
|
||||||
|
|
||||||
|
channel-type.config.freecurrency.exchange-rate.currency1.label = Currency 1
|
||||||
|
channel-type.config.freecurrency.exchange-rate.currency2.label = Currency 2
|
||||||
|
channel-type.config.freecurrency.exchange-rate.currency2.description = Optional, defaults to base currency if not configured.
|
@ -0,0 +1,42 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<thing:thing-descriptions bindingId="freecurrency"
|
||||||
|
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="info" extensible="exchange-rate">
|
||||||
|
<label>Currency Information</label>
|
||||||
|
<description>Provide exchanges rates between currencies and service status information.</description>
|
||||||
|
|
||||||
|
<channels>
|
||||||
|
<channel id="lastUpdate" typeId="last-update"/>
|
||||||
|
</channels>
|
||||||
|
|
||||||
|
</thing-type>
|
||||||
|
|
||||||
|
<channel-type id="last-update">
|
||||||
|
<item-type>DateTime</item-type>
|
||||||
|
<label>Last Update</label>
|
||||||
|
<description>The timestamp of the last retrieved set of exchange rates.</description>
|
||||||
|
<category>Time</category>
|
||||||
|
<state readOnly="true"/>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
<channel-type id="exchange-rate">
|
||||||
|
<item-type>Number</item-type>
|
||||||
|
<label>Exchange Rate</label>
|
||||||
|
<description>Exchange rate between two currencies</description>
|
||||||
|
<state pattern="%.6f" readOnly="true"/>
|
||||||
|
|
||||||
|
<config-description>
|
||||||
|
<parameter name="currency1" type="text" required="true">
|
||||||
|
<label>Currency 1</label>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="currency2" type="text">
|
||||||
|
<label>Currency 2</label>
|
||||||
|
<description>Optional, defaults to base currency if not configured.</description>
|
||||||
|
</parameter>
|
||||||
|
</config-description>
|
||||||
|
</channel-type>
|
||||||
|
|
||||||
|
</thing:thing-descriptions>
|
@ -147,6 +147,7 @@
|
|||||||
<module>org.openhab.binding.foobot</module>
|
<module>org.openhab.binding.foobot</module>
|
||||||
<module>org.openhab.binding.freebox</module>
|
<module>org.openhab.binding.freebox</module>
|
||||||
<module>org.openhab.binding.freeboxos</module>
|
<module>org.openhab.binding.freeboxos</module>
|
||||||
|
<module>org.openhab.binding.freecurrency</module>
|
||||||
<module>org.openhab.binding.fronius</module>
|
<module>org.openhab.binding.fronius</module>
|
||||||
<module>org.openhab.binding.fsinternetradio</module>
|
<module>org.openhab.binding.fsinternetradio</module>
|
||||||
<module>org.openhab.binding.ftpupload</module>
|
<module>org.openhab.binding.ftpupload</module>
|
||||||
|
Loading…
Reference in New Issue
Block a user