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.core.items.Item;
23 import org.openhab.core.items.ItemNotFoundException;
24 import org.openhab.core.thing.Channel;
25 import org.openhab.core.thing.ChannelUID;
26 import org.openhab.core.thing.type.ChannelType;
27 import org.openhab.core.types.Command;
28 import org.openhab.io.neeo.internal.NeeoUtil;
29 import org.openhab.io.neeo.internal.ServiceContext;
30 import org.openhab.io.neeo.internal.models.ItemSubType;
31 import org.openhab.io.neeo.internal.models.NeeoCapabilityType;
32 import org.openhab.io.neeo.internal.models.NeeoDeviceChannel;
33 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelDirectory;
34 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelDirectoryListItem;
35 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelKind;
36 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelRange;
37 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelText;
39 import com.google.gson.JsonDeserializationContext;
40 import com.google.gson.JsonDeserializer;
41 import com.google.gson.JsonElement;
42 import com.google.gson.JsonObject;
43 import com.google.gson.JsonParseException;
44 import com.google.gson.JsonSerializationContext;
45 import com.google.gson.JsonSerializer;
48 * Implementation of {@link JsonSerializer} and {@link JsonDeserializer} to serialize/deserial
49 * {@link NeeoDeviceChannel}
51 * @author Tim Roberts - Initial Contribution
54 public class NeeoDeviceChannelSerializer
55 implements JsonSerializer<NeeoDeviceChannel>, JsonDeserializer<NeeoDeviceChannel> {
57 /** The service context */
59 private final ServiceContext context;
62 * Creates the serializer with no context
64 public NeeoDeviceChannelSerializer() {
69 * Creates the serializer using the given context. A null context will suppress certain values on the returned
72 * @param context the possibly null context
74 public NeeoDeviceChannelSerializer(@Nullable ServiceContext context) {
75 this.context = context;
79 public JsonElement serialize(NeeoDeviceChannel chnl, Type type, JsonSerializationContext jsonContext) {
80 final JsonObject jo = new JsonObject();
82 jo.add("kind", jsonContext.serialize(chnl.getKind()));
83 jo.addProperty("itemName", chnl.getItemName());
84 jo.addProperty("label", chnl.getLabel());
85 jo.addProperty("value", chnl.getValue());
86 jo.addProperty("channelNbr", chnl.getChannelNbr());
87 jo.add("type", jsonContext.serialize(chnl.getType()));
88 jo.add("subType", jsonContext.serialize(chnl.getSubType()));
89 jo.add("range", jsonContext.serialize(chnl.getRange()));
91 final ServiceContext localContext = context;
92 if (localContext != null) {
93 final List<String> commandTypes = new ArrayList<>();
94 boolean isReadOnly = false;
95 String itemLabel = chnl.getLabel();
96 String itemType = null;
99 final Item item = localContext.getItemRegistry().getItem(chnl.getItemName());
100 itemType = item.getType();
102 if (StringUtils.isNotEmpty(item.getLabel())) {
103 itemLabel = item.getLabel();
106 for (Class<? extends Command> cmd : item.getAcceptedCommandTypes()) {
107 if (!StringUtils.equalsIgnoreCase(cmd.getSimpleName(), "refreshtype")) {
108 commandTypes.add(cmd.getSimpleName().toLowerCase());
112 for (ChannelUID channelUid : localContext.getItemChannelLinkRegistry()
113 .getBoundChannels(chnl.getItemName())) {
114 if (channelUid != null) {
115 jo.addProperty("groupId", channelUid.getGroupId());
116 final Channel channel = localContext.getThingRegistry().getChannel(channelUid);
117 if (channel != null) {
118 final ChannelType channelType = localContext.getChannelTypeRegistry()
119 .getChannelType(channel.getChannelTypeUID());
120 if (channelType != null && channelType.getState() != null) {
121 isReadOnly = channelType.getState().isReadOnly();
126 } catch (ItemNotFoundException e) {
130 if (StringUtils.isNotEmpty(itemLabel)) {
131 switch (chnl.getSubType()) {
133 itemType += " (Hue)";
137 itemType += " (Sat)";
141 itemType += " (Bri)";
149 jo.addProperty("itemType", itemType);
150 jo.addProperty("itemLabel", itemLabel);
151 jo.add("acceptedCommandTypes", jsonContext.serialize(commandTypes));
152 jo.addProperty("isReadOnly", isReadOnly);
156 if (chnl instanceof NeeoDeviceChannelText) {
157 jo.addProperty("labelVisible", ((NeeoDeviceChannelText) chnl).isLabelVisible());
158 } else if (chnl instanceof NeeoDeviceChannelDirectory) {
159 jo.add("listItems", jsonContext.serialize(((NeeoDeviceChannelDirectory) chnl).getListItems()));
166 public @Nullable NeeoDeviceChannel deserialize(JsonElement elm, Type type, JsonDeserializationContext context)
167 throws JsonParseException {
168 if (!(elm instanceof JsonObject)) {
169 throw new JsonParseException("Element not an instance of JsonObject: " + elm);
172 final JsonObject jo = (JsonObject) elm;
173 final String itemName = NeeoUtil.getString(jo, "itemName");
175 if (itemName == null || StringUtils.isEmpty(itemName)) {
176 throw new JsonParseException("Element requires an itemName attribute: " + elm);
179 final ItemSubType itemSubType = jo.has("subType") ? context.deserialize(jo.get("subType"), ItemSubType.class)
182 final String label = NeeoUtil.getString(jo, "label");
183 final String value = NeeoUtil.getString(jo, "value");
184 final Integer channelNbr = NeeoUtil.getInt(jo, "channelNbr");
186 if (channelNbr == null) {
187 throw new JsonParseException("Channel Number is not a valid integer");
189 final NeeoCapabilityType capType = context.deserialize(jo.get("type"), NeeoCapabilityType.class);
191 final NeeoDeviceChannelRange range = jo.has("range")
192 ? context.deserialize(jo.get("range"), NeeoDeviceChannelRange.class)
195 final NeeoDeviceChannelKind kind = jo.has("kind")
196 ? context.deserialize(jo.get("kind"), NeeoDeviceChannelKind.class)
197 : NeeoDeviceChannelKind.ITEM;
200 if (capType == NeeoCapabilityType.TEXTLABEL) {
201 final boolean labelVisible = jo.has("labelVisible") ? jo.get("labelVisible").getAsBoolean() : true;
203 return new NeeoDeviceChannelText(kind, itemName, channelNbr, capType, itemSubType,
204 label == null || StringUtils.isEmpty(label) ? NeeoUtil.NOTAVAILABLE : label, value, range,
206 } else if (capType == NeeoCapabilityType.DIRECTORY) {
207 final NeeoDeviceChannelDirectoryListItem[] listItems = jo.has("listItems")
208 ? context.deserialize(jo.get("listItems"), NeeoDeviceChannelDirectoryListItem[].class)
209 : new NeeoDeviceChannelDirectoryListItem[0];
211 return new NeeoDeviceChannelDirectory(kind, itemName, channelNbr, capType, itemSubType,
212 label == null || StringUtils.isEmpty(label) ? NeeoUtil.NOTAVAILABLE : label, value, range,
215 return new NeeoDeviceChannel(kind, itemName, channelNbr, capType, itemSubType,
216 label == null || StringUtils.isEmpty(label) ? NeeoUtil.NOTAVAILABLE : label, value, range);
218 } catch (NullPointerException | IllegalArgumentException e) {
219 throw new JsonParseException(e);