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.core.items.Item;
24 import org.openhab.core.items.ItemNotFoundException;
25 import org.openhab.core.thing.Channel;
26 import org.openhab.core.thing.ChannelUID;
27 import org.openhab.core.thing.type.ChannelType;
28 import org.openhab.core.types.Command;
29 import org.openhab.io.neeo.internal.NeeoUtil;
30 import org.openhab.io.neeo.internal.ServiceContext;
31 import org.openhab.io.neeo.internal.models.ItemSubType;
32 import org.openhab.io.neeo.internal.models.NeeoCapabilityType;
33 import org.openhab.io.neeo.internal.models.NeeoDeviceChannel;
34 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelDirectory;
35 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelDirectoryListItem;
36 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelKind;
37 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelRange;
38 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelText;
40 import com.google.gson.JsonDeserializationContext;
41 import com.google.gson.JsonDeserializer;
42 import com.google.gson.JsonElement;
43 import com.google.gson.JsonObject;
44 import com.google.gson.JsonParseException;
45 import com.google.gson.JsonSerializationContext;
46 import com.google.gson.JsonSerializer;
49 * Implementation of {@link JsonSerializer} and {@link JsonDeserializer} to serialize/deserial
50 * {@link NeeoDeviceChannel}
52 * @author Tim Roberts - Initial Contribution
55 public class NeeoDeviceChannelSerializer
56 implements JsonSerializer<NeeoDeviceChannel>, JsonDeserializer<NeeoDeviceChannel> {
58 /** The service context */
60 private final ServiceContext context;
63 * Creates the serializer with no context
65 public NeeoDeviceChannelSerializer() {
70 * Creates the serializer using the given context. A null context will suppress certain values on the returned
73 * @param context the possibly null context
75 public NeeoDeviceChannelSerializer(@Nullable ServiceContext context) {
76 this.context = context;
80 public JsonElement serialize(NeeoDeviceChannel chnl, @Nullable Type type,
81 @Nullable JsonSerializationContext jsonContext) {
82 Objects.requireNonNull(chnl, "chnl cannot be null");
83 Objects.requireNonNull(type, "type cannot be null");
84 Objects.requireNonNull(jsonContext, "jsonContext cannot be null");
86 final JsonObject jo = new JsonObject();
88 jo.add("kind", jsonContext.serialize(chnl.getKind()));
89 jo.addProperty("itemName", chnl.getItemName());
90 jo.addProperty("label", chnl.getLabel());
91 jo.addProperty("value", chnl.getValue());
92 jo.addProperty("channelNbr", chnl.getChannelNbr());
93 jo.add("type", jsonContext.serialize(chnl.getType()));
94 jo.add("subType", jsonContext.serialize(chnl.getSubType()));
95 jo.add("range", jsonContext.serialize(chnl.getRange()));
97 final ServiceContext localContext = context;
98 if (localContext != null) {
99 final List<String> commandTypes = new ArrayList<>();
100 boolean isReadOnly = false;
101 String itemLabel = chnl.getLabel();
102 String itemType = null;
105 final Item item = localContext.getItemRegistry().getItem(chnl.getItemName());
106 itemType = item.getType();
108 if (StringUtils.isNotEmpty(item.getLabel())) {
109 itemLabel = item.getLabel();
112 for (Class<? extends Command> cmd : item.getAcceptedCommandTypes()) {
113 if (!StringUtils.equalsIgnoreCase(cmd.getSimpleName(), "refreshtype")) {
114 commandTypes.add(cmd.getSimpleName().toLowerCase());
118 for (ChannelUID channelUid : localContext.getItemChannelLinkRegistry()
119 .getBoundChannels(chnl.getItemName())) {
120 if (channelUid != null) {
121 jo.addProperty("groupId", channelUid.getGroupId());
122 final Channel channel = localContext.getThingRegistry().getChannel(channelUid);
123 if (channel != null) {
124 final ChannelType channelType = localContext.getChannelTypeRegistry()
125 .getChannelType(channel.getChannelTypeUID());
126 if (channelType != null && channelType.getState() != null) {
127 isReadOnly = channelType.getState().isReadOnly();
132 } catch (ItemNotFoundException e) {
136 if (StringUtils.isNotEmpty(itemLabel)) {
137 switch (chnl.getSubType()) {
139 itemType += " (Hue)";
143 itemType += " (Sat)";
147 itemType += " (Bri)";
155 jo.addProperty("itemType", itemType);
156 jo.addProperty("itemLabel", itemLabel);
157 jo.add("acceptedCommandTypes", jsonContext.serialize(commandTypes));
158 jo.addProperty("isReadOnly", isReadOnly);
162 if (chnl instanceof NeeoDeviceChannelText) {
163 jo.addProperty("labelVisible", ((NeeoDeviceChannelText) chnl).isLabelVisible());
164 } else if (chnl instanceof NeeoDeviceChannelDirectory) {
165 jo.add("listItems", jsonContext.serialize(((NeeoDeviceChannelDirectory) chnl).getListItems()));
172 public NeeoDeviceChannel deserialize(@Nullable JsonElement elm, @Nullable Type type,
173 @Nullable JsonDeserializationContext context) throws JsonParseException {
174 Objects.requireNonNull(elm, "elm cannot be null");
175 Objects.requireNonNull(type, "type cannot be null");
176 Objects.requireNonNull(context, "context cannot be null");
178 if (!(elm instanceof JsonObject)) {
179 throw new JsonParseException("Element not an instance of JsonObject: " + elm);
182 final JsonObject jo = (JsonObject) elm;
183 final String itemName = NeeoUtil.getString(jo, "itemName");
185 if (itemName == null || StringUtils.isEmpty(itemName)) {
186 throw new JsonParseException("Element requires an itemName attribute: " + elm);
189 final ItemSubType itemSubType = jo.has("subType") ? context.deserialize(jo.get("subType"), ItemSubType.class)
192 final String label = NeeoUtil.getString(jo, "label");
193 final String value = NeeoUtil.getString(jo, "value");
194 final Integer channelNbr = NeeoUtil.getInt(jo, "channelNbr");
196 if (channelNbr == null) {
197 throw new JsonParseException("Channel Number is not a valid integer");
199 final NeeoCapabilityType capType = context.deserialize(jo.get("type"), NeeoCapabilityType.class);
201 final NeeoDeviceChannelRange range = jo.has("range")
202 ? context.deserialize(jo.get("range"), NeeoDeviceChannelRange.class)
205 final NeeoDeviceChannelKind kind = jo.has("kind")
206 ? context.deserialize(jo.get("kind"), NeeoDeviceChannelKind.class)
207 : NeeoDeviceChannelKind.ITEM;
210 if (capType == NeeoCapabilityType.TEXTLABEL) {
211 final boolean labelVisible = jo.has("labelVisible") ? jo.get("labelVisible").getAsBoolean() : true;
213 return new NeeoDeviceChannelText(kind, itemName, channelNbr, capType, itemSubType,
214 label == null || StringUtils.isEmpty(label) ? NeeoUtil.NOTAVAILABLE : label, value, range,
216 } else if (capType == NeeoCapabilityType.DIRECTORY) {
217 final NeeoDeviceChannelDirectoryListItem[] listItems = jo.has("listItems")
218 ? context.deserialize(jo.get("listItems"), NeeoDeviceChannelDirectoryListItem[].class)
219 : new NeeoDeviceChannelDirectoryListItem[0];
221 return new NeeoDeviceChannelDirectory(kind, itemName, channelNbr, capType, itemSubType,
222 label == null || StringUtils.isEmpty(label) ? NeeoUtil.NOTAVAILABLE : label, value, range,
225 return new NeeoDeviceChannel(kind, itemName, channelNbr, capType, itemSubType,
226 label == null || StringUtils.isEmpty(label) ? NeeoUtil.NOTAVAILABLE : label, value, range);
228 } catch (NullPointerException | IllegalArgumentException e) {
229 throw new JsonParseException(e);