[mielecloud] Add channels energy and water consumption (#14456)

* 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 <bjoern.lange@itemis.de>
This commit is contained in:
Björn Lange
2023-03-24 23:15:28 +01:00
committed by GitHub
parent 983efd76ea
commit fbcb412353
37 changed files with 1133 additions and 41 deletions

View File

@@ -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";
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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<Quantity> 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<String> 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<State> 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();
}
}
}

View File

@@ -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());
}

View File

@@ -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<Quantity> 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<Quantity> 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.
*

View File

@@ -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<String> unit;
public Quantity(double value, @Nullable String unit) {
this.value = value;
this.unit = Optional.ofNullable(unit);
}
public double getValue() {
return value;
}
public Optional<String> 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 + "]";
}
}

View File

@@ -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<WaterConsumption> getCurrentWaterConsumption() {
return Optional.ofNullable(currentWaterConsumption);
}
public Optional<EnergyConsumption> getCurrentEnergyConsumption() {
return Optional.ofNullable(currentEnergyConsumption);
}
/**
* Gets the relative water usage for the selected program from 0 to 1.
*/
public Optional<Double> getWaterForecast() {
return Optional.ofNullable(waterForecast);
}
/**
* Gets the relative energy usage for the selected program from 0 to 1.
*/
public Optional<Double> 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
+ "]";
}
}

View File

@@ -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<String> getUnit() {
return Optional.ofNullable(unit);
}
/**
* Gets the amount of energy.
*/
public Optional<Double> 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 + "]";
}
}

View File

@@ -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> plateStep = null;
@Nullable
private EcoFeedback ecoFeedback;
@Nullable
private Integer batteryLevel;
public Optional<Status> getStatus() {
@@ -189,6 +191,10 @@ public class State {
return Collections.unmodifiableList(plateStep);
}
public Optional<EcoFeedback> getEcoFeedback() {
return Optional.ofNullable(ecoFeedback);
}
public Optional<Integer> 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 + "]";
}
}

View File

@@ -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<String> getUnit() {
return Optional.ofNullable(unit);
}
/**
* Gets the amount of water.
*/
public Optional<Double> 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 + "]";
}
}

View File

@@ -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.

View File

@@ -438,6 +438,30 @@
<state readOnly="true"/>
</channel-type>
<channel-type id="water_consumption_current">
<item-type>Number:Volume</item-type>
<label>@text/channel-type.mielecloud.water_consumption_current.label</label>
<description>@text/channel-type.mielecloud.water_consumption_current.description</description>
<category>Water</category>
<tags>
<tag>Measurement</tag>
<tag>Water</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="energy_consumption_current">
<item-type>Number:Energy</item-type>
<label>@text/channel-type.mielecloud.energy_consumption_current.label</label>
<description>@text/channel-type.mielecloud.energy_consumption_current.description</description>
<category>Energy</category>
<tags>
<tag>Measurement</tag>
<tag>Energy</tag>
</tags>
<state pattern="%.1f %unit%" readOnly="true"/>
</channel-type>
<channel-type id="battery_level">
<item-type>Number</item-type>
<label>@text/channel-type.mielecloud.battery_level.label</label>

View File

@@ -34,9 +34,12 @@
<channel id="error_state" typeId="error_state"/>
<channel id="info_state" typeId="info_state"/>
<channel id="door_state" typeId="door_state"/>
<channel id="water_consumption_current" typeId="water_consumption_current"/>
<channel id="energy_consumption_current" typeId="energy_consumption_current"/>
</channels>
<properties>
<property name="thingTypeVersion">1</property>
<property name="vendor">Miele</property>
</properties>

View File

@@ -38,9 +38,11 @@
<channel id="light_switch" typeId="light_switch"/>
<channel id="light_can_be_controlled" typeId="light_can_be_controlled"/>
<channel id="door_state" typeId="door_state"/>
<channel id="energy_consumption_current" typeId="energy_consumption_current"/>
</channels>
<properties>
<property name="thingTypeVersion">1</property>
<property name="vendor">Miele</property>
</properties>

View File

@@ -41,9 +41,12 @@
<channel id="light_switch" typeId="light_switch"/>
<channel id="light_can_be_controlled" typeId="light_can_be_controlled"/>
<channel id="door_state" typeId="door_state"/>
<channel id="water_consumption_current" typeId="water_consumption_current"/>
<channel id="energy_consumption_current" typeId="energy_consumption_current"/>
</channels>
<properties>
<property name="thingTypeVersion">1</property>
<property name="vendor">Miele</property>
</properties>

View File

@@ -39,9 +39,12 @@
<channel id="light_switch" typeId="light_switch"/>
<channel id="light_can_be_controlled" typeId="light_can_be_controlled"/>
<channel id="door_state" typeId="door_state"/>
<channel id="water_consumption_current" typeId="water_consumption_current"/>
<channel id="energy_consumption_current" typeId="energy_consumption_current"/>
</channels>
<properties>
<property name="thingTypeVersion">1</property>
<property name="vendor">Miele</property>
</properties>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">
<thing-type uid="mielecloud:dishwasher">
<instruction-set targetVersion="1">
<add-channel id="water_consumption_current">
<type>mielecloud:water_consumption_current</type>
</add-channel>
<add-channel id="energy_consumption_current">
<type>mielecloud:energy_consumption_current</type>
</add-channel>
</instruction-set>
</thing-type>
<thing-type uid="mielecloud:dryer">
<instruction-set targetVersion="1">
<add-channel id="energy_consumption_current">
<type>mielecloud:energy_consumption_current</type>
</add-channel>
</instruction-set>
</thing-type>
<thing-type uid="mielecloud:washer_dryer">
<instruction-set targetVersion="1">
<add-channel id="water_consumption_current">
<type>mielecloud:water_consumption_current</type>
</add-channel>
<add-channel id="energy_consumption_current">
<type>mielecloud:energy_consumption_current</type>
</add-channel>
</instruction-set>
</thing-type>
<thing-type uid="mielecloud:washing_machine">
<instruction-set targetVersion="1">
<add-channel id="water_consumption_current">
<type>mielecloud:water_consumption_current</type>
</add-channel>
<add-channel id="energy_consumption_current">
<type>mielecloud:energy_consumption_current</type>
</add-channel>
</instruction-set>
</thing-type>
</update:update-descriptions>