+++ /dev/null
-/**
- * Copyright (c) 2010-2022 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.miele.internal;
-
-import java.util.Map.Entry;
-
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-
-/**
- * The {@link DeviceMetaData} class represents the Metadata node in the response JSON.
- *
- * @author Jacob Laursen - Initial contribution
- */
-public class DeviceMetaData {
- public String Filter;
- public String description;
- public String LocalizedID;
- public String LocalizedValue;
- public JsonObject MieleEnum;
- public String access;
-
- public String getMieleEnum(String s) {
- if (this.MieleEnum == null) {
- return null;
- }
-
- for (Entry<String, JsonElement> enumEntry : this.MieleEnum.entrySet()) {
- if (enumEntry.getValue().getAsString().trim().equals(s.trim())) {
- return enumEntry.getKey();
- }
- }
-
- return null;
- }
-}
import java.nio.charset.StandardCharsets;
import java.util.Map;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.miele.internal.api.dto.DeviceMetaData;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.SIUnits;
*
* @author Jacob Laursen - Initial contribution
*/
+@NonNullByDefault
public class DeviceUtil {
private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
private static final String TEMPERATURE_UNDEFINED = "32768";
private static final String TEMPERATURE_COLD = "-32760";
private static final String TEXT_PREFIX = "miele.";
- private static final Map<String, String> states = Map.ofEntries(Map.entry("1", "off"), Map.entry("2", "stand-by"),
+ private static final Map<String, String> STATES = Map.ofEntries(Map.entry("1", "off"), Map.entry("2", "stand-by"),
Map.entry("3", "programmed"), Map.entry("4", "waiting-to-start"), Map.entry("5", "running"),
Map.entry("6", "paused"), Map.entry("7", "end"), Map.entry("8", "failure"), Map.entry("9", "abort"),
Map.entry("10", "idle"), Map.entry("11", "rinse-hold"), Map.entry("12", "service"),
* Get state text for provided string taking into consideration {@link DeviceMetaData}
* as well as built-in/translated strings.
*/
- public static State getStateTextState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- return getTextState(s, dmd, translationProvider, states, MISSING_STATE_TEXT_PREFIX, "");
+ public static State getStateTextState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return getTextState(s, dmd, translationProvider, STATES, MISSING_STATE_TEXT_PREFIX, "");
}
/**
* @param appliancePrefix Appliance prefix appended to text key (including dot)
* @return Text string as State
*/
- public static State getTextState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider,
- Map<String, String> valueMap, String propertyPrefix, String appliancePrefix) {
+ public static State getTextState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider, Map<String, String> valueMap, String propertyPrefix,
+ String appliancePrefix) {
if ("0".equals(s)) {
return UnDefType.UNDEF;
}
}
String value = valueMap.get(s);
- if (value != null) {
+ if (value != null && translationProvider != null) {
String key = TEXT_PREFIX + propertyPrefix + appliancePrefix + value;
return new StringType(
translationProvider.getText(key, gatewayText != null ? gatewayText : propertyPrefix + s));
*/
package org.openhab.binding.miele.internal;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
/**
* The {@link FullyQualifiedApplianceIdentifier} class represents a fully qualified appliance identifier.
* Example: "hdm:ZigBee:0123456789abcdef#210"
*
* @author Jacob Laursen - Initial contribution
*/
+@NonNullByDefault
public class FullyQualifiedApplianceIdentifier {
private String uid;
private String protocol;
}
/**
- * @return Protocol prefix of fully qualified appliance identifier (e.g. "hdmi:ZigBee:"")
+ * @return Protocol prefix of fully qualified appliance identifier (e.g. "hdmi:ZigBee:")
*/
public String getProtocol() {
return this.protocol;
public static final String BINDING_ID = "miele";
public static final String APPLIANCE_ID = "uid";
+ public static final String MIELE_CLASS = "com.miele.xgw3000.gateway.hdm.deviceclasses.Miele";
// Properties
public static final String PROPERTY_DEVICE_CLASS = "deviceClass";
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miele.internal.discovery.MieleApplianceDiscoveryService;
import org.openhab.binding.miele.internal.handler.CoffeeMachineHandler;
import org.openhab.binding.miele.internal.handler.DishWasherHandler;
*
* @author Karel Goderis - Initial contribution
*/
+@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.miele")
public class MieleHandlerFactory extends BaseThingHandlerFactory {
}
@Override
- public Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, ThingUID thingUID,
- ThingUID bridgeUID) {
+ public @Nullable Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration,
+ @Nullable ThingUID thingUID, @Nullable ThingUID bridgeUID) {
if (MieleBridgeHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
ThingUID mieleBridgeUID = getBridgeThingUID(thingTypeUID, thingUID, configuration);
return super.createThing(thingTypeUID, configuration, mieleBridgeUID, null);
}
@Override
- protected ThingHandler createHandler(Thing thing) {
+ protected @Nullable ThingHandler createHandler(Thing thing) {
if (MieleBridgeHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
MieleBridgeHandler handler = new MieleBridgeHandler((Bridge) thing);
registerApplianceDiscoveryService(handler);
return null;
}
- private ThingUID getBridgeThingUID(ThingTypeUID thingTypeUID, ThingUID thingUID, Configuration configuration) {
+ private ThingUID getBridgeThingUID(ThingTypeUID thingTypeUID, @Nullable ThingUID thingUID,
+ Configuration configuration) {
if (thingUID == null) {
String hostID = (String) configuration.get(HOST);
thingUID = new ThingUID(thingTypeUID, hostID);
return thingUID;
}
- private ThingUID getApplianceUID(ThingTypeUID thingTypeUID, ThingUID thingUID, Configuration configuration,
- ThingUID bridgeUID) {
+ private ThingUID getApplianceUID(ThingTypeUID thingTypeUID, @Nullable ThingUID thingUID,
+ Configuration configuration, @Nullable ThingUID bridgeUID) {
String applianceId = (String) configuration.get(APPLIANCE_ID);
if (thingUID == null) {
- thingUID = new ThingUID(thingTypeUID, applianceId, bridgeUID.getId());
+ if (bridgeUID == null) {
+ thingUID = new ThingUID(thingTypeUID, applianceId);
+ } else {
+ thingUID = new ThingUID(thingTypeUID, bridgeUID, applianceId);
+ }
}
return thingUID;
}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.miele.internal.api.dto;
+
+import com.google.gson.JsonArray;
+
+/**
+ * The {@link DeviceClassObject} class represents the DeviceClassObject node in the response JSON.
+ *
+ * @author Jacob Laursen - Initial contribution
+ **/
+public class DeviceClassObject {
+ public String DeviceClassType;
+ public JsonArray Operations;
+ public String DeviceClass;
+ public JsonArray Properties;
+
+ public DeviceClassObject() {
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.miele.internal.api.dto;
+
+import java.util.Map.Entry;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+/**
+ * The {@link DeviceMetaData} class represents the Metadata node in the response JSON.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+public class DeviceMetaData {
+ public String Filter;
+ public String description;
+ public String LocalizedID;
+ public String LocalizedValue;
+ public JsonObject MieleEnum;
+ public String access;
+
+ public String getMieleEnum(String s) {
+ if (this.MieleEnum == null) {
+ return null;
+ }
+
+ for (Entry<String, JsonElement> enumEntry : this.MieleEnum.entrySet()) {
+ if (enumEntry.getValue().getAsString().trim().equals(s.trim())) {
+ return enumEntry.getKey();
+ }
+ }
+
+ return null;
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.miele.internal.api.dto;
+
+import com.google.gson.JsonObject;
+
+/**
+ * The {@link DeviceProperty} class represents the DeviceProperty node in the response JSON.
+ *
+ * @author Jacob Laursen - Initial contribution
+ **/
+public class DeviceProperty {
+ public String Name;
+ public String Value;
+ public int Polling;
+ public JsonObject Metadata;
+
+ public DeviceProperty() {
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.miele.internal.api.dto;
+
+import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
+import org.openhab.binding.miele.internal.MieleBindingConstants;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+/**
+ * The {@link HomeDevice} class represents the HomeDevice node in the response JSON.
+ *
+ * @author Jacob Laursen - Initial contribution
+ **/
+public class HomeDevice {
+
+ private static final String MIELE_APPLIANCE_CLASS = "com.miele.xgw3000.gateway.hdm.deviceclasses.MieleAppliance";
+
+ public String Name;
+ public String Status;
+ public String ParentUID;
+ public String ProtocolAdapterName;
+ public String Vendor;
+ public String UID;
+ public String Type;
+ public JsonArray DeviceClasses;
+ public String Version;
+ public String TimestampAdded;
+ public JsonObject Error;
+ public JsonObject Properties;
+
+ public HomeDevice() {
+ }
+
+ public FullyQualifiedApplianceIdentifier getApplianceIdentifier() {
+ return new FullyQualifiedApplianceIdentifier(this.UID);
+ }
+
+ public String getSerialNumber() {
+ return Properties.get("serial.number").getAsString();
+ }
+
+ public String getFirmwareVersion() {
+ return Properties.get("firmware.version").getAsString();
+ }
+
+ public String getRemoteUid() {
+ JsonElement remoteUid = Properties.get("remote.uid");
+ if (remoteUid == null) {
+ // remote.uid and serial.number seems to be the same. If remote.uid
+ // is missing for some reason, it makes sense to provide fallback
+ // to serial number.
+ return getSerialNumber();
+ }
+ return remoteUid.getAsString();
+ }
+
+ public String getConnectionType() {
+ JsonElement connectionType = Properties.get("connection.type");
+ if (connectionType == null) {
+ return null;
+ }
+ return connectionType.getAsString();
+ }
+
+ public String getConnectionBaudRate() {
+ JsonElement baudRate = Properties.get("connection.baud.rate");
+ if (baudRate == null) {
+ return null;
+ }
+ return baudRate.getAsString();
+ }
+
+ public String getApplianceModel() {
+ JsonElement model = Properties.get("miele.model");
+ if (model == null) {
+ return "";
+ }
+ return model.getAsString();
+ }
+
+ public String getDeviceClass() {
+ for (JsonElement dc : DeviceClasses) {
+ String dcStr = dc.getAsString();
+ if (dcStr.contains(MieleBindingConstants.MIELE_CLASS) && !dcStr.equals(MIELE_APPLIANCE_CLASS)) {
+ return dcStr.substring(MieleBindingConstants.MIELE_CLASS.length());
+ }
+ }
+ return null;
+ }
+}
import java.util.Map;
import java.util.Set;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
+import org.openhab.binding.miele.internal.api.dto.DeviceClassObject;
+import org.openhab.binding.miele.internal.api.dto.DeviceProperty;
+import org.openhab.binding.miele.internal.api.dto.HomeDevice;
import org.openhab.binding.miele.internal.handler.ApplianceStatusListener;
import org.openhab.binding.miele.internal.handler.MieleApplianceHandler;
import org.openhab.binding.miele.internal.handler.MieleBridgeHandler;
-import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceClassObject;
-import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceProperty;
-import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.HomeDevice;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
* @author Martin Lepsy - Added protocol information in order so support WiFi devices
* @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
+@NonNullByDefault
public class MieleApplianceDiscoveryService extends AbstractDiscoveryService implements ApplianceStatusListener {
private final Logger logger = LoggerFactory.getLogger(MieleApplianceDiscoveryService.class);
@Override
public void startScan() {
List<HomeDevice> appliances = mieleBridgeHandler.getHomeDevices();
- if (appliances != null) {
- for (HomeDevice l : appliances) {
- onApplianceAddedInternal(l);
- }
+ for (HomeDevice l : appliances) {
+ onApplianceAddedInternal(l);
}
}
Map<String, Object> properties = new HashMap<>(9);
FullyQualifiedApplianceIdentifier applianceIdentifier = appliance.getApplianceIdentifier();
- properties.put(Thing.PROPERTY_VENDOR, appliance.Vendor);
+ String vendor = appliance.Vendor;
+ if (vendor != null) {
+ properties.put(Thing.PROPERTY_VENDOR, vendor);
+ }
properties.put(Thing.PROPERTY_MODEL_ID, appliance.getApplianceModel());
properties.put(Thing.PROPERTY_SERIAL_NUMBER, appliance.getSerialNumber());
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, appliance.getFirmwareVersion());
- properties.put(PROPERTY_PROTOCOL_ADAPTER, appliance.ProtocolAdapterName);
+ String protocolAdapterName = appliance.ProtocolAdapterName;
+ if (protocolAdapterName != null) {
+ properties.put(PROPERTY_PROTOCOL_ADAPTER, protocolAdapterName);
+ }
properties.put(APPLIANCE_ID, applianceIdentifier.getApplianceId());
String deviceClass = appliance.getDeviceClass();
if (deviceClass != null) {
// nothing to do
}
- private ThingUID getThingUID(HomeDevice appliance) {
+ private @Nullable ThingUID getThingUID(HomeDevice appliance) {
ThingUID bridgeUID = mieleBridgeHandler.getThing().getUID();
String modelId = appliance.getDeviceClass();
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.miele.internal.exceptions;
+
+import java.io.IOException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link MieleRpcException} indicates failure to perform JSON-RPC call.
+ *
+ * @author Jacob Laursen - Initial contribution
+ */
+@NonNullByDefault
+public class MieleRpcException extends IOException {
+
+ private static final long serialVersionUID = -8147063891196639054L;
+
+ public MieleRpcException(String message) {
+ super(message);
+ }
+
+ public MieleRpcException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
*/
package org.openhab.binding.miele.internal.handler;
-import org.openhab.binding.miele.internal.DeviceMetaData;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miele.internal.MieleTranslationProvider;
+import org.openhab.binding.miele.internal.api.dto.DeviceMetaData;
import org.openhab.core.types.State;
/**
* @author Karel Goderis - Initial contribution
* @author Jacob Laursen - Added power/water consumption channels
*/
+@NonNullByDefault
public interface ApplianceChannelSelector {
@Override
* @param dmd - the device meta data
* @param translationProvider {@link MieleTranslationProvider} instance
*/
- State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider);
+ State getState(String s, @Nullable DeviceMetaData dmd, @Nullable MieleTranslationProvider translationProvider);
/**
* Returns a State for the given string, taking into
* @param s - the value to be used to instantiate the State
* @param dmd - the device meta data
*/
- State getState(String s, DeviceMetaData dmd);
+ State getState(String s, @Nullable DeviceMetaData dmd);
/**
* Returns a raw State for the given string, not taking into
*/
package org.openhab.binding.miele.internal.handler;
+import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
-import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceClassObject;
-import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceProperty;
-import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.HomeDevice;
+import org.openhab.binding.miele.internal.api.dto.DeviceClassObject;
+import org.openhab.binding.miele.internal.api.dto.DeviceProperty;
+import org.openhab.binding.miele.internal.api.dto.HomeDevice;
/**
*
* @author Karel Goderis - Initial contribution
* @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
+@NonNullByDefault
public interface ApplianceStatusListener {
/**
import static org.openhab.binding.miele.internal.MieleBindingConstants.*;
import java.lang.reflect.Method;
-import java.util.Collections;
import java.util.Map;
-import org.openhab.binding.miele.internal.DeviceMetaData;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miele.internal.DeviceUtil;
import org.openhab.binding.miele.internal.MieleTranslationProvider;
+import org.openhab.binding.miele.internal.api.dto.DeviceMetaData;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
* @author Stephan Esch - Initial contribution
* @author Jacob Laursen - Added raw channels
*/
+@NonNullByDefault
public enum CoffeeMachineChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getStateTextState(s, dmd, translationProvider);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getStateTextState(s, dmd, translationProvider);
}
},
- STATE(null, STATE_CHANNEL_ID, DecimalType.class, false),
+ STATE("", STATE_CHANNEL_ID, DecimalType.class, false),
PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getTextState(s, dmd, translationProvider, programs, MISSING_PROGRAM_TEXT_PREFIX,
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getTextState(s, dmd, translationProvider, PROGRAMS, MISSING_PROGRAM_TEXT_PREFIX,
MIELE_COFFEE_MACHINE_TEXT_PREFIX);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
}
},
- PROGRAM(null, PROGRAM_CHANNEL_ID, DecimalType.class, false),
+ PROGRAM("", PROGRAM_CHANNEL_ID, DecimalType.class, false),
PROGRAMTYPE("programType", "type", StringType.class, false),
PROGRAM_PHASE_TEXT(PHASE_PROPERTY_NAME, PHASE_TEXT_CHANNEL_ID, StringType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getTextState(s, dmd, translationProvider, phases, MISSING_PHASE_TEXT_PREFIX,
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getTextState(s, dmd, translationProvider, PHASES, MISSING_PHASE_TEXT_PREFIX,
MIELE_COFFEE_MACHINE_TEXT_PREFIX);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
}
},
PROGRAM_PHASE(RAW_PHASE_PROPERTY_NAME, PHASE_CHANNEL_ID, DecimalType.class, false),
// lightingStatus signalFailure signalInfo
DOOR("signalDoor", "door", OpenClosedType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
if ("true".equals(s)) {
return getState("OPEN");
}
return UnDefType.UNDEF;
}
},
- SWITCH(null, "switch", OnOffType.class, false);
+ SWITCH("", "switch", OnOffType.class, false);
private final Logger logger = LoggerFactory.getLogger(CoffeeMachineChannelSelector.class);
- private static final Map<String, String> programs = Collections.<String, String> emptyMap();
+ private static final Map<String, String> PROGRAMS = Map.of();
- private static final Map<String, String> phases = Collections.<String, String> emptyMap();
+ private static final Map<String, String> PHASES = Map.of();
private final String mieleID;
private final String channelID;
}
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return this.getState(s, dmd);
}
@Override
- public State getState(String s, DeviceMetaData dmd) {
+ public State getState(String s, @Nullable DeviceMetaData dmd) {
if (dmd != null) {
String localizedValue = dmd.getMieleEnum(s);
if (localizedValue == null) {
logger.error("An exception occurred while converting '{}' into a State", s);
}
- return null;
+ return UnDefType.UNDEF;
}
}
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_COFFEE_SYSTEM;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.miele.internal.exceptions.MieleRpcException;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.library.types.OnOffType;
* @author Martin Lepsy - fixed handling of empty JSON results
* @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
+@NonNullByDefault
public class CoffeeMachineHandler extends MieleApplianceHandler<CoffeeMachineChannelSelector> {
private final Logger logger = LoggerFactory.getLogger(CoffeeMachineHandler.class);
String channelID = channelUID.getId();
String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ if (applianceId == null) {
+ logger.warn("Command '{}' failed, appliance id is unknown", command);
+ return;
+ }
CoffeeMachineChannelSelector selector = (CoffeeMachineChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null;
try {
- if (selector != null) {
- switch (selector) {
- case SWITCH: {
- if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOn");
- } else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOff");
- }
- break;
+ switch (selector) {
+ case SWITCH: {
+ MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+ if (bridgeHandler == null) {
+ logger.warn("Command '{}' failed, missing bridge handler", command);
+ return;
+ }
+ if (command.equals(OnOffType.ON)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOn");
+ } else if (command.equals(OnOffType.OFF)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOff");
}
- default: {
- if (!(command instanceof RefreshType)) {
- logger.debug("{} is a read-only channel that does not accept commands",
- selector.getChannelID());
- }
+ break;
+ }
+ default: {
+ if (!(command instanceof RefreshType)) {
+ logger.debug("{} is a read-only channel that does not accept commands",
+ selector.getChannelID());
}
}
}
logger.warn(
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
channelID, command.toString());
+ } catch (MieleRpcException e) {
+ Throwable cause = e.getCause();
+ if (cause == null) {
+ logger.warn("An error occurred while trying to invoke operation: {}", e.getMessage());
+ } else {
+ logger.warn("An error occurred while trying to invoke operation: {} -> {}", e.getMessage(),
+ cause.getMessage());
+ }
}
}
}
import java.math.BigDecimal;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.miele.internal.exceptions.MieleRpcException;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.library.types.OnOffType;
* @author Martin Lepsy - fixed handling of empty JSON results
* @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN), added power/water consumption channels
*/
+@NonNullByDefault
public class DishWasherHandler extends MieleApplianceHandler<DishwasherChannelSelector>
implements ExtendedDeviceStateListener {
String channelID = channelUID.getId();
String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ if (applianceId == null) {
+ logger.warn("Command '{}' failed, appliance id is unknown", command);
+ return;
+ }
DishwasherChannelSelector selector = (DishwasherChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null;
try {
- if (selector != null) {
- switch (selector) {
- case SWITCH: {
- if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "start");
- } else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "stop");
- }
- break;
+ switch (selector) {
+ case SWITCH: {
+ MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+ if (bridgeHandler == null) {
+ logger.warn("Command '{}' failed, missing bridge handler", command);
+ return;
}
- default: {
- if (!(command instanceof RefreshType)) {
- logger.debug("{} is a read-only channel that does not accept commands",
- selector.getChannelID());
- }
+ if (command.equals(OnOffType.ON)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "start");
+ } else if (command.equals(OnOffType.OFF)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "stop");
+ }
+ break;
+ }
+ default: {
+ if (!(command instanceof RefreshType)) {
+ logger.debug("{} is a read-only channel that does not accept commands",
+ selector.getChannelID());
}
}
}
logger.warn(
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
channelID, command.toString());
+ } catch (MieleRpcException e) {
+ Throwable cause = e.getCause();
+ if (cause == null) {
+ logger.warn("An error occurred while trying to invoke operation: {}", e.getMessage());
+ } else {
+ logger.warn("An error occurred while trying to invoke operation: {} -> {}", e.getMessage(),
+ cause.getMessage());
+ }
}
}
import java.util.Map;
import java.util.TimeZone;
-import org.openhab.binding.miele.internal.DeviceMetaData;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miele.internal.DeviceUtil;
import org.openhab.binding.miele.internal.MieleTranslationProvider;
+import org.openhab.binding.miele.internal.api.dto.DeviceMetaData;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
* @author Kai Kreuzer - Changed START_TIME to DateTimeType
* @author Jacob Laursen - Added power/water consumption channels, raw channels
*/
+@NonNullByDefault
public enum DishwasherChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true, false),
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true, false),
STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getStateTextState(s, dmd, translationProvider);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getStateTextState(s, dmd, translationProvider);
}
},
- STATE(null, STATE_CHANNEL_ID, DecimalType.class, false, false),
+ STATE("", STATE_CHANNEL_ID, DecimalType.class, false, false),
PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getTextState(s, dmd, translationProvider, programs, MISSING_PROGRAM_TEXT_PREFIX,
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getTextState(s, dmd, translationProvider, PROGRAMS, MISSING_PROGRAM_TEXT_PREFIX,
MIELE_DISHWASHER_TEXT_PREFIX);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
}
},
- PROGRAM(null, PROGRAM_CHANNEL_ID, DecimalType.class, false, false),
+ PROGRAM("", PROGRAM_CHANNEL_ID, DecimalType.class, false, false),
PROGRAM_PHASE_TEXT(PHASE_PROPERTY_NAME, PHASE_TEXT_CHANNEL_ID, StringType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getTextState(s, dmd, translationProvider, phases, MISSING_PHASE_TEXT_PREFIX,
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getTextState(s, dmd, translationProvider, PHASES, MISSING_PHASE_TEXT_PREFIX,
MIELE_DISHWASHER_TEXT_PREFIX);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
}
},
PROGRAM_PHASE(RAW_PHASE_PROPERTY_NAME, PHASE_CHANNEL_ID, DecimalType.class, false, false),
START_TIME("startTime", "start", DateTimeType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
DURATION("duration", "duration", DateTimeType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
ELAPSED_TIME("elapsedTime", "elapsed", DateTimeType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
FINISH_TIME("finishTime", "finish", DateTimeType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
DOOR("signalDoor", "door", OpenClosedType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
if ("true".equals(s)) {
return getState("OPEN");
}
return UnDefType.UNDEF;
}
},
- SWITCH(null, "switch", OnOffType.class, false, false),
+ SWITCH("", "switch", OnOffType.class, false, false),
POWER_CONSUMPTION(EXTENDED_DEVICE_STATE_PROPERTY_NAME, POWER_CONSUMPTION_CHANNEL_ID, QuantityType.class, false,
true),
WATER_CONSUMPTION(EXTENDED_DEVICE_STATE_PROPERTY_NAME, WATER_CONSUMPTION_CHANNEL_ID, QuantityType.class, false,
private final Logger logger = LoggerFactory.getLogger(DishwasherChannelSelector.class);
- private static final Map<String, String> programs = Map.ofEntries(entry("26", "intensive"),
+ private static final Map<String, String> PROGRAMS = Map.ofEntries(entry("26", "intensive"),
entry("27", "maintenance-programme"), entry("28", "eco"), entry("30", "normal"), entry("32", "automatic"),
entry("34", "solarsave"), entry("35", "gentle"), entry("36", "extra-quiet"), entry("37", "hygiene"),
entry("38", "quickpowerwash"), entry("42", "tall-items"));
- private static final Map<String, String> phases = Map.ofEntries(entry("2", "pre-wash"), entry("3", "main-wash"),
+ private static final Map<String, String> PHASES = Map.ofEntries(entry("2", "pre-wash"), entry("3", "main-wash"),
entry("4", "rinses"), entry("6", "final-rinse"), entry("7", "drying"), entry("8", "finished"));
private final String mieleID;
}
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return this.getState(s, dmd);
}
@Override
- public State getState(String s, DeviceMetaData dmd) {
+ public State getState(String s, @Nullable DeviceMetaData dmd) {
if (dmd != null) {
String localizedValue = dmd.getMieleEnum(s);
if (localizedValue == null) {
logger.error("An exception occurred while converting '{}' into a State", s);
}
- return null;
+ return UnDefType.UNDEF;
}
}
*/
package org.openhab.binding.miele.internal.handler;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
/**
* Appliance handlers can implement the {@link ExtendedDeviceStateListener} interface
* to extract additional information from the ExtendedDeviceState property.
*
* @author Jacob Laursen - Initial contribution
*/
+@NonNullByDefault
public interface ExtendedDeviceStateListener {
void onApplianceExtendedStateChanged(byte[] extendedDeviceState);
}
import java.lang.reflect.Method;
-import org.openhab.binding.miele.internal.DeviceMetaData;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miele.internal.DeviceUtil;
import org.openhab.binding.miele.internal.MieleTranslationProvider;
+import org.openhab.binding.miele.internal.api.dto.DeviceMetaData;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
* @author Karel Goderis - Initial contribution
* @author Jacob Laursen - Added UoM for temperatures, raw channels
*/
+@NonNullByDefault
public enum FridgeChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getStateTextState(s, dmd, translationProvider);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getStateTextState(s, dmd, translationProvider);
}
},
- STATE(null, STATE_CHANNEL_ID, DecimalType.class, false),
- SUPERCOOL(null, SUPERCOOL_CHANNEL_ID, OnOffType.class, false),
+ STATE("", STATE_CHANNEL_ID, DecimalType.class, false),
+ SUPERCOOL("", SUPERCOOL_CHANNEL_ID, OnOffType.class, false),
FRIDGECURRENTTEMP("currentTemperature", "current", QuantityType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getTemperatureState(s);
}
},
FRIDGETARGETTEMP("targetTemperature", "target", QuantityType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getTemperatureState(s);
}
},
DOOR("signalDoor", "door", OpenClosedType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
if ("true".equals(s)) {
return getState("OPEN");
}
return UnDefType.UNDEF;
}
},
- START(null, "start", OnOffType.class, false);
+ START("", "start", OnOffType.class, false);
private final Logger logger = LoggerFactory.getLogger(FridgeChannelSelector.class);
}
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return this.getState(s, dmd);
}
@Override
- public State getState(String s, DeviceMetaData dmd) {
+ public State getState(String s, @Nullable DeviceMetaData dmd) {
if (dmd != null) {
String localizedValue = dmd.getMieleEnum(s);
if (localizedValue == null) {
logger.error("An exception occurred while converting '{}' into a State", s);
}
- return null;
+ return UnDefType.UNDEF;
}
public State getTemperatureState(String s) {
import java.lang.reflect.Method;
-import org.openhab.binding.miele.internal.DeviceMetaData;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miele.internal.DeviceUtil;
import org.openhab.binding.miele.internal.MieleTranslationProvider;
+import org.openhab.binding.miele.internal.api.dto.DeviceMetaData;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
* @author Karel Goderis - Initial contribution
* @author Jacob Laursen - Added UoM for temperatures, raw channels
*/
+@NonNullByDefault
public enum FridgeFreezerChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getStateTextState(s, dmd, translationProvider);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getStateTextState(s, dmd, translationProvider);
}
},
- STATE(null, STATE_CHANNEL_ID, DecimalType.class, false),
+ STATE("", STATE_CHANNEL_ID, DecimalType.class, false),
FREEZERSTATE("freezerState", "freezerstate", StringType.class, false),
FRIDGESTATE("fridgeState", "fridgestate", StringType.class, false),
- SUPERCOOL(null, SUPERCOOL_CHANNEL_ID, OnOffType.class, false),
- SUPERFREEZE(null, SUPERFREEZE_CHANNEL_ID, OnOffType.class, false),
+ SUPERCOOL("", SUPERCOOL_CHANNEL_ID, OnOffType.class, false),
+ SUPERFREEZE("", SUPERFREEZE_CHANNEL_ID, OnOffType.class, false),
FREEZERCURRENTTEMP("freezerCurrentTemperature", "freezercurrent", QuantityType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getTemperatureState(s);
}
},
FREEZERTARGETTEMP("freezerTargetTemperature", "freezertarget", QuantityType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getTemperatureState(s);
}
},
FRIDGECURRENTTEMP("fridgeCurrentTemperature", "fridgecurrent", QuantityType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getTemperatureState(s);
}
},
FRIDGETARGETTEMP("fridgeTargetTemperature", "fridgetarget", QuantityType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getTemperatureState(s);
}
},
DOOR("signalDoor", "door", OpenClosedType.class, false) {
@Override
-
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
if ("true".equals(s)) {
return getState("OPEN");
}
return UnDefType.UNDEF;
}
},
- START(null, "start", OnOffType.class, false);
+ START("", "start", OnOffType.class, false);
private final Logger logger = LoggerFactory.getLogger(FridgeFreezerChannelSelector.class);
}
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return this.getState(s, dmd);
}
@Override
- public State getState(String s, DeviceMetaData dmd) {
+ public State getState(String s, @Nullable DeviceMetaData dmd) {
if (dmd != null) {
String localizedValue = dmd.getMieleEnum(s);
if (localizedValue == null) {
logger.error("An exception occurred while converting '{}' into a State", s);
}
- return null;
+ return UnDefType.UNDEF;
}
public State getTemperatureState(String s) {
import static org.openhab.binding.miele.internal.MieleBindingConstants.*;
-import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceProperty;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.miele.internal.api.dto.DeviceProperty;
+import org.openhab.binding.miele.internal.exceptions.MieleRpcException;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.library.types.OnOffType;
* @author Martin Lepsy - fixed handling of empty JSON results
* @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
+@NonNullByDefault
public class FridgeFreezerHandler extends MieleApplianceHandler<FridgeFreezerChannelSelector> {
private final Logger logger = LoggerFactory.getLogger(FridgeFreezerHandler.class);
String channelID = channelUID.getId();
String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ if (applianceId == null) {
+ logger.warn("Command '{}' failed, appliance id is unknown", command);
+ return;
+ }
FridgeFreezerChannelSelector selector = (FridgeFreezerChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null;
try {
- if (selector != null) {
- switch (selector) {
- case SUPERCOOL: {
- if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "startSuperCooling");
- } else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "stopSuperCooling");
- }
- break;
- }
- case SUPERFREEZE: {
- if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "startSuperFreezing");
- } else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "stopSuperFreezing");
- }
- break;
+ MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+ if (bridgeHandler == null) {
+ logger.warn("Command '{}' failed, missing bridge handler", command);
+ return;
+ }
+ switch (selector) {
+ case SUPERCOOL: {
+ if (command.equals(OnOffType.ON)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "startSuperCooling");
+ } else if (command.equals(OnOffType.OFF)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "stopSuperCooling");
}
- default: {
- logger.debug("{} is a read-only channel that does not accept commands",
- selector.getChannelID());
+ break;
+ }
+ case SUPERFREEZE: {
+ if (command.equals(OnOffType.ON)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "startSuperFreezing");
+ } else if (command.equals(OnOffType.OFF)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "stopSuperFreezing");
}
+ break;
+ }
+ default: {
+ logger.debug("{} is a read-only channel that does not accept commands", selector.getChannelID());
}
}
// process result
logger.warn(
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
channelID, command.toString());
+ } catch (MieleRpcException e) {
+ Throwable cause = e.getCause();
+ if (cause == null) {
+ logger.warn("An error occurred while trying to invoke operation: {}", e.getMessage());
+ } else {
+ logger.warn("An error occurred while trying to invoke operation: {} -> {}", e.getMessage(),
+ cause.getMessage());
+ }
}
}
import static org.openhab.binding.miele.internal.MieleBindingConstants.*;
-import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceProperty;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.miele.internal.api.dto.DeviceProperty;
+import org.openhab.binding.miele.internal.exceptions.MieleRpcException;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.library.types.OnOffType;
* @author Martin Lepsy - fixed handling of empty JSON results
* @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
+@NonNullByDefault
public class FridgeHandler extends MieleApplianceHandler<FridgeChannelSelector> {
private final Logger logger = LoggerFactory.getLogger(FridgeHandler.class);
String channelID = channelUID.getId();
String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ if (applianceId == null) {
+ logger.warn("Command '{}' failed, appliance id is unknown", command);
+ return;
+ }
FridgeChannelSelector selector = (FridgeChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null;
try {
- if (selector != null) {
- switch (selector) {
- case SUPERCOOL: {
- if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "startSuperCooling");
- } else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "stopSuperCooling");
- }
- break;
+ MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+ if (bridgeHandler == null) {
+ logger.warn("Command '{}' failed, missing bridge handler", command);
+ return;
+ }
+ switch (selector) {
+ case SUPERCOOL: {
+ if (command.equals(OnOffType.ON)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "startSuperCooling");
+ } else if (command.equals(OnOffType.OFF)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "stopSuperCooling");
}
- case START: {
- if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "start");
- }
- break;
+ break;
+ }
+ case START: {
+ if (command.equals(OnOffType.ON)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "start");
}
- default: {
- if (!(command instanceof RefreshType)) {
- logger.debug("{} is a read-only channel that does not accept commands",
- selector.getChannelID());
- }
+ break;
+ }
+ default: {
+ if (!(command instanceof RefreshType)) {
+ logger.debug("{} is a read-only channel that does not accept commands",
+ selector.getChannelID());
}
}
}
logger.warn(
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
channelID, command.toString());
+ } catch (MieleRpcException e) {
+ Throwable cause = e.getCause();
+ if (cause == null) {
+ logger.warn("An error occurred while trying to invoke operation: {}", e.getMessage());
+ } else {
+ logger.warn("An error occurred while trying to invoke operation: {} -> {}", e.getMessage(),
+ cause.getMessage());
+ }
}
}
import java.lang.reflect.Method;
-import org.openhab.binding.miele.internal.DeviceMetaData;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miele.internal.DeviceUtil;
import org.openhab.binding.miele.internal.MieleTranslationProvider;
+import org.openhab.binding.miele.internal.api.dto.DeviceMetaData;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.types.State;
import org.openhab.core.types.Type;
+import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
* @author Karel Goderis - Initial contribution
* @author Jacob Laursen - Added raw channels
*/
+@NonNullByDefault
public enum HobChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getStateTextState(s, dmd, translationProvider);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getStateTextState(s, dmd, translationProvider);
}
},
- STATE(null, STATE_CHANNEL_ID, DecimalType.class, false),
+ STATE("", STATE_CHANNEL_ID, DecimalType.class, false),
PLATES("plateNumbers", "plates", DecimalType.class, true),
PLATE1_POWER("plate1PowerStep", "plate1power", DecimalType.class, false),
PLATE1_HEAT("plate1RemainingHeat", "plate1heat", DecimalType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
// If there is remaining heat, the device metadata contains some informative string which can not be
// converted into a DecimalType. We therefore ignore the metadata and return the device property value as a
// State
PLATE2_POWER("plate2PowerStep", "plate2power", DecimalType.class, false),
PLATE2_HEAT("plate2RemainingHeat", "plate2heat", DecimalType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getState(s);
}
},
PLATE3_POWER("plate3PowerStep", "plate3power", DecimalType.class, false),
PLATE3_HEAT("plate3RemainingHeat", "plate3heat", DecimalType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getState(s);
}
},
PLATE4_POWER("plate4PowerStep", "plate4power", DecimalType.class, false),
PLATE4_HEAT("plate4RemainingHeat", "plate4heat", DecimalType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getState(s);
}
},
PLATE5_POWER("plate5PowerStep", "plate5power", DecimalType.class, false),
PLATE5_HEAT("plate5RemainingHeat", "plate5heat", DecimalType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getState(s);
}
},
PLATE6_POWER("plate6PowerStep", "plate6power", DecimalType.class, false),
PLATE6_HEAT("plate6RemainingHeat", "plate6heat", DecimalType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getState(s);
}
},
}
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return this.getState(s, dmd);
}
@Override
- public State getState(String s, DeviceMetaData dmd) {
+ public State getState(String s, @Nullable DeviceMetaData dmd) {
if (dmd != null) {
String localizedValue = dmd.getMieleEnum(s);
if (localizedValue == null) {
logger.error("An exception occurred while converting '{}' into a State", s);
}
- return null;
+ return UnDefType.UNDEF;
}
}
import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_HOB;
+import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.thing.ChannelUID;
* @author Karel Goderis - Initial contribution
* @author Kai Kreuzer - fixed handling of REFRESH commands
*/
+@NonNullByDefault
public class HobHandler extends MieleApplianceHandler<HobChannelSelector> {
public HobHandler(Thing thing, TranslationProvider i18nProvider, LocaleProvider localeProvider) {
import java.lang.reflect.Method;
-import org.openhab.binding.miele.internal.DeviceMetaData;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miele.internal.DeviceUtil;
import org.openhab.binding.miele.internal.MieleTranslationProvider;
+import org.openhab.binding.miele.internal.api.dto.DeviceMetaData;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.StringType;
* @author Karel Goderis - Initial contribution
* @author Jacob Laursen - Added raw channels
*/
+@NonNullByDefault
public enum HoodChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getStateTextState(s, dmd, translationProvider);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getStateTextState(s, dmd, translationProvider);
}
},
- STATE(null, STATE_CHANNEL_ID, DecimalType.class, false),
+ STATE("", STATE_CHANNEL_ID, DecimalType.class, false),
VENTILATION("ventilationPower", "ventilation", DecimalType.class, false),
LIGHT("lightingStatus", "light", OnOffType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
if ("true".equals(s)) {
return getState("ON");
}
return UnDefType.UNDEF;
}
},
- STOP(null, "stop", OnOffType.class, false);
+ STOP("", "stop", OnOffType.class, false);
private final Logger logger = LoggerFactory.getLogger(HoodChannelSelector.class);
}
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return this.getState(s, dmd);
}
@Override
- public State getState(String s, DeviceMetaData dmd) {
+ public State getState(String s, @Nullable DeviceMetaData dmd) {
if (dmd != null) {
String localizedValue = dmd.getMieleEnum(s);
if (localizedValue == null) {
logger.error("An exception occurred while converting '{}' into a State", s);
}
- return null;
+ return UnDefType.UNDEF;
}
}
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_HOOD;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.miele.internal.exceptions.MieleRpcException;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.library.types.OnOffType;
* @author Martin Lepsy - fixed handling of empty JSON results
* @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
**/
+@NonNullByDefault
public class HoodHandler extends MieleApplianceHandler<HoodChannelSelector> {
private final Logger logger = LoggerFactory.getLogger(HoodHandler.class);
String channelID = channelUID.getId();
String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ if (applianceId == null) {
+ logger.warn("Command '{}' failed, appliance id is unknown", command);
+ return;
+ }
HoodChannelSelector selector = (HoodChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null;
try {
- if (selector != null) {
- switch (selector) {
- case LIGHT: {
- if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "startLighting");
- } else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "stopLighting");
- }
- break;
- }
- case STOP: {
- if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "stop");
- }
- break;
+ MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+ if (bridgeHandler == null) {
+ logger.warn("Command '{}' failed, missing bridge handler", command);
+ return;
+ }
+ switch (selector) {
+ case LIGHT: {
+ if (command.equals(OnOffType.ON)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "startLighting");
+ } else if (command.equals(OnOffType.OFF)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "stopLighting");
}
- default: {
- logger.debug("{} is a read-only channel that does not accept commands",
- selector.getChannelID());
+ break;
+ }
+ case STOP: {
+ if (command.equals(OnOffType.ON)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "stop");
}
+ break;
+ }
+ default: {
+ logger.debug("{} is a read-only channel that does not accept commands", selector.getChannelID());
}
}
// process result
logger.warn(
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
channelID, command.toString());
+ } catch (MieleRpcException e) {
+ Throwable cause = e.getCause();
+ if (cause == null) {
+ logger.warn("An error occurred while trying to invoke operation: {}", e.getMessage());
+ } else {
+ logger.warn("An error occurred while trying to invoke operation: {} -> {}", e.getMessage(),
+ cause.getMessage());
+ }
}
}
}
import java.util.Locale;
import java.util.Map;
import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import org.eclipse.jdt.annotation.NonNull;
-import org.openhab.binding.miele.internal.DeviceMetaData;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miele.internal.DeviceUtil;
import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
import org.openhab.binding.miele.internal.MieleTranslationProvider;
-import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceClassObject;
-import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.DeviceProperty;
-import org.openhab.binding.miele.internal.handler.MieleBridgeHandler.HomeDevice;
+import org.openhab.binding.miele.internal.api.dto.DeviceClassObject;
+import org.openhab.binding.miele.internal.api.dto.DeviceMetaData;
+import org.openhab.binding.miele.internal.api.dto.DeviceProperty;
+import org.openhab.binding.miele.internal.api.dto.HomeDevice;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.thing.Bridge;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
-import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
+import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
* @author Martin Lepsy - Added check for JsonNull result
* @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
+@NonNullByDefault
public abstract class MieleApplianceHandler<E extends Enum<E> & ApplianceChannelSelector> extends BaseThingHandler
implements ApplianceStatusListener {
private final Logger logger = LoggerFactory.getLogger(MieleApplianceHandler.class);
- public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Stream
- .of(THING_TYPE_DISHWASHER, THING_TYPE_OVEN, THING_TYPE_FRIDGE, THING_TYPE_DRYER, THING_TYPE_HOB,
- THING_TYPE_FRIDGEFREEZER, THING_TYPE_HOOD, THING_TYPE_WASHINGMACHINE, THING_TYPE_COFFEEMACHINE)
- .collect(Collectors.toSet());
+ public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_DISHWASHER, THING_TYPE_OVEN,
+ THING_TYPE_FRIDGE, THING_TYPE_DRYER, THING_TYPE_HOB, THING_TYPE_FRIDGEFREEZER, THING_TYPE_HOOD,
+ THING_TYPE_WASHINGMACHINE, THING_TYPE_COFFEEMACHINE);
protected Gson gson = new Gson();
- protected String applianceId;
- protected MieleBridgeHandler bridgeHandler;
+ protected @Nullable String applianceId;
+ protected @Nullable MieleBridgeHandler bridgeHandler;
protected TranslationProvider i18nProvider;
protected LocaleProvider localeProvider;
- protected MieleTranslationProvider translationProvider;
+ protected @Nullable MieleTranslationProvider translationProvider;
private Class<E> selectorType;
protected String modelID;
String.format("Could not get enum constants for value selector: %s", valueSelectorText));
}
for (ApplianceChannelSelector c : enumConstants) {
- if (c != null && c.getChannelID() != null && c.getChannelID().equals(valueSelectorText)) {
+ if (c.getChannelID().equals(valueSelectorText)) {
return c;
}
}
String.format("Could not get enum constants for value selector: %s", valueSelectorText));
}
for (ApplianceChannelSelector c : enumConstants) {
- if (c != null && c.getMieleID() != null && c.getMieleID().equals(valueSelectorText)) {
+ if (!c.getMieleID().isEmpty() && c.getMieleID().equals(valueSelectorText)) {
return c;
}
}
if (applianceId != null) {
MieleBridgeHandler bridgeHandler = getMieleBridgeHandler();
if (bridgeHandler != null) {
- getMieleBridgeHandler().unregisterApplianceStatusListener(this);
+ bridgeHandler.unregisterApplianceStatusListener(this);
}
applianceId = null;
}
@Override
public void onApplianceStateChanged(FullyQualifiedApplianceIdentifier applicationIdentifier,
DeviceClassObject dco) {
- String myApplianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
- if (myApplianceId == null || !myApplianceId.equals(applicationIdentifier.getApplianceId())) {
+ String applianceId = this.applianceId;
+ if (applianceId == null || !applianceId.equals(applicationIdentifier.getApplianceId())) {
return;
}
- for (JsonElement prop : dco.Properties.getAsJsonArray()) {
+ JsonArray properties = dco.Properties;
+ if (properties == null) {
+ return;
+ }
+
+ for (JsonElement prop : properties.getAsJsonArray()) {
try {
DeviceProperty dp = gson.fromJson(prop, DeviceProperty.class);
if (dp == null) {
@Override
public void onAppliancePropertyChanged(FullyQualifiedApplianceIdentifier applicationIdentifier, DeviceProperty dp) {
- String myApplianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ String applianceId = this.applianceId;
- if (myApplianceId == null || !myApplianceId.equals(applicationIdentifier.getApplianceId())) {
+ if (applianceId == null || !applianceId.equals(applicationIdentifier.getApplianceId())) {
return;
}
if (dp.Metadata == null) {
String metadata = metaDataCache.get(new StringBuilder().append(dp.Name).toString().trim());
if (metadata != null) {
- JsonObject jsonMetaData = (JsonObject) JsonParser.parseString(metadata);
- dmd = gson.fromJson(jsonMetaData, DeviceMetaData.class);
+ JsonObject jsonMetadata = (JsonObject) JsonParser.parseString(metadata);
+ dmd = gson.fromJson(jsonMetadata, DeviceMetaData.class);
// only keep the enum, if any - that's all we care for events we receive via multicast
// all other fields are nulled
if (dmd != null) {
}
}
}
- if (dp.Metadata != null) {
- String metadata = dp.Metadata.toString().replace("enum", "MieleEnum");
+ JsonObject jsonMetadata = dp.Metadata;
+ if (jsonMetadata != null) {
+ String metadata = jsonMetadata.toString().replace("enum", "MieleEnum");
JsonObject jsonMetaData = (JsonObject) JsonParser.parseString(metadata);
dmd = gson.fromJson(jsonMetaData, DeviceMetaData.class);
metaDataCache.put(new StringBuilder().append(dp.Name).toString().trim(), metadata);
if (!selector.isProperty()) {
ChannelUID theChannelUID = new ChannelUID(getThing().getUID(), selector.getChannelID());
- if (dp.Value != null) {
- State state = selector.getState(dpValue, dmd, this.translationProvider);
- logger.trace("Update state of {} with getState '{}'", theChannelUID, state);
- updateState(theChannelUID, state);
- updateRawChannel(dp.Name, dpValue);
- } else {
- updateState(theChannelUID, UnDefType.UNDEF);
- }
+ State state = selector.getState(dpValue, dmd, this.translationProvider);
+ logger.trace("Update state of {} with getState '{}'", theChannelUID, state);
+ updateState(theChannelUID, state);
+ updateRawChannel(dp.Name, dpValue);
} else {
logger.debug("Updating the property '{}' of '{}' to '{}'", selector.getChannelID(),
getThing().getUID(), selector.getState(dpValue, dmd, this.translationProvider).toString());
- @NonNull
- Map<@NonNull String, @NonNull String> properties = editProperties();
+ Map<String, String> properties = editProperties();
properties.put(selector.getChannelID(),
selector.getState(dpValue, dmd, this.translationProvider).toString());
updateProperties(properties);
@Override
public void onApplianceRemoved(HomeDevice appliance) {
+ String applianceId = this.applianceId;
if (applianceId == null) {
return;
}
- if (applianceId.equals(appliance.getApplianceIdentifier().getApplianceId())) {
+ FullyQualifiedApplianceIdentifier applianceIdentifier = appliance.getApplianceIdentifier();
+ if (applianceIdentifier == null) {
+ return;
+ }
+
+ if (applianceId.equals(applianceIdentifier.getApplianceId())) {
updateStatus(ThingStatus.OFFLINE);
}
}
@Override
public void onApplianceAdded(HomeDevice appliance) {
+ String applianceId = this.applianceId;
if (applianceId == null) {
return;
}
FullyQualifiedApplianceIdentifier applianceIdentifier = appliance.getApplianceIdentifier();
+ if (applianceIdentifier == null) {
+ return;
+ }
if (applianceId.equals(applianceIdentifier.getApplianceId())) {
- @NonNull
- Map<@NonNull String, @NonNull String> properties = editProperties();
- properties.put(Thing.PROPERTY_VENDOR, appliance.Vendor);
+ Map<String, String> properties = editProperties();
+ String vendor = appliance.Vendor;
+ if (vendor != null) {
+ properties.put(Thing.PROPERTY_VENDOR, vendor);
+ }
properties.put(Thing.PROPERTY_MODEL_ID, appliance.getApplianceModel());
properties.put(Thing.PROPERTY_SERIAL_NUMBER, appliance.getSerialNumber());
properties.put(Thing.PROPERTY_FIRMWARE_VERSION, appliance.getFirmwareVersion());
- properties.put(PROPERTY_PROTOCOL_ADAPTER, appliance.ProtocolAdapterName);
+ String protocolAdapterName = appliance.ProtocolAdapterName;
+ if (protocolAdapterName != null) {
+ properties.put(PROPERTY_PROTOCOL_ADAPTER, protocolAdapterName);
+ }
String deviceClass = appliance.getDeviceClass();
if (deviceClass != null) {
properties.put(PROPERTY_DEVICE_CLASS, deviceClass);
}
}
- private synchronized MieleBridgeHandler getMieleBridgeHandler() {
+ private synchronized @Nullable MieleBridgeHandler getMieleBridgeHandler() {
if (this.bridgeHandler == null) {
Bridge bridge = getBridge();
if (bridge == null) {
}
ThingHandler handler = bridge.getHandler();
if (handler instanceof MieleBridgeHandler) {
- this.bridgeHandler = (MieleBridgeHandler) handler;
- this.bridgeHandler.registerApplianceStatusListener(this);
+ var bridgeHandler = (MieleBridgeHandler) handler;
+ this.bridgeHandler = bridgeHandler;
+ bridgeHandler.registerApplianceStatusListener(this);
} else {
return null;
}
}
protected boolean isResultProcessable(JsonElement result) {
- if (result == null) {
- throw new IllegalArgumentException("Provided result is null");
- }
return !result.isJsonNull();
}
}
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.IllformedLocaleException;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
-import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miele.internal.FullyQualifiedApplianceIdentifier;
+import org.openhab.binding.miele.internal.api.dto.DeviceClassObject;
+import org.openhab.binding.miele.internal.api.dto.DeviceProperty;
+import org.openhab.binding.miele.internal.api.dto.HomeDevice;
+import org.openhab.binding.miele.internal.exceptions.MieleRpcException;
import org.openhab.core.common.NamedThreadFactory;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Bridge;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
/**
* @author Martin Lepsy - Added protocol information to support WiFi devices & some refactoring for HomeDevice
* @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
**/
+@NonNullByDefault
public class MieleBridgeHandler extends BaseBridgeHandler {
- @NonNull
- public static final Set<@NonNull ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_XGW3000);
-
- private static final String MIELE_CLASS = "com.miele.xgw3000.gateway.hdm.deviceclasses.Miele";
+ public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Set.of(THING_TYPE_XGW3000);
private static final Pattern IP_PATTERN = Pattern
.compile("^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");
private final Logger logger = LoggerFactory.getLogger(MieleBridgeHandler.class);
protected List<ApplianceStatusListener> applianceStatusListeners = new CopyOnWriteArrayList<>();
- protected ScheduledFuture<?> pollingJob;
- protected ExecutorService executor;
- protected Future<?> eventListenerJob;
+ protected @Nullable ScheduledFuture<?> pollingJob;
+ protected @Nullable ExecutorService executor;
+ protected @Nullable Future<?> eventListenerJob;
- @NonNull
protected Map<String, HomeDevice> cachedHomeDevicesByApplianceId = new ConcurrentHashMap<String, HomeDevice>();
protected Map<String, HomeDevice> cachedHomeDevicesByRemoteUid = new ConcurrentHashMap<String, HomeDevice>();
- protected URL url;
- protected Map<String, String> headers;
-
- // Data structures to de-JSONify whatever Miele appliances are sending us
- public class HomeDevice {
-
- private static final String MIELE_APPLIANCE_CLASS = "com.miele.xgw3000.gateway.hdm.deviceclasses.MieleAppliance";
-
- public String Name;
- public String Status;
- public String ParentUID;
- public String ProtocolAdapterName;
- public String Vendor;
- public String UID;
- public String Type;
- public JsonArray DeviceClasses;
- public String Version;
- public String TimestampAdded;
- public JsonObject Error;
- public JsonObject Properties;
-
- HomeDevice() {
- }
-
- public FullyQualifiedApplianceIdentifier getApplianceIdentifier() {
- return new FullyQualifiedApplianceIdentifier(this.UID);
- }
-
- @NonNull
- public String getSerialNumber() {
- return Properties.get("serial.number").getAsString();
- }
-
- @NonNull
- public String getFirmwareVersion() {
- return Properties.get("firmware.version").getAsString();
- }
-
- @NonNull
- public String getRemoteUid() {
- JsonElement remoteUid = Properties.get("remote.uid");
- if (remoteUid == null) {
- // remote.uid and serial.number seems to be the same. If remote.uid
- // is missing for some reason, it makes sense to provide fallback
- // to serial number.
- return getSerialNumber();
- }
- return remoteUid.getAsString();
- }
-
- public String getConnectionType() {
- JsonElement connectionType = Properties.get("connection.type");
- if (connectionType == null) {
- return null;
- }
- return connectionType.getAsString();
- }
-
- public String getConnectionBaudRate() {
- JsonElement baudRate = Properties.get("connection.baud.rate");
- if (baudRate == null) {
- return null;
- }
- return baudRate.getAsString();
- }
-
- @NonNull
- public String getApplianceModel() {
- JsonElement model = Properties.get("miele.model");
- if (model == null) {
- return "";
- }
- return model.getAsString();
- }
-
- public String getDeviceClass() {
- for (JsonElement dc : DeviceClasses) {
- String dcStr = dc.getAsString();
- if (dcStr.contains(MIELE_CLASS) && !dcStr.equals(MIELE_APPLIANCE_CLASS)) {
- return dcStr.substring(MIELE_CLASS.length());
- }
- }
- return null;
- }
- }
-
- public class DeviceClassObject {
- public String DeviceClassType;
- public JsonArray Operations;
- public String DeviceClass;
- public JsonArray Properties;
-
- DeviceClassObject() {
- }
- }
-
- public class DeviceOperation {
- public String Name;
- public String Arguments;
- public JsonObject Metadata;
-
- DeviceOperation() {
- }
- }
-
- public class DeviceProperty {
- public String Name;
- public String Value;
- public int Polling;
- public JsonObject Metadata;
-
- DeviceProperty() {
- }
- }
+ protected @Nullable URL url;
public MieleBridgeHandler(Bridge bridge) {
super(bridge);
return;
}
- // for future usage - no headers to be set for now
- headers = new HashMap<>();
-
onUpdate();
lastBridgeConnectionState = false;
updateStatus(ThingStatus.UNKNOWN);
cachedHomeDevicesByRemoteUid.put(hd.getRemoteUid(), hd);
}
- @NonNull
- Set<@NonNull Entry<String, HomeDevice>> cachedEntries = cachedHomeDevicesByApplianceId.entrySet();
- @NonNull
- Iterator<@NonNull Entry<String, HomeDevice>> iterator = cachedEntries.iterator();
+ Set<Entry<String, HomeDevice>> cachedEntries = cachedHomeDevicesByApplianceId.entrySet();
+ Iterator<Entry<String, HomeDevice>> iterator = cachedEntries.iterator();
while (iterator.hasNext()) {
Entry<String, HomeDevice> cachedEntry = iterator.next();
for (Thing appliance : getThing().getThings()) {
if (appliance.getStatus() == ThingStatus.ONLINE) {
String applianceId = (String) appliance.getConfiguration().getProperties().get(APPLIANCE_ID);
- FullyQualifiedApplianceIdentifier applianceIdentifier = getApplianceIdentifierFromApplianceId(
- applianceId);
+ FullyQualifiedApplianceIdentifier applianceIdentifier = null;
+ if (applianceId != null) {
+ applianceIdentifier = getApplianceIdentifierFromApplianceId(applianceId);
+ }
if (applianceIdentifier == null) {
- logger.error("The appliance with ID '{}' was not found in appliance list from bridge.",
+ logger.warn("The appliance with ID '{}' was not found in appliance list from bridge.",
applianceId);
continue;
}
args[1] = true;
JsonElement result = invokeRPC("HDAccess/getDeviceClassObjects", args);
- if (result != null) {
- for (JsonElement obj : result.getAsJsonArray()) {
- try {
- DeviceClassObject dco = gson.fromJson(obj, DeviceClassObject.class);
+ for (JsonElement obj : result.getAsJsonArray()) {
+ try {
+ DeviceClassObject dco = gson.fromJson(obj, DeviceClassObject.class);
- // Skip com.prosyst.mbs.services.zigbee.hdm.deviceclasses.ReportingControl
- if (dco == null || !dco.DeviceClass.startsWith(MIELE_CLASS)) {
- continue;
- }
+ // Skip com.prosyst.mbs.services.zigbee.hdm.deviceclasses.ReportingControl
+ if (dco == null || !dco.DeviceClass.startsWith(MIELE_CLASS)) {
+ continue;
+ }
- for (ApplianceStatusListener listener : applianceStatusListeners) {
- listener.onApplianceStateChanged(applianceIdentifier, dco);
- }
- } catch (Exception e) {
- logger.debug("An exception occurred while querying an appliance : '{}'",
- e.getMessage());
+ for (ApplianceStatusListener listener : applianceStatusListeners) {
+ listener.onApplianceStateChanged(applianceIdentifier, dco);
}
+ } catch (Exception e) {
+ logger.debug("An exception occurred while querying an appliance : '{}'",
+ e.getMessage());
}
}
}
}
- } catch (Exception e) {
- logger.debug("An exception occurred while polling an appliance :'{}'", e.getMessage());
+ } catch (MieleRpcException e) {
+ Throwable cause = e.getCause();
+ if (cause == null) {
+ logger.debug("An exception occurred while polling an appliance: '{}'", e.getMessage());
+ } else {
+ logger.debug("An exception occurred while polling an appliance: '{}' -> '{}'", e.getMessage(),
+ cause.getMessage());
+ }
}
}
// That's why we do an HTTP access instead
// If there is no connection, this line will fail
- JsonElement result = invokeRPC("system.listMethods", null);
- if (result == null) {
- logger.debug("{} is not reachable", ipAddress);
- return false;
- }
- } catch (Exception e) {
+ invokeRPC("system.listMethods", new Object[0]);
+ } catch (MieleRpcException e) {
+ logger.debug("{} is not reachable", ipAddress);
return false;
}
for (JsonElement obj : result.getAsJsonArray()) {
HomeDevice hd = gson.fromJson(obj, HomeDevice.class);
- devices.add(hd);
+ if (hd != null) {
+ devices.add(hd);
+ }
+ }
+ } catch (MieleRpcException e) {
+ Throwable cause = e.getCause();
+ if (cause == null) {
+ logger.debug("An exception occurred while getting the home devices: '{}'", e.getMessage());
+ } else {
+ logger.debug("An exception occurred while getting the home devices: '{}' -> '{}", e.getMessage(),
+ cause.getMessage());
}
- } catch (Exception e) {
- logger.debug("An exception occurred while getting the home devices :'{}'", e.getMessage());
}
}
return devices;
}
- private FullyQualifiedApplianceIdentifier getApplianceIdentifierFromApplianceId(String applianceId) {
+ private @Nullable FullyQualifiedApplianceIdentifier getApplianceIdentifierFromApplianceId(String applianceId) {
HomeDevice homeDevice = this.cachedHomeDevicesByApplianceId.get(applianceId);
if (homeDevice == null) {
return null;
logger.debug("Received a multicast event '{}' from '{}:{}'", event, packet.getAddress(),
packet.getPort());
- DeviceProperty dp = new DeviceProperty();
- String id = null;
-
String[] parts = event.split("&");
+ String id = null, name = null, value = null;
for (String p : parts) {
String[] subparts = p.split("=");
switch (subparts[0]) {
case "property": {
- dp.Name = subparts[1];
+ name = subparts[1];
break;
}
case "value": {
- dp.Value = subparts[1].strip().trim();
+ value = subparts[1].strip().trim();
break;
}
case "id": {
}
}
- if (id == null) {
+ if (id == null || name == null || value == null) {
continue;
}
}
applianceIdentifier = device.getApplianceIdentifier();
}
+ var deviceProperty = new DeviceProperty();
+ deviceProperty.Name = name;
+ deviceProperty.Value = value;
for (ApplianceStatusListener listener : applianceStatusListeners) {
- listener.onAppliancePropertyChanged(applianceIdentifier, dp);
+ listener.onAppliancePropertyChanged(applianceIdentifier, deviceProperty);
}
} catch (SocketTimeoutException e) {
try {
}
};
- public JsonElement invokeOperation(String applianceId, String modelID, String methodName) {
+ public JsonElement invokeOperation(String applianceId, String modelID, String methodName) throws MieleRpcException {
if (getThing().getStatus() != ThingStatus.ONLINE) {
- logger.debug("The Bridge is offline - operations can not be invoked.");
- return null;
+ throw new MieleRpcException("Bridge is offline, operations can not be invoked");
}
FullyQualifiedApplianceIdentifier applianceIdentifier = getApplianceIdentifierFromApplianceId(applianceId);
if (applianceIdentifier == null) {
- logger.error(
- "The appliance with ID '{}' was not found in appliance list from bridge - operations can not be invoked.",
- applianceId);
- return null;
+ throw new MieleRpcException("Appliance with ID" + applianceId
+ + " was not found in appliance list from gateway - operations can not be invoked");
}
Object[] args = new Object[4];
return invokeRPC("HDAccess/invokeDCOOperation", args);
}
- protected JsonElement invokeRPC(String methodName, Object[] args) {
- int id = rand.nextInt(Integer.MAX_VALUE);
+ protected JsonElement invokeRPC(String methodName, Object[] args) throws MieleRpcException {
+ JsonElement result = null;
+ URL url = this.url;
+ if (url == null) {
+ throw new MieleRpcException("URL is not set");
+ }
JsonObject req = new JsonObject();
+ int id = rand.nextInt(Integer.MAX_VALUE);
req.addProperty("jsonrpc", "2.0");
req.addProperty("id", id);
req.addProperty("method", methodName);
- JsonElement result = null;
-
JsonArray params = new JsonArray();
- if (args != null) {
- for (Object o : args) {
- params.add(gson.toJsonTree(o));
- }
+ for (Object o : args) {
+ params.add(gson.toJsonTree(o));
}
req.add("params", params);
String requestData = req.toString();
String responseData = null;
try {
- responseData = post(url, headers, requestData);
- } catch (Exception e) {
- logger.debug("An exception occurred while posting data : '{}'", e.getMessage());
- }
-
- if (responseData != null) {
- logger.trace("The request '{}' yields '{}'", requestData, responseData);
- JsonObject resp = (JsonObject) JsonParser.parseReader(new StringReader(responseData));
-
- result = resp.get("result");
- JsonElement error = resp.get("error");
-
- if (error != null && !error.isJsonNull()) {
- if (error.isJsonPrimitive()) {
- logger.debug("A remote exception occurred: '{}'", error.getAsString());
- } else if (error.isJsonObject()) {
- JsonObject o = error.getAsJsonObject();
- Integer code = (o.has("code") ? o.get("code").getAsInt() : null);
- String message = (o.has("message") ? o.get("message").getAsString() : null);
- String data = (o.has("data") ? (o.get("data") instanceof JsonObject ? o.get("data").toString()
- : o.get("data").getAsString()) : null);
- logger.debug("A remote exception occurred: '{}':'{}':'{}'", code, message, data);
- } else {
- logger.debug("An unknown remote exception occurred: '{}'", error.toString());
- }
+ responseData = post(url, Collections.emptyMap(), requestData);
+ } catch (IOException e) {
+ throw new MieleRpcException("Exception occurred while posting data", e);
+ }
+
+ logger.trace("The request '{}' yields '{}'", requestData, responseData);
+ JsonObject parsedResponse = null;
+ try {
+ parsedResponse = (JsonObject) JsonParser.parseReader(new StringReader(responseData));
+ } catch (JsonParseException e) {
+ throw new MieleRpcException("Error parsing JSON response", e);
+ }
+
+ JsonElement error = parsedResponse.get("error");
+ if (error != null && !error.isJsonNull()) {
+ if (error.isJsonPrimitive()) {
+ throw new MieleRpcException("Remote exception occurred: '" + error.getAsString() + "'");
+ } else if (error.isJsonObject()) {
+ JsonObject o = error.getAsJsonObject();
+ Integer code = (o.has("code") ? o.get("code").getAsInt() : null);
+ String message = (o.has("message") ? o.get("message").getAsString() : null);
+ String data = (o.has("data")
+ ? (o.get("data") instanceof JsonObject ? o.get("data").toString() : o.get("data").getAsString())
+ : null);
+ throw new MieleRpcException(
+ "Remote exception occurred: '" + code + "':'" + message + "':'" + data + "'");
+ } else {
+ throw new MieleRpcException("Unknown remote exception occurred: '" + error.toString() + "'");
}
}
+ result = parsedResponse.get("result");
+ if (result == null) {
+ throw new MieleRpcException("Result is missing in response");
+ }
+
return result;
}
protected String post(URL url, Map<String, String> headers, String data) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- if (headers != null) {
- for (Map.Entry<String, String> entry : headers.entrySet()) {
- connection.addRequestProperty(entry.getKey(), entry.getValue());
- }
+ for (Map.Entry<String, String> entry : headers.entrySet()) {
+ connection.addRequestProperty(entry.getKey(), entry.getValue());
}
connection.addRequestProperty("Accept-Encoding", "gzip");
private synchronized void onUpdate() {
logger.debug("Scheduling the Miele polling job");
+ ScheduledFuture<?> pollingJob = this.pollingJob;
if (pollingJob == null || pollingJob.isCancelled()) {
logger.trace("Scheduling the Miele polling job period is {}", POLLING_PERIOD);
pollingJob = scheduler.scheduleWithFixedDelay(pollingRunnable, 0, POLLING_PERIOD, TimeUnit.SECONDS);
+ this.pollingJob = pollingJob;
logger.trace("Scheduling the Miele polling job Job is done ?{}", pollingJob.isDone());
}
logger.debug("Scheduling the Miele event listener job");
+ Future<?> eventListenerJob = this.eventListenerJob;
if (eventListenerJob == null || eventListenerJob.isCancelled()) {
- executor = Executors.newSingleThreadExecutor(new NamedThreadFactory("binding-miele"));
- eventListenerJob = executor.submit(eventListenerRunnable);
+ ExecutorService executor = Executors
+ .newSingleThreadExecutor(new NamedThreadFactory("binding-" + BINDING_ID));
+ this.executor = executor;
+ this.eventListenerJob = executor.submit(eventListenerRunnable);
}
}
}
public boolean registerApplianceStatusListener(ApplianceStatusListener applianceStatusListener) {
- if (applianceStatusListener == null) {
- throw new IllegalArgumentException("It's not allowed to pass a null ApplianceStatusListener.");
- }
boolean result = applianceStatusListeners.add(applianceStatusListener);
if (result && isInitialized()) {
onUpdate();
@Override
public void dispose() {
super.dispose();
+ ScheduledFuture<?> pollingJob = this.pollingJob;
if (pollingJob != null) {
pollingJob.cancel(true);
- pollingJob = null;
+ this.pollingJob = null;
}
+ Future<?> eventListenerJob = this.eventListenerJob;
if (eventListenerJob != null) {
eventListenerJob.cancel(true);
- eventListenerJob = null;
+ this.eventListenerJob = null;
}
+ ExecutorService executor = this.executor;
if (executor != null) {
executor.shutdownNow();
- executor = null;
+ this.executor = null;
}
}
}
import java.util.Map;
import java.util.TimeZone;
-import org.openhab.binding.miele.internal.DeviceMetaData;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miele.internal.DeviceUtil;
import org.openhab.binding.miele.internal.MieleTranslationProvider;
+import org.openhab.binding.miele.internal.api.dto.DeviceMetaData;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
* @author Kai Kreuzer - Changed START_TIME to DateTimeType
* @author Jacob Laursen - Added UoM for temperatures, raw channels
*/
+@NonNullByDefault
public enum OvenChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getStateTextState(s, dmd, translationProvider);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getStateTextState(s, dmd, translationProvider);
}
},
- STATE(null, STATE_CHANNEL_ID, DecimalType.class, false),
+ STATE("", STATE_CHANNEL_ID, DecimalType.class, false),
PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false),
- PROGRAM(null, PROGRAM_CHANNEL_ID, DecimalType.class, false),
+ PROGRAM("", PROGRAM_CHANNEL_ID, DecimalType.class, false),
PROGRAMTYPE("programType", "type", StringType.class, false),
PROGRAM_PHASE_TEXT(PHASE_PROPERTY_NAME, PHASE_TEXT_CHANNEL_ID, StringType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getTextState(s, dmd, translationProvider, phases, MISSING_PHASE_TEXT_PREFIX,
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getTextState(s, dmd, translationProvider, PHASES, MISSING_PHASE_TEXT_PREFIX,
MIELE_OVEN_TEXT_PREFIX);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
}
},
PROGRAM_PHASE(RAW_PHASE_PROPERTY_NAME, PHASE_CHANNEL_ID, DecimalType.class, false),
START_TIME("startTime", "start", DateTimeType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
DURATION("duration", "duration", DateTimeType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
ELAPSED_TIME("elapsedTime", "elapsed", DateTimeType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
FINISH_TIME("finishTime", "finish", DateTimeType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
TARGET_TEMP("targetTemperature", "target", QuantityType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getTemperatureState(s);
}
},
MEASURED_TEMP("measuredTemperature", "measured", QuantityType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getTemperatureState(s);
}
},
DEVICE_TEMP_ONE("deviceTemperature1", "temp1", QuantityType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getTemperatureState(s);
}
},
DEVICE_TEMP_TWO("deviceTemperature2", "temp2", QuantityType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getTemperatureState(s);
}
},
DOOR("signalDoor", "door", OpenClosedType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
if ("true".equals(s)) {
return getState("OPEN");
}
return UnDefType.UNDEF;
}
},
- STOP(null, "stop", OnOffType.class, false),
- SWITCH(null, "switch", OnOffType.class, false);
+ STOP("", "stop", OnOffType.class, false),
+ SWITCH("", "switch", OnOffType.class, false);
private final Logger logger = LoggerFactory.getLogger(OvenChannelSelector.class);
- private static final Map<String, String> phases = Map.ofEntries(entry("1", "heating"), entry("2", "temp-hold"),
+ private static final Map<String, String> PHASES = Map.ofEntries(entry("1", "heating"), entry("2", "temp-hold"),
entry("3", "door-open"), entry("4", "pyrolysis"), entry("7", "lighting"), entry("8", "searing-phase"),
entry("10", "defrost"), entry("11", "cooling-down"), entry("12", "energy-save-phase"));
}
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return this.getState(s, dmd);
}
@Override
- public State getState(String s, DeviceMetaData dmd) {
+ public State getState(String s, @Nullable DeviceMetaData dmd) {
if (dmd != null) {
String localizedValue = dmd.getMieleEnum(s);
if (localizedValue == null) {
logger.error("An exception occurred while converting '{}' into a State", s);
}
- return null;
+ return UnDefType.UNDEF;
}
public State getTemperatureState(String s) {
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_OVEN;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.miele.internal.exceptions.MieleRpcException;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.library.types.OnOffType;
* @author Martin Lepsy - fixed handling of empty JSON results
* @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
*/
+@NonNullByDefault
public class OvenHandler extends MieleApplianceHandler<OvenChannelSelector> {
private final Logger logger = LoggerFactory.getLogger(OvenHandler.class);
String channelID = channelUID.getId();
String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ if (applianceId == null) {
+ logger.warn("Command '{}' failed, appliance id is unknown", command);
+ return;
+ }
OvenChannelSelector selector = (OvenChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null;
try {
- if (selector != null) {
- switch (selector) {
- case SWITCH: {
- if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOn");
- } else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOff");
- }
- break;
+ MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+ if (bridgeHandler == null) {
+ logger.warn("Command '{}' failed, missing bridge handler", command);
+ return;
+ }
+ switch (selector) {
+ case SWITCH: {
+ if (command.equals(OnOffType.ON)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOn");
+ } else if (command.equals(OnOffType.OFF)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "switchOff");
}
- case STOP: {
- if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "stop");
- }
- break;
+ break;
+ }
+ case STOP: {
+ if (command.equals(OnOffType.ON)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "stop");
}
- default: {
- if (!(command instanceof RefreshType)) {
- logger.debug("{} is a read-only channel that does not accept commands",
- selector.getChannelID());
- }
+ break;
+ }
+ default: {
+ if (!(command instanceof RefreshType)) {
+ logger.debug("{} is a read-only channel that does not accept commands",
+ selector.getChannelID());
}
}
}
logger.warn(
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
channelID, command.toString());
+ } catch (MieleRpcException e) {
+ Throwable cause = e.getCause();
+ if (cause == null) {
+ logger.warn("An error occurred while trying to invoke operation: {}", e.getMessage());
+ } else {
+ logger.warn("An error occurred while trying to invoke operation: {} -> {}", e.getMessage(),
+ cause.getMessage());
+ }
}
}
}
import java.util.Map;
import java.util.TimeZone;
-import org.openhab.binding.miele.internal.DeviceMetaData;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miele.internal.DeviceUtil;
import org.openhab.binding.miele.internal.MieleTranslationProvider;
+import org.openhab.binding.miele.internal.api.dto.DeviceMetaData;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
* @author Kai Kreuzer - Changed START_TIME to DateTimeType
* @author Jacob Laursen - Added raw channels
*/
+@NonNullByDefault
public enum TumbleDryerChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true),
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true),
STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getStateTextState(s, dmd, translationProvider);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getStateTextState(s, dmd, translationProvider);
}
},
- STATE(null, STATE_CHANNEL_ID, DecimalType.class, false),
+ STATE("", STATE_CHANNEL_ID, DecimalType.class, false),
PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getTextState(s, dmd, translationProvider, programs, MISSING_PROGRAM_TEXT_PREFIX,
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getTextState(s, dmd, translationProvider, PROGRAMS, MISSING_PROGRAM_TEXT_PREFIX,
MIELE_TUMBLE_DRYER_TEXT_PREFIX);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
}
},
- PROGRAM(null, PROGRAM_CHANNEL_ID, DecimalType.class, false),
+ PROGRAM("", PROGRAM_CHANNEL_ID, DecimalType.class, false),
PROGRAMTYPE("programType", "type", StringType.class, false),
PROGRAM_PHASE_TEXT(PHASE_PROPERTY_NAME, PHASE_TEXT_CHANNEL_ID, StringType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getTextState(s, dmd, translationProvider, phases, MISSING_PHASE_TEXT_PREFIX,
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getTextState(s, dmd, translationProvider, PHASES, MISSING_PHASE_TEXT_PREFIX,
MIELE_TUMBLE_DRYER_TEXT_PREFIX);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
}
},
PROGRAM_PHASE(RAW_PHASE_PROPERTY_NAME, PHASE_CHANNEL_ID, DecimalType.class, false),
START_TIME("startTime", "start", DateTimeType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
DURATION("duration", "duration", DateTimeType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
ELAPSED_TIME("elapsedTime", "elapsed", DateTimeType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
FINISH_TIME("finishTime", "finish", DateTimeType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
DRYING_STEP("dryingStep", "step", DecimalType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getState(s);
}
},
DOOR("signalDoor", "door", OpenClosedType.class, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
if ("true".equals(s)) {
return getState("OPEN");
}
return UnDefType.UNDEF;
}
},
- SWITCH(null, "switch", OnOffType.class, false);
+ SWITCH("", "switch", OnOffType.class, false);
private final Logger logger = LoggerFactory.getLogger(TumbleDryerChannelSelector.class);
- private static final Map<String, String> programs = Map.ofEntries(entry("10", "automatic-plus"),
+ private static final Map<String, String> PROGRAMS = Map.ofEntries(entry("10", "automatic-plus"),
entry("20", "cottons"), entry("23", "cottons-hygiene"), entry("30", "minimum-iron"),
entry("31", "gentle-minimum-iron"), entry("40", "woollens-handcare"), entry("50", "delicates"),
entry("60", "warm-air"), entry("70", "cool-air"), entry("80", "express"), entry("90", "cottons-eco"),
entry("190", "standard-pillows"), entry("220", "basket-programme"), entry("240", "smoothing"),
entry("65000", "cottons-auto-load-control"), entry("65001", "minimum-iron-auto-load-control"));
- private static final Map<String, String> phases = Map.ofEntries(entry("1", "programme-running"),
+ private static final Map<String, String> PHASES = Map.ofEntries(entry("1", "programme-running"),
entry("2", "drying"), entry("3", "drying-machine-iron"), entry("4", "drying-hand-iron"),
entry("5", "drying-normal"), entry("6", "drying-normal-plus"), entry("7", "cooling-down"),
entry("8", "drying-hand-iron"), entry("10", "finished"));
}
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return this.getState(s, dmd);
}
@Override
- public State getState(String s, DeviceMetaData dmd) {
+ public State getState(String s, @Nullable DeviceMetaData dmd) {
if (dmd != null) {
String localizedValue = dmd.getMieleEnum(s);
if (localizedValue == null) {
logger.error("An exception occurred while converting '{}' into a State", s);
}
- return null;
+ return UnDefType.UNDEF;
}
}
import static org.openhab.binding.miele.internal.MieleBindingConstants.APPLIANCE_ID;
import static org.openhab.binding.miele.internal.MieleBindingConstants.MIELE_DEVICE_CLASS_TUMBLE_DRYER;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.miele.internal.exceptions.MieleRpcException;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.library.types.OnOffType;
* @author Martin Lepsy - fixed handling of empty JSON results
* @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN)
**/
+@NonNullByDefault
public class TumbleDryerHandler extends MieleApplianceHandler<TumbleDryerChannelSelector> {
private final Logger logger = LoggerFactory.getLogger(TumbleDryerHandler.class);
String channelID = channelUID.getId();
String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ if (applianceId == null) {
+ logger.warn("Command '{}' failed, appliance id is unknown", command);
+ return;
+ }
TumbleDryerChannelSelector selector = (TumbleDryerChannelSelector) getValueSelectorFromChannelID(channelID);
JsonElement result = null;
try {
- if (selector != null) {
- switch (selector) {
- case SWITCH: {
- if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "start");
- } else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "stop");
- }
- break;
+ switch (selector) {
+ case SWITCH: {
+ MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+ if (bridgeHandler == null) {
+ logger.warn("Command '{}' failed, missing bridge handler", command);
+ return;
+ }
+ if (command.equals(OnOffType.ON)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "start");
+ } else if (command.equals(OnOffType.OFF)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "stop");
}
- default: {
- if (!(command instanceof RefreshType)) {
- logger.debug("{} is a read-only channel that does not accept commands",
- selector.getChannelID());
- }
+ break;
+ }
+ default: {
+ if (!(command instanceof RefreshType)) {
+ logger.debug("{} is a read-only channel that does not accept commands",
+ selector.getChannelID());
}
}
}
logger.warn(
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
channelID, command.toString());
+ } catch (MieleRpcException e) {
+ Throwable cause = e.getCause();
+ if (cause == null) {
+ logger.warn("An error occurred while trying to invoke operation: {}", e.getMessage());
+ } else {
+ logger.warn("An error occurred while trying to invoke operation: {} -> {}", e.getMessage(),
+ cause.getMessage());
+ }
}
}
}
import java.util.Map;
import java.util.TimeZone;
-import org.openhab.binding.miele.internal.DeviceMetaData;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.miele.internal.DeviceUtil;
import org.openhab.binding.miele.internal.MieleTranslationProvider;
+import org.openhab.binding.miele.internal.api.dto.DeviceMetaData;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
* @author Kai Kreuzer - Changed START_TIME to DateTimeType
* @author Jacob Laursen - Added power/water consumption channels, UoM for temperatures, raw channels
*/
+@NonNullByDefault
public enum WashingMachineChannelSelector implements ApplianceChannelSelector {
PRODUCT_TYPE("productTypeId", "productType", StringType.class, true, false),
DEVICE_TYPE("mieleDeviceType", "deviceType", StringType.class, true, false),
STATE_TEXT(STATE_PROPERTY_NAME, STATE_TEXT_CHANNEL_ID, StringType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getStateTextState(s, dmd, translationProvider);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getStateTextState(s, dmd, translationProvider);
}
},
- STATE(null, STATE_CHANNEL_ID, DecimalType.class, false, false),
+ STATE("", STATE_CHANNEL_ID, DecimalType.class, false, false),
PROGRAM_TEXT(PROGRAM_ID_PROPERTY_NAME, PROGRAM_TEXT_CHANNEL_ID, StringType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getTextState(s, dmd, translationProvider, programs, MISSING_PROGRAM_TEXT_PREFIX,
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getTextState(s, dmd, translationProvider, PROGRAMS, MISSING_PROGRAM_TEXT_PREFIX,
MIELE_WASHING_MACHINE_TEXT_PREFIX);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
}
},
- PROGRAM(null, PROGRAM_CHANNEL_ID, DecimalType.class, false, false),
+ PROGRAM("", PROGRAM_CHANNEL_ID, DecimalType.class, false, false),
PROGRAMTYPE("programType", "type", StringType.class, false, false),
PROGRAM_PHASE_TEXT(PHASE_PROPERTY_NAME, PHASE_TEXT_CHANNEL_ID, StringType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
- State state = DeviceUtil.getTextState(s, dmd, translationProvider, phases, MISSING_PHASE_TEXT_PREFIX,
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
+ return DeviceUtil.getTextState(s, dmd, translationProvider, PHASES, MISSING_PHASE_TEXT_PREFIX,
MIELE_WASHING_MACHINE_TEXT_PREFIX);
- if (state != null) {
- return state;
- }
- return super.getState(s, dmd, translationProvider);
}
},
PROGRAM_PHASE(RAW_PHASE_PROPERTY_NAME, PHASE_CHANNEL_ID, DecimalType.class, false, false),
START_TIME("startTime", "start", DateTimeType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
DURATION("duration", "duration", DateTimeType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
ELAPSED_TIME("elapsedTime", "elapsed", DateTimeType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
FINISH_TIME("finishTime", "finish", DateTimeType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
Date date = new Date();
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
},
TARGET_TEMP("targetTemperature", "target", QuantityType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return getTemperatureState(s);
}
},
SPINNING_SPEED("spinningSpeed", "spinningspeed", StringType.class, false, false) {
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
if ("0".equals(s)) {
return getState("Without spinning");
}
},
DOOR("signalDoor", "door", OpenClosedType.class, false, false) {
@Override
-
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
if ("true".equals(s)) {
return getState("OPEN");
}
return UnDefType.UNDEF;
}
},
- SWITCH(null, "switch", OnOffType.class, false, false),
+ SWITCH("", "switch", OnOffType.class, false, false),
POWER_CONSUMPTION(EXTENDED_DEVICE_STATE_PROPERTY_NAME, POWER_CONSUMPTION_CHANNEL_ID, QuantityType.class, false,
true),
WATER_CONSUMPTION(EXTENDED_DEVICE_STATE_PROPERTY_NAME, WATER_CONSUMPTION_CHANNEL_ID, QuantityType.class, false,
private final Logger logger = LoggerFactory.getLogger(WashingMachineChannelSelector.class);
- private static final Map<String, String> programs = Map.ofEntries(entry("1", "cottons"), entry("3", "minimum-iron"),
+ private static final Map<String, String> PROGRAMS = Map.ofEntries(entry("1", "cottons"), entry("3", "minimum-iron"),
entry("4", "delicates"), entry("8", "woollens"), entry("9", "silks"), entry("17", "starch"),
entry("18", "rinse"), entry("21", "drain-spin"), entry("22", "curtains"), entry("23", "shirts"),
entry("24", "denim"), entry("27", "proofing"), entry("29", "sportswear"), entry("31", "automatic-plus"),
entry("95", "down-duvets"), entry("122", "express-20"), entry("129", "down-filled-items"),
entry("133", "cottons-eco"), entry("146", "quickpowerwash"), entry("65532", "mix"));
- private static final Map<String, String> phases = Map.ofEntries(entry("1", "pre-wash"), entry("4", "washing"),
+ private static final Map<String, String> PHASES = Map.ofEntries(entry("1", "pre-wash"), entry("4", "washing"),
entry("5", "rinses"), entry("7", "clean"), entry("9", "drain"), entry("10", "spin"),
entry("11", "anti-crease"), entry("12", "finished"));
}
@Override
- public State getState(String s, DeviceMetaData dmd, MieleTranslationProvider translationProvider) {
+ public State getState(String s, @Nullable DeviceMetaData dmd,
+ @Nullable MieleTranslationProvider translationProvider) {
return this.getState(s, dmd);
}
@Override
- public State getState(String s, DeviceMetaData dmd) {
+ public State getState(String s, @Nullable DeviceMetaData dmd) {
if (dmd != null) {
String localizedValue = dmd.getMieleEnum(s);
if (localizedValue == null) {
logger.error("An exception occurred while converting '{}' into a State", s);
}
- return null;
+ return UnDefType.UNDEF;
}
public State getTemperatureState(String s) {
import java.math.BigDecimal;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.miele.internal.exceptions.MieleRpcException;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
import org.openhab.core.library.types.OnOffType;
* @author Martin Lepsy - fixed handling of empty JSON results
* @author Jacob Laursen - Fixed multicast and protocol support (ZigBee/LAN), added power/water consumption channels
**/
+@NonNullByDefault
public class WashingMachineHandler extends MieleApplianceHandler<WashingMachineChannelSelector>
implements ExtendedDeviceStateListener {
String channelID = channelUID.getId();
String applianceId = (String) getThing().getConfiguration().getProperties().get(APPLIANCE_ID);
+ if (applianceId == null) {
+ logger.warn("Command '{}' failed, appliance id is unknown", command);
+ return;
+ }
WashingMachineChannelSelector selector = (WashingMachineChannelSelector) getValueSelectorFromChannelID(
channelID);
JsonElement result = null;
try {
- if (selector != null) {
- switch (selector) {
- case SWITCH: {
- if (command.equals(OnOffType.ON)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "start");
- } else if (command.equals(OnOffType.OFF)) {
- result = bridgeHandler.invokeOperation(applianceId, modelID, "stop");
- }
- break;
+ switch (selector) {
+ case SWITCH: {
+ MieleBridgeHandler bridgeHandler = this.bridgeHandler;
+ if (bridgeHandler == null) {
+ logger.warn("Command '{}' failed, missing bridge handler", command);
+ return;
}
- default: {
- if (!(command instanceof RefreshType)) {
- logger.debug("{} is a read-only channel that does not accept commands",
- selector.getChannelID());
- }
+ if (command.equals(OnOffType.ON)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "start");
+ } else if (command.equals(OnOffType.OFF)) {
+ result = bridgeHandler.invokeOperation(applianceId, modelID, "stop");
+ }
+ break;
+ }
+ default: {
+ if (!(command instanceof RefreshType)) {
+ logger.debug("{} is a read-only channel that does not accept commands",
+ selector.getChannelID());
}
}
}
logger.warn(
"An error occurred while trying to set the read-only variable associated with channel '{}' to '{}'",
channelID, command.toString());
+ } catch (MieleRpcException e) {
+ Throwable cause = e.getCause();
+ if (cause == null) {
+ logger.warn("An error occurred while trying to invoke operation: {}", e.getMessage());
+ } else {
+ logger.warn("An error occurred while trying to invoke operation: {} -> {}", e.getMessage(),
+ cause.getMessage());
+ }
}
}
package org.openhab.binding.miele.internal;
import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.openhab.binding.miele.internal.api.dto.DeviceMetaData;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.test.java.JavaTest;
*
* @author Jacob Laursen - Initial contribution
*/
-
+@NonNullByDefault
+@ExtendWith(MockitoExtension.class)
public class DeviceUtilTest extends JavaTest {
+ private @NonNullByDefault({}) @Mock MieleTranslationProvider translationProvider;
+
@Test
public void bytesToHexWhenTopBitIsUsedReturnsCorrectString() {
String actual = DeviceUtil.bytesToHex(new byte[] { (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef });
assertThrows(NumberFormatException.class, () -> DeviceUtil.getTemperatureState("A"));
}
- @Test
- public void getTemperatureStateNullValueThrowsNumberFormatException() {
- assertThrows(NumberFormatException.class, () -> DeviceUtil.getTemperatureState(null));
- }
-
@Test
public void getStateTextStateProviderHasPrecedence() {
assertEquals("I brug", this.getStateTextState("5", "Running", "miele.state.running", "I brug"));
@Test
public void getStateTextStateGatewayTextIsReturnedWhenKeyIsUnknown() {
- assertEquals("Running", this.getStateTextState("-1", "Running", "key.unknown", "I brug"));
+ assertEquals("Running", this.getStateTextState("-1", "Running"));
}
@Test
public void getStateTextStateKeyIsReturnedWhenUnknownByGatewayAndProvider() {
- assertEquals("state.99", this.getStateTextState("99", null, "key.unknown", "I brug"));
+ assertEquals("state.99", this.getStateTextState("99", null));
}
private String getStateTextState(String value, String localizedValue, String mockedKey, String mockedValue) {
+ when(translationProvider.getText(mockedKey, localizedValue)).thenReturn(mockedValue);
+ return getStateTextState(value, localizedValue);
+ }
+
+ private String getStateTextState(String value, @Nullable String localizedValue) {
var metaData = new DeviceMetaData();
metaData.LocalizedValue = localizedValue;
- var translationProvider = mock(MieleTranslationProvider.class);
- when(translationProvider.getText(mockedKey, metaData.LocalizedValue)).thenReturn(mockedValue);
return DeviceUtil.getStateTextState(value, metaData, translationProvider).toString();
}
import static org.junit.jupiter.api.Assertions.*;
+import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.core.test.java.JavaTest;
*
* @author Jacob Laursen - Initial contribution
*/
+@NonNullByDefault
public class FullyQualifiedApplianceIdentifierTest extends JavaTest {
@Test