From f910a435359dbc7a45fb342164a3e8009e697617 Mon Sep 17 00:00:00 2001 From: Jacob Laursen Date: Thu, 2 Mar 2023 23:06:06 +0100 Subject: [PATCH] Initial contribution Signed-off-by: Jacob Laursen --- CODEOWNERS | 1 + bom/openhab-addons/pom.xml | 5 + bundles/org.openhab.transform.vat/NOTICE | 13 ++ bundles/org.openhab.transform.vat/README.md | 41 ++++ bundles/org.openhab.transform.vat/pom.xml | 17 ++ .../src/main/feature/feature.xml | 9 + .../internal/VATTransformationConstants.java | 205 ++++++++++++++++++ .../internal/VATTransformationService.java | 66 ++++++ .../vat/internal/config/VATConfig.java | 26 +++ .../profile/VATTransformationProfile.java | 131 +++++++++++ .../VATTransformationProfileFactory.java | 111 ++++++++++ .../src/main/resources/OH-INF/addon/addon.xml | 10 + .../resources/OH-INF/config/vatProfile.xml | 14 ++ .../main/resources/OH-INF/i18n/vat.properties | 10 + bundles/pom.xml | 1 + 15 files changed, 660 insertions(+) create mode 100644 bundles/org.openhab.transform.vat/NOTICE create mode 100644 bundles/org.openhab.transform.vat/README.md create mode 100644 bundles/org.openhab.transform.vat/pom.xml create mode 100644 bundles/org.openhab.transform.vat/src/main/feature/feature.xml create mode 100644 bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/VATTransformationConstants.java create mode 100644 bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/VATTransformationService.java create mode 100644 bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/config/VATConfig.java create mode 100644 bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/profile/VATTransformationProfile.java create mode 100644 bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/profile/VATTransformationProfileFactory.java create mode 100644 bundles/org.openhab.transform.vat/src/main/resources/OH-INF/addon/addon.xml create mode 100644 bundles/org.openhab.transform.vat/src/main/resources/OH-INF/config/vatProfile.xml create mode 100644 bundles/org.openhab.transform.vat/src/main/resources/OH-INF/i18n/vat.properties diff --git a/CODEOWNERS b/CODEOWNERS index f1f204fa412cd..cb464ccedf374 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -401,6 +401,7 @@ /bundles/org.openhab.transform.regex/ @openhab/add-ons-maintainers /bundles/org.openhab.transform.rollershutterposition/ @jsjames /bundles/org.openhab.transform.scale/ @clinique +/bundles/org.openhab.transform.vat/ @jlaur /bundles/org.openhab.transform.xpath/ @openhab/add-ons-maintainers /bundles/org.openhab.transform.xslt/ @openhab/add-ons-maintainers /bundles/org.openhab.voice.googlestt/ @GiviMAD diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index efe39a63389da..c4ac341410ca7 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -2001,6 +2001,11 @@ org.openhab.transform.scale ${project.version} + + org.openhab.addons.bundles + org.openhab.transform.vat + ${project.version} + org.openhab.addons.bundles org.openhab.transform.xpath diff --git a/bundles/org.openhab.transform.vat/NOTICE b/bundles/org.openhab.transform.vat/NOTICE new file mode 100644 index 0000000000000..38d625e349232 --- /dev/null +++ b/bundles/org.openhab.transform.vat/NOTICE @@ -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 diff --git a/bundles/org.openhab.transform.vat/README.md b/bundles/org.openhab.transform.vat/README.md new file mode 100644 index 0000000000000..a39046905d340 --- /dev/null +++ b/bundles/org.openhab.transform.vat/README.md @@ -0,0 +1,41 @@ +# VAT Transformation Service + +The VAT Transformation Service adds VAT (Value-Added Tax) to a given input amount. +The input string must be either an ISO 3166 alpha-2 country code or a percentage, i.e. numerical format. + +## Examples + +### Display + +```java +Number CurrentSpotPrice "Current Spot Price incl. VAT [VAT(12.5):%s]" +``` + +### In a Rule + +Add Danish VAT to price: + +```java +var Number price = 499 +logInfo("Price", "Price incl. VAT: " + transform("VAT", "DK", price.toString)) +``` + +## Usage as a Profile + +The functionality of this `TransformationService` can also be used in a `Profile` on an `ItemChannelLink`. +This is the most powerful usage since VAT will be added without providing any explicit country code, percentage or configuration. +To use this, an `.items` file can be configured as follows: + +```java +Number CurrentSpotPrice "Current Spot Price" { channel="" [profile="transform:VAT"] } +``` + +To override VAT percentage for configured system country: + +```java +Number CurrentSpotPrice "Current Spot Price" { channel="" [profile="transform:VAT", percentage="12.5"] } +``` + +If VAT is not known for the configured country or the provided percentage is invalid, the default is 0%, so the input value will be put into the transformation without any changes. + +Please note: This profile is a one-way transformation, i.e. only values from a device towards the item are changed, the other direction is left untouched. diff --git a/bundles/org.openhab.transform.vat/pom.xml b/bundles/org.openhab.transform.vat/pom.xml new file mode 100644 index 0000000000000..f8660d1cc686e --- /dev/null +++ b/bundles/org.openhab.transform.vat/pom.xml @@ -0,0 +1,17 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 4.0.0-SNAPSHOT + + + org.openhab.transform.vat + + openHAB Add-ons :: Bundles :: Transformation Service :: Value-Added Tax + + diff --git a/bundles/org.openhab.transform.vat/src/main/feature/feature.xml b/bundles/org.openhab.transform.vat/src/main/feature/feature.xml new file mode 100644 index 0000000000000..2a75b2a2bbb93 --- /dev/null +++ b/bundles/org.openhab.transform.vat/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.transform.vat/${project.version} + + diff --git a/bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/VATTransformationConstants.java b/bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/VATTransformationConstants.java new file mode 100644 index 0000000000000..99689ee9a4207 --- /dev/null +++ b/bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/VATTransformationConstants.java @@ -0,0 +1,205 @@ +/** + * Copyright (c) 2010-2023 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.transform.vat.internal; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.profiles.ProfileTypeUID; +import org.openhab.core.transform.TransformationService; + +/** + * The {@link VATTransformationConstants} class defines constants + * used across the whole profile. + * + * @author Jacob Laursen - Initial contribution + */ +@NonNullByDefault +public class VATTransformationConstants { + + public static final ProfileTypeUID PROFILE_TYPE_UID = new ProfileTypeUID( + TransformationService.TRANSFORM_PROFILE_SCOPE, "VAT"); + + public static final Map RATES = Map.ofEntries( + // European Union countries + Map.entry("AT", "20"), // Austria + Map.entry("BE", "21"), // Belgium + Map.entry("BG", "20"), // Bulgaria + Map.entry("HR", "25"), // Croatia + Map.entry("CY", "19"), // Cyprus + Map.entry("CZ", "21"), // Czech Republic + Map.entry("DK", "25"), // Denmark + Map.entry("EE", "20"), // Estonia + Map.entry("FI", "24"), // Finland + Map.entry("FR", "20"), // France + Map.entry("DE", "19"), // Germany + Map.entry("GR", "24"), // Greece + Map.entry("HU", "27"), // Hungary + Map.entry("IE", "23"), // Ireland + Map.entry("IT", "22"), // Italy + Map.entry("LV", "21"), // Latvia + Map.entry("LT", "21"), // Lithuania + Map.entry("LU", "17"), // Luxembourg + Map.entry("MT", "18"), // Malta + Map.entry("NL", "21"), // Netherlands + Map.entry("PL", "23"), // Poland + Map.entry("PT", "23"), // Portugal + Map.entry("RO", "19"), // Romania + Map.entry("SK", "20"), // Slovakia + Map.entry("SI", "22"), // Slovenia + Map.entry("ES", "21"), // Spain + Map.entry("SE", "25"), // Sweden + + // Non-European Union countries + Map.entry("AL", "20"), // Albania + Map.entry("DZ", "19"), // Algeria + Map.entry("AD", "4.5"), // Andorra + Map.entry("AO", "14"), // Angola + Map.entry("AG", "15"), // Antigua and Barbuda + Map.entry("AR", "21"), // Argentina + Map.entry("AM", "20"), // Armenia + Map.entry("AU", "10"), // Australia + Map.entry("AZ", "18"), // Azerbaijan + Map.entry("BS", "12"), // Bahamas + Map.entry("BH", "10"), // Bahrain + Map.entry("BD", "15"), // Bangladesh + Map.entry("BB", "17.5"), // Barbados + Map.entry("BY", "20"), // Belarus + Map.entry("BZ", "12.5"), // Belize + Map.entry("BJ", "18"), // Benin + Map.entry("BO", "13"), // Bolivia + Map.entry("BA", "17"), // Bosnia and Herzegovina + Map.entry("BW", "12"), // Botswana + Map.entry("BR", "20"), // Brazil + Map.entry("BF", "18"), // Burkina Faso + Map.entry("BI", "18"), // Burundi + Map.entry("KH", "10"), // Cambodia + Map.entry("CM", "19.25"), // Cameroon + Map.entry("CA", "5"), // Canada + Map.entry("CV", "15"), // Cape Verde + Map.entry("CF", "19"), // Central African Republic + Map.entry("TD", "18"), // Chad + Map.entry("CL", "19"), // Chile + Map.entry("CN", "13"), // China + Map.entry("CO", "19"), // Colombia + Map.entry("CR", "13"), // Costa Rica + Map.entry("CD", "16"), // Democratic Republic of the Congo + Map.entry("DM", "15"), // Dominica + Map.entry("DO", "18"), // Dominican Republic + Map.entry("EC", "12"), // Ecuador + Map.entry("EG", "14"), // Egypt + Map.entry("SV", "13"), // El Salvador + Map.entry("GQ", "15"), // Equatorial Guinea + Map.entry("ET", "15"), // Ethiopia + Map.entry("FO", "25"), // Faroe Islands + Map.entry("FJ", "15"), // Fiji + Map.entry("GA", "18"), // Gabon + Map.entry("GM", "15"), // Gambia + Map.entry("GE", "18"), // Georgia + Map.entry("GH", "15"), // Ghana + Map.entry("GD", "15"), // Grenada + Map.entry("GT", "12"), // Guatemala + Map.entry("GN", "18"), // Guinea + Map.entry("GW", "15"), // Guinea-Bissau + Map.entry("GY", "16"), // Guyana + Map.entry("HT", "10"), // Haiti + Map.entry("HN", "15"), // Honduras + Map.entry("IS", "24"), // Iceland + Map.entry("IN", "5.5"), // India + Map.entry("ID", "11"), // Indonesia + Map.entry("IR", "9"), // Iran + Map.entry("IM", "20"), // Isle of Man + Map.entry("IL", "17"), // Israel + Map.entry("CI", "18"), // Ivory Coast + Map.entry("JM", "12.5"), // Jamaica + Map.entry("JP", "10"), // Japan + Map.entry("JE", "5"), // Jersey + Map.entry("JO", "16"), // Jordan + Map.entry("KZ", "12"), // Kazakhstan + Map.entry("KE", "16"), // Kenya + Map.entry("KG", "20"), // Kyrgyzstan + Map.entry("LA", "10"), // Laos + Map.entry("LB", "11"), // Lebanon + Map.entry("LS", "14"), // Lesotho + Map.entry("LI", "7.7"), // Liechtenstein + Map.entry("MG", "20"), // Madagascar + Map.entry("MW", "16.5"), // Malawi + Map.entry("MY", "6"), // Malaysia + Map.entry("MV", "6"), // Maldives + Map.entry("ML", "18"), // Mali + Map.entry("MR", "14"), // Mauritania + Map.entry("MU", "15"), // Mauritius + Map.entry("MX", "16"), // Mexico + Map.entry("MD", "20"), // Moldova + Map.entry("MC", "19.6"), // Monaco + Map.entry("MN", "10"), // Mongolia + Map.entry("ME", "21"), // Montenegro + Map.entry("MA", "20"), // Morocco + Map.entry("MZ", "17"), // Mozambique + Map.entry("NA", "15"), // Namibia + Map.entry("NP", "13"), // Nepal + Map.entry("NZ", "15"), // New Zealand + Map.entry("NI", "15"), // Nicaragua + Map.entry("NE", "19"), // Niger + Map.entry("NG", "7.5"), // Nigeria + Map.entry("NU", "5"), // Niue + Map.entry("MK", "18"), // North Macedonia + Map.entry("NO", "25"), // Norway + Map.entry("PK", "17"), // Pakistan + Map.entry("PW", "10"), // Palau + Map.entry("PS", "16"), // Palestine + Map.entry("PA", "7"), // Panama + Map.entry("PG", "10"), // Papua New Guinea + Map.entry("PY", "10"), // Paraguay + Map.entry("PE", "18"), // Peru + Map.entry("PH", "12"), // Philippines + Map.entry("CG", "16"), // Republic of Congo + Map.entry("RU", "20"), // Russia + Map.entry("RW", "18"), // Rwanda + Map.entry("KN", "17"), // Saint Kitts and Nevis + Map.entry("VC", "15"), // Saint Vincent and the Grenadines + Map.entry("WS", "15"), // Samoa + Map.entry("SA", "15"), // Saudi Arabia + Map.entry("SN", "18"), // Senegal + Map.entry("RS", "20"), // Serbia + Map.entry("SC", "15"), // Seychelles + Map.entry("SL", "15"), // Sierra Leone + Map.entry("SG", "8"), // Singapore + Map.entry("ZA", "15"), // South Africa + Map.entry("KR", "10"), // South Korea + Map.entry("LK", "12"), // Sri Lanka + Map.entry("SD", "17"), // Sudan + Map.entry("CH", "7.7"), // Switzerland + Map.entry("TW", "5"), // Taiwan + Map.entry("TJ", "20"), // Tajikistan + Map.entry("TZ", "18"), // Tanzania + Map.entry("TH", "10"), // Thailand + Map.entry("TG", "18"), // Togo + Map.entry("TO", "15"), // Tonga + Map.entry("TT", "12.5"), // Trinidad and Tobago + Map.entry("TN", "18"), // Tunisia + Map.entry("TR", "18"), // Turkey + Map.entry("TM", "15"), // Turkmenistan + Map.entry("UG", "18"), // Uganda + Map.entry("UA", "20"), // Ukraine + Map.entry("AE", "5"), // United Arab Emirates + Map.entry("GB", "20"), // United Kingdom + Map.entry("UY", ""), // Uruguay + Map.entry("UZ", "12"), // Uzbekistan + Map.entry("VU", "13"), // Vanuatu + Map.entry("VN", "10"), // Vietnam + Map.entry("VE", "12"), // Venezuela + Map.entry("ZM", "16"), // Zambia + Map.entry("ZW", "15") // Zimbabwe + ); +} diff --git a/bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/VATTransformationService.java b/bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/VATTransformationService.java new file mode 100644 index 0000000000000..a0518c8811d2d --- /dev/null +++ b/bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/VATTransformationService.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2010-2023 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.transform.vat.internal; + +import static org.openhab.transform.vat.internal.VATTransformationConstants.*; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.transform.TransformationException; +import org.openhab.core.transform.TransformationService; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This {@link TransformationService} adds VAT to the input according to configured country. + * + * @author Jacob Laursen - Initial contribution + */ +@NonNullByDefault +@Component(service = { TransformationService.class }, property = { "openhab.transform=VAT" }) +public class VATTransformationService implements TransformationService { + + private final Logger logger = LoggerFactory.getLogger(VATTransformationService.class); + + @Override + public @Nullable String transform(String valueString, String sourceString) throws TransformationException { + QuantityType source; + try { + source = new QuantityType<>(sourceString); + } catch (IllegalArgumentException e) { + logger.warn("Input value '{}' could not be converted to a valid number", sourceString); + throw new TransformationException("VAT Transformation can only be used with numeric inputs", e); + } + BigDecimal value; + try { + value = new BigDecimal(valueString); + } catch (NumberFormatException e) { + String rate = RATES.get(valueString); + if (rate == null) { + logger.warn("Input value '{}' could not be converted to a valid number or country code", valueString); + throw new TransformationException("VAT Transformation can only be used with numeric inputs", e); + } + value = new BigDecimal(rate); + } + + return addVAT(source, value).toString(); + } + + private QuantityType addVAT(QuantityType source, BigDecimal percentage) { + return source.multiply(percentage.divide(new BigDecimal("100")).add(BigDecimal.ONE)); + } +} diff --git a/bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/config/VATConfig.java b/bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/config/VATConfig.java new file mode 100644 index 0000000000000..c2d83a45885b3 --- /dev/null +++ b/bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/config/VATConfig.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2010-2023 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.transform.vat.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link VATConfig} class contains the parameters for the VAT transformation. + * + * @author Jacob Laursen - initial contribution + * + */ +@NonNullByDefault +public class VATConfig { + public String percentage = ""; +} diff --git a/bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/profile/VATTransformationProfile.java b/bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/profile/VATTransformationProfile.java new file mode 100644 index 0000000000000..1985d9beb5f66 --- /dev/null +++ b/bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/profile/VATTransformationProfile.java @@ -0,0 +1,131 @@ +/** + * Copyright (c) 2010-2023 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.transform.vat.internal.profile; + +import static org.openhab.transform.vat.internal.VATTransformationConstants.*; + +import java.math.BigDecimal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.i18n.LocaleProvider; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.thing.profiles.ProfileCallback; +import org.openhab.core.thing.profiles.ProfileContext; +import org.openhab.core.thing.profiles.ProfileTypeUID; +import org.openhab.core.thing.profiles.StateProfile; +import org.openhab.core.transform.TransformationException; +import org.openhab.core.transform.TransformationHelper; +import org.openhab.core.transform.TransformationService; +import org.openhab.core.types.Command; +import org.openhab.core.types.State; +import org.openhab.core.types.Type; +import org.openhab.transform.vat.internal.config.VATConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Profile to offer the {@link VATTransformationService} on an ItemChannelLink. + * + * @author Jacob Laursen - Initial contribution + */ +@NonNullByDefault +public class VATTransformationProfile implements StateProfile { + + private final Logger logger = LoggerFactory.getLogger(VATTransformationProfile.class); + + private final ProfileCallback callback; + private final TransformationService service; + private final LocaleProvider localeProvider; + + private VATConfig configuration; + + public VATTransformationProfile(final ProfileCallback callback, final TransformationService service, + final ProfileContext context, LocaleProvider localeProvider) { + this.callback = callback; + this.service = service; + this.localeProvider = localeProvider; + this.configuration = context.getConfiguration().as(VATConfig.class); + } + + @Override + public ProfileTypeUID getProfileTypeUID() { + return PROFILE_TYPE_UID; + } + + @Override + public void onCommandFromItem(Command command) { + callback.handleCommand(command); + } + + @Override + public void onStateUpdateFromItem(State state) { + } + + @Override + public void onCommandFromHandler(Command command) { + callback.sendCommand((Command) transformState(command)); + } + + @Override + public void onStateUpdateFromHandler(State state) { + callback.sendUpdate((State) transformState(state)); + } + + private Type transformState(Type state) { + String result = state.toFullString(); + try { + result = TransformationHelper.transform(service, getVATPercentage(), "%s", result); + } catch (TransformationException e) { + logger.warn("Could not apply '{}' transformation on state '{}' with value '{}'.", PROFILE_TYPE_UID.getId(), + state, getVATPercentage()); + } + Type resultType = state; + if (result != null) { + if (state instanceof DecimalType) { + resultType = DecimalType.valueOf(result); + } else if (state instanceof QuantityType) { + resultType = QuantityType.valueOf(result); + } + logger.debug("Transformed '{}' into '{}'", state, resultType); + } + return resultType; + } + + private String getVATPercentage() { + if (!configuration.percentage.isBlank()) { + return getOverriddenVAT(); + } + + String country = localeProvider.getLocale().getCountry(); + String rate = RATES.get(country); + if (rate == null) { + logger.warn("No VAT rate for country {}", country); + return "0"; + } + return rate; + } + + private String getOverriddenVAT() { + String percentage = configuration.percentage.trim(); + if (percentage.endsWith("%")) { + percentage = percentage.substring(0, percentage.length() - 1).trim(); + } + try { + return new BigDecimal(percentage).toString(); + } catch (NumberFormatException e) { + logger.warn("{} is not a valid percentage", percentage); + return "0"; + } + } +} diff --git a/bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/profile/VATTransformationProfileFactory.java b/bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/profile/VATTransformationProfileFactory.java new file mode 100644 index 0000000000000..67f254d6807ae --- /dev/null +++ b/bundles/org.openhab.transform.vat/src/main/java/org/openhab/transform/vat/internal/profile/VATTransformationProfileFactory.java @@ -0,0 +1,111 @@ +/** + * Copyright (c) 2010-2023 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.transform.vat.internal.profile; + +import static org.openhab.transform.vat.internal.VATTransformationConstants.PROFILE_TYPE_UID; + +import java.util.Collection; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.i18n.LocaleProvider; +import org.openhab.core.i18n.LocalizedKey; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.thing.profiles.Profile; +import org.openhab.core.thing.profiles.ProfileCallback; +import org.openhab.core.thing.profiles.ProfileContext; +import org.openhab.core.thing.profiles.ProfileFactory; +import org.openhab.core.thing.profiles.ProfileType; +import org.openhab.core.thing.profiles.ProfileTypeBuilder; +import org.openhab.core.thing.profiles.ProfileTypeProvider; +import org.openhab.core.thing.profiles.ProfileTypeUID; +import org.openhab.core.thing.profiles.i18n.ProfileTypeI18nLocalizationService; +import org.openhab.core.transform.TransformationService; +import org.openhab.core.util.BundleResolver; +import org.osgi.framework.Bundle; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +/** + * The {@link VATTransformationProfileFactory} is responsible for creating profiles. + * + * @author Jacob Laursen - Initial contribution + */ +@NonNullByDefault +@Component(service = { ProfileFactory.class, ProfileTypeProvider.class }) +public class VATTransformationProfileFactory implements ProfileFactory, ProfileTypeProvider { + + private final LocaleProvider localeProvider; + private final ProfileTypeI18nLocalizationService profileTypeI18nLocalizationService; + private final Map localizedProfileTypeCache = new ConcurrentHashMap<>(); + private final Bundle bundle; + + private @NonNullByDefault({}) TransformationService transformationService; + + @Activate + public VATTransformationProfileFactory(final @Reference LocaleProvider localeProvider, + final @Reference ProfileTypeI18nLocalizationService profileTypeI18nLocalizationService, + final @Reference BundleResolver bundleResolver) { + this.localeProvider = localeProvider; + this.profileTypeI18nLocalizationService = profileTypeI18nLocalizationService; + this.bundle = bundleResolver.resolveBundle(VATTransformationProfileFactory.class); + } + + @Override + public Collection getProfileTypes(@Nullable Locale locale) { + return Set.of(createLocalizedProfileType(ProfileTypeBuilder.newState(PROFILE_TYPE_UID, PROFILE_TYPE_UID.getId()) + .withSupportedItemTypes(CoreItemFactory.NUMBER).build(), locale)); + } + + @Override + public @Nullable Profile createProfile(ProfileTypeUID profileTypeUID, ProfileCallback callback, + ProfileContext profileContext) { + return new VATTransformationProfile(callback, transformationService, profileContext, localeProvider); + } + + private ProfileType createLocalizedProfileType(ProfileType profileType, @Nullable Locale locale) { + final LocalizedKey localizedKey = new LocalizedKey(profileType.getUID(), + locale != null ? locale.toLanguageTag() : null); + + final ProfileType cachedlocalizedProfileType = localizedProfileTypeCache.get(localizedKey); + if (cachedlocalizedProfileType != null) { + return cachedlocalizedProfileType; + } + + final ProfileType localizedProfileType = profileTypeI18nLocalizationService.createLocalizedProfileType(bundle, + profileType, locale); + localizedProfileTypeCache.put(localizedKey, localizedProfileType); + + return localizedProfileType; + } + + @Override + public Collection getSupportedProfileTypeUIDs() { + return List.of(PROFILE_TYPE_UID); + } + + @Reference(target = "(openhab.transform=VAT)") + public void addTransformationService(TransformationService service) { + this.transformationService = service; + } + + public void removeTransformationService(TransformationService service) { + this.transformationService = null; + } +} diff --git a/bundles/org.openhab.transform.vat/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.transform.vat/src/main/resources/OH-INF/addon/addon.xml new file mode 100644 index 0000000000000..21368d9969a9f --- /dev/null +++ b/bundles/org.openhab.transform.vat/src/main/resources/OH-INF/addon/addon.xml @@ -0,0 +1,10 @@ + + + + transformation + VAT transformation + Transforms the input by adding Value-Added Tax according to configured country or percentage. + + diff --git a/bundles/org.openhab.transform.vat/src/main/resources/OH-INF/config/vatProfile.xml b/bundles/org.openhab.transform.vat/src/main/resources/OH-INF/config/vatProfile.xml new file mode 100644 index 0000000000000..3d95b561a7fbe --- /dev/null +++ b/bundles/org.openhab.transform.vat/src/main/resources/OH-INF/config/vatProfile.xml @@ -0,0 +1,14 @@ + + + + + + + Manually configured VAT percentage (overriding VAT rate for configured country). + true + + + diff --git a/bundles/org.openhab.transform.vat/src/main/resources/OH-INF/i18n/vat.properties b/bundles/org.openhab.transform.vat/src/main/resources/OH-INF/i18n/vat.properties new file mode 100644 index 0000000000000..a4bf2f3ae7f16 --- /dev/null +++ b/bundles/org.openhab.transform.vat/src/main/resources/OH-INF/i18n/vat.properties @@ -0,0 +1,10 @@ +# add-on + +addon.vat.name = VAT transformation +addon.vat.description = Transforms the input by adding Value-Added Tax according to configured country or percentage. + +# profile + +profile-type.transform.VAT.label = Value-Added Tax +profile.config.transform.VAT.percentage.label = VAT Percentage +profile.config.transform.VAT.percentage.description = Manually configured VAT percentage (overriding VAT rate for configured country). diff --git a/bundles/pom.xml b/bundles/pom.xml index e22d555988c61..331fb17020a33 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -41,6 +41,7 @@ org.openhab.transform.regex org.openhab.transform.rollershutterposition org.openhab.transform.scale + org.openhab.transform.vat org.openhab.transform.xpath org.openhab.transform.xslt