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.core.items.Item;
22 import org.openhab.core.items.ItemNotFoundException;
23 import org.openhab.core.thing.Channel;
24 import org.openhab.core.thing.ChannelUID;
25 import org.openhab.core.thing.type.ChannelType;
26 import org.openhab.core.types.Command;
27 import org.openhab.io.neeo.internal.NeeoUtil;
28 import org.openhab.io.neeo.internal.ServiceContext;
29 import org.openhab.io.neeo.internal.models.ItemSubType;
30 import org.openhab.io.neeo.internal.models.NeeoCapabilityType;
31 import org.openhab.io.neeo.internal.models.NeeoDeviceChannel;
32 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelDirectory;
33 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelDirectoryListItem;
34 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelKind;
35 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelRange;
36 import org.openhab.io.neeo.internal.models.NeeoDeviceChannelText;
38 import com.google.gson.JsonDeserializationContext;
39 import com.google.gson.JsonDeserializer;
40 import com.google.gson.JsonElement;
41 import com.google.gson.JsonObject;
42 import com.google.gson.JsonParseException;
43 import com.google.gson.JsonSerializationContext;
44 import com.google.gson.JsonSerializer;
47 * Implementation of {@link JsonSerializer} and {@link JsonDeserializer} to serialize/deserial
48 * {@link NeeoDeviceChannel}
50 * @author Tim Roberts - Initial Contribution
53 public class NeeoDeviceChannelSerializer
54 implements JsonSerializer<NeeoDeviceChannel>, JsonDeserializer<NeeoDeviceChannel> {
56 /** The service context */
58 private final ServiceContext context;
61 * Creates the serializer with no context
63 public NeeoDeviceChannelSerializer() {
68 * Creates the serializer using the given context. A null context will suppress certain values on the returned
71 * @param context the possibly null context
73 public NeeoDeviceChannelSerializer(@Nullable ServiceContext context) {
74 this.context = context;
78 public JsonElement serialize(NeeoDeviceChannel chnl, Type type, JsonSerializationContext jsonContext) {
79 final JsonObject jo = new JsonObject();
81 jo.add("kind", jsonContext.serialize(chnl.getKind()));
82 jo.addProperty("itemName", chnl.getItemName());
83 jo.addProperty("label", chnl.getLabel());
84 jo.addProperty("value", chnl.getValue());
85 jo.addProperty("channelNbr", chnl.getChannelNbr());
86 jo.add("type", jsonContext.serialize(chnl.getType()));
87 jo.add("subType", jsonContext.serialize(chnl.getSubType()));
88 jo.add("range", jsonContext.serialize(chnl.getRange()));
90 final ServiceContext localContext = context;
91 if (localContext != null) {
92 final List<String> commandTypes = new ArrayList<>();
93 boolean isReadOnly = false;
94 String itemLabel = chnl.getLabel();
95 String itemType = null;
98 final Item item = localContext.getItemRegistry().getItem(chnl.getItemName());
99 itemType = item.getType();
101 String label = item.getLabel();
102 if (label != null && !label.isEmpty()) {
106 for (Class<? extends Command> cmd : item.getAcceptedCommandTypes()) {
107 if (!cmd.getSimpleName().equalsIgnoreCase("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 (!itemLabel.isEmpty()) {
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 || itemName.isEmpty()) {
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 || label.isEmpty() ? NeeoUtil.NOTAVAILABLE : label, value, range, labelVisible);
205 } else if (capType == NeeoCapabilityType.DIRECTORY) {
206 final NeeoDeviceChannelDirectoryListItem[] listItems = jo.has("listItems")
207 ? context.deserialize(jo.get("listItems"), NeeoDeviceChannelDirectoryListItem[].class)
208 : new NeeoDeviceChannelDirectoryListItem[0];
210 return new NeeoDeviceChannelDirectory(kind, itemName, channelNbr, capType, itemSubType,
211 label == null || label.isEmpty() ? NeeoUtil.NOTAVAILABLE : label, value, range, listItems);
213 return new NeeoDeviceChannel(kind, itemName, channelNbr, capType, itemSubType,
214 label == null || label.isEmpty() ? NeeoUtil.NOTAVAILABLE : label, value, range);
216 } catch (NullPointerException | IllegalArgumentException e) {
217 throw new JsonParseException(e);