]> git.basschouten.com Git - openhab-addons.git/blob
fcc9482e6a129a8dc53516577e040198fa04d630
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.shelly.internal.handler;
14
15 import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
16 import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
17
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.LinkedHashMap;
21 import java.util.Map;
22 import java.util.Set;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyControlRoller;
26 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsEMeter;
27 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsMeter;
28 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsRelay;
29 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsStatus;
30 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusRelay;
31 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusSensor;
32 import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
33 import org.openhab.binding.shelly.internal.util.ShellyTranslationProvider;
34 import org.openhab.core.thing.Channel;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.binding.builder.ChannelBuilder;
38 import org.openhab.core.thing.type.ChannelTypeUID;
39
40 /**
41  * The {@link ShellyCHANNEL_DEFINITIONSDTO} defines channel information for dynamically created channels. Those will be
42  * added on the first thing status update
43  *
44  * @author Markus Michels - Initial contribution
45  */
46 @NonNullByDefault
47 public class ShellyChannelDefinitionsDTO {
48
49     private static final ChannelMap CHANNEL_DEFINITIONS = new ChannelMap();
50
51     // shortcuts to avoid line breaks (make code more readable)
52     private static final String CHGR_DEVST = CHANNEL_GROUP_DEV_STATUS;
53     private static final String CHGR_RELAY = CHANNEL_GROUP_RELAY_CONTROL;
54     private static final String CHGR_ROLLER = CHANNEL_GROUP_ROL_CONTROL;
55     private static final String CHGR_STATUS = CHANNEL_GROUP_STATUS;
56     private static final String CHGR_METER = CHANNEL_GROUP_METER;
57     private static final String CHGR_SENSOR = CHANNEL_GROUP_SENSOR;
58     private static final String CHGR_BAT = CHANNEL_GROUP_BATTERY;
59
60     public static final String ITEM_TYPE_NUMBER = "Number";
61     public static final String ITEM_TYPE_STRING = "String";
62     public static final String ITEM_TYPE_SWITCH = "Switch";
63     public static final String ITEM_TYPE_CONTACT = "Contact";
64     public static final String ITEM_TYPE_DATETIME = "DateTime";
65     public static final String ITEM_TYPE_TEMP = "Number:Temperature";
66     public static final String ITEM_TYPE_LUX = "Number:Illuminance";
67     public static final String ITEM_TYPE_POWER = "Number:Power";
68     public static final String ITEM_TYPE_ENERGY = "Number:Energy";
69     public static final String ITEM_TYPE_VOLT = "Number:ElectricPotential";
70     public static final String ITEM_TYPE_AMP = "Number:ElectricPotential";
71     public static final String ITEM_TYPE_PERCENT = "Number:Dimensionless";
72     public static final String ITEM_TYPE_ANGLE = "Number:Angle";
73
74     public static final String PREFIX_GROUP = "definitions.shelly.group.";
75     public static final String PREFIX_CHANNEL = "channel-type.shelly.";
76     public static final String SUFFIX_LABEL = ".label";
77     public static final String SUFFIX_DESCR = ".description";
78
79     public ShellyChannelDefinitionsDTO(ShellyTranslationProvider m) {
80         // Device: Internal Temp
81         CHANNEL_DEFINITIONS
82                 // Device
83                 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_NAME, "deviceName", ITEM_TYPE_STRING))
84                 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ITEMP, "deviceTemp", ITEM_TYPE_TEMP))
85                 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_WAKEUP, "sensorWakeup", ITEM_TYPE_STRING))
86                 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCUWATTS, "meterAccuWatts", ITEM_TYPE_POWER))
87                 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCUTOTAL, "meterAccuTotal", ITEM_TYPE_POWER))
88                 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCURETURNED, "meterAccuReturned", ITEM_TYPE_POWER))
89                 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_CHARGER, "charger", ITEM_TYPE_SWITCH))
90                 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_LED_STATUS_DISABLE, "ledStatusDisable", ITEM_TYPE_SWITCH))
91                 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_LED_POWER_DISABLE, "ledPowerDisable", ITEM_TYPE_SWITCH))
92                 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_SELFTTEST, "selfTest", ITEM_TYPE_STRING))
93                 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_UPTIME, "uptime", ITEM_TYPE_NUMBER))
94                 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_HEARTBEAT, "heartBeat", ITEM_TYPE_DATETIME))
95                 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_UPDATE, "updateAvailable", ITEM_TYPE_SWITCH))
96
97                 // Relay
98                 .add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_OUTPUT_NAME, "outputName", ITEM_TYPE_STRING))
99
100                 // Roller
101                 .add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_ROL_CONTROL_STATE, "rollerState", ITEM_TYPE_STRING))
102
103                 // RGBW2
104                 .add(new ShellyChannel(m, CHANNEL_GROUP_LIGHT_CONTROL, CHANNEL_INPUT, "inputState", ITEM_TYPE_SWITCH))
105
106                 // Power Meter
107                 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_CURRENTWATTS, "meterWatts", ITEM_TYPE_POWER))
108                 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_TOTALKWH, "meterTotal", ITEM_TYPE_ENERGY))
109                 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_LASTMIN1, "lastPower1", ITEM_TYPE_ENERGY))
110                 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_LAST_UPDATE, "lastUpdate", ITEM_TYPE_DATETIME))
111
112                 // EMeter
113                 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_TOTALRET, "meterReturned", ITEM_TYPE_ENERGY))
114                 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_REACTWATTS, "meterReactive", ITEM_TYPE_POWER))
115                 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_VOLTAGE, "meterVoltage", ITEM_TYPE_VOLT))
116                 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_CURRENT, "meterCurrent", ITEM_TYPE_AMP))
117                 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_PFACTOR, "meterPowerFactor", ITEM_TYPE_NUMBER))
118
119                 // Sensors
120                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_TEMP, "sensorTemp", ITEM_TYPE_TEMP))
121                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_HUM, "sensorHumidity", ITEM_TYPE_PERCENT))
122                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_LUX, "sensorLux", ITEM_TYPE_LUX))
123                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_ILLUM, "sensorIllumination", ITEM_TYPE_STRING))
124                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_CONTACT, "sensorContact", ITEM_TYPE_CONTACT))
125                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_SSTATE, "sensorState", ITEM_TYPE_STRING))
126                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VIBRATION, "sensorVibration", ITEM_TYPE_SWITCH))
127                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_TILT, "sensorTilt", ITEM_TYPE_ANGLE))
128                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_MOTION, "sensorMotion", ITEM_TYPE_SWITCH))
129                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_FLOOD, "sensorFlood", ITEM_TYPE_SWITCH))
130                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_SMOKE, "sensorSmoke", ITEM_TYPE_SWITCH))
131                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_PPM, "sensorPPM", ITEM_TYPE_NUMBER))
132                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VALVE, "sensorValve", ITEM_TYPE_STRING))
133                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_ALARM_STATE, "alarmState", ITEM_TYPE_STRING))
134                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_ERROR, "sensorError", ITEM_TYPE_STRING))
135                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_LAST_UPDATE, "lastUpdate", ITEM_TYPE_DATETIME))
136
137                 // Button/ix3
138                 .add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_INPUT, "inputState", ITEM_TYPE_SWITCH))
139                 .add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_STATUS_EVENTTYPE, "eventType", ITEM_TYPE_STRING))
140                 .add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_STATUS_EVENTCOUNT, "eventCount", ITEM_TYPE_NUMBER))
141                 .add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_BUTTON_TRIGGER, "system.button", ITEM_TYPE_STRING))
142                 .add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_LAST_UPDATE, "lastUpdate", ITEM_TYPE_DATETIME))
143
144                 // Addon with external sensors
145                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP1, "sensorExtTemp", ITEM_TYPE_TEMP))
146                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP2, "sensorExtTemp", ITEM_TYPE_TEMP))
147                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP3, "sensorExtTemp", ITEM_TYPE_TEMP))
148                 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_HUMIDITY, "sensorExtHum", ITEM_TYPE_PERCENT))
149
150                 // Battery
151                 .add(new ShellyChannel(m, CHGR_BAT, CHANNEL_SENSOR_BAT_LEVEL, "system:battery-level",
152                         ITEM_TYPE_PERCENT))
153                 .add(new ShellyChannel(m, CHGR_BAT, CHANNEL_SENSOR_BAT_LOW, "system:low-battery", ITEM_TYPE_SWITCH));
154     }
155
156     public static ShellyChannel getDefinition(String channelName) throws IllegalArgumentException {
157         String group = substringBefore(channelName, "#");
158         String channel = substringAfter(channelName, "#");
159         if (group.contains(CHANNEL_GROUP_METER)) {
160             group = CHANNEL_GROUP_METER; // map meter1..n to meter
161         } else if (group.contains(CHANNEL_GROUP_RELAY_CONTROL)) {
162             group = CHANNEL_GROUP_RELAY_CONTROL; // map meter1..n to meter
163         } else if (group.contains(CHANNEL_GROUP_LIGHT_CHANNEL)) {
164             group = CHANNEL_GROUP_LIGHT_CHANNEL;
165         } else if (group.contains(CHANNEL_GROUP_STATUS)) {
166             group = CHANNEL_GROUP_STATUS; // map status1..n to meter
167         }
168         String channelId = group + "#" + channel;
169         return CHANNEL_DEFINITIONS.get(channelId);
170     }
171
172     /**
173      * Auto-create relay channels depending on relay type/mode
174      *
175      * @return ArrayList<Channel> of channels to be added to the thing
176      */
177     public static Map<String, Channel> createDeviceChannels(final Thing thing, final ShellyDeviceProfile profile,
178             final ShellySettingsStatus status) {
179         Map<String, Channel> add = new LinkedHashMap<>();
180
181         addChannel(thing, add, profile.settings.name != null, CHGR_DEVST, CHANNEL_DEVST_NAME);
182
183         if (!profile.isSensor) {
184             // Only some devices report the internal device temp
185             addChannel(thing, add, (status.tmp != null) || (status.temperature != null), CHGR_DEVST,
186                     CHANNEL_DEVST_ITEMP);
187         }
188
189         // RGBW2
190         addChannel(thing, add, status.input != null, CHANNEL_GROUP_LIGHT_CONTROL, CHANNEL_INPUT);
191
192         // If device has more than 1 meter the channel accumulatedWatts receives the accumulated value
193         boolean accuChannel = (((status.meters != null) && (status.meters.size() > 1) && !profile.isRoller
194                 && !profile.isRGBW2) || ((status.emeters != null && status.emeters.size() > 1)));
195         addChannel(thing, add, accuChannel, CHGR_DEVST, CHANNEL_DEVST_ACCUWATTS);
196         addChannel(thing, add, accuChannel, CHGR_DEVST, CHANNEL_DEVST_ACCUTOTAL);
197         addChannel(thing, add, accuChannel && (status.emeters != null), CHGR_DEVST, CHANNEL_DEVST_ACCURETURNED);
198         addChannel(thing, add, true, CHGR_DEVST, CHANNEL_DEVST_UPDATE);
199         addChannel(thing, add, true, CHGR_DEVST, CHANNEL_DEVST_UPTIME);
200         addChannel(thing, add, true, CHGR_DEVST, CHANNEL_DEVST_HEARTBEAT);
201
202         if (profile.settings.ledPowerDisable != null) {
203             addChannel(thing, add, true, CHGR_DEVST, CHANNEL_LED_POWER_DISABLE);
204         }
205         if (profile.settings.ledStatusDisable != null) {
206             addChannel(thing, add, true, CHGR_DEVST, CHANNEL_LED_STATUS_DISABLE); // WiFi status LED
207         }
208         return add;
209     }
210
211     /**
212      * Auto-create relay channels depending on relay type/mode
213      *
214      * @return ArrayList<Channel> of channels to be added to the thing
215      */
216     public static Map<String, Channel> createRelayChannels(final Thing thing, final ShellyDeviceProfile profile,
217             final ShellyStatusRelay relay, int idx) {
218         Map<String, Channel> add = new LinkedHashMap<>();
219         String group = profile.getControlGroup(idx);
220
221         ShellySettingsRelay rs = profile.settings.relays.get(idx);
222         addChannel(thing, add, rs.name != null, group, CHANNEL_OUTPUT_NAME);
223
224         // Shelly 1/1PM Addon
225         if (relay.extTemperature != null) {
226             addChannel(thing, add, relay.extTemperature.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP1);
227             addChannel(thing, add, relay.extTemperature.sensor2 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP2);
228             addChannel(thing, add, relay.extTemperature.sensor3 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP3);
229         }
230         if (relay.extHumidity != null) {
231             addChannel(thing, add, relay.extHumidity.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENDOR_HUMIDITY);
232         }
233
234         return add;
235     }
236
237     public static Map<String, Channel> createRollerChannels(Thing thing, final ShellyControlRoller roller) {
238         Map<String, Channel> add = new LinkedHashMap<>();
239         addChannel(thing, add, roller.state != null, CHGR_ROLLER, CHANNEL_ROL_CONTROL_STATE);
240         return add;
241     }
242
243     public static Map<String, Channel> createMeterChannels(Thing thing, final ShellySettingsMeter meter, String group) {
244         Map<String, Channel> newChannels = new LinkedHashMap<>();
245         addChannel(thing, newChannels, meter.power != null, group, CHANNEL_METER_CURRENTWATTS);
246         addChannel(thing, newChannels, meter.total != null, group, CHANNEL_METER_TOTALKWH);
247         if (meter.counters != null) {
248             addChannel(thing, newChannels, meter.counters[0] != null, group, CHANNEL_METER_LASTMIN1);
249         }
250         addChannel(thing, newChannels, meter.timestamp != null, group, CHANNEL_LAST_UPDATE);
251         return newChannels;
252     }
253
254     public static Map<String, Channel> createEMeterChannels(final Thing thing, final ShellySettingsEMeter emeter,
255             String group) {
256         Map<String, Channel> newChannels = new LinkedHashMap<>();
257         addChannel(thing, newChannels, emeter.power != null, group, CHANNEL_METER_CURRENTWATTS);
258         addChannel(thing, newChannels, emeter.total != null, group, CHANNEL_METER_TOTALKWH);
259         addChannel(thing, newChannels, emeter.totalReturned != null, group, CHANNEL_EMETER_TOTALRET);
260         addChannel(thing, newChannels, emeter.reactive != null, group, CHANNEL_EMETER_REACTWATTS);
261         addChannel(thing, newChannels, emeter.voltage != null, group, CHANNEL_EMETER_VOLTAGE);
262         addChannel(thing, newChannels, emeter.current != null, group, CHANNEL_EMETER_CURRENT);
263         addChannel(thing, newChannels, emeter.pf != null, group, CHANNEL_EMETER_PFACTOR);
264         addChannel(thing, newChannels, true, group, CHANNEL_LAST_UPDATE);
265         return newChannels;
266     }
267
268     public static Map<String, Channel> createSensorChannels(final Thing thing, final ShellyDeviceProfile profile,
269             final ShellyStatusSensor sdata) {
270         Map<String, Channel> newChannels = new LinkedHashMap<>();
271
272         // Sensor data
273         addChannel(thing, newChannels, sdata.tmp != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TEMP);
274         addChannel(thing, newChannels, sdata.hum != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_HUM);
275         addChannel(thing, newChannels, sdata.lux != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_LUX);
276         if (sdata.accel != null) {
277             addChannel(thing, newChannels, sdata.accel.vibration != null, CHANNEL_GROUP_SENSOR,
278                     CHANNEL_SENSOR_VIBRATION);
279             addChannel(thing, newChannels, sdata.accel.tilt != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TILT);
280         }
281         addChannel(thing, newChannels, sdata.flood != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD);
282         addChannel(thing, newChannels, sdata.smoke != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD);
283         addChannel(thing, newChannels, sdata.lux != null && sdata.lux.illumination != null, CHANNEL_GROUP_SENSOR,
284                 CHANNEL_SENSOR_ILLUM);
285         addChannel(thing, newChannels, sdata.contact != null && sdata.contact.state != null, CHANNEL_GROUP_SENSOR,
286                 CHANNEL_SENSOR_CONTACT);
287         addChannel(thing, newChannels, sdata.motion != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_MOTION);
288         addChannel(thing, newChannels, sdata.charger != null, CHGR_DEVST, CHANNEL_DEVST_CHARGER);
289         addChannel(thing, newChannels, sdata.sensorError != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_ERROR);
290         addChannel(thing, newChannels, sdata.actReasons != null, CHGR_DEVST, CHANNEL_DEVST_WAKEUP);
291
292         // Gas
293         if (sdata.gasSensor != null) {
294             addChannel(thing, newChannels, sdata.gasSensor.selfTestState != null, CHGR_DEVST, CHANNEL_DEVST_SELFTTEST);
295             addChannel(thing, newChannels, sdata.gasSensor.sensorState != null, CHANNEL_GROUP_SENSOR,
296                     CHANNEL_SENSOR_SSTATE);
297             addChannel(thing, newChannels, sdata.concentration != null && sdata.concentration.ppm != null,
298                     CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_PPM);
299             addChannel(thing, newChannels, sdata.valves != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VALVE);
300             addChannel(thing, newChannels, sdata.gasSensor.sensorState != null, CHANNEL_GROUP_SENSOR,
301                     CHANNEL_SENSOR_ALARM_STATE);
302         }
303
304         // Battery
305         if (sdata.bat != null) {
306             addChannel(thing, newChannels, sdata.bat.value != null, CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LEVEL);
307             addChannel(thing, newChannels, sdata.bat.value != null, CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LOW);
308         }
309
310         addChannel(thing, newChannels, true, profile.getControlGroup(0), CHANNEL_LAST_UPDATE);
311         return newChannels;
312     }
313
314     private static void addChannel(Thing thing, Map<String, Channel> newChannels, boolean supported, String group,
315             String channelName) throws IllegalArgumentException {
316         if (supported) {
317             final String channelId = group + "#" + channelName;
318             final ShellyChannel channelDef = getDefinition(channelId);
319             final ChannelUID channelUID = new ChannelUID(thing.getUID(), channelId);
320             final ChannelTypeUID channelTypeUID = channelDef.typeId.contains("system:")
321                     ? new ChannelTypeUID(channelDef.typeId)
322                     : new ChannelTypeUID(BINDING_ID, channelDef.typeId);
323             Channel channel = ChannelBuilder.create(channelUID, channelDef.itemType).withType(channelTypeUID).build();
324             newChannels.put(channelId, channel);
325         }
326     }
327
328     public class ShellyChannel {
329         private final ShellyTranslationProvider messages;
330         public String group = "";
331         public String groupLabel = "";
332         public String groupDescription = "";
333
334         public String channel = "";
335         public String label = "";
336         public String description = "";
337         public String itemType = "";
338         public String typeId = "";
339         public String category = "";
340         public Set<String> tags = new HashSet<>();
341
342         public ShellyChannel(ShellyTranslationProvider messages, String group, String channel, String typeId,
343                 String itemType, String... category) {
344             this.messages = messages;
345             this.group = group;
346             this.channel = channel;
347             this.itemType = itemType;
348             this.typeId = typeId;
349
350             groupLabel = getText(PREFIX_GROUP + group + SUFFIX_LABEL);
351             groupDescription = getText(PREFIX_GROUP + group + SUFFIX_DESCR);
352             label = getText(PREFIX_CHANNEL + channel + SUFFIX_LABEL);
353             description = getText(PREFIX_CHANNEL + channel + SUFFIX_DESCR);
354         }
355
356         public String getChanneId() {
357             return group + "#" + channel;
358         }
359
360         private String getText(String key) {
361             String text = messages.get(key);
362             return text != null ? text : "";
363         }
364     }
365
366     public static class ChannelMap {
367         private final Map<String, ShellyChannel> map = new LinkedHashMap<>();
368
369         private ChannelMap add(ShellyChannel def) {
370             map.put(def.getChanneId(), def);
371             return this;
372         }
373
374         public ShellyChannel get(String channelName) throws IllegalArgumentException {
375             ShellyChannel def = null;
376             if (channelName.contains("#")) {
377                 def = map.get(channelName);
378             }
379             for (HashMap.Entry<String, ShellyChannel> entry : map.entrySet()) {
380                 if (entry.getValue().channel.contains("#" + channelName)) {
381                     def = entry.getValue();
382                     break;
383                 }
384             }
385
386             if (def == null) {
387                 throw new IllegalArgumentException("Channel definition for " + channelName + " not found!");
388             }
389
390             return def;
391         }
392     }
393 }