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.io.neeo.internal.serialization;
15 import java.lang.reflect.Type;
16 import java.util.ArrayList;
17 import java.util.List;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.io.neeo.internal.NeeoConstants;
22 import org.openhab.io.neeo.internal.NeeoUtil;
23 import org.openhab.io.neeo.internal.models.ButtonInfo;
24 import org.openhab.io.neeo.internal.models.NeeoButtonGroup;
25 import org.openhab.io.neeo.internal.models.NeeoCapabilityType;
26 import org.openhab.io.neeo.internal.models.NeeoDevice;
27 import org.openhab.io.neeo.internal.models.NeeoDeviceChannel;
28 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelKind;
29 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelRange;
30 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelText;
31 import org.openhab.io.neeo.internal.models.NeeoDeviceTiming;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
35 import com.google.gson.JsonArray;
36 import com.google.gson.JsonElement;
37 import com.google.gson.JsonObject;
38 import com.google.gson.JsonPrimitive;
39 import com.google.gson.JsonSerializationContext;
40 import com.google.gson.JsonSerializer;
43 * Implementation of {@link JsonSerializer} that will serialize a {@link NeeoDevice} for communications going to the
46 * @author Tim Roberts - Initial Contribution
49 public class NeeoBrainDeviceSerializer implements JsonSerializer<NeeoDevice> {
52 private final Logger logger = LoggerFactory.getLogger(NeeoBrainDeviceSerializer.class);
55 public JsonElement serialize(NeeoDevice device, Type deviceType, JsonSerializationContext jsonContext) {
56 final JsonObject jsonObject = new JsonObject();
58 final String adapterName = device.getUid().getNeeoUID();
59 // jsonObject.addProperty("apiversion", "1.0"); // haven't decided if needed
60 jsonObject.addProperty("adapterName", adapterName);
61 jsonObject.addProperty("driverVersion", device.getDriverVersion());
63 jsonObject.addProperty("type", device.getType().toString());
64 jsonObject.addProperty("manufacturer", device.getManufacturer());
65 jsonObject.addProperty("name", device.getName());
66 jsonObject.addProperty("tokens", "");
68 final NeeoDeviceTiming timing = device.getDeviceTiming();
70 final JsonObject timingObj = new JsonObject();
71 timingObj.addProperty("standbyCommandDelay", timing.getStandbyCommandDelay());
72 timingObj.addProperty("sourceSwitchDelay", timing.getSourceSwitchDelay());
73 timingObj.addProperty("shutdownDelay", timing.getShutdownDelay());
74 jsonObject.add("timing", timingObj);
78 * Setup only really good for SDK discovery (which we don't do)
79 * 'setup': { 'discovery': true,'registration': false,'introheader': 'header text','introtext': 'some hints'}
81 jsonObject.add("setup", new JsonObject());
83 jsonObject.add("deviceCapabilities", jsonContext.serialize(device.getDeviceCapabilities()));
85 final JsonObject deviceObj = new JsonObject();
86 final String deviceName = device.getName();
87 deviceObj.addProperty("name", deviceName);
88 deviceObj.add("tokens", new JsonArray());
89 jsonObject.add("device", deviceObj);
91 final String specificName = device.getSpecificName();
92 if (specificName != null && !specificName.isEmpty()) {
93 deviceObj.addProperty("specificname", specificName);
94 jsonObject.addProperty("specificname", specificName);
95 } else if (!deviceName.isEmpty()) {
96 deviceObj.addProperty("specificname", deviceName);
97 jsonObject.addProperty("specificname", deviceName);
100 final String iconName = device.getIconName();
101 if (iconName != null && !iconName.isEmpty()) {
102 deviceObj.addProperty("icon", iconName);
103 jsonObject.addProperty("icon", iconName);
106 final List<JsonObject> capabilities = new ArrayList<>();
107 for (NeeoDeviceChannel channel : device.getExposedChannels()) {
108 final NeeoCapabilityType capabilityType = channel.getType();
110 final String compPath = NeeoConstants.CAPABILITY_PATH_PREFIX + "/" + adapterName + "/"
111 + channel.getItemName() + "/" + channel.getSubType() + "/" + channel.getChannelNbr();
113 final String uniqueItemName = channel.getUniqueItemName();
114 final String sensorItemName = uniqueItemName
115 + (uniqueItemName.toLowerCase().endsWith(NeeoConstants.NEEO_SENSOR_SUFFIX.toLowerCase()) ? ""
116 : NeeoConstants.NEEO_SENSOR_SUFFIX);
118 if (capabilityType == NeeoCapabilityType.BUTTON) {
119 final String name = channel.getLabel().isEmpty() ? uniqueItemName : channel.getLabel();
121 if (channel.getKind() == NeeoDeviceChannelKind.TRIGGER) {
122 final String path = compPath + "/button/trigger";
123 capabilities.add(createBase(name, channel.getLabel(), capabilityType.toString(), path));
125 final String value = channel.getValue();
126 final String path = compPath + "/button/"
127 + (value == null || value.isEmpty() ? "on" : NeeoUtil.encodeURIComponent(value.trim()));
128 capabilities.add(createBase(name, channel.getLabel(), capabilityType.toString(), path));
130 } else if (capabilityType == NeeoCapabilityType.SENSOR_POWER) {
131 final JsonObject sensorTypeObj = new JsonObject();
132 sensorTypeObj.addProperty("type", NeeoCapabilityType.SENSOR_POWER.toString());
134 // power should NOT use the sensor suffix
135 capabilities.add(createBase(uniqueItemName, channel.getLabel(), NeeoCapabilityType.SENSOR.toString(),
136 compPath + "/switch/power", sensorTypeObj));
137 } else if (capabilityType == NeeoCapabilityType.SENSOR) {
138 final JsonObject sensor = new JsonObject();
139 sensor.addProperty("type", NeeoCapabilityType.SENSOR_RANGE.toString());
141 final NeeoDeviceChannelRange channelRange = channel.getRange();
142 final int[] range = new int[] { channelRange.getMinValue(), channelRange.getMaxValue() };
143 sensor.add("range", jsonContext.serialize(range));
144 sensor.addProperty("unit", channelRange.getUnit());
146 capabilities.add(createBase(sensorItemName, channel.getLabel(), capabilityType.toString(),
147 compPath + "/sensor/sensor", sensor));
148 } else if (capabilityType == NeeoCapabilityType.SLIDER) {
149 final JsonObject sliderSensor = new JsonObject();
150 sliderSensor.addProperty("type", NeeoCapabilityType.SENSOR_RANGE.toString());
151 sliderSensor.addProperty("sensor", sensorItemName);
153 final NeeoDeviceChannelRange channelRange = channel.getRange();
154 final int[] range = new int[] { channelRange.getMinValue(), channelRange.getMaxValue() };
155 sliderSensor.add("range", jsonContext.serialize(range));
156 sliderSensor.addProperty("unit", channelRange.getUnit());
157 capabilities.add(createBase(uniqueItemName, channel.getLabel(), capabilityType.toString(),
158 compPath + "/slider/actor", "slider", sliderSensor));
160 final JsonObject sensorTypeObj = new JsonObject();
161 sensorTypeObj.addProperty("type", NeeoCapabilityType.SENSOR_RANGE.toString());
162 sensorTypeObj.add("range", jsonContext.serialize(range));
163 sensorTypeObj.addProperty("unit", channelRange.getUnit());
165 capabilities.add(createBase(sensorItemName, channel.getLabel(), NeeoCapabilityType.SENSOR.toString(),
166 compPath + "/slider/sensor", sensorTypeObj));
167 } else if (capabilityType == NeeoCapabilityType.SWITCH) {
168 final String label = channel.getLabel();
170 final NeeoButtonGroup buttons = NeeoButtonGroup.parse(label);
171 if (buttons == null) {
172 capabilities.add(createBase(uniqueItemName, channel.getLabel(), capabilityType.toString(),
173 compPath + "/switch/actor", new JsonPrimitive(sensorItemName)));
175 final JsonObject sensorTypeObj = new JsonObject();
176 sensorTypeObj.addProperty("type", NeeoCapabilityType.SENSOR_BINARY.toString());
178 capabilities.add(createBase(sensorItemName, channel.getLabel(),
179 NeeoCapabilityType.SENSOR.toString(), compPath + "/switch/sensor", sensorTypeObj));
181 for (final ButtonInfo bi : buttons.getButtonInfos()) {
182 capabilities.add(createBase(bi.getLabel(), bi.getLabel(), NeeoCapabilityType.BUTTON.toString(),
183 compPath + "/button/" + bi.getSuffix()));
187 } else if (capabilityType == NeeoCapabilityType.IMAGEURL) {
188 final String value = channel.getValue();
189 final String size = (value == null || value.isEmpty() ? "large" : value.trim()).toLowerCase();
191 final JsonObject jo = createBase(uniqueItemName, channel.getLabel(), capabilityType.toString(),
192 compPath + "/image/actor", "sensor", new JsonPrimitive(sensorItemName));
193 jo.addProperty("size", size);
194 capabilities.add(jo);
196 final JsonObject sensorTypeObj = new JsonObject();
197 sensorTypeObj.addProperty("type", NeeoCapabilityType.IMAGEURL.toString());
199 capabilities.add(createBase(sensorItemName, channel.getLabel(), NeeoCapabilityType.SENSOR.toString(),
200 compPath + "/image/sensor", sensorTypeObj));
201 } else if (capabilityType == NeeoCapabilityType.TEXTLABEL) {
202 final JsonObject capObj = createBase(uniqueItemName, channel.getLabel(), capabilityType.toString(),
203 compPath + "/textlabel/actor", new JsonPrimitive(sensorItemName));
205 capObj.addProperty("isLabelVisible",
206 channel instanceof NeeoDeviceChannelText ndct ? ndct.isLabelVisible() : true);
208 capabilities.add(capObj);
210 final JsonObject sensorTypeObj = new JsonObject();
211 sensorTypeObj.addProperty("type", NeeoCapabilityType.SENSOR_CUSTOM.toString());
213 capabilities.add(createBase(sensorItemName, channel.getLabel(), NeeoCapabilityType.SENSOR.toString(),
214 compPath + "/textlabel/sensor", sensorTypeObj));
215 } else if (capabilityType == NeeoCapabilityType.DIRECTORY) {
216 final JsonObject capObj = createBase(uniqueItemName, channel.getLabel(), capabilityType.toString(),
217 compPath + "/directory/actor");
219 capabilities.add(capObj);
221 logger.debug("Unknown capability type: {} for channel {}", capabilityType, channel);
226 jsonObject.add("capabilities", jsonContext.serialize(capabilities));
232 * Helper method to create a base element with the given name/label/type/path
234 * @param name the element name
235 * @param label the element label
236 * @param type the element type
237 * @param path the element path
238 * @return the json object representing the base element
240 private JsonObject createBase(String name, String label, String type, String path) {
241 return createBase(name, label, type, path, null, null);
245 * Helper method to create a base element with the given name/label/type/path/sensor
247 * @param name the element name
248 * @param label the element label
249 * @param type the element type
250 * @param path the element path
251 * @param sensor the element sensor
252 * @return the json object representing the base element
254 private JsonObject createBase(String name, String label, String type, String path, JsonElement sensor) {
255 return createBase(name, label, type, path, "sensor", sensor);
259 * Helper method to create a base element with the given name/label/type/path/sensorname/sensor
261 * @param name the element name
262 * @param label the element label
263 * @param type the element type
264 * @param path the element path
265 * @param sensorName the element sensor name
266 * @param sensor the element sensor
267 * @return the json object representing the base element
269 private JsonObject createBase(String name, String label, String type, String path, @Nullable String sensorName,
270 @Nullable JsonElement sensor) {
271 final JsonObject compObj = new JsonObject();
272 compObj.addProperty("name", NeeoUtil.encodeURIComponent(name));
273 compObj.addProperty("label", label);
274 compObj.addProperty("type", type);
276 compObj.addProperty("path", NeeoUtil.encodeURIComponent(path));
277 if (sensor != null && sensorName != null && !sensorName.isEmpty()) {
278 if (sensor instanceof JsonPrimitive) {
279 compObj.addProperty(sensorName, sensor.getAsString());
281 compObj.add(sensorName, sensor);