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.Arrays;
18 import java.util.List;
19 import java.util.Objects;
22 import org.apache.commons.lang.StringUtils;
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.core.thing.Thing;
26 import org.openhab.core.thing.ThingStatus;
27 import org.openhab.core.thing.type.ThingType;
28 import org.openhab.io.neeo.NeeoService;
29 import org.openhab.io.neeo.internal.NeeoBrainServlet;
30 import org.openhab.io.neeo.internal.NeeoConstants;
31 import org.openhab.io.neeo.internal.NeeoDeviceKeys;
32 import org.openhab.io.neeo.internal.NeeoUtil;
33 import org.openhab.io.neeo.internal.ServiceContext;
34 import org.openhab.io.neeo.internal.models.NeeoDevice;
35 import org.openhab.io.neeo.internal.models.NeeoDeviceChannel;
36 import org.openhab.io.neeo.internal.models.NeeoDeviceTiming;
37 import org.openhab.io.neeo.internal.models.NeeoDeviceType;
38 import org.openhab.io.neeo.internal.models.NeeoThingUID;
40 import com.google.gson.JsonArray;
41 import com.google.gson.JsonDeserializationContext;
42 import com.google.gson.JsonDeserializer;
43 import com.google.gson.JsonElement;
44 import com.google.gson.JsonObject;
45 import com.google.gson.JsonParseException;
46 import com.google.gson.JsonSerializationContext;
47 import com.google.gson.JsonSerializer;
50 * Implementation of {@link JsonSerializer} and {@link JsonDeserializer} to serialize/deserial
51 * {@link NeeoDevice}. This implementation should NOT be used in communications with the NEEO brain (use
52 * {@link NeeoBrainDeviceSerializer} instead)
54 * @author Tim Roberts - Initial Contribution
57 public class NeeoDeviceSerializer implements JsonSerializer<NeeoDevice>, JsonDeserializer<NeeoDevice> {
61 private final NeeoService service;
63 /** The service context */
65 private final ServiceContext context;
68 * Constructs the object with no service or context
70 public NeeoDeviceSerializer() {
75 * Constructs the object from the service and context. A null service or context will suppress certain values on the
76 * returned json object
78 * @param service the possibly null service
79 * @param context the possibly null context
81 public NeeoDeviceSerializer(@Nullable NeeoService service, @Nullable ServiceContext context) {
82 this.service = service;
83 this.context = context;
87 public NeeoDevice deserialize(@Nullable JsonElement elm, @Nullable Type type,
88 @Nullable JsonDeserializationContext jsonContext) throws JsonParseException {
89 Objects.requireNonNull(elm, "elm cannot be null");
90 Objects.requireNonNull(type, "type cannot be null");
91 Objects.requireNonNull(jsonContext, "jsonContext cannot be null");
93 if (!(elm instanceof JsonObject)) {
94 throw new JsonParseException("Element not an instance of JsonObject: " + elm);
97 final JsonObject jo = (JsonObject) elm;
98 final NeeoThingUID uid = jsonContext.deserialize(jo.get("uid"), NeeoThingUID.class);
99 final NeeoDeviceType devType = jsonContext.deserialize(jo.get("type"), NeeoDeviceType.class);
100 final String manufacturer = NeeoUtil.getString(jo, "manufacturer");
101 final String name = NeeoUtil.getString(jo, "name");
102 final NeeoDeviceChannel[] channels = jsonContext.deserialize(jo.get("channels"), NeeoDeviceChannel[].class);
103 final NeeoDeviceTiming timing = jo.has("timing")
104 ? jsonContext.deserialize(jo.get("timing"), NeeoDeviceTiming.class)
107 final String[] deviceCapabilities = jo.has("deviceCapabilities")
108 ? jsonContext.deserialize(jo.get("deviceCapabilities"), String[].class)
111 final String specificName = jo.has("specificName") ? jo.get("specificName").getAsString() : null;
113 final String iconName = jo.has("iconName") ? jo.get("iconName").getAsString() : null;
114 final int driverVersion = jo.has("driverVersion") ? jo.get("driverVersion").getAsInt() : 0;
117 return new NeeoDevice(uid, driverVersion, devType,
118 manufacturer == null || StringUtils.isEmpty(manufacturer) ? NeeoUtil.NOTAVAILABLE : manufacturer,
119 name, Arrays.asList(channels), timing,
120 deviceCapabilities == null ? null : Arrays.asList(deviceCapabilities), specificName, iconName);
121 } catch (NullPointerException | IllegalArgumentException e) {
122 throw new JsonParseException(e);
127 public JsonElement serialize(NeeoDevice device, @Nullable Type deviceType,
128 @Nullable JsonSerializationContext jsonContext) {
129 Objects.requireNonNull(device, "device cannot be null");
130 Objects.requireNonNull(deviceType, "deviceType cannot be null");
131 Objects.requireNonNull(jsonContext, "jsonContext cannot be null");
133 final JsonObject jsonObject = new JsonObject();
135 final NeeoThingUID uid = device.getUid();
136 jsonObject.add("uid", jsonContext.serialize(uid));
137 jsonObject.add("type", jsonContext.serialize(device.getType()));
138 jsonObject.addProperty("manufacturer", device.getManufacturer());
139 jsonObject.addProperty("name", device.getName());
140 jsonObject.addProperty("specificName", device.getSpecificName());
141 jsonObject.addProperty("iconName", device.getIconName());
142 jsonObject.addProperty("driverVersion", device.getDriverVersion());
144 final JsonArray channels = (JsonArray) jsonContext.serialize(device.getChannels());
146 final NeeoDeviceTiming timing = device.getDeviceTiming();
147 jsonObject.add("timing", jsonContext.serialize(timing == null ? new NeeoDeviceTiming() : timing));
149 jsonObject.add("deviceCapabilities", jsonContext.serialize(device.getDeviceCapabilities()));
151 jsonObject.addProperty("thingType", uid.getThingType());
153 if (StringUtils.equalsIgnoreCase(NeeoConstants.NEEOIO_BINDING_ID, uid.getBindingId())) {
154 jsonObject.addProperty("thingStatus", uid.getThingType().toUpperCase());
157 final ServiceContext localContext = context;
158 if (localContext != null) {
159 if (!StringUtils.equalsIgnoreCase(NeeoConstants.NEEOIO_BINDING_ID, uid.getBindingId())) {
160 final Thing thing = localContext.getThingRegistry().get(device.getUid().asThingUID());
161 jsonObject.addProperty("thingStatus",
162 thing == null ? ThingStatus.UNKNOWN.name() : thing.getStatus().name());
165 final ThingType thingType = localContext.getThingTypeRegistry()
166 .getThingType(thing.getThingTypeUID());
168 if (thingType != null) {
169 for (JsonElement chnl : channels) {
170 JsonObject jo = (JsonObject) chnl;
171 if (jo.has("groupId") && jo.has("itemLabel")) {
172 final String groupId = jo.get("groupId").getAsString();
173 final String groupLabel = NeeoUtil.getGroupLabel(thingType, groupId);
174 if (StringUtils.isNotEmpty(groupLabel)) {
175 final JsonElement itemLabel = jo.remove("itemLabel");
176 jo.addProperty("itemLabel", groupLabel + "#" + itemLabel.getAsString());
177 } else if (StringUtils.isNotEmpty("groupId")) {
178 // have a groupid but no group definition found (usually error on binding)
179 // just default to "Others" like the Paperui does.
180 final JsonElement itemLabel = jo.remove("itemLabel");
181 jo.addProperty("itemLabel", "Others#" + itemLabel.getAsString());
190 jsonObject.add("channels", channels);
192 final NeeoService localService = service;
193 if (localService != null) {
194 List<String> foundKeys = new ArrayList<>();
195 for (final NeeoBrainServlet servlet : localService.getServlets()) {
196 final NeeoDeviceKeys servletKeys = servlet.getDeviceKeys();
197 final Set<String> keys = servletKeys.get(device.getUid());
198 foundKeys.addAll(keys);
200 jsonObject.add("keys", jsonContext.serialize(foundKeys));