2 * Copyright (c) 2010-2020 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.io.neeo.internal.serialization;
15 import java.lang.reflect.Type;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.Objects;
20 import org.apache.commons.lang.StringUtils;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.io.neeo.internal.NeeoConstants;
24 import org.openhab.io.neeo.internal.NeeoUtil;
25 import org.openhab.io.neeo.internal.models.ButtonInfo;
26 import org.openhab.io.neeo.internal.models.NeeoButtonGroup;
27 import org.openhab.io.neeo.internal.models.NeeoCapabilityType;
28 import org.openhab.io.neeo.internal.models.NeeoDevice;
29 import org.openhab.io.neeo.internal.models.NeeoDeviceChannel;
30 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelKind;
31 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelRange;
32 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelText;
33 import org.openhab.io.neeo.internal.models.NeeoDeviceTiming;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
37 import com.google.gson.JsonArray;
38 import com.google.gson.JsonElement;
39 import com.google.gson.JsonObject;
40 import com.google.gson.JsonPrimitive;
41 import com.google.gson.JsonSerializationContext;
42 import com.google.gson.JsonSerializer;
45 * Implementation of {@link JsonSerializer} that will serialize a {@link NeeoDevice} for communications going to the
48 * @author Tim Roberts - Initial Contribution
51 public class NeeoBrainDeviceSerializer implements JsonSerializer<NeeoDevice> {
54 private final Logger logger = LoggerFactory.getLogger(NeeoBrainDeviceSerializer.class);
57 public JsonElement serialize(NeeoDevice device, @Nullable Type deviceType,
58 @Nullable JsonSerializationContext jsonContext) {
59 Objects.requireNonNull(device, "device cannot be null");
60 Objects.requireNonNull(deviceType, "deviceType cannot be null");
61 Objects.requireNonNull(jsonContext, "jsonContext cannot be null");
63 final JsonObject jsonObject = new JsonObject();
65 final String adapterName = device.getUid().getNeeoUID();
66 // jsonObject.addProperty("apiversion", "1.0"); // haven't decided if needed
67 jsonObject.addProperty("adapterName", adapterName);
68 jsonObject.addProperty("driverVersion", device.getDriverVersion());
70 jsonObject.addProperty("type", device.getType().toString());
71 jsonObject.addProperty("manufacturer", device.getManufacturer());
72 jsonObject.addProperty("name", device.getName());
73 jsonObject.addProperty("tokens", "");
75 final NeeoDeviceTiming timing = device.getDeviceTiming();
77 final JsonObject timingObj = new JsonObject();
78 timingObj.addProperty("standbyCommandDelay", timing.getStandbyCommandDelay());
79 timingObj.addProperty("sourceSwitchDelay", timing.getSourceSwitchDelay());
80 timingObj.addProperty("shutdownDelay", timing.getShutdownDelay());
81 jsonObject.add("timing", timingObj);
85 * Setup only really good for SDK discovery (which we don't do)
86 * 'setup': { 'discovery': true,'registration': false,'introheader': 'header text','introtext': 'some hints'}
88 jsonObject.add("setup", new JsonObject());
90 jsonObject.add("deviceCapabilities", jsonContext.serialize(device.getDeviceCapabilities()));
92 final JsonObject deviceObj = new JsonObject();
93 final String deviceName = device.getName();
94 deviceObj.addProperty("name", deviceName);
95 deviceObj.add("tokens", new JsonArray());
96 jsonObject.add("device", deviceObj);
98 final String specificName = device.getSpecificName();
99 if (specificName != null && StringUtils.isNotEmpty(specificName)) {
100 deviceObj.addProperty("specificname", specificName);
101 jsonObject.addProperty("specificname", specificName);
102 } else if (StringUtils.isNotEmpty(deviceName)) {
103 deviceObj.addProperty("specificname", deviceName);
104 jsonObject.addProperty("specificname", deviceName);
107 final String iconName = device.getIconName();
108 if (iconName != null && StringUtils.isNotEmpty(iconName)) {
109 deviceObj.addProperty("icon", iconName);
110 jsonObject.addProperty("icon", iconName);
113 final List<JsonObject> capabilities = new ArrayList<>();
114 for (NeeoDeviceChannel channel : device.getExposedChannels()) {
115 final NeeoCapabilityType capabilityType = channel.getType();
117 final String compPath = NeeoConstants.CAPABILITY_PATH_PREFIX + "/" + adapterName + "/"
118 + channel.getItemName() + "/" + channel.getSubType() + "/" + channel.getChannelNbr();
120 final String uniqueItemName = channel.getUniqueItemName();
121 final String sensorItemName = uniqueItemName
122 + (StringUtils.endsWithIgnoreCase(uniqueItemName, NeeoConstants.NEEO_SENSOR_SUFFIX) ? ""
123 : NeeoConstants.NEEO_SENSOR_SUFFIX);
125 if (capabilityType == NeeoCapabilityType.BUTTON) {
126 final String name = StringUtils.isEmpty(channel.getLabel()) ? uniqueItemName : channel.getLabel();
128 if (channel.getKind() == NeeoDeviceChannelKind.TRIGGER) {
129 final String path = compPath + "/button/trigger";
130 capabilities.add(createBase(name, channel.getLabel(), capabilityType.toString(), path));
132 final String value = channel.getValue();
133 final String path = compPath + "/button/" + (value == null || StringUtils.isEmpty(value) ? "on"
134 : NeeoUtil.encodeURIComponent(value.trim()));
135 capabilities.add(createBase(name, channel.getLabel(), capabilityType.toString(), path));
137 } else if (capabilityType == NeeoCapabilityType.SENSOR_POWER) {
138 final JsonObject sensorTypeObj = new JsonObject();
139 sensorTypeObj.addProperty("type", NeeoCapabilityType.SENSOR_POWER.toString());
141 // power should NOT use the sensor suffix
142 capabilities.add(createBase(uniqueItemName, channel.getLabel(), NeeoCapabilityType.SENSOR.toString(),
143 compPath + "/switch/power", sensorTypeObj));
144 } else if (capabilityType == NeeoCapabilityType.SENSOR) {
145 final JsonObject sensor = new JsonObject();
146 sensor.addProperty("type", NeeoCapabilityType.SENSOR_RANGE.toString());
148 final NeeoDeviceChannelRange channelRange = channel.getRange();
149 final int[] range = new int[] { channelRange.getMinValue(), channelRange.getMaxValue() };
150 sensor.add("range", jsonContext.serialize(range));
151 sensor.addProperty("unit", channelRange.getUnit());
153 capabilities.add(createBase(sensorItemName, channel.getLabel(), capabilityType.toString(),
154 compPath + "/sensor/sensor", sensor));
155 } else if (capabilityType == NeeoCapabilityType.SLIDER) {
156 final JsonObject sliderSensor = new JsonObject();
157 sliderSensor.addProperty("type", NeeoCapabilityType.SENSOR_RANGE.toString());
158 sliderSensor.addProperty("sensor", sensorItemName);
160 final NeeoDeviceChannelRange channelRange = channel.getRange();
161 final int[] range = new int[] { channelRange.getMinValue(), channelRange.getMaxValue() };
162 sliderSensor.add("range", jsonContext.serialize(range));
163 sliderSensor.addProperty("unit", channelRange.getUnit());
164 capabilities.add(createBase(uniqueItemName, channel.getLabel(), capabilityType.toString(),
165 compPath + "/slider/actor", "slider", sliderSensor));
167 final JsonObject sensorTypeObj = new JsonObject();
168 sensorTypeObj.addProperty("type", NeeoCapabilityType.SENSOR_RANGE.toString());
169 sensorTypeObj.add("range", jsonContext.serialize(range));
170 sensorTypeObj.addProperty("unit", channelRange.getUnit());
172 capabilities.add(createBase(sensorItemName, channel.getLabel(), NeeoCapabilityType.SENSOR.toString(),
173 compPath + "/slider/sensor", sensorTypeObj));
174 } else if (capabilityType == NeeoCapabilityType.SWITCH) {
175 final String label = channel.getLabel();
177 final NeeoButtonGroup buttons = NeeoButtonGroup.parse(label);
178 if (buttons == null) {
179 capabilities.add(createBase(uniqueItemName, channel.getLabel(), capabilityType.toString(),
180 compPath + "/switch/actor", new JsonPrimitive(sensorItemName)));
182 final JsonObject sensorTypeObj = new JsonObject();
183 sensorTypeObj.addProperty("type", NeeoCapabilityType.SENSOR_BINARY.toString());
185 capabilities.add(createBase(sensorItemName, channel.getLabel(),
186 NeeoCapabilityType.SENSOR.toString(), compPath + "/switch/sensor", sensorTypeObj));
188 for (final ButtonInfo bi : buttons.getButtonInfos()) {
189 capabilities.add(createBase(bi.getLabel(), bi.getLabel(), NeeoCapabilityType.BUTTON.toString(),
190 compPath + "/button/" + bi.getSuffix()));
194 } else if (capabilityType == NeeoCapabilityType.IMAGEURL) {
195 final String value = channel.getValue();
196 final String size = (value == null || StringUtils.isEmpty(value) ? "large" : value.trim())
199 final JsonObject jo = createBase(uniqueItemName, channel.getLabel(), capabilityType.toString(),
200 compPath + "/image/actor", "sensor", new JsonPrimitive(sensorItemName));
201 jo.addProperty("size", size);
202 capabilities.add(jo);
204 final JsonObject sensorTypeObj = new JsonObject();
205 sensorTypeObj.addProperty("type", NeeoCapabilityType.IMAGEURL.toString());
207 capabilities.add(createBase(sensorItemName, channel.getLabel(), NeeoCapabilityType.SENSOR.toString(),
208 compPath + "/image/sensor", sensorTypeObj));
209 } else if (capabilityType == NeeoCapabilityType.TEXTLABEL) {
210 final JsonObject capObj = createBase(uniqueItemName, channel.getLabel(), capabilityType.toString(),
211 compPath + "/textlabel/actor", new JsonPrimitive(sensorItemName));
213 capObj.addProperty("isLabelVisible",
214 channel instanceof NeeoDeviceChannelText ? ((NeeoDeviceChannelText) channel).isLabelVisible()
217 capabilities.add(capObj);
219 final JsonObject sensorTypeObj = new JsonObject();
220 sensorTypeObj.addProperty("type", NeeoCapabilityType.SENSOR_CUSTOM.toString());
222 capabilities.add(createBase(sensorItemName, channel.getLabel(), NeeoCapabilityType.SENSOR.toString(),
223 compPath + "/textlabel/sensor", sensorTypeObj));
224 } else if (capabilityType == NeeoCapabilityType.DIRECTORY) {
225 final JsonObject capObj = createBase(uniqueItemName, channel.getLabel(), capabilityType.toString(),
226 compPath + "/directory/actor");
228 capabilities.add(capObj);
230 logger.debug("Unknown capability type: {} for channel {}", capabilityType, channel);
235 jsonObject.add("capabilities", jsonContext.serialize(capabilities));
241 * Helper method to create a base element with the given name/label/type/path
243 * @param name the element name
244 * @param label the element label
245 * @param type the element type
246 * @param path the element path
247 * @return the json object representing the base element
249 private JsonObject createBase(String name, String label, String type, String path) {
250 return createBase(name, label, type, path, null, null);
254 * Helper method to create a base element with the given name/label/type/path/sensor
256 * @param name the element name
257 * @param label the element label
258 * @param type the element type
259 * @param path the element path
260 * @param sensor the element sensor
261 * @return the json object representing the base element
263 private JsonObject createBase(String name, String label, String type, String path, JsonElement sensor) {
264 return createBase(name, label, type, path, "sensor", sensor);
268 * Helper method to create a base element with the given name/label/type/path/sensorname/sensor
270 * @param name the element name
271 * @param label the element label
272 * @param type the element type
273 * @param path the element path
274 * @param sensorName the element sensor name
275 * @param sensor the element sensor
276 * @return the json object representing the base element
278 private JsonObject createBase(String name, String label, String type, String path, @Nullable String sensorName,
279 @Nullable JsonElement sensor) {
280 final JsonObject compObj = new JsonObject();
281 compObj.addProperty("name", NeeoUtil.encodeURIComponent(name));
282 compObj.addProperty("label", label);
283 compObj.addProperty("type", type);
285 compObj.addProperty("path", NeeoUtil.encodeURIComponent(path));
286 if (sensor != null && StringUtils.isNotEmpty(sensorName)) {
287 if (sensor instanceof JsonPrimitive) {
288 compObj.addProperty(sensorName, sensor.getAsString());
290 compObj.add(sensorName, sensor);