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.binding.shelly.internal.provider;
15 import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
16 import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.LinkedHashMap;
22 import java.util.Optional;
25 import javax.measure.Unit;
27 import org.eclipse.jdt.annotation.NonNullByDefault;
28 import org.eclipse.jdt.annotation.Nullable;
29 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyControlRoller;
30 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyInputState;
31 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsDimmer;
32 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsEMeter;
33 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsGlobal;
34 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsMeter;
35 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsRelay;
36 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsRgbwLight;
37 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsStatus;
38 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusLightChannel;
39 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusRelay;
40 import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusSensor;
41 import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
42 import org.openhab.binding.shelly.internal.handler.ShellyBaseHandler;
43 import org.openhab.core.thing.Channel;
44 import org.openhab.core.thing.ChannelUID;
45 import org.openhab.core.thing.Thing;
46 import org.openhab.core.thing.binding.builder.ChannelBuilder;
47 import org.openhab.core.thing.type.ChannelKind;
48 import org.openhab.core.thing.type.ChannelTypeUID;
49 import org.osgi.service.component.annotations.Activate;
50 import org.osgi.service.component.annotations.Component;
51 import org.osgi.service.component.annotations.Reference;
54 * The {@link ShellyCHANNEL_DEFINITIONSDTO} defines channel information for dynamically created channels. Those will be
55 * added on the first thing status update
57 * @author Markus Michels - Initial contribution
60 @Component(service = ShellyChannelDefinitions.class)
61 public class ShellyChannelDefinitions {
63 public static final String ITEMT_STRING = "String";
64 public static final String ITEMT_NUMBER = "Number";
65 public static final String ITEMT_SWITCH = "Switch";
66 public static final String ITEMT_CONTACT = "Contact";
67 public static final String ITEMT_ROLLER = "Rollershutter";
68 public static final String ITEMT_DIMMER = "Dimmer";
69 public static final String ITEMT_LOCATION = "Location";
70 public static final String ITEMT_DATETIME = "DateTime";
71 public static final String ITEMT_TEMP = "Number:Temperature";
72 public static final String ITEMT_LUX = "Number:Illuminance";
73 public static final String ITEMT_POWER = "Number:Power";
74 public static final String ITEMT_ENERGY = "Number:Energy";
75 public static final String ITEMT_VOLT = "Number:ElectricPotential";
76 public static final String ITEMT_AMP = "Number:ElectricPotential";
77 public static final String ITEMT_ANGLE = "Number:Angle";
78 public static final String ITEMT_DISTANCE = "Number:Length";
79 public static final String ITEMT_SPEED = "Number:Speed";
80 public static final String ITEMT_VOLUME = "Number:Volume";
81 public static final String ITEMT_TIME = "Number:Time";
82 public static final String ITEMT_PERCENT = "Number:Dimensionless";
84 // shortcuts to avoid line breaks (make code more readable)
85 private static final String CHGR_DEVST = CHANNEL_GROUP_DEV_STATUS;
86 private static final String CHGR_RELAY = CHANNEL_GROUP_RELAY_CONTROL;
87 private static final String CHGR_ROLLER = CHANNEL_GROUP_ROL_CONTROL;
88 private static final String CHGR_LIGHT = CHANNEL_GROUP_LIGHT_CONTROL;
89 private static final String CHGR_STATUS = CHANNEL_GROUP_STATUS;
90 private static final String CHGR_METER = CHANNEL_GROUP_METER;
91 private static final String CHGR_SENSOR = CHANNEL_GROUP_SENSOR;
92 private static final String CHGR_BAT = CHANNEL_GROUP_BATTERY;
94 public static final String PREFIX_GROUP = "group-type." + BINDING_ID + ".";
95 public static final String PREFIX_CHANNEL = "channel-type." + BINDING_ID + ".";
97 private static final ChannelMap CHANNEL_DEFINITIONS = new ChannelMap();
100 public ShellyChannelDefinitions(@Reference ShellyTranslationProvider translationProvider) {
101 ShellyTranslationProvider m = translationProvider;
106 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_NAME, "deviceName", ITEMT_STRING))
107 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ITEMP, "deviceTemp", ITEMT_TEMP))
108 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_WAKEUP, "sensorWakeup", ITEMT_STRING))
109 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCUWATTS, "meterAccuWatts", ITEMT_POWER))
110 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCUTOTAL, "meterAccuTotal", ITEMT_POWER))
111 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCURETURNED, "meterAccuReturned", ITEMT_POWER))
112 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_CHARGER, "charger", ITEMT_SWITCH))
113 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_LED_STATUS_DISABLE, "ledStatusDisable", ITEMT_SWITCH))
114 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_LED_POWER_DISABLE, "ledPowerDisable", ITEMT_SWITCH))
115 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_SELFTTEST, "selfTest", ITEMT_STRING))
116 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_UPTIME, "uptime", ITEMT_NUMBER))
117 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_HEARTBEAT, "heartBeat", ITEMT_DATETIME))
118 .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_UPDATE, "updateAvailable", ITEMT_SWITCH))
121 .add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_OUTPUT_NAME, "outputName", ITEMT_STRING))
122 .add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_OUTPUT, "system:power", ITEMT_SWITCH))
123 .add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_INPUT, "inputState", ITEMT_SWITCH))
124 .add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_BUTTON_TRIGGER, "system:button", ITEMT_STRING))
125 .add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_STATUS_EVENTTYPE, "lastEvent", ITEMT_STRING))
126 .add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_STATUS_EVENTCOUNT, "eventCount", ITEMT_NUMBER))
127 .add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_TIMER_AUTOON, "timerAutoOn", ITEMT_TIME))
128 .add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_TIMER_AUTOOFF, "timerAutoOff", ITEMT_TIME))
129 .add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_TIMER_ACTIVE, "timerActive", ITEMT_SWITCH))
132 .add(new ShellyChannel(m, CHANNEL_GROUP_DIMMER_CONTROL, CHANNEL_BRIGHTNESS, "dimmerBrightness",
136 .add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_ROL_CONTROL_CONTROL, "rollerShutter", ITEMT_ROLLER))
137 .add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_ROL_CONTROL_POS, "rollerPosition", ITEMT_DIMMER))
138 .add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_ROL_CONTROL_FAV, "rollerFavorite", ITEMT_NUMBER))
139 .add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_ROL_CONTROL_STATE, "rollerState", ITEMT_STRING))
140 .add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_ROL_CONTROL_STOPR, "rollerStop", ITEMT_STRING))
141 .add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_ROL_CONTROL_SAFETY, "rollerSafety", ITEMT_SWITCH))
142 .add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_INPUT, "inputState", ITEMT_SWITCH))
143 .add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_STATUS_EVENTTYPE, "lastEvent", ITEMT_STRING))
144 .add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_STATUS_EVENTCOUNT, "eventCount", ITEMT_NUMBER))
145 .add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_EVENT_TRIGGER, "system:button", "system:button"))
148 .add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_LIGHT_POWER, "system:power", ITEMT_SWITCH))
149 .add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_INPUT, "inputState", ITEMT_SWITCH))
150 .add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_BUTTON_TRIGGER, "system:button", ITEMT_STRING))
151 .add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_STATUS_EVENTTYPE, "lastEvent", ITEMT_STRING))
152 .add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_STATUS_EVENTCOUNT, "eventCount", ITEMT_NUMBER))
153 .add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_TIMER_AUTOON, "timerAutoOn", ITEMT_TIME))
154 .add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_TIMER_AUTOOFF, "timerAutoOff", ITEMT_TIME))
155 .add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_TIMER_ACTIVE, "timerActive", ITEMT_SWITCH))
158 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_CURRENTWATTS, "meterWatts", ITEMT_POWER))
159 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_TOTALKWH, "meterTotal", ITEMT_ENERGY))
160 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_LASTMIN1, "lastPower1", ITEMT_ENERGY))
161 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_LAST_UPDATE, "lastUpdate", ITEMT_DATETIME))
164 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_TOTALRET, "meterReturned", ITEMT_ENERGY))
165 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_REACTWATTS, "meterReactive", ITEMT_POWER))
166 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_VOLTAGE, "meterVoltage", ITEMT_VOLT))
167 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_CURRENT, "meterCurrent", ITEMT_AMP))
168 .add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_PFACTOR, "meterPowerFactor", ITEMT_NUMBER))
171 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_TEMP, "sensorTemp", ITEMT_TEMP))
172 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_HUM, "sensorHumidity", ITEMT_PERCENT))
173 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_LUX, "sensorLux", ITEMT_LUX))
174 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_ILLUM, "sensorIllumination", ITEMT_STRING))
175 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VOLTAGE, "sensorADC", ITEMT_VOLT))
176 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_CONTACT, "sensorContact", ITEMT_CONTACT))
177 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_SSTATE, "sensorState", ITEMT_STRING))
178 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VIBRATION, "sensorVibration", ITEMT_SWITCH))
179 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_TILT, "sensorTilt", ITEMT_ANGLE))
180 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_MOTION, "sensorMotion", ITEMT_SWITCH))
181 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_MOTION_TS, "motionTimestamp", ITEMT_DATETIME))
182 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_MOTION_ACT, "motionActive", ITEMT_SWITCH))
183 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_FLOOD, "sensorFlood", ITEMT_SWITCH))
184 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_SMOKE, "sensorSmoke", ITEMT_SWITCH))
185 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_PPM, "sensorPPM", ITEMT_NUMBER))
186 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VALVE, "sensorValve", ITEMT_STRING))
187 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_ALARM_STATE, "alarmState", ITEMT_STRING))
188 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_ERROR, "sensorError", ITEMT_STRING))
189 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_SLEEPTIME, "sensorSleepTime", ITEMT_NUMBER))
190 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_LAST_UPDATE, "lastUpdate", ITEMT_DATETIME))
193 .add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_INPUT, "inputState", ITEMT_SWITCH))
194 .add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_STATUS_EVENTTYPE, "lastEvent", ITEMT_STRING))
195 .add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_STATUS_EVENTCOUNT, "eventCount", ITEMT_NUMBER))
196 .add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_BUTTON_TRIGGER, "system:button", ITEMT_STRING))
197 .add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_LAST_UPDATE, "lastUpdate", ITEMT_DATETIME))
199 // Addon with external sensors
200 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP1, "sensorExtTemp", ITEMT_TEMP))
201 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP2, "sensorExtTemp", ITEMT_TEMP))
202 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP3, "sensorExtTemp", ITEMT_TEMP))
203 .add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_HUMIDITY, "sensorExtHum", ITEMT_PERCENT))
206 .add(new ShellyChannel(m, CHGR_BAT, CHANNEL_SENSOR_BAT_LEVEL, "system:battery-level", ITEMT_PERCENT))
207 .add(new ShellyChannel(m, CHGR_BAT, CHANNEL_SENSOR_BAT_LOW, "system:low-battery", ITEMT_SWITCH));
210 public static @Nullable ShellyChannel getDefinition(String channelName) throws IllegalArgumentException {
211 String group = substringBefore(channelName, "#");
212 String channel = substringAfter(channelName, "#");
214 if (group.contains(CHANNEL_GROUP_METER)) {
215 group = CHANNEL_GROUP_METER; // map meter1..n to meter
216 } else if (group.contains(CHANNEL_GROUP_RELAY_CONTROL)) {
217 group = CHANNEL_GROUP_RELAY_CONTROL; // map meter1..n to meter
218 } else if (group.contains(CHANNEL_GROUP_LIGHT_CHANNEL)) {
219 group = CHANNEL_GROUP_LIGHT_CHANNEL;
220 } else if (group.contains(CHANNEL_GROUP_STATUS)) {
221 group = CHANNEL_GROUP_STATUS; // map status1..n to meter
224 if (channel.startsWith(CHANNEL_INPUT)) {
225 channel = CHANNEL_INPUT;
226 } else if (channel.startsWith(CHANNEL_BUTTON_TRIGGER)) {
227 channel = CHANNEL_BUTTON_TRIGGER;
228 } else if (channel.startsWith(CHANNEL_STATUS_EVENTTYPE)) {
229 channel = CHANNEL_STATUS_EVENTTYPE;
230 } else if (channel.startsWith(CHANNEL_STATUS_EVENTCOUNT)) {
231 channel = CHANNEL_STATUS_EVENTCOUNT;
234 String channelId = group + "#" + channel;
235 return CHANNEL_DEFINITIONS.get(channelId);
239 * Auto-create relay channels depending on relay type/mode
241 * @return ArrayList<Channel> of channels to be added to the thing
243 public static Map<String, Channel> createDeviceChannels(final Thing thing, final ShellyDeviceProfile profile,
244 final ShellySettingsStatus status) {
245 Map<String, Channel> add = new LinkedHashMap<>();
247 addChannel(thing, add, profile.settings.name != null, CHGR_DEVST, CHANNEL_DEVST_NAME);
249 if (!profile.isSensor) {
250 // Only some devices report the internal device temp
251 addChannel(thing, add, (status.tmp != null) || (status.temperature != null), CHGR_DEVST,
252 CHANNEL_DEVST_ITEMP);
254 addChannel(thing, add, profile.settings.sleepTime != null, CHGR_SENSOR, CHANNEL_SENSOR_SLEEPTIME);
256 // If device has more than 1 meter the channel accumulatedWatts receives the accumulated value
257 boolean accuChannel = (((status.meters != null) && (status.meters.size() > 1) && !profile.isRoller
258 && !profile.isRGBW2) || ((status.emeters != null && status.emeters.size() > 1)));
259 addChannel(thing, add, accuChannel, CHGR_DEVST, CHANNEL_DEVST_ACCUWATTS);
260 addChannel(thing, add, accuChannel, CHGR_DEVST, CHANNEL_DEVST_ACCUTOTAL);
261 addChannel(thing, add, accuChannel && (status.emeters != null), CHGR_DEVST, CHANNEL_DEVST_ACCURETURNED);
262 addChannel(thing, add, true, CHGR_DEVST, CHANNEL_DEVST_UPDATE);
263 addChannel(thing, add, true, CHGR_DEVST, CHANNEL_DEVST_UPTIME);
264 addChannel(thing, add, true, CHGR_DEVST, CHANNEL_DEVST_HEARTBEAT);
265 addChannel(thing, add, profile.settings.ledPowerDisable != null, CHGR_DEVST, CHANNEL_LED_POWER_DISABLE);
266 addChannel(thing, add, profile.settings.ledPowerDisable != null, CHGR_DEVST, CHANNEL_LED_STATUS_DISABLE); // WiFi
272 * Auto-create relay channels depending on relay type/mode
274 * @return ArrayList<Channel> of channels to be added to the thing
276 public static Map<String, Channel> createRelayChannels(final Thing thing, final ShellyDeviceProfile profile,
277 final ShellyStatusRelay relay, int idx) {
278 Map<String, Channel> add = new LinkedHashMap<>();
279 String group = profile.getControlGroup(idx);
281 ShellySettingsRelay rs = profile.settings.relays.get(idx);
282 addChannel(thing, add, rs.ison != null, group, CHANNEL_OUTPUT);
283 addChannel(thing, add, rs.name != null, group, CHANNEL_OUTPUT_NAME);
284 addChannel(thing, add, rs.autoOn != null, group, CHANNEL_TIMER_AUTOON);
285 addChannel(thing, add, rs.autoOff != null, group, CHANNEL_TIMER_AUTOOFF);
286 addChannel(thing, add, rs.hasTimer != null, group, CHANNEL_TIMER_ACTIVE);
288 // Shelly 1/1PM Addon
289 if (relay.extTemperature != null) {
290 addChannel(thing, add, relay.extTemperature.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP1);
291 addChannel(thing, add, relay.extTemperature.sensor2 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP2);
292 addChannel(thing, add, relay.extTemperature.sensor3 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP3);
294 if (relay.extHumidity != null) {
295 addChannel(thing, add, relay.extHumidity.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENDOR_HUMIDITY);
301 public static Map<String, Channel> createDimmerChannels(final Thing thing, final ShellyDeviceProfile profile,
302 final ShellySettingsStatus dstatus, int idx) {
303 Map<String, Channel> add = new LinkedHashMap<>();
304 String group = profile.getControlGroup(idx);
306 // Shelly Dimmer has an additional brightness channel
307 addChannel(thing, add, profile.isDimmer, group, CHANNEL_BRIGHTNESS);
309 ShellySettingsDimmer ds = profile.settings.dimmers.get(idx);
310 addChannel(thing, add, ds.autoOn != null, group, CHANNEL_TIMER_AUTOON);
311 addChannel(thing, add, ds.autoOff != null, group, CHANNEL_TIMER_AUTOOFF);
315 public static Map<String, Channel> createLightChannels(final Thing thing, final ShellyDeviceProfile profile,
316 final ShellyStatusLightChannel status, int idx) {
317 Map<String, Channel> add = new LinkedHashMap<>();
318 String group = profile.getControlGroup(idx);
320 ShellySettingsRgbwLight light = profile.settings.lights.get(idx);
321 // The is no brightness channel in color mode, so we need a power channel
322 addChannel(thing, add, profile.inColor, group, CHANNEL_LIGHT_POWER);
324 addChannel(thing, add, light.autoOn != null, group, CHANNEL_TIMER_AUTOON);
325 addChannel(thing, add, light.autoOff != null, group, CHANNEL_TIMER_AUTOOFF);
326 addChannel(thing, add, status.hasTimer != null, group, CHANNEL_TIMER_ACTIVE);
330 public static Map<String, Channel> createInputChannels(final Thing thing, final ShellyDeviceProfile profile,
331 final ShellySettingsStatus status, String group) {
332 Map<String, Channel> add = new LinkedHashMap<>();
333 if (status.inputs != null) {
334 // Create channels per input. For devices with more than 1 input (Dimmer, 1L) multiple channel sets are
335 // created by adding the index to the channel name
336 boolean multi = ((profile.numRelays == 1) || profile.isDimmer || profile.isRoller)
337 && (profile.numInputs >= 2);
338 for (int i = 0; i < profile.numInputs; i++) {
339 String suffix = multi ? String.valueOf(i + 1) : "";
340 ShellyInputState input = status.inputs.get(i);
341 addChannel(thing, add, true, group, CHANNEL_INPUT + suffix);
342 if (profile.inButtonMode(i)) {
343 addChannel(thing, add, input.event != null, group, CHANNEL_STATUS_EVENTTYPE + suffix);
344 addChannel(thing, add, input.eventCount != null, group, CHANNEL_STATUS_EVENTCOUNT + suffix);
346 addChannel(thing, add, true, group,
347 (!profile.isRoller ? CHANNEL_BUTTON_TRIGGER + suffix : CHANNEL_EVENT_TRIGGER));
349 } else if (status.input != null) {
350 // old RGBW2 firmware
351 addChannel(thing, add, true, group, CHANNEL_INPUT);
352 addChannel(thing, add, true, group, CHANNEL_BUTTON_TRIGGER);
357 public static Map<String, Channel> createRollerChannels(Thing thing, final ShellyControlRoller roller) {
358 Map<String, Channel> add = new LinkedHashMap<>();
359 addChannel(thing, add, true, CHGR_ROLLER, CHANNEL_ROL_CONTROL_CONTROL);
360 addChannel(thing, add, true, CHGR_ROLLER, CHANNEL_ROL_CONTROL_STATE);
361 addChannel(thing, add, true, CHGR_ROLLER, CHANNEL_EVENT_TRIGGER);
362 addChannel(thing, add, roller.currentPos != null, CHGR_ROLLER, CHANNEL_ROL_CONTROL_POS);
363 addChannel(thing, add, roller.stopReason != null, CHGR_ROLLER, CHANNEL_ROL_CONTROL_STOPR);
364 addChannel(thing, add, roller.safetySwitch != null, CHGR_ROLLER, CHANNEL_ROL_CONTROL_SAFETY);
366 ShellyBaseHandler handler = (ShellyBaseHandler) thing.getHandler();
367 if (handler != null) {
368 ShellySettingsGlobal settings = handler.getProfile().settings;
369 if (getBool(settings.favoritesEnabled) && (settings.favorites != null)) {
370 addChannel(thing, add, roller.currentPos != null, CHGR_ROLLER, CHANNEL_ROL_CONTROL_FAV);
376 public static Map<String, Channel> createMeterChannels(Thing thing, final ShellySettingsMeter meter, String group) {
377 Map<String, Channel> newChannels = new LinkedHashMap<>();
378 addChannel(thing, newChannels, meter.power != null, group, CHANNEL_METER_CURRENTWATTS);
379 addChannel(thing, newChannels, meter.total != null, group, CHANNEL_METER_TOTALKWH);
380 addChannel(thing, newChannels, (meter.counters != null) && (meter.counters[0] != null), group,
381 CHANNEL_METER_LASTMIN1);
382 addChannel(thing, newChannels, meter.timestamp != null, group, CHANNEL_LAST_UPDATE);
386 public static Map<String, Channel> createEMeterChannels(final Thing thing, final ShellySettingsEMeter emeter,
388 Map<String, Channel> newChannels = new LinkedHashMap<>();
389 addChannel(thing, newChannels, emeter.power != null, group, CHANNEL_METER_CURRENTWATTS);
390 addChannel(thing, newChannels, emeter.total != null, group, CHANNEL_METER_TOTALKWH);
391 addChannel(thing, newChannels, emeter.totalReturned != null, group, CHANNEL_EMETER_TOTALRET);
392 addChannel(thing, newChannels, emeter.reactive != null, group, CHANNEL_EMETER_REACTWATTS);
393 addChannel(thing, newChannels, emeter.voltage != null, group, CHANNEL_EMETER_VOLTAGE);
394 addChannel(thing, newChannels, emeter.current != null, group, CHANNEL_EMETER_CURRENT);
395 addChannel(thing, newChannels, emeter.pf != null, group, CHANNEL_EMETER_PFACTOR);
396 addChannel(thing, newChannels, true, group, CHANNEL_LAST_UPDATE);
400 public static Map<String, Channel> createSensorChannels(final Thing thing, final ShellyDeviceProfile profile,
401 final ShellyStatusSensor sdata) {
402 Map<String, Channel> newChannels = new LinkedHashMap<>();
405 addChannel(thing, newChannels, sdata.tmp != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TEMP);
406 addChannel(thing, newChannels, sdata.hum != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_HUM);
407 addChannel(thing, newChannels, sdata.lux != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_LUX);
408 addChannel(thing, newChannels, sdata.lux != null && sdata.lux.illumination != null, CHANNEL_GROUP_SENSOR,
409 CHANNEL_SENSOR_ILLUM);
410 addChannel(thing, newChannels, sdata.flood != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD);
411 addChannel(thing, newChannels, sdata.smoke != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD);
412 addChannel(thing, newChannels, (profile.settings.externalPower != null) || (sdata.charger != null), CHGR_DEVST,
413 CHANNEL_DEVST_CHARGER);
414 addChannel(thing, newChannels,
415 sdata.motion != null || ((sdata.sensor != null) && (sdata.sensor.motion != null)), CHANNEL_GROUP_SENSOR,
416 CHANNEL_SENSOR_MOTION);
417 if (sdata.sensor != null) { // DW, Sense or Motion
418 addChannel(thing, newChannels, sdata.sensor.state != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_CONTACT); // DW/DW2
419 addChannel(thing, newChannels, sdata.sensor.motionActive != null, CHANNEL_GROUP_SENSOR, // Motion
420 CHANNEL_SENSOR_MOTION_ACT);
421 addChannel(thing, newChannels, sdata.sensor.motionTimestamp != null, CHANNEL_GROUP_SENSOR, // Motion
422 CHANNEL_SENSOR_MOTION_TS);
423 addChannel(thing, newChannels, sdata.sensor.vibration != null, CHANNEL_GROUP_SENSOR,
424 CHANNEL_SENSOR_VIBRATION);
426 if (sdata.accel != null) { // DW2
427 addChannel(thing, newChannels, sdata.accel.vibration != null, CHANNEL_GROUP_SENSOR,
428 CHANNEL_SENSOR_VIBRATION);
429 addChannel(thing, newChannels, sdata.accel.tilt != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TILT);
433 if (sdata.gasSensor != null) {
434 addChannel(thing, newChannels, sdata.gasSensor.selfTestState != null, CHGR_DEVST, CHANNEL_DEVST_SELFTTEST);
435 addChannel(thing, newChannels, sdata.gasSensor.sensorState != null, CHANNEL_GROUP_SENSOR,
436 CHANNEL_SENSOR_SSTATE);
437 addChannel(thing, newChannels, sdata.concentration != null && sdata.concentration.ppm != null,
438 CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_PPM);
439 addChannel(thing, newChannels, sdata.valves != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VALVE);
440 addChannel(thing, newChannels, sdata.gasSensor.sensorState != null, CHANNEL_GROUP_SENSOR,
441 CHANNEL_SENSOR_ALARM_STATE);
444 addChannel(thing, newChannels, sdata.adcs != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VOLTAGE); // UNI
447 if (sdata.bat != null) {
448 addChannel(thing, newChannels, sdata.bat.value != null, CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LEVEL);
449 addChannel(thing, newChannels, sdata.bat.value != null, CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LOW);
452 addChannel(thing, newChannels, sdata.sensorError != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_ERROR);
453 addChannel(thing, newChannels, sdata.actReasons != null, CHGR_DEVST, CHANNEL_DEVST_WAKEUP);
454 addChannel(thing, newChannels, true, profile.isButton ? CHANNEL_GROUP_STATUS : CHANNEL_GROUP_SENSOR,
455 CHANNEL_LAST_UPDATE);
459 private static void addChannel(Thing thing, Map<String, Channel> newChannels, boolean supported, String group,
460 String channelName) throws IllegalArgumentException {
462 String channelId = group + "#" + channelName;
463 ChannelUID channelUID = new ChannelUID(thing.getUID(), channelId);
464 ShellyChannel channelDef = getDefinition(channelId);
465 if (channelDef != null) {
466 ChannelTypeUID channelTypeUID = channelDef.typeId.contains("system:")
467 ? new ChannelTypeUID(channelDef.typeId)
468 : new ChannelTypeUID(BINDING_ID, channelDef.typeId);
470 if (channelDef.typeId.equalsIgnoreCase("system:button")) {
471 channel = ChannelBuilder.create(channelUID, null).withKind(ChannelKind.TRIGGER)
472 .withType(channelTypeUID).build();
474 channel = ChannelBuilder.create(channelUID, channelDef.itemType).withType(channelTypeUID).build();
476 newChannels.put(channelId, channel);
481 public class ShellyChannel {
482 private final ShellyTranslationProvider messages;
483 public String group = "";
484 public String groupLabel = "";
485 public String groupDescription = "";
487 public String channel = "";
488 public String label = "";
489 public String description = "";
490 public String itemType = "";
491 public String typeId = "";
492 public String category = "";
493 public Set<String> tags = new HashSet<>();
494 public @Nullable Unit<?> unit;
495 public Optional<Integer> min = Optional.empty();
496 public Optional<Integer> max = Optional.empty();
497 public Optional<Integer> step = Optional.empty();
498 public Optional<String> pattern = Optional.empty();
500 public ShellyChannel(ShellyTranslationProvider messages, String group, String channel, String typeId,
501 String itemType, String... category) {
502 this.messages = messages;
504 this.channel = channel;
505 this.itemType = itemType;
506 this.typeId = typeId;
508 groupLabel = getText(PREFIX_GROUP + group + ".label");
509 groupDescription = getText(PREFIX_GROUP + group + ".description");
510 label = getText(PREFIX_CHANNEL + channel + ".label");
511 description = getText(PREFIX_CHANNEL + channel + ".description");
514 public String getChanneId() {
515 return group + "#" + channel;
518 public String getGroupLabel() {
519 return getGroupAttribute("group");
522 public String getGroupDescription() {
523 return getGroupAttribute("group");
526 public String getLabel() {
527 return getChannelAttribute("label");
530 public String getDescription() {
531 return getChannelAttribute("description");
534 public boolean getAdvanced() {
535 String attr = getChannelAttribute("advanced");
536 return attr.isEmpty() ? false : Boolean.valueOf(attr);
539 public boolean getReadyOnly() {
540 String attr = getChannelAttribute("advanced");
541 return attr.isEmpty() ? false : Boolean.valueOf(attr);
544 public String getCategory() {
545 return getChannelAttribute("category");
548 public String getMin() {
549 return getChannelAttribute("min");
552 public String getMax() {
553 return getChannelAttribute("max");
556 public String getStep() {
557 return getChannelAttribute("step");
560 public String getPattern() {
561 return getChannelAttribute("pattern");
564 public String getGroupAttribute(String attribute) {
565 String key = PREFIX_GROUP + group + "." + attribute;
566 String value = messages.getText(key);
567 return value != null && !value.equals(key) ? value : "";
570 public String getChannelAttribute(String attribute) {
571 String key = PREFIX_CHANNEL + channel + "." + attribute;
572 String value = messages.getText(key);
573 return value != null && !value.equals(key) ? value : "";
576 private String getText(String key) {
577 String text = messages.get(key);
578 return text != null ? text : "";
582 public static class ChannelMap {
583 private final Map<String, ShellyChannel> map = new HashMap<>();
585 private ChannelMap add(ShellyChannel def) {
586 map.put(def.getChanneId(), def);
590 public ShellyChannel get(String channelName) throws IllegalArgumentException {
591 ShellyChannel def = null;
592 if (channelName.contains("#")) {
593 def = map.get(channelName);
598 for (HashMap.Entry<String, ShellyChannel> entry : map.entrySet()) {
599 if (entry.getValue().channel.contains("#" + channelName)) {
600 def = entry.getValue();
606 throw new IllegalArgumentException("Channel definition for " + channelName + " not found!");