]> git.basschouten.com Git - openhab-addons.git/blob
e3d307c55b1ac46b51566da4bd33414a6483f5ab
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.smartthings.internal.converter;
14
15 import java.util.Map;
16
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;
44
45 /**
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
49  *
50  * @author Bob Raker - Initial contribution
51  */
52 @NonNullByDefault
53 public abstract class SmartthingsConverter {
54
55     private final Logger logger = LoggerFactory.getLogger(SmartthingsConverter.class);
56
57     protected String smartthingsName;
58     protected String thingTypeId;
59
60     SmartthingsConverter(Thing thing) {
61         smartthingsName = thing.getConfiguration().as(SmartthingsThingConfig.class).smartthingsName;
62         thingTypeId = thing.getThingTypeUID().getId();
63     }
64
65     public abstract String convertToSmartthings(ChannelUID channelUid, Command command);
66
67     public abstract State convertToOpenHab(@Nullable String acceptedChannelType,
68             SmartthingsStateData dataFromSmartthings);
69
70     /**
71      * Provide a default converter in the base call so it can be used in sub-classes if needed
72      *
73      * @param command
74      * @return The json string to send to Smartthings
75      */
76     protected String defaultConvertToSmartthings(ChannelUID channelUid, Command command) {
77         String value;
78
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
99             logger.warn(
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());
117         } else {
118             logger.warn(
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();
122         }
123
124         String jsonMsg = String.format(
125                 "{\"capabilityKey\": \"%s\", \"deviceDisplayName\": \"%s\", \"capabilityAttribute\": \"%s\", \"value\": %s}",
126                 thingTypeId, smartthingsName, channelUid.getId(), value);
127
128         return jsonMsg;
129     }
130
131     protected String surroundWithQuotes(String param) {
132         return (new StringBuilder()).append('"').append(param).append('"').toString();
133     }
134
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;
140         }
141
142         String deviceType = dataFromSmartthings.capabilityAttribute;
143         Object deviceValue = dataFromSmartthings.value;
144
145         // deviceValue can be null, handle that up front
146         if (deviceValue == null) {
147             return UnDefType.NULL;
148         }
149
150         switch (acceptedChannelType) {
151             case "Color":
152                 logger.warn(
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;
156             case "Contact":
157                 return "open".equals(deviceValue) ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
158             case "DateTime":
159                 return UnDefType.UNDEF;
160             case "Dimmer":
161                 // The value coming in should be a number
162                 if (deviceValue instanceof String) {
163                     return new PercentType((String) deviceValue);
164                 } else {
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;
168                 }
169             case "Number":
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);
176                 } else {
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;
180                 }
181             case "Player":
182                 logger.warn("Conversion of Player is not currently supported. Need to provide support for message {}.",
183                         deviceValue);
184                 return UnDefType.UNDEF;
185             case "Rollershutter":
186                 return "open".equals(deviceValue) ? UpDownType.DOWN : UpDownType.UP;
187             case "String":
188                 return new StringType((String) deviceValue);
189             case "Switch":
190                 return "on".equals(deviceValue) ? OnOffType.ON : OnOffType.OFF;
191
192             // Vector3 can't be triggered now but keep it to handle acceleration device
193             case "Vector3":
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);
205                 } else {
206                     logger.warn(
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;
210                 }
211             default:
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;
215         }
216     }
217 }