2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.smartthings.internal.converter;
17 import org.eclipse.jdt.annotation.NonNullByDefault;
18 import org.eclipse.jdt.annotation.Nullable;
19 import org.openhab.binding.smartthings.internal.dto.SmartthingsStateData;
20 import org.openhab.binding.smartthings.internal.handler.SmartthingsThingConfig;
21 import org.openhab.core.library.types.DateTimeType;
22 import org.openhab.core.library.types.DecimalType;
23 import org.openhab.core.library.types.HSBType;
24 import org.openhab.core.library.types.IncreaseDecreaseType;
25 import org.openhab.core.library.types.NextPreviousType;
26 import org.openhab.core.library.types.OnOffType;
27 import org.openhab.core.library.types.OpenClosedType;
28 import org.openhab.core.library.types.PercentType;
29 import org.openhab.core.library.types.PlayPauseType;
30 import org.openhab.core.library.types.PointType;
31 import org.openhab.core.library.types.RewindFastforwardType;
32 import org.openhab.core.library.types.StopMoveType;
33 import org.openhab.core.library.types.StringListType;
34 import org.openhab.core.library.types.StringType;
35 import org.openhab.core.library.types.UpDownType;
36 import org.openhab.core.thing.ChannelUID;
37 import org.openhab.core.thing.Thing;
38 import org.openhab.core.types.Command;
39 import org.openhab.core.types.RefreshType;
40 import org.openhab.core.types.State;
41 import org.openhab.core.types.UnDefType;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
46 * Base converter class.
47 * The converter classes are responsible for converting "state" messages from the smartthings hub into openHAB States.
48 * And, converting handler.handleCommand() into messages to be sent to smartthings
50 * @author Bob Raker - Initial contribution
53 public abstract class SmartthingsConverter {
55 private final Logger logger = LoggerFactory.getLogger(SmartthingsConverter.class);
57 protected String smartthingsName;
58 protected String thingTypeId;
60 SmartthingsConverter(Thing thing) {
61 smartthingsName = thing.getConfiguration().as(SmartthingsThingConfig.class).smartthingsName;
62 thingTypeId = thing.getThingTypeUID().getId();
65 public abstract String convertToSmartthings(ChannelUID channelUid, Command command);
67 public abstract State convertToOpenHab(@Nullable String acceptedChannelType,
68 SmartthingsStateData dataFromSmartthings);
71 * Provide a default converter in the base call so it can be used in sub-classes if needed
74 * @return The json string to send to Smartthings
76 protected String defaultConvertToSmartthings(ChannelUID channelUid, Command command) {
79 if (command instanceof DateTimeType) {
80 DateTimeType dt = (DateTimeType) command;
81 value = dt.format("%m/%d/%Y %H.%M.%S");
82 } else if (command instanceof HSBType) {
83 HSBType hsb = (HSBType) command;
84 value = String.format("[%d, %d, %d ]", hsb.getHue().intValue(), hsb.getSaturation().intValue(),
85 hsb.getBrightness().intValue());
86 } else if (command instanceof DecimalType) {
87 value = command.toString();
88 } else if (command instanceof IncreaseDecreaseType) { // Need to surround with double quotes
89 value = surroundWithQuotes(command.toString().toLowerCase());
90 } else if (command instanceof NextPreviousType) { // Need to surround with double quotes
91 value = surroundWithQuotes(command.toString().toLowerCase());
92 } else if (command instanceof OnOffType) { // Need to surround with double quotes
93 value = surroundWithQuotes(command.toString().toLowerCase());
94 } else if (command instanceof OpenClosedType) { // Need to surround with double quotes
95 value = surroundWithQuotes(command.toString().toLowerCase());
96 } else if (command instanceof PercentType) {
97 value = command.toString();
98 } else if (command instanceof PointType) { // There is not a comparable type in Smartthings, log and send value
100 "Warning - PointType Command is not supported by Smartthings. Please configure to use a different command type. CapabilityKey: {}, displayName: {}, capabilityAttribute {}",
101 thingTypeId, smartthingsName, channelUid.getId());
102 value = command.toFullString();
103 } else if (command instanceof RefreshType) { // Need to surround with double quotes
104 value = surroundWithQuotes(command.toString().toLowerCase());
105 } else if (command instanceof RewindFastforwardType) { // Need to surround with double quotes
106 value = surroundWithQuotes(command.toString().toLowerCase());
107 } else if (command instanceof StopMoveType) { // Need to surround with double quotes
108 value = surroundWithQuotes(command.toString().toLowerCase());
109 } else if (command instanceof PlayPauseType) { // Need to surround with double quotes
110 value = surroundWithQuotes(command.toString().toLowerCase());
111 } else if (command instanceof StringListType) {
112 value = surroundWithQuotes(command.toString());
113 } else if (command instanceof StringType) {
114 value = surroundWithQuotes(command.toString());
115 } else if (command instanceof UpDownType) { // Need to surround with double quotes
116 value = surroundWithQuotes(command.toString().toLowerCase());
119 "Warning - The Smartthings converter does not know how to handle the {} command. The Smartthingsonverter class should be updated. CapabilityKey: {}, displayName: {}, capabilityAttribute {}",
120 command.getClass().getName(), thingTypeId, smartthingsName, channelUid.getId());
121 value = command.toString().toLowerCase();
124 String jsonMsg = String.format(
125 "{\"capabilityKey\": \"%s\", \"deviceDisplayName\": \"%s\", \"capabilityAttribute\": \"%s\", \"value\": %s}",
126 thingTypeId, smartthingsName, channelUid.getId(), value);
131 protected String surroundWithQuotes(String param) {
132 return (new StringBuilder()).append('"').append(param).append('"').toString();
135 protected State defaultConvertToOpenHab(@Nullable String acceptedChannelType,
136 SmartthingsStateData dataFromSmartthings) {
137 // If there is no stateMap the just return null State
138 if (acceptedChannelType == null) {
139 return UnDefType.NULL;
142 String deviceType = dataFromSmartthings.capabilityAttribute;
143 Object deviceValue = dataFromSmartthings.value;
145 // deviceValue can be null, handle that up front
146 if (deviceValue == null) {
147 return UnDefType.NULL;
150 switch (acceptedChannelType) {
153 "Conversion of Color is not supported by the default Smartthings to opemHAB converter. The ThingType should specify an appropriate converter. Device name: {}, Attribute: {}.",
154 dataFromSmartthings.deviceDisplayName, deviceType);
155 return UnDefType.UNDEF;
157 return "open".equals(deviceValue) ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
159 return UnDefType.UNDEF;
161 // The value coming in should be a number
162 if (deviceValue instanceof String) {
163 return new PercentType((String) deviceValue);
165 logger.warn("Failed to convert {} with a value of {} from class {} to an appropriate type.",
166 deviceType, deviceValue, deviceValue.getClass().getName());
167 return UnDefType.UNDEF;
170 if (deviceValue instanceof String) {
171 return new DecimalType(Double.parseDouble((String) deviceValue));
172 } else if (deviceValue instanceof Double) {
173 return new DecimalType((Double) deviceValue);
174 } else if (deviceValue instanceof Long) {
175 return new DecimalType((Long) deviceValue);
177 logger.warn("Failed to convert Number {} with a value of {} from class {} to an appropriate type.",
178 deviceType, deviceValue, deviceValue.getClass().getName());
179 return UnDefType.UNDEF;
182 logger.warn("Conversion of Player is not currently supported. Need to provide support for message {}.",
184 return UnDefType.UNDEF;
185 case "Rollershutter":
186 return "open".equals(deviceValue) ? UpDownType.DOWN : UpDownType.UP;
188 return new StringType((String) deviceValue);
190 return "on".equals(deviceValue) ? OnOffType.ON : OnOffType.OFF;
192 // Vector3 can't be triggered now but keep it to handle acceleration device
194 // This is a weird result from Smartthings. If the messages is from a "state" request the result will
195 // look like: "value":{"z":22,"y":-36,"x":-987}
196 // But if the result is from sensor change via a subscription to a threeAxis device the results will
197 // be a String of the format "value":"-873,-70,484"
198 // which GSON returns as a LinkedTreeMap
199 if (deviceValue instanceof String) {
200 return new StringType((String) deviceValue);
201 } else if (deviceValue instanceof Map<?, ?>) {
202 Map<String, String> map = (Map<String, String>) deviceValue;
203 String s = String.format("%.0f,%.0f,%.0f", map.get("x"), map.get("y"), map.get("z"));
204 return new StringType(s);
207 "Unable to convert {} which should be in Smartthings Vector3 format to a string. The returned datatype from Smartthings is {}.",
208 deviceType, deviceValue.getClass().getName());
209 return UnDefType.UNDEF;
212 logger.warn("No type defined to convert {} with a value of {} from class {} to an appropriate type.",
213 deviceType, deviceValue, deviceValue.getClass().getName());
214 return UnDefType.UNDEF;