2 * Copyright (c) 2010-2021 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;
19 import org.apache.commons.lang.StringUtils;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.io.neeo.internal.NeeoConstants;
23 import org.openhab.io.neeo.internal.NeeoUtil;
24 import org.openhab.io.neeo.internal.models.ButtonInfo;
25 import org.openhab.io.neeo.internal.models.NeeoButtonGroup;
26 import org.openhab.io.neeo.internal.models.NeeoCapabilityType;
27 import org.openhab.io.neeo.internal.models.NeeoDevice;
28 import org.openhab.io.neeo.internal.models.NeeoDeviceChannel;
29 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelKind;
30 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelRange;
31 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelText;
32 import org.openhab.io.neeo.internal.models.NeeoDeviceTiming;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
36 import com.google.gson.JsonArray;
37 import com.google.gson.JsonElement;
38 import com.google.gson.JsonObject;
39 import com.google.gson.JsonPrimitive;
40 import com.google.gson.JsonSerializationContext;
41 import com.google.gson.JsonSerializer;
44 * Implementation of {@link JsonSerializer} that will serialize a {@link NeeoDevice} for communications going to the
47 * @author Tim Roberts - Initial Contribution
50 public class NeeoBrainDeviceSerializer implements JsonSerializer<NeeoDevice> {
53 private final Logger logger = LoggerFactory.getLogger(NeeoBrainDeviceSerializer.class);
56 public JsonElement serialize(NeeoDevice device, Type deviceType, JsonSerializationContext jsonContext) {
57 final JsonObject jsonObject = new JsonObject();
59 final String adapterName = device.getUid().getNeeoUID();
60 // jsonObject.addProperty("apiversion", "1.0"); // haven't decided if needed
61 jsonObject.addProperty("adapterName", adapterName);
62 jsonObject.addProperty("driverVersion", device.getDriverVersion());
64 jsonObject.addProperty("type", device.getType().toString());
65 jsonObject.addProperty("manufacturer", device.getManufacturer());
66 jsonObject.addProperty("name", device.getName());
67 jsonObject.addProperty("tokens", "");
69 final NeeoDeviceTiming timing = device.getDeviceTiming();
71 final JsonObject timingObj = new JsonObject();
72 timingObj.addProperty("standbyCommandDelay", timing.getStandbyCommandDelay());
73 timingObj.addProperty("sourceSwitchDelay", timing.getSourceSwitchDelay());
74 timingObj.addProperty("shutdownDelay", timing.getShutdownDelay());
75 jsonObject.add("timing", timingObj);
79 * Setup only really good for SDK discovery (which we don't do)
80 * 'setup': { 'discovery': true,'registration': false,'introheader': 'header text','introtext': 'some hints'}
82 jsonObject.add("setup", new JsonObject());
84 jsonObject.add("deviceCapabilities", jsonContext.serialize(device.getDeviceCapabilities()));
86 final JsonObject deviceObj = new JsonObject();
87 final String deviceName = device.getName();
88 deviceObj.addProperty("name", deviceName);
89 deviceObj.add("tokens", new JsonArray());
90 jsonObject.add("device", deviceObj);
92 final String specificName = device.getSpecificName();
93 if (specificName != null && StringUtils.isNotEmpty(specificName)) {
94 deviceObj.addProperty("specificname", specificName);
95 jsonObject.addProperty("specificname", specificName);
96 } else if (StringUtils.isNotEmpty(deviceName)) {
97 deviceObj.addProperty("specificname", deviceName);
98 jsonObject.addProperty("specificname", deviceName);
101 final String iconName = device.getIconName();
102 if (iconName != null && StringUtils.isNotEmpty(iconName)) {
103 deviceObj.addProperty("icon", iconName);
104 jsonObject.addProperty("icon", iconName);
107 final List<JsonObject> capabilities = new ArrayList<>();
108 for (NeeoDeviceChannel channel : device.getExposedChannels()) {
109 final NeeoCapabilityType capabilityType = channel.getType();
111 final String compPath = NeeoConstants.CAPABILITY_PATH_PREFIX + "/" + adapterName + "/"
112 + channel.getItemName() + "/" + channel.getSubType() + "/" + channel.getChannelNbr();
114 final String uniqueItemName = channel.getUniqueItemName();
115 final String sensorItemName = uniqueItemName
116 + (StringUtils.endsWithIgnoreCase(uniqueItemName, NeeoConstants.NEEO_SENSOR_SUFFIX) ? ""
117 : NeeoConstants.NEEO_SENSOR_SUFFIX);
119 if (capabilityType == NeeoCapabilityType.BUTTON) {
120 final String name = StringUtils.isEmpty(channel.getLabel()) ? uniqueItemName : channel.getLabel();
122 if (channel.getKind() == NeeoDeviceChannelKind.TRIGGER) {
123 final String path = compPath + "/button/trigger";
124 capabilities.add(createBase(name, channel.getLabel(), capabilityType.toString(), path));
126 final String value = channel.getValue();
127 final String path = compPath + "/button/" + (value == null || StringUtils.isEmpty(value) ? "on"
128 : NeeoUtil.encodeURIComponent(value.trim()));
129 capabilities.add(createBase(name, channel.getLabel(), capabilityType.toString(), path));
131 } else if (capabilityType == NeeoCapabilityType.SENSOR_POWER) {
132 final JsonObject sensorTypeObj = new JsonObject();
133 sensorTypeObj.addProperty("type", NeeoCapabilityType.SENSOR_POWER.toString());
135 // power should NOT use the sensor suffix
136 capabilities.add(createBase(uniqueItemName, channel.getLabel(), NeeoCapabilityType.SENSOR.toString(),
137 compPath + "/switch/power", sensorTypeObj));
138 } else if (capabilityType == NeeoCapabilityType.SENSOR) {
139 final JsonObject sensor = new JsonObject();
140 sensor.addProperty("type", NeeoCapabilityType.SENSOR_RANGE.toString());
142 final NeeoDeviceChannelRange channelRange = channel.getRange();
143 final int[] range = new int[] { channelRange.getMinValue(), channelRange.getMaxValue() };
144 sensor.add("range", jsonContext.serialize(range));
145 sensor.addProperty("unit", channelRange.getUnit());
147 capabilities.add(createBase(sensorItemName, channel.getLabel(), capabilityType.toString(),
148 compPath + "/sensor/sensor", sensor));
149 } else if (capabilityType == NeeoCapabilityType.SLIDER) {
150 final JsonObject sliderSensor = new JsonObject();
151 sliderSensor.addProperty("type", NeeoCapabilityType.SENSOR_RANGE.toString());
152 sliderSensor.addProperty("sensor", sensorItemName);
154 final NeeoDeviceChannelRange channelRange = channel.getRange();
155 final int[] range = new int[] { channelRange.getMinValue(), channelRange.getMaxValue() };
156 sliderSensor.add("range", jsonContext.serialize(range));
157 sliderSensor.addProperty("unit", channelRange.getUnit());
158 capabilities.add(createBase(uniqueItemName, channel.getLabel(), capabilityType.toString(),
159 compPath + "/slider/actor", "slider", sliderSensor));
161 final JsonObject sensorTypeObj = new JsonObject();
162 sensorTypeObj.addProperty("type", NeeoCapabilityType.SENSOR_RANGE.toString());
163 sensorTypeObj.add("range", jsonContext.serialize(range));
164 sensorTypeObj.addProperty("unit", channelRange.getUnit());
166 capabilities.add(createBase(sensorItemName, channel.getLabel(), NeeoCapabilityType.SENSOR.toString(),
167 compPath + "/slider/sensor", sensorTypeObj));
168 } else if (capabilityType == NeeoCapabilityType.SWITCH) {
169 final String label = channel.getLabel();
171 final NeeoButtonGroup buttons = NeeoButtonGroup.parse(label);
172 if (buttons == null) {
173 capabilities.add(createBase(uniqueItemName, channel.getLabel(), capabilityType.toString(),
174 compPath + "/switch/actor", new JsonPrimitive(sensorItemName)));
176 final JsonObject sensorTypeObj = new JsonObject();
177 sensorTypeObj.addProperty("type", NeeoCapabilityType.SENSOR_BINARY.toString());
179 capabilities.add(createBase(sensorItemName, channel.getLabel(),
180 NeeoCapabilityType.SENSOR.toString(), compPath + "/switch/sensor", sensorTypeObj));
182 for (final ButtonInfo bi : buttons.getButtonInfos()) {
183 capabilities.add(createBase(bi.getLabel(), bi.getLabel(), NeeoCapabilityType.BUTTON.toString(),
184 compPath + "/button/" + bi.getSuffix()));
188 } else if (capabilityType == NeeoCapabilityType.IMAGEURL) {
189 final String value = channel.getValue();
190 final String size = (value == null || StringUtils.isEmpty(value) ? "large" : value.trim())
193 final JsonObject jo = createBase(uniqueItemName, channel.getLabel(), capabilityType.toString(),
194 compPath + "/image/actor", "sensor", new JsonPrimitive(sensorItemName));
195 jo.addProperty("size", size);
196 capabilities.add(jo);
198 final JsonObject sensorTypeObj = new JsonObject();
199 sensorTypeObj.addProperty("type", NeeoCapabilityType.IMAGEURL.toString());
201 capabilities.add(createBase(sensorItemName, channel.getLabel(), NeeoCapabilityType.SENSOR.toString(),
202 compPath + "/image/sensor", sensorTypeObj));
203 } else if (capabilityType == NeeoCapabilityType.TEXTLABEL) {
204 final JsonObject capObj = createBase(uniqueItemName, channel.getLabel(), capabilityType.toString(),
205 compPath + "/textlabel/actor", new JsonPrimitive(sensorItemName));
207 capObj.addProperty("isLabelVisible",
208 channel instanceof NeeoDeviceChannelText ? ((NeeoDeviceChannelText) channel).isLabelVisible()
211 capabilities.add(capObj);
213 final JsonObject sensorTypeObj = new JsonObject();
214 sensorTypeObj.addProperty("type", NeeoCapabilityType.SENSOR_CUSTOM.toString());
216 capabilities.add(createBase(sensorItemName, channel.getLabel(), NeeoCapabilityType.SENSOR.toString(),
217 compPath + "/textlabel/sensor", sensorTypeObj));
218 } else if (capabilityType == NeeoCapabilityType.DIRECTORY) {
219 final JsonObject capObj = createBase(uniqueItemName, channel.getLabel(), capabilityType.toString(),
220 compPath + "/directory/actor");
222 capabilities.add(capObj);
224 logger.debug("Unknown capability type: {} for channel {}", capabilityType, channel);
229 jsonObject.add("capabilities", jsonContext.serialize(capabilities));
235 * Helper method to create a base element with the given name/label/type/path
237 * @param name the element name
238 * @param label the element label
239 * @param type the element type
240 * @param path the element path
241 * @return the json object representing the base element
243 private JsonObject createBase(String name, String label, String type, String path) {
244 return createBase(name, label, type, path, null, null);
248 * Helper method to create a base element with the given name/label/type/path/sensor
250 * @param name the element name
251 * @param label the element label
252 * @param type the element type
253 * @param path the element path
254 * @param sensor the element sensor
255 * @return the json object representing the base element
257 private JsonObject createBase(String name, String label, String type, String path, JsonElement sensor) {
258 return createBase(name, label, type, path, "sensor", sensor);
262 * Helper method to create a base element with the given name/label/type/path/sensorname/sensor
264 * @param name the element name
265 * @param label the element label
266 * @param type the element type
267 * @param path the element path
268 * @param sensorName the element sensor name
269 * @param sensor the element sensor
270 * @return the json object representing the base element
272 private JsonObject createBase(String name, String label, String type, String path, @Nullable String sensorName,
273 @Nullable JsonElement sensor) {
274 final JsonObject compObj = new JsonObject();
275 compObj.addProperty("name", NeeoUtil.encodeURIComponent(name));
276 compObj.addProperty("label", label);
277 compObj.addProperty("type", type);
279 compObj.addProperty("path", NeeoUtil.encodeURIComponent(path));
280 if (sensor != null && StringUtils.isNotEmpty(sensorName)) {
281 if (sensor instanceof JsonPrimitive) {
282 compObj.addProperty(sensorName, sensor.getAsString());
284 compObj.add(sensorName, sensor);