From fbcb412353c98615c22c3aa67a92f08a2d9f2fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Lange?= Date: Fri, 24 Mar 2023 23:15:28 +0100 Subject: [PATCH] [mielecloud] Add channels energy and water consumption (#14456) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add POJOs for ecoFeedback from Miele REST API * DeviceState offers water and energy consumption * Convert Quantity to State for channel population * Add eco feedback channels to devices * Fix item types and categories * Add update instructions for eco feedback channels Signed-off-by: Björn Lange --- .../org.openhab.binding.mielecloud/README.md | 9 + .../internal/MieleCloudBindingConstants.java | 4 +- .../handler/DishwasherDeviceThingHandler.java | 4 +- .../handler/DryerDeviceThingHandler.java | 3 +- .../handler/WashingDeviceThingHandler.java | 4 +- .../handler/channel/ChannelTypeUtil.java | 55 +++ .../handler/channel/DeviceChannelState.java | 10 +- .../internal/webservice/api/DeviceState.java | 33 +- .../internal/webservice/api/Quantity.java | 70 ++++ .../webservice/api/json/EcoFeedback.java | 89 +++++ .../api/json/EnergyConsumption.java | 71 ++++ .../internal/webservice/api/json/State.java | 16 +- .../webservice/api/json/WaterConsumption.java | 71 ++++ .../OH-INF/i18n/mielecloud.properties | 6 + .../resources/OH-INF/thing/channelTypes.xml | 24 ++ .../OH-INF/thing/dishwasherDevice.xml | 3 + .../resources/OH-INF/thing/dryerDevice.xml | 2 + .../resources/OH-INF/thing/washerDryer.xml | 3 + .../resources/OH-INF/thing/washingMachine.xml | 3 + .../resources/OH-INF/update/instructions.xml | 46 +++ .../handler/channel/ChannelTypeUtilTest.java | 53 +++ .../webservice/api/DeviceStateTest.java | 335 +++++++++++++++++- .../api/json/DeviceCollectionTest.java | 29 ++ .../json/deviceCollectionWithEcoFeedback.json | 142 ++++++++ .../AbstractMieleThingHandlerTest.java | 6 +- .../handler/CoffeeDeviceThingHandlerTest.java | 2 +- .../CoolingDeviceThingHandlerTest.java | 2 +- .../DishWarmerDeviceThingHandlerTest.java | 2 +- .../DishwasherDeviceThingHandlerTest.java | 15 +- .../handler/DryerDeviceThingHandlerTest.java | 11 +- .../handler/HobDeviceThingHandlerTest.java | 2 +- .../handler/HoodDeviceThingHandlerTest.java | 2 +- .../handler/MieleHandlerFactoryTest.java | 26 +- .../handler/OvenDeviceThingHandlerTest.java | 2 +- ...icVacuumCleanerDeviceThingHandlerTest.java | 3 +- .../WashingDeviceThingHandlerTest.java | 14 +- .../WineStorageDeviceThingHandlerTest.java | 2 +- 37 files changed, 1133 insertions(+), 41 deletions(-) create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/Quantity.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/EcoFeedback.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/EnergyConsumption.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/WaterConsumption.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/update/instructions.xml create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/handler/channel/ChannelTypeUtilTest.java create mode 100644 bundles/org.openhab.binding.mielecloud/src/test/resources/org/openhab/binding/mielecloud/internal/webservice/api/json/deviceCollectionWithEcoFeedback.json diff --git a/bundles/org.openhab.binding.mielecloud/README.md b/bundles/org.openhab.binding.mielecloud/README.md index eca75279f5b..8e8e42e21a7 100644 --- a/bundles/org.openhab.binding.mielecloud/README.md +++ b/bundles/org.openhab.binding.mielecloud/README.md @@ -151,6 +151,8 @@ Channel ID and channel type ID match unless noted. | plate_power_step_raw | Number | The raw power level of the heating plate. | Yes | | door_state | Switch | Indicates if the door of the device is open. | Yes | | door_alarm | Switch | Indicates if the door alarm of the device is active. | Yes | +| water_consumption_current | Number | The amount of water used by the current running program up to the present moment. | Yes | +| energy_consumption_current | Number | The amount of energy used by the current running program up to the present moment. | Yes | | battery_level | Number | The battery level of the robotic vacuum cleaner. | Yes | ### Coffee System @@ -215,6 +217,8 @@ Channel ID and channel type ID match unless noted. - error_state - info_state - door_state +- water_consumption_current +- energy_consumption_current ### Tumble Dryer @@ -242,6 +246,7 @@ Channel ID and channel type ID match unless noted. - light_switch - light_can_be_controlled - door_state +- energy_consumption_current ### Freezer @@ -387,6 +392,8 @@ Channel ID and channel type ID match unless noted. - light_switch - light_can_be_controlled - door_state +- water_consumption_current +- energy_consumption_current ### Washing Machine @@ -415,6 +422,8 @@ Channel ID and channel type ID match unless noted. - light_switch - light_can_be_controlled - door_state +- water_consumption_current +- energy_consumption_current ### Wine Storage diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/MieleCloudBindingConstants.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/MieleCloudBindingConstants.java index cbb3f881069..5e9549afdf1 100644 --- a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/MieleCloudBindingConstants.java +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/MieleCloudBindingConstants.java @@ -23,7 +23,7 @@ import org.openhab.core.thing.ThingTypeUID; * @author Björn Lange - Added locale config parameter, added i18n key collection * @author Benjamin Bolte - Add pre-heat finished and plate step channels, door state and door alarm channels, info * state channel and map signal flags from API - * @author Björn Lange - Add elapsed time channel, dish warmer thing, removed e-mail validation + * @author Björn Lange - Add elapsed time channel, dish warmer thing, removed e-mail validation, add eco feedback */ @NonNullByDefault public final class MieleCloudBindingConstants { @@ -214,6 +214,8 @@ public final class MieleCloudBindingConstants { public static final String PLATE_6_POWER_STEP_RAW = "plate_6_power_step_raw"; public static final String DOOR_STATE = "door_state"; public static final String DOOR_ALARM = "door_alarm"; + public static final String WATER_CONSUMPTION_CURRENT = "water_consumption_current"; + public static final String ENERGY_CONSUMPTION_CURRENT = "energy_consumption_current"; public static final String BATTERY_LEVEL = "battery_level"; } diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishwasherDeviceThingHandler.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishwasherDeviceThingHandler.java index 1d5dd0faba5..d210207bce3 100644 --- a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishwasherDeviceThingHandler.java +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishwasherDeviceThingHandler.java @@ -26,7 +26,7 @@ import org.openhab.core.thing.Thing; * @author Roland Edelhoff - Initial contribution * @author Björn Lange - Add channel state wrappers * @author Benjamin Bolte - Add info state channel and map signal flags from API - * @author Björn Lange - Add elapsed time channel + * @author Björn Lange - Add elapsed time, current water and energy consumption channels */ @NonNullByDefault public class DishwasherDeviceThingHandler extends AbstractMieleThingHandler { @@ -54,6 +54,8 @@ public class DishwasherDeviceThingHandler extends AbstractMieleThingHandler { updateState(channel(ERROR_STATE), device.getErrorState()); updateState(channel(INFO_STATE), device.getInfoState()); updateState(channel(DOOR_STATE), device.getDoorState()); + updateState(channel(WATER_CONSUMPTION_CURRENT), device.getCurrentWaterConsumption()); + updateState(channel(ENERGY_CONSUMPTION_CURRENT), device.getCurrentEnergyConsumption()); } @Override diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/DryerDeviceThingHandler.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/DryerDeviceThingHandler.java index a69b2c3c70d..d78ce68bcfb 100644 --- a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/DryerDeviceThingHandler.java +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/DryerDeviceThingHandler.java @@ -26,7 +26,7 @@ import org.openhab.core.thing.Thing; * @author Roland Edelhoff - Initial contribution * @author Björn Lange - Add channel state wrappers * @author Benjamin Bolte - Add info state channel and map signal flags from API - * @author Björn Lange - Add elapsed time channel + * @author Björn Lange - Add elapsed time, current water and energy consumption channels */ @NonNullByDefault public class DryerDeviceThingHandler extends AbstractMieleThingHandler { @@ -57,6 +57,7 @@ public class DryerDeviceThingHandler extends AbstractMieleThingHandler { updateState(channel(INFO_STATE), device.getInfoState()); updateState(channel(LIGHT_SWITCH), device.getLightSwitch()); updateState(channel(DOOR_STATE), device.getDoorState()); + updateState(channel(ENERGY_CONSUMPTION_CURRENT), device.getCurrentEnergyConsumption()); } @Override diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/WashingDeviceThingHandler.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/WashingDeviceThingHandler.java index 08e05a26d7d..6102e0e32c6 100644 --- a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/WashingDeviceThingHandler.java +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/WashingDeviceThingHandler.java @@ -26,7 +26,7 @@ import org.openhab.core.thing.Thing; * @author Roland Edelhoff - Initial contribution * @author Björn Lange - Add channel state wrappers * @author Benjamin Bolte - Add info state channel and map signal flags from API - * @author Björn Lange - Add elapsed time channel + * @author Björn Lange - Add elapsed time, current water and energy consumption channels */ @NonNullByDefault public class WashingDeviceThingHandler extends AbstractMieleThingHandler { @@ -58,6 +58,8 @@ public class WashingDeviceThingHandler extends AbstractMieleThingHandler { updateState(channel(INFO_STATE), device.getInfoState()); updateState(channel(LIGHT_SWITCH), device.getLightSwitch()); updateState(channel(DOOR_STATE), device.getDoorState()); + updateState(channel(WATER_CONSUMPTION_CURRENT), device.getCurrentWaterConsumption()); + updateState(channel(ENERGY_CONSUMPTION_CURRENT), device.getCurrentEnergyConsumption()); } @Override diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/channel/ChannelTypeUtil.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/channel/ChannelTypeUtil.java index 864de697573..a1a50d28a71 100644 --- a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/channel/ChannelTypeUtil.java +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/channel/ChannelTypeUtil.java @@ -13,9 +13,12 @@ package org.openhab.binding.mielecloud.internal.handler.channel; import java.math.BigDecimal; +import java.text.NumberFormat; +import java.util.Locale; import java.util.Optional; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.mielecloud.internal.webservice.api.Quantity; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.QuantityType; @@ -23,6 +26,8 @@ import org.openhab.core.library.types.StringType; import org.openhab.core.library.unit.SIUnits; import org.openhab.core.types.State; import org.openhab.core.types.UnDefType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Utility class handling type conversions from Java types to channel types. @@ -31,6 +36,8 @@ import org.openhab.core.types.UnDefType; */ @NonNullByDefault public final class ChannelTypeUtil { + private static final Logger LOGGER = LoggerFactory.getLogger(ChannelTypeUtil.class); + private ChannelTypeUtil() { throw new IllegalStateException("ChannelTypeUtil cannot be instantiated."); } @@ -71,4 +78,52 @@ public final class ChannelTypeUtil { // The Miele 3rd Party API always provides temperatures in °C (even if the device uses another unit). return value.map(v -> (State) new QuantityType<>(v, SIUnits.CELSIUS)).orElse(UnDefType.UNDEF); } + + /** + * Converts an {@link Optional} of {@link Quantity} to {@link State}. + */ + public static State quantityToState(Optional value) { + return value.flatMap(ChannelTypeUtil::formatQuantity).flatMap(ChannelTypeUtil::parseQuantityType) + .orElse(UnDefType.UNDEF); + } + + /** + * Formats the quantity as "value unit" with the given locale. + * + * @param locale The locale to format with. + * @return An {@link Optional} containing the formatted quantity value or an empty {@link Optional} if formatting + * for the given locale failed. + */ + private static Optional formatQuantity(Quantity quantity) { + double value = quantity.getValue(); + try { + var formatted = NumberFormat.getInstance(Locale.ENGLISH).format(value); + + var unit = quantity.getUnit(); + if (unit.isPresent()) { + formatted = formatted + " " + unit.get(); + } + + return Optional.of(formatted); + } catch (ArithmeticException e) { + LOGGER.warn("Failed to format {}", value, e); + return Optional.empty(); + } + } + + /** + * Parses a previously formatted {@link Quantity} into a {@link State}. + * + * @param value The quantity value formatted as "value unit". + * @return An {@link Optional} containing the parsed {@link State} or an empty {@link Optional} if the quantity + * including unit could not be parsed. + */ + private static Optional parseQuantityType(String value) { + try { + return Optional.of((State) new QuantityType<>(value)); + } catch (IllegalArgumentException e) { + LOGGER.warn("Failed to convert {} to quantity: {}", value, e.getMessage(), e); + return Optional.empty(); + } + } } diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/channel/DeviceChannelState.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/channel/DeviceChannelState.java index f517002f297..dd472fc635c 100644 --- a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/channel/DeviceChannelState.java +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/handler/channel/DeviceChannelState.java @@ -35,7 +35,7 @@ import org.openhab.core.types.State; * @author Björn Lange - Initial contribution * @author Benjamin Bolte - Add pre-heat finished, plate step, door state, door alarm and info state channel and map * signal flags from API - * @author Björn Lange - Add elapsed time channel, dish warmer and robotic vacuum cleaner thing + * @author Björn Lange - Add elapsed time channel, dish warmer and robotic vacuum cleaner thing, eco feedback */ @NonNullByDefault public final class DeviceChannelState { @@ -185,6 +185,14 @@ public final class DeviceChannelState { return ChannelTypeUtil.intToState(device.getSpinningSpeedRaw()); } + public State getCurrentWaterConsumption() { + return ChannelTypeUtil.quantityToState(device.getCurrentWaterConsumption()); + } + + public State getCurrentEnergyConsumption() { + return ChannelTypeUtil.quantityToState(device.getCurrentEnergyConsumption()); + } + public State getBatteryLevel() { return ChannelTypeUtil.intToState(device.getBatteryLevel()); } diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/DeviceState.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/DeviceState.java index 7c99e1b54f3..64477b6f911 100644 --- a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/DeviceState.java +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/DeviceState.java @@ -22,6 +22,7 @@ import org.openhab.binding.mielecloud.internal.webservice.api.json.Device; import org.openhab.binding.mielecloud.internal.webservice.api.json.DeviceIdentLabel; import org.openhab.binding.mielecloud.internal.webservice.api.json.DeviceType; import org.openhab.binding.mielecloud.internal.webservice.api.json.DryingStep; +import org.openhab.binding.mielecloud.internal.webservice.api.json.EcoFeedback; import org.openhab.binding.mielecloud.internal.webservice.api.json.Ident; import org.openhab.binding.mielecloud.internal.webservice.api.json.Light; import org.openhab.binding.mielecloud.internal.webservice.api.json.PlateStep; @@ -43,7 +44,7 @@ import org.openhab.binding.mielecloud.internal.webservice.api.json.VentilationSt * @author Björn Lange - Introduced null handling * @author Benjamin Bolte - Add pre-heat finished, plate step, door state, door alarm, info state channel and map signal * flags from API - * @author Björn Lange - Add elapsed time channel, dish warmer and robotic vacuum cleaner things + * @author Björn Lange - Add elapsed time channel, dish warmer and robotic vacuum cleaner things, eco feedback */ @NonNullByDefault public class DeviceState { @@ -458,6 +459,36 @@ public class DeviceState { return Optional.of(doorState.get() && failure.get()); } + /** + * Gets the amount of water consumed since the currently running program started. + * + * @return The amount of water consumed since the currently running program started. + */ + public Optional getCurrentWaterConsumption() { + if (deviceIsInOffState()) { + return Optional.empty(); + } + + return device.flatMap(Device::getState).flatMap(State::getEcoFeedback) + .flatMap(EcoFeedback::getCurrentWaterConsumption).flatMap(consumption -> consumption.getValue() + .map(value -> new Quantity(value, consumption.getUnit().orElse(null)))); + } + + /** + * Gets the amount of energy consumed since the currently running program started. + * + * @return The amount of energy consumed since the currently running program started. + */ + public Optional getCurrentEnergyConsumption() { + if (deviceIsInOffState()) { + return Optional.empty(); + } + + return device.flatMap(Device::getState).flatMap(State::getEcoFeedback) + .flatMap(EcoFeedback::getCurrentEnergyConsumption).flatMap(consumption -> consumption.getValue() + .map(value -> new Quantity(value, consumption.getUnit().orElse(null)))); + } + /** * Gets the battery level. * diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/Quantity.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/Quantity.java new file mode 100644 index 00000000000..f5291900cb7 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/Quantity.java @@ -0,0 +1,70 @@ +/** + * 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.binding.mielecloud.internal.webservice.api; + +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * A physical quantity as obtained from the Miele REST API. + * + * @author Björn Lange - Initial contribution + */ +@NonNullByDefault +public class Quantity { + double value; + Optional unit; + + public Quantity(double value, @Nullable String unit) { + this.value = value; + this.unit = Optional.ofNullable(unit); + } + + public double getValue() { + return value; + } + + public Optional getUnit() { + return unit; + } + + @Override + public int hashCode() { + return Objects.hash(value, unit); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Quantity other = (Quantity) obj; + return Double.doubleToLongBits(value) == Double.doubleToLongBits(other.value) + && Objects.equals(unit, other.unit); + } + + @Override + public String toString() { + return "Quantity [value=" + value + ", unit=" + unit + "]"; + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/EcoFeedback.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/EcoFeedback.java new file mode 100644 index 00000000000..f90cfd20715 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/EcoFeedback.java @@ -0,0 +1,89 @@ +/** + * 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.binding.mielecloud.internal.webservice.api.json; + +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Immutable POJO representing the amount of water and energy used by the current running program up to the present + * moment. Queried from the Miele REST API. + * + * @author Björn Lange - Initial contribution + */ +@NonNullByDefault +public class EcoFeedback { + @Nullable + private WaterConsumption currentWaterConsumption; + @Nullable + private EnergyConsumption currentEnergyConsumption; + @Nullable + private Double waterForecast; + @Nullable + private Double energyForecast; + + public Optional getCurrentWaterConsumption() { + return Optional.ofNullable(currentWaterConsumption); + } + + public Optional getCurrentEnergyConsumption() { + return Optional.ofNullable(currentEnergyConsumption); + } + + /** + * Gets the relative water usage for the selected program from 0 to 1. + */ + public Optional getWaterForecast() { + return Optional.ofNullable(waterForecast); + } + + /** + * Gets the relative energy usage for the selected program from 0 to 1. + */ + public Optional getEnergyForecast() { + return Optional.ofNullable(energyForecast); + } + + @Override + public int hashCode() { + return Objects.hash(currentWaterConsumption, currentEnergyConsumption, waterForecast, energyForecast); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + EcoFeedback other = (EcoFeedback) obj; + return Objects.equals(currentWaterConsumption, other.currentWaterConsumption) + && Objects.equals(currentEnergyConsumption, other.currentEnergyConsumption) + && Objects.equals(waterForecast, other.waterForecast) + && Objects.equals(energyForecast, other.energyForecast); + } + + @Override + public String toString() { + return "EcoFeedback [currentWaterConsumption=" + currentWaterConsumption + ", currentEnergyConsumption=" + + currentEnergyConsumption + ", waterForecast=" + waterForecast + ", energyForecast=" + energyForecast + + "]"; + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/EnergyConsumption.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/EnergyConsumption.java new file mode 100644 index 00000000000..8bdf770de24 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/EnergyConsumption.java @@ -0,0 +1,71 @@ +/** + * 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.binding.mielecloud.internal.webservice.api.json; + +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Immutable POJO representing an amount of consumed energy. Queried from the Miele REST API. + * + * @author Björn Lange - Initial contribution + */ +@NonNullByDefault +public class EnergyConsumption { + @Nullable + private String unit; + @Nullable + private Double value; + + /** + * Gets the measurement unit which represents energy. + */ + public Optional getUnit() { + return Optional.ofNullable(unit); + } + + /** + * Gets the amount of energy. + */ + public Optional getValue() { + return Optional.ofNullable(value); + } + + @Override + public int hashCode() { + return Objects.hash(unit, value); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + EnergyConsumption other = (EnergyConsumption) obj; + return Objects.equals(unit, other.unit) && Objects.equals(value, other.value); + } + + @Override + public String toString() { + return "EnergyConsumption [unit=" + unit + ", value=" + value + "]"; + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/State.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/State.java index 6b813e7a089..e95bcca8fb6 100644 --- a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/State.java +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/State.java @@ -25,7 +25,7 @@ import org.eclipse.jdt.annotation.Nullable; * * @author Björn Lange - Initial contribution * @author Benjamin Bolte - Add plate step - * @author Björn Lange - Add elapsed time channel + * @author Björn Lange - Add elapsed time channel, add eco feedback */ @NonNullByDefault public class State { @@ -74,6 +74,8 @@ public class State { @Nullable private final List plateStep = null; @Nullable + private EcoFeedback ecoFeedback; + @Nullable private Integer batteryLevel; public Optional getStatus() { @@ -189,6 +191,10 @@ public class State { return Collections.unmodifiableList(plateStep); } + public Optional getEcoFeedback() { + return Optional.ofNullable(ecoFeedback); + } + public Optional getBatteryLevel() { return Optional.ofNullable(batteryLevel); } @@ -197,7 +203,7 @@ public class State { public int hashCode() { return Objects.hash(dryingStep, elapsedTime, light, programPhase, ProgramID, programId, programType, remainingTime, remoteEnable, signalDoor, signalFailure, signalInfo, startTime, status, - targetTemperature, temperature, ventilationStep, plateStep, batteryLevel); + targetTemperature, temperature, ventilationStep, plateStep, ecoFeedback, batteryLevel); } @Override @@ -222,7 +228,7 @@ public class State { && Objects.equals(targetTemperature, other.targetTemperature) && Objects.equals(temperature, other.temperature) && Objects.equals(ventilationStep, other.ventilationStep) && Objects.equals(plateStep, other.plateStep) - && Objects.equals(batteryLevel, other.batteryLevel); + && Objects.equals(ecoFeedback, other.ecoFeedback) && Objects.equals(batteryLevel, other.batteryLevel); } @Override @@ -232,7 +238,7 @@ public class State { + ", targetTemperature=" + targetTemperature + ", temperature=" + temperature + ", signalInfo=" + signalInfo + ", signalFailure=" + signalFailure + ", signalDoor=" + signalDoor + ", remoteEnable=" + remoteEnable + ", light=" + light + ", elapsedTime=" + elapsedTime + ", dryingStep=" + dryingStep - + ", ventilationStep=" + ventilationStep + ", plateStep=" + plateStep + ", batteryLevel=" + batteryLevel - + "]"; + + ", ventilationStep=" + ventilationStep + ", plateStep=" + plateStep + ", ecoFeedback=" + ecoFeedback + + ", batteryLevel=" + batteryLevel + "]"; } } diff --git a/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/WaterConsumption.java b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/WaterConsumption.java new file mode 100644 index 00000000000..aa95995e64c --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/java/org/openhab/binding/mielecloud/internal/webservice/api/json/WaterConsumption.java @@ -0,0 +1,71 @@ +/** + * 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.binding.mielecloud.internal.webservice.api.json; + +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +/** + * Immutable POJO representing an amount of consumed water. Queried from the Miele REST API. + * + * @author Björn Lange - Initial contribution + */ +@NonNullByDefault +public class WaterConsumption { + @Nullable + private String unit; + @Nullable + private Double value; + + /** + * Gets the measurement unit which represents a volume. + */ + public Optional getUnit() { + return Optional.ofNullable(unit); + } + + /** + * Gets the amount of water. + */ + public Optional getValue() { + return Optional.ofNullable(value); + } + + @Override + public int hashCode() { + return Objects.hash(unit, value); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + WaterConsumption other = (WaterConsumption) obj; + return Objects.equals(unit, other.unit) && Objects.equals(value, other.value); + } + + @Override + public String toString() { + return "WaterConsumption [unit=" + unit + ", value=" + value + "]"; + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/i18n/mielecloud.properties b/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/i18n/mielecloud.properties index e74b7d860a3..95e138e1155 100644 --- a/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/i18n/mielecloud.properties +++ b/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/i18n/mielecloud.properties @@ -238,6 +238,12 @@ channel-type.mielecloud.door_state.description=Indicates if the door of the devi channel-type.mielecloud.door_alarm.label=Door Alarm channel-type.mielecloud.door_alarm.description=Indicates if the door alarm of the device is active. +channel-type.mielecloud.water_consumption_current.label=Current Water Consumption +channel-type.mielecloud.water_consumption_current.description=The amount of water used by the current running program up to the present moment. + +channel-type.mielecloud.energy_consumption_current.label=Current Energy Consumption +channel-type.mielecloud.energy_consumption_current.description=The amount of energy used by the current running program up to the present moment. + channel-type.mielecloud.battery_level.label=Battery Level channel-type.mielecloud.battery_level.description=The battery level of the robotic vacuum cleaner. diff --git a/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/channelTypes.xml b/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/channelTypes.xml index e1cb16cca92..c0451fc296c 100644 --- a/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/channelTypes.xml +++ b/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/channelTypes.xml @@ -438,6 +438,30 @@ + + Number:Volume + + @text/channel-type.mielecloud.water_consumption_current.description + Water + + Measurement + Water + + + + + + Number:Energy + + @text/channel-type.mielecloud.energy_consumption_current.description + Energy + + Measurement + Energy + + + + Number diff --git a/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/dishwasherDevice.xml b/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/dishwasherDevice.xml index 2d97427f21b..64fe077e019 100644 --- a/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/dishwasherDevice.xml +++ b/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/dishwasherDevice.xml @@ -34,9 +34,12 @@ + + + 1 Miele diff --git a/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/dryerDevice.xml b/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/dryerDevice.xml index 0522f002100..6d26b619804 100644 --- a/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/dryerDevice.xml +++ b/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/dryerDevice.xml @@ -38,9 +38,11 @@ + + 1 Miele diff --git a/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/washerDryer.xml b/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/washerDryer.xml index 21f21cb0e66..a9b7895c1a4 100644 --- a/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/washerDryer.xml +++ b/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/washerDryer.xml @@ -41,9 +41,12 @@ + + + 1 Miele diff --git a/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/washingMachine.xml b/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/washingMachine.xml index 8143ebb70f0..8be636fb364 100644 --- a/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/washingMachine.xml +++ b/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/thing/washingMachine.xml @@ -39,9 +39,12 @@ + + + 1 Miele diff --git a/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/update/instructions.xml b/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/update/instructions.xml new file mode 100644 index 00000000000..07d1291a388 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/main/resources/OH-INF/update/instructions.xml @@ -0,0 +1,46 @@ + + + + + + + mielecloud:water_consumption_current + + + mielecloud:energy_consumption_current + + + + + + + + mielecloud:energy_consumption_current + + + + + + + + mielecloud:water_consumption_current + + + mielecloud:energy_consumption_current + + + + + + + + mielecloud:water_consumption_current + + + mielecloud:energy_consumption_current + + + + diff --git a/bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/handler/channel/ChannelTypeUtilTest.java b/bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/handler/channel/ChannelTypeUtilTest.java new file mode 100644 index 00000000000..70f5a28b7b8 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/handler/channel/ChannelTypeUtilTest.java @@ -0,0 +1,53 @@ +/** + * 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.binding.mielecloud.internal.handler.channel; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Optional; +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.openhab.binding.mielecloud.internal.webservice.api.Quantity; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; + +/** + * @author Björn Lange - Initial contribution + */ +@NonNullByDefault +public class ChannelTypeUtilTest { + private static Stream quantityToStateConversionArguments() { + return Stream.of(Arguments.of(Optional.empty(), UnDefType.UNDEF), + Arguments.of(Optional.of(new Quantity(10.0, "Gold")), UnDefType.UNDEF), + Arguments.of(Optional.of(new Quantity(3.0, null)), new QuantityType<>(3.0, Units.ONE)), + Arguments.of(Optional.of(new Quantity(1.0 / 3.0, "l")), new QuantityType<>(0.333, Units.LITRE)), + Arguments.of(Optional.of(new Quantity(20.123, "kWh")), new QuantityType<>(20.123, Units.KILOWATT_HOUR)), + Arguments.of(Optional.of(new Quantity(0.5, "l")), new QuantityType<>(0.5, Units.LITRE))); + } + + @ParameterizedTest + @MethodSource("quantityToStateConversionArguments") + void quantityCanBeConvertedToState(Optional input, State expected) { + // when: + var state = ChannelTypeUtil.quantityToState(input); + + // then: + assertEquals(expected, state); + } +} diff --git a/bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/DeviceStateTest.java b/bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/DeviceStateTest.java index d2387d0d0f7..616c9fbdd0c 100644 --- a/bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/DeviceStateTest.java +++ b/bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/DeviceStateTest.java @@ -26,6 +26,8 @@ import org.openhab.binding.mielecloud.internal.webservice.api.json.Device; import org.openhab.binding.mielecloud.internal.webservice.api.json.DeviceIdentLabel; import org.openhab.binding.mielecloud.internal.webservice.api.json.DeviceType; import org.openhab.binding.mielecloud.internal.webservice.api.json.DryingStep; +import org.openhab.binding.mielecloud.internal.webservice.api.json.EcoFeedback; +import org.openhab.binding.mielecloud.internal.webservice.api.json.EnergyConsumption; import org.openhab.binding.mielecloud.internal.webservice.api.json.Ident; import org.openhab.binding.mielecloud.internal.webservice.api.json.Light; import org.openhab.binding.mielecloud.internal.webservice.api.json.PlateStep; @@ -40,12 +42,13 @@ import org.openhab.binding.mielecloud.internal.webservice.api.json.Status; import org.openhab.binding.mielecloud.internal.webservice.api.json.Temperature; import org.openhab.binding.mielecloud.internal.webservice.api.json.Type; import org.openhab.binding.mielecloud.internal.webservice.api.json.VentilationStep; +import org.openhab.binding.mielecloud.internal.webservice.api.json.WaterConsumption; /** * @author Björn Lange - Initial contribution * @author Benjamin Bolte - Add pre-heat finished, plate step, door state, door alarm and info state channels and map * signal flags from API - * @author Björn Lange - Add elapsed time channel, robotic vacuum cleaner + * @author Björn Lange - Add elapsed time channel, robotic vacuum cleaner, eco feedback */ @NonNullByDefault public class DeviceStateTest { @@ -2106,6 +2109,336 @@ public class DeviceStateTest { assertFalse(lightState.isPresent()); } + @Test + public void testGetCurrentWaterConsumptionWhenEcoFeedbackIsNotPresent() { + // given: + Status status = mock(Status.class); + when(status.getValueRaw()).thenReturn(Optional.of(StateType.ON.getCode())); + + State state = mock(State.class); + when(state.getStatus()).thenReturn(Optional.of(status)); + when(state.getEcoFeedback()).thenReturn(Optional.empty()); + + Device device = mock(Device.class); + when(device.getState()).thenReturn(Optional.of(state)); + + DeviceState deviceState = new DeviceState(DEVICE_IDENTIFIER, device); + + // when: + Optional waterConsumption = deviceState.getCurrentWaterConsumption(); + + // then: + assertFalse(waterConsumption.isPresent()); + } + + @Test + public void testGetCurrentWaterConsumptionWhenCurrentWaterConsumptionIsNotPresent() { + // given: + Status status = mock(Status.class); + when(status.getValueRaw()).thenReturn(Optional.of(StateType.ON.getCode())); + + EcoFeedback ecoFeedback = mock(EcoFeedback.class); + when(ecoFeedback.getCurrentWaterConsumption()).thenReturn(Optional.empty()); + + State state = mock(State.class); + when(state.getStatus()).thenReturn(Optional.of(status)); + when(state.getEcoFeedback()).thenReturn(Optional.of(ecoFeedback)); + + Device device = mock(Device.class); + when(device.getState()).thenReturn(Optional.of(state)); + + DeviceState deviceState = new DeviceState(DEVICE_IDENTIFIER, device); + + // when: + Optional waterConsumption = deviceState.getCurrentWaterConsumption(); + + // then: + assertFalse(waterConsumption.isPresent()); + } + + @Test + public void testGetCurrentWaterConsumptionWhenCurrentWaterConsumptionIsEmpty() { + // given: + Status status = mock(Status.class); + when(status.getValueRaw()).thenReturn(Optional.of(StateType.ON.getCode())); + + WaterConsumption currentWaterConsumption = mock(WaterConsumption.class); + when(currentWaterConsumption.getUnit()).thenReturn(Optional.empty()); + when(currentWaterConsumption.getValue()).thenReturn(Optional.empty()); + + EcoFeedback ecoFeedback = mock(EcoFeedback.class); + when(ecoFeedback.getCurrentWaterConsumption()).thenReturn(Optional.of(currentWaterConsumption)); + + State state = mock(State.class); + when(state.getStatus()).thenReturn(Optional.of(status)); + when(state.getEcoFeedback()).thenReturn(Optional.of(ecoFeedback)); + + Device device = mock(Device.class); + when(device.getState()).thenReturn(Optional.of(state)); + + DeviceState deviceState = new DeviceState(DEVICE_IDENTIFIER, device); + + // when: + Optional waterConsumption = deviceState.getCurrentWaterConsumption(); + + // then: + assertFalse(waterConsumption.isPresent()); + } + + @Test + public void testGetCurrentWaterConsumptionWhenValueIsNotPresent() { + // given: + Status status = mock(Status.class); + when(status.getValueRaw()).thenReturn(Optional.of(StateType.ON.getCode())); + + WaterConsumption currentWaterConsumption = mock(WaterConsumption.class); + when(currentWaterConsumption.getUnit()).thenReturn(Optional.of("l")); + when(currentWaterConsumption.getValue()).thenReturn(Optional.empty()); + + EcoFeedback ecoFeedback = mock(EcoFeedback.class); + when(ecoFeedback.getCurrentWaterConsumption()).thenReturn(Optional.of(currentWaterConsumption)); + + State state = mock(State.class); + when(state.getStatus()).thenReturn(Optional.of(status)); + when(state.getEcoFeedback()).thenReturn(Optional.of(ecoFeedback)); + + Device device = mock(Device.class); + when(device.getState()).thenReturn(Optional.of(state)); + + DeviceState deviceState = new DeviceState(DEVICE_IDENTIFIER, device); + + // when: + Optional waterConsumption = deviceState.getCurrentWaterConsumption(); + + // then: + assertFalse(waterConsumption.isPresent()); + } + + @Test + public void testGetCurrentWaterConsumptionWhenUnitIsNotPresent() { + // given: + Status status = mock(Status.class); + when(status.getValueRaw()).thenReturn(Optional.of(StateType.ON.getCode())); + + WaterConsumption currentWaterConsumption = mock(WaterConsumption.class); + when(currentWaterConsumption.getUnit()).thenReturn(Optional.empty()); + when(currentWaterConsumption.getValue()).thenReturn(Optional.of(0.5)); + + EcoFeedback ecoFeedback = mock(EcoFeedback.class); + when(ecoFeedback.getCurrentWaterConsumption()).thenReturn(Optional.of(currentWaterConsumption)); + + State state = mock(State.class); + when(state.getStatus()).thenReturn(Optional.of(status)); + when(state.getEcoFeedback()).thenReturn(Optional.of(ecoFeedback)); + + Device device = mock(Device.class); + when(device.getState()).thenReturn(Optional.of(state)); + + DeviceState deviceState = new DeviceState(DEVICE_IDENTIFIER, device); + + // when: + Quantity waterConsumption = deviceState.getCurrentWaterConsumption().get(); + + // then: + assertEquals(0.5, waterConsumption.getValue()); + assertFalse(waterConsumption.getUnit().isPresent()); + } + + @Test + public void testGetCurrentWaterConsumption() { + // given: + Status status = mock(Status.class); + when(status.getValueRaw()).thenReturn(Optional.of(StateType.ON.getCode())); + + WaterConsumption currentWaterConsumption = mock(WaterConsumption.class); + when(currentWaterConsumption.getUnit()).thenReturn(Optional.of("l")); + when(currentWaterConsumption.getValue()).thenReturn(Optional.of(0.5)); + + EcoFeedback ecoFeedback = mock(EcoFeedback.class); + when(ecoFeedback.getCurrentWaterConsumption()).thenReturn(Optional.of(currentWaterConsumption)); + + State state = mock(State.class); + when(state.getStatus()).thenReturn(Optional.of(status)); + when(state.getEcoFeedback()).thenReturn(Optional.of(ecoFeedback)); + + Device device = mock(Device.class); + when(device.getState()).thenReturn(Optional.of(state)); + + DeviceState deviceState = new DeviceState(DEVICE_IDENTIFIER, device); + + // when: + Quantity waterConsumption = deviceState.getCurrentWaterConsumption().get(); + + // then: + assertEquals(0.5, waterConsumption.getValue()); + assertEquals(Optional.of("l"), waterConsumption.getUnit()); + } + + @Test + public void testGetCurrentEnergyConsumptionWhenEcoFeedbackIsNotPresent() { + // given: + Status status = mock(Status.class); + when(status.getValueRaw()).thenReturn(Optional.of(StateType.ON.getCode())); + + State state = mock(State.class); + when(state.getStatus()).thenReturn(Optional.of(status)); + when(state.getEcoFeedback()).thenReturn(Optional.empty()); + + Device device = mock(Device.class); + when(device.getState()).thenReturn(Optional.of(state)); + + DeviceState deviceState = new DeviceState(DEVICE_IDENTIFIER, device); + + // when: + Optional energyConsumption = deviceState.getCurrentEnergyConsumption(); + + // then: + assertFalse(energyConsumption.isPresent()); + } + + @Test + public void testGetCurrentEnergyConsumptionWhenCurrentEnergyConsumptionIsNotPresent() { + // given: + Status status = mock(Status.class); + when(status.getValueRaw()).thenReturn(Optional.of(StateType.ON.getCode())); + + EcoFeedback ecoFeedback = mock(EcoFeedback.class); + when(ecoFeedback.getCurrentEnergyConsumption()).thenReturn(Optional.empty()); + + State state = mock(State.class); + when(state.getStatus()).thenReturn(Optional.of(status)); + when(state.getEcoFeedback()).thenReturn(Optional.of(ecoFeedback)); + + Device device = mock(Device.class); + when(device.getState()).thenReturn(Optional.of(state)); + + DeviceState deviceState = new DeviceState(DEVICE_IDENTIFIER, device); + + // when: + Optional energyConsumption = deviceState.getCurrentEnergyConsumption(); + + // then: + assertFalse(energyConsumption.isPresent()); + } + + @Test + public void testGetCurrentEnergyConsumptionWhenCurrentEnergyConsumptionIsEmpty() { + // given: + Status status = mock(Status.class); + when(status.getValueRaw()).thenReturn(Optional.of(StateType.ON.getCode())); + + EnergyConsumption currentEnergyConsumption = mock(EnergyConsumption.class); + when(currentEnergyConsumption.getUnit()).thenReturn(Optional.empty()); + when(currentEnergyConsumption.getValue()).thenReturn(Optional.empty()); + + EcoFeedback ecoFeedback = mock(EcoFeedback.class); + when(ecoFeedback.getCurrentEnergyConsumption()).thenReturn(Optional.of(currentEnergyConsumption)); + + State state = mock(State.class); + when(state.getStatus()).thenReturn(Optional.of(status)); + when(state.getEcoFeedback()).thenReturn(Optional.of(ecoFeedback)); + + Device device = mock(Device.class); + when(device.getState()).thenReturn(Optional.of(state)); + + DeviceState deviceState = new DeviceState(DEVICE_IDENTIFIER, device); + + // when: + Optional energyConsumption = deviceState.getCurrentEnergyConsumption(); + + // then: + assertFalse(energyConsumption.isPresent()); + } + + @Test + public void testGetCurrentEnergyConsumptionWhenValueIsNotPresent() { + // given: + Status status = mock(Status.class); + when(status.getValueRaw()).thenReturn(Optional.of(StateType.ON.getCode())); + + EnergyConsumption currentEnergyConsumption = mock(EnergyConsumption.class); + when(currentEnergyConsumption.getUnit()).thenReturn(Optional.of("kWh")); + when(currentEnergyConsumption.getValue()).thenReturn(Optional.empty()); + + EcoFeedback ecoFeedback = mock(EcoFeedback.class); + when(ecoFeedback.getCurrentEnergyConsumption()).thenReturn(Optional.of(currentEnergyConsumption)); + + State state = mock(State.class); + when(state.getStatus()).thenReturn(Optional.of(status)); + when(state.getEcoFeedback()).thenReturn(Optional.of(ecoFeedback)); + + Device device = mock(Device.class); + when(device.getState()).thenReturn(Optional.of(state)); + + DeviceState deviceState = new DeviceState(DEVICE_IDENTIFIER, device); + + // when: + Optional energyConsumption = deviceState.getCurrentEnergyConsumption(); + + // then: + assertFalse(energyConsumption.isPresent()); + } + + @Test + public void testGetCurrentEnergyConsumptionWhenUnitIsNotPresent() { + // given: + Status status = mock(Status.class); + when(status.getValueRaw()).thenReturn(Optional.of(StateType.ON.getCode())); + + EnergyConsumption currentEnergyConsumption = mock(EnergyConsumption.class); + when(currentEnergyConsumption.getUnit()).thenReturn(Optional.empty()); + when(currentEnergyConsumption.getValue()).thenReturn(Optional.of(0.5)); + + EcoFeedback ecoFeedback = mock(EcoFeedback.class); + when(ecoFeedback.getCurrentEnergyConsumption()).thenReturn(Optional.of(currentEnergyConsumption)); + + State state = mock(State.class); + when(state.getStatus()).thenReturn(Optional.of(status)); + when(state.getEcoFeedback()).thenReturn(Optional.of(ecoFeedback)); + + Device device = mock(Device.class); + when(device.getState()).thenReturn(Optional.of(state)); + + DeviceState deviceState = new DeviceState(DEVICE_IDENTIFIER, device); + + // when: + Quantity energyConsumption = deviceState.getCurrentEnergyConsumption().get(); + + // then: + assertEquals(0.5, energyConsumption.getValue()); + assertFalse(energyConsumption.getUnit().isPresent()); + } + + @Test + public void testGetCurrentEnergyConsumption() { + // given: + Status status = mock(Status.class); + when(status.getValueRaw()).thenReturn(Optional.of(StateType.ON.getCode())); + + EnergyConsumption currentEnergyConsumption = mock(EnergyConsumption.class); + when(currentEnergyConsumption.getUnit()).thenReturn(Optional.of("kWh")); + when(currentEnergyConsumption.getValue()).thenReturn(Optional.of(0.5)); + + EcoFeedback ecoFeedback = mock(EcoFeedback.class); + when(ecoFeedback.getCurrentEnergyConsumption()).thenReturn(Optional.of(currentEnergyConsumption)); + + State state = mock(State.class); + when(state.getStatus()).thenReturn(Optional.of(status)); + when(state.getEcoFeedback()).thenReturn(Optional.of(ecoFeedback)); + + Device device = mock(Device.class); + when(device.getState()).thenReturn(Optional.of(state)); + + DeviceState deviceState = new DeviceState(DEVICE_IDENTIFIER, device); + + // when: + Quantity energyConsumption = deviceState.getCurrentEnergyConsumption().get(); + + // then: + assertEquals(0.5, energyConsumption.getValue()); + assertEquals(Optional.of("kWh"), energyConsumption.getUnit()); + } + @Test public void testGetBatteryLevel() { // given: diff --git a/bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/json/DeviceCollectionTest.java b/bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/json/DeviceCollectionTest.java index b73051127c0..26c9325e119 100644 --- a/bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/json/DeviceCollectionTest.java +++ b/bundles/org.openhab.binding.mielecloud/src/test/java/org/openhab/binding/mielecloud/internal/webservice/api/json/DeviceCollectionTest.java @@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test; /** * @author Björn Lange - Initial contribution * @author Benjamin Bolte - Add plate step + * @author Björn Lange - Add eco feedback */ @NonNullByDefault public class DeviceCollectionTest { @@ -287,4 +288,32 @@ public class DeviceCollectionTest { assertEquals(Integer.valueOf(0), targetTemperature.getValueLocalized().get()); assertEquals("Celsius", targetTemperature.getUnit().get()); } + + @Test + public void testCreateDeviceCollectionWithEcoFeedback() throws IOException { + // given: + String json = getResourceAsString( + "/org/openhab/binding/mielecloud/internal/webservice/api/json/deviceCollectionWithEcoFeedback.json"); + + // when: + DeviceCollection collection = DeviceCollection.fromJson(json); + + // then: + assertEquals(1, collection.getDeviceIdentifiers().size()); + Device device = collection.getDevice(collection.getDeviceIdentifiers().iterator().next()); + + State state = device.getState().get(); + EcoFeedback ecoFeedback = state.getEcoFeedback().get(); + + WaterConsumption currentWaterConsumption = ecoFeedback.getCurrentWaterConsumption().get(); + assertEquals("l", currentWaterConsumption.getUnit().get()); + assertEquals(0.0, currentWaterConsumption.getValue().get()); + + EnergyConsumption currentEnergyConsumption = ecoFeedback.getCurrentEnergyConsumption().get(); + assertEquals("kWh", currentEnergyConsumption.getUnit().get()); + assertEquals(0.5, currentEnergyConsumption.getValue().get()); + + assertEquals(0.0, ecoFeedback.getWaterForecast().get()); + assertEquals(0.6, ecoFeedback.getEnergyForecast().get()); + } } diff --git a/bundles/org.openhab.binding.mielecloud/src/test/resources/org/openhab/binding/mielecloud/internal/webservice/api/json/deviceCollectionWithEcoFeedback.json b/bundles/org.openhab.binding.mielecloud/src/test/resources/org/openhab/binding/mielecloud/internal/webservice/api/json/deviceCollectionWithEcoFeedback.json new file mode 100644 index 00000000000..6ca32260629 --- /dev/null +++ b/bundles/org.openhab.binding.mielecloud/src/test/resources/org/openhab/binding/mielecloud/internal/webservice/api/json/deviceCollectionWithEcoFeedback.json @@ -0,0 +1,142 @@ +{ + "000124430017": { + "ident": { + "type": { + "key_localized": "Device type", + "value_raw": 2, + "value_localized": "Tumble dryer" + }, + "deviceName": "TWH780WP", + "protocolVersion": 4, + "deviceIdentLabel": { + "fabNumber": "000124430017", + "fabIndex": "44", + "techType": "TWH780WP", + "matNumber": "11219891", + "swids": [ + "5678", + "25359", + "25360", + "20559", + "25277", + "5136", + "20445", + "25234", + "4657" + ] + }, + "xkmIdentLabel": { + "techType": "EK057", + "releaseVersion": "08.10" + } + }, + "state": { + "ProgramID": { + "value_raw": 2, + "value_localized": "Cottons", + "key_localized": "Program name" + }, + "status": { + "value_raw": 5, + "value_localized": "In use", + "key_localized": "status" + }, + "programType": { + "value_raw": 3, + "value_localized": "Cleaning/Care programme", + "key_localized": "Program type" + }, + "programPhase": { + "value_raw": 514, + "value_localized": "Drying", + "key_localized": "Program phase" + }, + "remainingTime": [ + 2, + 7 + ], + "startTime": [ + 0, + 0 + ], + "targetTemperature": [ + { + "value_raw": -32768, + "value_localized": null, + "unit": "Celsius" + }, + { + "value_raw": -32768, + "value_localized": null, + "unit": "Celsius" + }, + { + "value_raw": -32768, + "value_localized": null, + "unit": "Celsius" + } + ], + "temperature": [ + { + "value_raw": -32768, + "value_localized": null, + "unit": "Celsius" + }, + { + "value_raw": -32768, + "value_localized": null, + "unit": "Celsius" + }, + { + "value_raw": -32768, + "value_localized": null, + "unit": "Celsius" + } + ], + "signalInfo": false, + "signalFailure": false, + "signalDoor": false, + "remoteEnable": { + "fullRemoteControl": true, + "smartGrid": false, + "mobileStart": false + }, + "ambientLight": null, + "light": null, + "elapsedTime": [ + 1, + 9 + ], + "spinningSpeed": { + "unit": "rpm", + "value_raw": null, + "value_localized": null, + "key_localized": "Spin speed" + }, + "dryingStep": { + "value_raw": 0, + "value_localized": "Extra dry", + "key_localized": "Drying level" + }, + "ventilationStep": { + "value_raw": null, + "value_localized": "", + "key_localized": "Fan level" + }, + "plateStep": [], + "ecoFeedback": { + "currentWaterConsumption": { + "unit": "l", + "value": 0 + }, + "currentEnergyConsumption": { + "unit": "kWh", + "value": 0.5 + }, + "waterForecast": 0, + "energyForecast": 0.6 + }, + "batteryLevel": null + } + } +} \ No newline at end of file diff --git a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/AbstractMieleThingHandlerTest.java b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/AbstractMieleThingHandlerTest.java index 7ed43b57253..c29a9f15c3c 100644 --- a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/AbstractMieleThingHandlerTest.java +++ b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/AbstractMieleThingHandlerTest.java @@ -209,7 +209,8 @@ public abstract class AbstractMieleThingHandlerTest extends JavaOSGiTest { } protected AbstractMieleThingHandler createThingHandler(ThingTypeUID thingTypeUid, ThingUID thingUid, - Class expectedHandlerClass, String deviceIdentifier) { + Class expectedHandlerClass, String deviceIdentifier, + String thingTypeVersion) { ThingRegistry registry = getThingRegistry(); List channels = createChannelsForThingHandler(thingTypeUid, thingUid); @@ -217,7 +218,8 @@ public abstract class AbstractMieleThingHandlerTest extends JavaOSGiTest { Thing thing = ThingBuilder.create(thingTypeUid, thingUid) .withConfiguration(new Configuration(Collections .singletonMap(MieleCloudBindingConstants.CONFIG_PARAM_DEVICE_IDENTIFIER, deviceIdentifier))) - .withBridge(getBridge().getUID()).withChannels(channels).withLabel("DA-6996").build(); + .withBridge(getBridge().getUID()).withChannels(channels).withLabel("DA-6996") + .withProperty("thingTypeVersion", thingTypeVersion).build(); assertNotNull(thing); registry.add(thing); diff --git a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/CoffeeDeviceThingHandlerTest.java b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/CoffeeDeviceThingHandlerTest.java index fecedc11efe..d97582c375e 100644 --- a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/CoffeeDeviceThingHandlerTest.java +++ b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/CoffeeDeviceThingHandlerTest.java @@ -42,7 +42,7 @@ public class CoffeeDeviceThingHandlerTest extends AbstractMieleThingHandlerTest @Override protected AbstractMieleThingHandler setUpThingHandler() { return createThingHandler(MieleCloudBindingConstants.THING_TYPE_COFFEE_SYSTEM, COFFEE_SYSTEM_THING_UID, - CoffeeSystemThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER); + CoffeeSystemThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER, "0"); } @Test diff --git a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/CoolingDeviceThingHandlerTest.java b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/CoolingDeviceThingHandlerTest.java index fcb8d41b104..70024bca1c9 100644 --- a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/CoolingDeviceThingHandlerTest.java +++ b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/CoolingDeviceThingHandlerTest.java @@ -45,7 +45,7 @@ public class CoolingDeviceThingHandlerTest extends AbstractMieleThingHandlerTest @Override protected AbstractMieleThingHandler setUpThingHandler() { return createThingHandler(MieleCloudBindingConstants.THING_TYPE_FRIDGE_FREEZER, FRIDGE_FREEZER_DEVICE_THING_UID, - CoolingDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER); + CoolingDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER, "0"); } @Test diff --git a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishWarmerDeviceThingHandlerTest.java b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishWarmerDeviceThingHandlerTest.java index 7f949838848..ef685835f3b 100644 --- a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishWarmerDeviceThingHandlerTest.java +++ b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishWarmerDeviceThingHandlerTest.java @@ -42,7 +42,7 @@ public class DishWarmerDeviceThingHandlerTest extends AbstractMieleThingHandlerT protected AbstractMieleThingHandler setUpThingHandler() { return createThingHandler(MieleCloudBindingConstants.THING_TYPE_DISH_WARMER, MieleCloudBindingIntegrationTestConstants.DISH_WARMER_DEVICE_THING_UID, - DishWarmerDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER); + DishWarmerDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER, "0"); } @Test diff --git a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishwasherDeviceThingHandlerTest.java b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishwasherDeviceThingHandlerTest.java index ca453fe30f0..a4d73edbcab 100644 --- a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishwasherDeviceThingHandlerTest.java +++ b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/DishwasherDeviceThingHandlerTest.java @@ -28,22 +28,25 @@ import org.openhab.binding.mielecloud.internal.webservice.api.ActionsState; import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState; import org.openhab.binding.mielecloud.internal.webservice.api.PowerStatus; import org.openhab.binding.mielecloud.internal.webservice.api.ProgramStatus; +import org.openhab.binding.mielecloud.internal.webservice.api.Quantity; import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; +import org.openhab.core.library.unit.Units; /** * @author Björn Lange - Initial contribution * @author Benjamin Bolte - Add info state channel and map signal flags from API tests - * @author Björn Lange - Add elapsed time channel + * @author Björn Lange - Add elapsed time, current water and energy consumption channels */ @NonNullByDefault public class DishwasherDeviceThingHandlerTest extends AbstractMieleThingHandlerTest { @Override protected AbstractMieleThingHandler setUpThingHandler() { return createThingHandler(MieleCloudBindingConstants.THING_TYPE_DISHWASHER, DISHWASHER_DEVICE_THING_UID, - DishwasherDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER); + DishwasherDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER, "1"); } @Test @@ -64,6 +67,8 @@ public class DishwasherDeviceThingHandlerTest extends AbstractMieleThingHandlerT when(deviceState.getStartTime()).thenReturn(Optional.empty()); when(deviceState.getElapsedTime()).thenReturn(Optional.empty()); when(deviceState.getDoorState()).thenReturn(Optional.empty()); + when(deviceState.getCurrentWaterConsumption()).thenReturn(Optional.empty()); + when(deviceState.getCurrentEnergyConsumption()).thenReturn(Optional.empty()); // when: getBridgeHandler().onDeviceStateUpdated(deviceState); @@ -81,6 +86,8 @@ public class DishwasherDeviceThingHandlerTest extends AbstractMieleThingHandlerT assertEquals(NULL_VALUE_STATE, getChannelState(DELAYED_START_TIME)); assertEquals(NULL_VALUE_STATE, getChannelState(PROGRAM_ELAPSED_TIME)); assertEquals(NULL_VALUE_STATE, getChannelState(DOOR_STATE)); + assertEquals(NULL_VALUE_STATE, getChannelState(WATER_CONSUMPTION_CURRENT)); + assertEquals(NULL_VALUE_STATE, getChannelState(ENERGY_CONSUMPTION_CURRENT)); }); } @@ -105,6 +112,8 @@ public class DishwasherDeviceThingHandlerTest extends AbstractMieleThingHandlerT when(deviceState.hasError()).thenReturn(false); when(deviceState.hasInfo()).thenReturn(true); when(deviceState.getDoorState()).thenReturn(Optional.of(true)); + when(deviceState.getCurrentWaterConsumption()).thenReturn(Optional.of(new Quantity(1.0, "l"))); + when(deviceState.getCurrentEnergyConsumption()).thenReturn(Optional.of(new Quantity(2.5, "kWh"))); // when: getBridgeHandler().onDeviceStateUpdated(deviceState); @@ -124,6 +133,8 @@ public class DishwasherDeviceThingHandlerTest extends AbstractMieleThingHandlerT assertEquals(OnOffType.OFF, getChannelState(ERROR_STATE)); assertEquals(OnOffType.ON, getChannelState(INFO_STATE)); assertEquals(OnOffType.ON, getChannelState(DOOR_STATE)); + assertEquals(new QuantityType<>(1.0, Units.LITRE), getChannelState(WATER_CONSUMPTION_CURRENT)); + assertEquals(new QuantityType<>(2.5, Units.KILOWATT_HOUR), getChannelState(ENERGY_CONSUMPTION_CURRENT)); }); } diff --git a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/DryerDeviceThingHandlerTest.java b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/DryerDeviceThingHandlerTest.java index c1bb65f9bdd..c7069ff784e 100644 --- a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/DryerDeviceThingHandlerTest.java +++ b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/DryerDeviceThingHandlerTest.java @@ -28,22 +28,25 @@ import org.openhab.binding.mielecloud.internal.webservice.api.ActionsState; import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState; import org.openhab.binding.mielecloud.internal.webservice.api.PowerStatus; import org.openhab.binding.mielecloud.internal.webservice.api.ProgramStatus; +import org.openhab.binding.mielecloud.internal.webservice.api.Quantity; import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; +import org.openhab.core.library.unit.Units; /** * @author Björn Lange - Initial contribution * @author Benjamin Bolte - Add info state channel and map signal flags from API tests - * @author Björn Lange - Add elapsed time channel + * @author Björn Lange - Add elapsed time, current water and energy consumption channels */ @NonNullByDefault public class DryerDeviceThingHandlerTest extends AbstractMieleThingHandlerTest { @Override protected AbstractMieleThingHandler setUpThingHandler() { return createThingHandler(MieleCloudBindingConstants.THING_TYPE_DRYER, DRYER_DEVICE_THING_UID, - DryerDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER); + DryerDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER, "1"); } @Test @@ -67,6 +70,7 @@ public class DryerDeviceThingHandlerTest extends AbstractMieleThingHandlerTest { when(deviceState.getDryingTargetRaw()).thenReturn(Optional.empty()); when(deviceState.getLightState()).thenReturn(Optional.empty()); when(deviceState.getDoorState()).thenReturn(Optional.empty()); + when(deviceState.getCurrentEnergyConsumption()).thenReturn(Optional.empty()); // when: getBridgeHandler().onDeviceStateUpdated(deviceState); @@ -87,6 +91,7 @@ public class DryerDeviceThingHandlerTest extends AbstractMieleThingHandlerTest { assertEquals(NULL_VALUE_STATE, getChannelState(DRYING_TARGET_RAW)); assertEquals(NULL_VALUE_STATE, getChannelState(LIGHT_SWITCH)); assertEquals(NULL_VALUE_STATE, getChannelState(DOOR_STATE)); + assertEquals(NULL_VALUE_STATE, getChannelState(ENERGY_CONSUMPTION_CURRENT)); }); } @@ -114,6 +119,7 @@ public class DryerDeviceThingHandlerTest extends AbstractMieleThingHandlerTest { when(deviceState.hasInfo()).thenReturn(true); when(deviceState.getLightState()).thenReturn(Optional.of(false)); when(deviceState.getDoorState()).thenReturn(Optional.of(false)); + when(deviceState.getCurrentEnergyConsumption()).thenReturn(Optional.of(new Quantity(2.5, "Wh"))); // when: getBridgeHandler().onDeviceStateUpdated(deviceState); @@ -136,6 +142,7 @@ public class DryerDeviceThingHandlerTest extends AbstractMieleThingHandlerTest { assertEquals(OnOffType.ON, getChannelState(INFO_STATE)); assertEquals(OnOffType.OFF, getChannelState(LIGHT_SWITCH)); assertEquals(OnOffType.OFF, getChannelState(DOOR_STATE)); + assertEquals(new QuantityType<>(2.5, Units.WATT_HOUR), getChannelState(ENERGY_CONSUMPTION_CURRENT)); }); } diff --git a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/HobDeviceThingHandlerTest.java b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/HobDeviceThingHandlerTest.java index 4a57cc474e9..007ddd08cc5 100644 --- a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/HobDeviceThingHandlerTest.java +++ b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/HobDeviceThingHandlerTest.java @@ -40,7 +40,7 @@ public class HobDeviceThingHandlerTest extends AbstractMieleThingHandlerTest { @Override protected AbstractMieleThingHandler setUpThingHandler() { return createThingHandler(MieleCloudBindingConstants.THING_TYPE_HOB, HOB_DEVICE_THING_UID, - HobDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER); + HobDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER, "0"); } @Test diff --git a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/HoodDeviceThingHandlerTest.java b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/HoodDeviceThingHandlerTest.java index ef87d664860..ecef8bfde7b 100644 --- a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/HoodDeviceThingHandlerTest.java +++ b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/HoodDeviceThingHandlerTest.java @@ -40,7 +40,7 @@ public class HoodDeviceThingHandlerTest extends AbstractMieleThingHandlerTest { @Override protected AbstractMieleThingHandler setUpThingHandler() { return createThingHandler(MieleCloudBindingConstants.THING_TYPE_HOOD, HOOD_DEVICE_THING_UID, - HoodDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER); + HoodDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER, "0"); } @Test diff --git a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/MieleHandlerFactoryTest.java b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/MieleHandlerFactoryTest.java index 61b810142e1..ac7ed386a74 100644 --- a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/MieleHandlerFactoryTest.java +++ b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/MieleHandlerFactoryTest.java @@ -184,7 +184,7 @@ public class MieleHandlerFactoryTest extends JavaOSGiTest { } private void testHandlerCanBeCreatedForMieleDevice(ThingTypeUID thingTypeUid, ThingUID thingUid, String label, - Class expectedHandlerClass) + Class expectedHandlerClass, String thingTypeVersion) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { // given: MieleWebservice webservice = mock(MieleWebservice.class); @@ -196,7 +196,7 @@ public class MieleHandlerFactoryTest extends JavaOSGiTest { Thing device = ThingBuilder.create(thingTypeUid, thingUid) .withConfiguration(new Configuration(Collections .singletonMap(MieleCloudBindingConstants.CONFIG_PARAM_DEVICE_IDENTIFIER, DEVICE_IDENTIFIER))) - .withLabel(label).build(); + .withLabel(label).withProperty("thingTypeVersion", thingTypeVersion).build(); assertNotNull(device); verifyHandlerCreation(webservice, device, expectedHandlerClass); @@ -227,77 +227,77 @@ public class MieleHandlerFactoryTest extends JavaOSGiTest { public void testHandlerCanBeCreatedForWashingDevice() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_WASHING_MACHINE, - WASHING_MACHINE_TYPE, "DA-6996", WashingDeviceThingHandler.class); + WASHING_MACHINE_TYPE, "DA-6996", WashingDeviceThingHandler.class, "1"); } @Test public void testHandlerCanBeCreatedForOvenDevice() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_OVEN, OVEN_DEVICE_TYPE, "OV-6887", - OvenDeviceThingHandler.class); + OvenDeviceThingHandler.class, "0"); } @Test public void testHandlerCanBeCreatedForHobDevice() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_HOB, HOB_DEVICE_TYPE, "HB-3887", - HobDeviceThingHandler.class); + HobDeviceThingHandler.class, "0"); } @Test public void testHandlerCanBeCreatedForFridgeFreezerDevice() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_FRIDGE_FREEZER, - FRIDGE_FREEZER_DEVICE_TYPE, "CD-6097", CoolingDeviceThingHandler.class); + FRIDGE_FREEZER_DEVICE_TYPE, "CD-6097", CoolingDeviceThingHandler.class, "0"); } @Test public void testHandlerCanBeCreatedForHoodDevice() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_HOOD, HOOD_DEVICE_TYPE, "HD-2097", - HoodDeviceThingHandler.class); + HoodDeviceThingHandler.class, "0"); } @Test public void testHandlerCanBeCreatedForCoffeeDevice() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_COFFEE_SYSTEM, COFFEE_DEVICE_TYPE, - "DA-6997", CoffeeSystemThingHandler.class); + "DA-6997", CoffeeSystemThingHandler.class, "0"); } @Test public void testHandlerCanBeCreatedForWineStorageDevice() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_WINE_STORAGE, - WINE_STORAGE_DEVICE_TYPE, "WS-6907", WineStorageDeviceThingHandler.class); + WINE_STORAGE_DEVICE_TYPE, "WS-6907", WineStorageDeviceThingHandler.class, "0"); } @Test public void testHandlerCanBeCreatedForDryerDevice() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_DRYER, DRYER_DEVICE_TYPE, "DR-0907", - DryerDeviceThingHandler.class); + DryerDeviceThingHandler.class, "1"); } @Test public void testHandlerCanBeCreatedForDishwasherDevice() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_DISHWASHER, DISHWASHER_DEVICE_TYPE, - "DR-0907", DishwasherDeviceThingHandler.class); + "DR-0907", DishwasherDeviceThingHandler.class, "1"); } @Test public void testHandlerCanBeCreatedForDishWarmerDevice() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_DISH_WARMER, - DISH_WARMER_DEVICE_TYPE, "DW-0907", DishWarmerDeviceThingHandler.class); + DISH_WARMER_DEVICE_TYPE, "DW-0907", DishWarmerDeviceThingHandler.class, "0"); } @Test public void testHandlerCanBeCreatedForRoboticVacuumCleanerDevice() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { testHandlerCanBeCreatedForMieleDevice(MieleCloudBindingConstants.THING_TYPE_ROBOTIC_VACUUM_CLEANER, - ROBOTIC_VACUUM_CLEANER_DEVICE_TYPE, "RVC-0907", RoboticVacuumCleanerDeviceThingHandler.class); + ROBOTIC_VACUUM_CLEANER_DEVICE_TYPE, "RVC-0907", RoboticVacuumCleanerDeviceThingHandler.class, "0"); } /** diff --git a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/OvenDeviceThingHandlerTest.java b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/OvenDeviceThingHandlerTest.java index 762483af7ba..beaa6a33b76 100644 --- a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/OvenDeviceThingHandlerTest.java +++ b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/OvenDeviceThingHandlerTest.java @@ -46,7 +46,7 @@ public class OvenDeviceThingHandlerTest extends AbstractMieleThingHandlerTest { @Override protected AbstractMieleThingHandler setUpThingHandler() { return createThingHandler(MieleCloudBindingConstants.THING_TYPE_OVEN, OVEN_DEVICE_THING_UID, - OvenDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER); + OvenDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER, "0"); } @Test diff --git a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/RoboticVacuumCleanerDeviceThingHandlerTest.java b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/RoboticVacuumCleanerDeviceThingHandlerTest.java index 23a74a9e72f..f63bea755ea 100644 --- a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/RoboticVacuumCleanerDeviceThingHandlerTest.java +++ b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/RoboticVacuumCleanerDeviceThingHandlerTest.java @@ -41,7 +41,8 @@ public class RoboticVacuumCleanerDeviceThingHandlerTest extends AbstractMieleThi protected AbstractMieleThingHandler setUpThingHandler() { return createThingHandler(MieleCloudBindingConstants.THING_TYPE_ROBOTIC_VACUUM_CLEANER, MieleCloudBindingIntegrationTestConstants.ROBOTIC_VACUUM_CLEANER_THING_UID, - RoboticVacuumCleanerDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER); + RoboticVacuumCleanerDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER, + "0"); } @Test diff --git a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/WashingDeviceThingHandlerTest.java b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/WashingDeviceThingHandlerTest.java index e495cab3ee7..176a2b259b2 100644 --- a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/WashingDeviceThingHandlerTest.java +++ b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/WashingDeviceThingHandlerTest.java @@ -28,17 +28,19 @@ import org.openhab.binding.mielecloud.internal.webservice.api.ActionsState; import org.openhab.binding.mielecloud.internal.webservice.api.DeviceState; import org.openhab.binding.mielecloud.internal.webservice.api.PowerStatus; import org.openhab.binding.mielecloud.internal.webservice.api.ProgramStatus; +import org.openhab.binding.mielecloud.internal.webservice.api.Quantity; import org.openhab.binding.mielecloud.internal.webservice.api.json.StateType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.library.unit.Units; /** * @author Björn Lange - Initial contribution * @author Benjamin Bolte - Add info state channel and map signal flags from API tests - * @author Björn Lange - Add elapsed time channel + * @author Björn Lange - Add elapsed time, current water and energy consumption channels */ @NonNullByDefault public class WashingDeviceThingHandlerTest extends AbstractMieleThingHandlerTest { @@ -46,7 +48,7 @@ public class WashingDeviceThingHandlerTest extends AbstractMieleThingHandlerTest @Override protected AbstractMieleThingHandler setUpThingHandler() { return createThingHandler(MieleCloudBindingConstants.THING_TYPE_WASHING_MACHINE, WASHING_MACHINE_THING_UID, - WashingDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER); + WashingDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER, "1"); } @Test @@ -71,6 +73,8 @@ public class WashingDeviceThingHandlerTest extends AbstractMieleThingHandlerTest when(deviceState.getTargetTemperature(0)).thenReturn(Optional.empty()); when(deviceState.getLightState()).thenReturn(Optional.empty()); when(deviceState.getDoorState()).thenReturn(Optional.empty()); + when(deviceState.getCurrentWaterConsumption()).thenReturn(Optional.empty()); + when(deviceState.getCurrentEnergyConsumption()).thenReturn(Optional.empty()); // when: getBridgeHandler().onDeviceStateUpdated(deviceState); @@ -92,6 +96,8 @@ public class WashingDeviceThingHandlerTest extends AbstractMieleThingHandlerTest assertEquals(NULL_VALUE_STATE, getChannelState(TEMPERATURE_TARGET)); assertEquals(NULL_VALUE_STATE, getChannelState(LIGHT_SWITCH)); assertEquals(NULL_VALUE_STATE, getChannelState(DOOR_STATE)); + assertEquals(NULL_VALUE_STATE, getChannelState(WATER_CONSUMPTION_CURRENT)); + assertEquals(NULL_VALUE_STATE, getChannelState(ENERGY_CONSUMPTION_CURRENT)); }); } @@ -120,6 +126,8 @@ public class WashingDeviceThingHandlerTest extends AbstractMieleThingHandlerTest when(deviceState.hasInfo()).thenReturn(true); when(deviceState.getLightState()).thenReturn(Optional.of(false)); when(deviceState.getDoorState()).thenReturn(Optional.of(true)); + when(deviceState.getCurrentWaterConsumption()).thenReturn(Optional.of(new Quantity(0.5, "l"))); + when(deviceState.getCurrentEnergyConsumption()).thenReturn(Optional.of(new Quantity(1.5, "kWh"))); // when: getBridgeHandler().onDeviceStateUpdated(deviceState); @@ -143,6 +151,8 @@ public class WashingDeviceThingHandlerTest extends AbstractMieleThingHandlerTest assertEquals(OnOffType.ON, getChannelState(INFO_STATE)); assertEquals(OnOffType.OFF, getChannelState(LIGHT_SWITCH)); assertEquals(OnOffType.ON, getChannelState(DOOR_STATE)); + assertEquals(new QuantityType<>(0.5, Units.LITRE), getChannelState(WATER_CONSUMPTION_CURRENT)); + assertEquals(new QuantityType<>(1.5, Units.KILOWATT_HOUR), getChannelState(ENERGY_CONSUMPTION_CURRENT)); }); } diff --git a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/WineStorageDeviceThingHandlerTest.java b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/WineStorageDeviceThingHandlerTest.java index b6540ba0e7d..8cfad8cf5b3 100644 --- a/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/WineStorageDeviceThingHandlerTest.java +++ b/itests/org.openhab.binding.mielecloud.tests/src/main/java/org/openhab/binding/mielecloud/internal/handler/WineStorageDeviceThingHandlerTest.java @@ -43,7 +43,7 @@ public class WineStorageDeviceThingHandlerTest extends AbstractMieleThingHandler @Override protected AbstractMieleThingHandler setUpThingHandler() { return createThingHandler(MieleCloudBindingConstants.THING_TYPE_WINE_STORAGE, WINE_STORAGE_DEVICE_THING_UID, - WineStorageDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER); + WineStorageDeviceThingHandler.class, MieleCloudBindingIntegrationTestConstants.SERIAL_NUMBER, "0"); } @Test