From 25ea32e2bfe5025bf1b7010cb594ce67993a4320 Mon Sep 17 00:00:00 2001 From: Boris Krivonog Date: Tue, 8 Dec 2020 00:05:29 +0100 Subject: [PATCH] [nikobus] - added new trigger channels for Nikobus push button (#9166) * Added new trigger channels for Nikobus push button. Signed-off-by: Boris Krivonog --- bundles/org.openhab.binding.nikobus/README.md | 24 ++ .../internal/NikobusBindingConstants.java | 2 + .../handler/NikobusPushButtonHandler.java | 366 ++++++++++++------ .../main/resources/OH-INF/config/config.xml | 38 ++ .../resources/OH-INF/thing/dimmer-module.xml | 63 +++ .../main/resources/OH-INF/thing/pc-link.xml | 26 ++ .../resources/OH-INF/thing/push-button.xml | 56 +++ .../OH-INF/thing/rollershutter-module.xml | 45 +++ .../resources/OH-INF/thing/switch-module.xml | 63 +++ .../resources/OH-INF/thing/thing-types.xml | 228 ----------- 10 files changed, 572 insertions(+), 339 deletions(-) create mode 100644 bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/config/config.xml create mode 100644 bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/dimmer-module.xml create mode 100644 bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/pc-link.xml create mode 100644 bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/push-button.xml create mode 100644 bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/rollershutter-module.xml create mode 100644 bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/switch-module.xml delete mode 100644 bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/thing-types.xml diff --git a/bundles/org.openhab.binding.nikobus/README.md b/bundles/org.openhab.binding.nikobus/README.md index c23fe401f..c5982f629 100644 --- a/bundles/org.openhab.binding.nikobus/README.md +++ b/bundles/org.openhab.binding.nikobus/README.md @@ -170,6 +170,29 @@ Thing push-button pb1 [ address = "28092A", impactedModules = "switch-module:s1: In addition to the status requests triggered by button presses, there is also a scheduled status update interval defined by the `refreshInterval` parameter and explained above. +#### Push Button Trigger Channels + +Beside receiving a status update (ON) when a physical Nikobus push button is pressed (and kept pressed), additional triggers can be added and configured to determine how press&hold of a physical push button should generate trigger events. Two types of trigger channels are supported: + +* filter trigger and +* button trigger. + +##### Filter Trigger + +* `command` - command to be send, +* `delay` - a required delay in milliseconds defining how much time must a button be pressed before an initial trigger event is fired, +* `period` - optional time in milliseconds between successive triggers. + +Examples: + +* `command = PRESSED, delay = 0, period = ` - triggers `PRESSED` event immediatelly when Nikobus button is pressed and is not triggered anymore while holding down the button, +* `command = INCREMENT, delay = 1000, period = 500` - triggers initial `INCREMENT` event after 1 second and then every half a second while holding down the button. + + +##### Button Trigger + +`threshold` - a required long-press threshold in miliseconds. Defines how long must a button be pressed before a long press event is triggered - pressing&holding a Nikobus push-button for `threshold` or more miliseconds will trigger long press event, otherwise a short press event will be triggered. + ## Discovery Pressing a physical Nikobus push-button will generate a new inbox entry with an exception of buttons already discovered or setup. @@ -245,6 +268,7 @@ so ``` Above example `14E7F4:3` would give: + * for 4 buttons switch - push button A, * for 8 buttons switch - push button 2A. diff --git a/bundles/org.openhab.binding.nikobus/src/main/java/org/openhab/binding/nikobus/internal/NikobusBindingConstants.java b/bundles/org.openhab.binding.nikobus/src/main/java/org/openhab/binding/nikobus/internal/NikobusBindingConstants.java index 15a38baf7..1cc7bdade 100644 --- a/bundles/org.openhab.binding.nikobus/src/main/java/org/openhab/binding/nikobus/internal/NikobusBindingConstants.java +++ b/bundles/org.openhab.binding.nikobus/src/main/java/org/openhab/binding/nikobus/internal/NikobusBindingConstants.java @@ -37,6 +37,8 @@ public class NikobusBindingConstants { // List of all Channel ids public static final String CHANNEL_BUTTON = "button"; + public static final String CHANNEL_TRIGGER_FILTER = "trigger-filter"; + public static final String CHANNEL_TRIGGER_BUTTON = "trigger-button"; public static final String CHANNEL_OUTPUT_PREFIX = "output-"; // Configuration parameters diff --git a/bundles/org.openhab.binding.nikobus/src/main/java/org/openhab/binding/nikobus/internal/handler/NikobusPushButtonHandler.java b/bundles/org.openhab.binding.nikobus/src/main/java/org/openhab/binding/nikobus/internal/handler/NikobusPushButtonHandler.java index bbf7244ea..ef8e6653b 100644 --- a/bundles/org.openhab.binding.nikobus/src/main/java/org/openhab/binding/nikobus/internal/handler/NikobusPushButtonHandler.java +++ b/bundles/org.openhab.binding.nikobus/src/main/java/org/openhab/binding/nikobus/internal/handler/NikobusPushButtonHandler.java @@ -15,9 +15,8 @@ package org.openhab.binding.nikobus.internal.handler; import static org.openhab.binding.nikobus.internal.NikobusBindingConstants.*; import static org.openhab.binding.nikobus.internal.protocol.SwitchModuleGroup.*; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -29,13 +28,16 @@ import org.openhab.binding.nikobus.internal.utils.Utils; import org.openhab.core.common.AbstractUID; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.CommonTriggerEvents; import org.openhab.core.thing.Thing; import org.openhab.core.thing.ThingStatus; import org.openhab.core.thing.ThingStatusDetail; import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingUID; import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.type.ChannelTypeUID; import org.openhab.core.types.Command; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,6 +49,176 @@ import org.slf4j.LoggerFactory; */ @NonNullByDefault public class NikobusPushButtonHandler extends NikobusBaseThingHandler { + private static final String END_OF_TRANSMISSION = "\r#E1"; + private final Logger logger = LoggerFactory.getLogger(NikobusPushButtonHandler.class); + private final List impactedModules = new CopyOnWriteArrayList<>(); + private final List triggerProcessors = new CopyOnWriteArrayList<>(); + private @Nullable Future requestUpdateFuture; + + public NikobusPushButtonHandler(Thing thing) { + super(thing); + } + + @Override + public void initialize() { + super.initialize(); + + if (thing.getStatus() == ThingStatus.OFFLINE) { + return; + } + + impactedModules.clear(); + triggerProcessors.clear(); + + Object impactedModulesObject = getConfig().get(CONFIG_IMPACTED_MODULES); + if (impactedModulesObject != null) { + try { + Bridge bridge = getBridge(); + if (bridge == null) { + throw new IllegalArgumentException("Bridge does not exist!"); + } + + ThingUID bridgeUID = thing.getBridgeUID(); + if (bridgeUID == null) { + throw new IllegalArgumentException("Unable to read BridgeUID!"); + } + + String[] impactedModulesString = impactedModulesObject.toString().split(","); + for (String impactedModuleString : impactedModulesString) { + ImpactedModuleUID impactedModuleUID = new ImpactedModuleUID(impactedModuleString.trim()); + ThingTypeUID thingTypeUID = new ThingTypeUID(bridgeUID.getBindingId(), + impactedModuleUID.getThingTypeId()); + ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, impactedModuleUID.getThingId()); + + if (!bridge.getThings().stream().anyMatch(thing -> thing.getUID().equals(thingUID))) { + throw new IllegalArgumentException( + "Impacted module " + thingUID + " not found for '" + impactedModuleString + "'"); + } + + impactedModules.add(new ImpactedModule(thingUID, impactedModuleUID.getGroup())); + } + } catch (RuntimeException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); + return; + } + + logger.debug("Impacted modules for {} = {}", thing.getUID(), impactedModules); + } + + try { + for (Channel channel : thing.getChannels()) { + TriggerProcessor processor = createTriggerProcessor(channel); + if (processor != null) { + triggerProcessors.add(processor); + } + } + } catch (RuntimeException e) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); + return; + } + + logger.debug("Trigger channels for {} = {}", thing.getUID(), triggerProcessors); + + NikobusPcLinkHandler pcLink = getPcLink(); + if (pcLink != null) { + pcLink.addListener(getAddress(), this::commandReceived); + } + } + + @Override + public void dispose() { + super.dispose(); + + Utils.cancel(requestUpdateFuture); + requestUpdateFuture = null; + + NikobusPcLinkHandler pcLink = getPcLink(); + if (pcLink != null) { + pcLink.removeListener(getAddress()); + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.debug("handleCommand '{}' '{}'", channelUID, command); + + if (!CHANNEL_BUTTON.equals(channelUID.getId())) { + return; + } + + // Whenever the button receives an ON command, + // we send a simulated button press to the Nikobus. + if (command == OnOffType.ON) { + NikobusPcLinkHandler pcLink = getPcLink(); + if (pcLink != null) { + pcLink.sendCommand(new NikobusCommand(getAddress() + END_OF_TRANSMISSION)); + } + } + } + + private void commandReceived() { + if (thing.getStatus() != ThingStatus.ONLINE) { + updateStatus(ThingStatus.ONLINE); + } + + updateState(CHANNEL_BUTTON, OnOffType.ON); + + if (!triggerProcessors.isEmpty()) { + long currentTimeMillis = System.currentTimeMillis(); + triggerProcessors.forEach(processor -> processor.process(currentTimeMillis)); + } + + if (!impactedModules.isEmpty()) { + Utils.cancel(requestUpdateFuture); + requestUpdateFuture = scheduler.schedule(this::update, 400, TimeUnit.MILLISECONDS); + } + } + + private void update() { + for (ImpactedModule module : impactedModules) { + NikobusModuleHandler switchModule = getModuleWithId(module.getThingUID()); + if (switchModule != null) { + switchModule.requestStatus(module.getGroup()); + } + } + } + + private @Nullable NikobusModuleHandler getModuleWithId(ThingUID thingUID) { + Bridge bridge = getBridge(); + if (bridge == null) { + return null; + } + + Thing thing = bridge.getThing(thingUID); + if (thing == null) { + return null; + } + + ThingHandler thingHandler = thing.getHandler(); + if (thingHandler instanceof NikobusModuleHandler) { + return (NikobusModuleHandler) thingHandler; + } + return null; + } + + @Override + protected String getAddress() { + return "#N" + super.getAddress(); + } + + private @Nullable TriggerProcessor createTriggerProcessor(Channel channel) { + ChannelTypeUID channelTypeUID = channel.getChannelTypeUID(); + if (channelTypeUID != null) { + switch (channelTypeUID.getId()) { + case CHANNEL_TRIGGER_FILTER: + return new TriggerFilter(channel); + case CHANNEL_TRIGGER_BUTTON: + return new TriggerButton(channel); + } + } + return null; + } + private static class ImpactedModule { private final ThingUID thingUID; private final SwitchModuleGroup group; @@ -99,139 +271,111 @@ public class NikobusPushButtonHandler extends NikobusBaseThingHandler { } } - private static final String END_OF_TRANSMISSION = "\r#E1"; - private final Logger logger = LoggerFactory.getLogger(NikobusPushButtonHandler.class); - private final List impactedModules = Collections.synchronizedList(new ArrayList<>()); - private @Nullable Future requestUpdateFuture; - - public NikobusPushButtonHandler(Thing thing) { - super(thing); + private interface TriggerProcessor { + void process(long currentTimeMillis); } - @Override - public void initialize() { - super.initialize(); + private abstract class AbstractTriggerProcessor implements TriggerProcessor { + private long lastCommandReceivedTimestamp = 0; + protected final ChannelUID channelUID; + protected final Config config; - if (thing.getStatus() == ThingStatus.OFFLINE) { - return; + // Nikobus push button will send a new message on bus every ~50ms so + // lets assume if we haven't received a new message in over 150ms that + // button was released and pressed again. + protected static final long BUTTON_RELEASED_MILIS = 150; + + protected AbstractTriggerProcessor(Class configType, Channel channel) { + this.channelUID = channel.getUID(); + this.config = channel.getConfiguration().as(configType); } - impactedModules.clear(); - - Object impactedModulesObject = getConfig().get(CONFIG_IMPACTED_MODULES); - if (impactedModulesObject != null) { - try { - Bridge bridge = getBridge(); - if (bridge == null) { - throw new IllegalArgumentException("Bridge does not exist!"); - } - - ThingUID bridgeUID = thing.getBridgeUID(); - if (bridgeUID == null) { - throw new IllegalArgumentException("Unable to read BridgeUID!"); - } - - String[] impactedModulesString = impactedModulesObject.toString().split(","); - for (String impactedModuleString : impactedModulesString) { - ImpactedModuleUID impactedModuleUID = new ImpactedModuleUID(impactedModuleString.trim()); - ThingTypeUID thingTypeUID = new ThingTypeUID(bridgeUID.getBindingId(), - impactedModuleUID.getThingTypeId()); - ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, impactedModuleUID.getThingId()); - - if (!bridge.getThings().stream().anyMatch(thing -> thing.getUID().equals(thingUID))) { - throw new IllegalArgumentException( - "Impacted module " + thingUID + " not found for '" + impactedModuleString + "'"); - } - - impactedModules.add(new ImpactedModule(thingUID, impactedModuleUID.getGroup())); - } - } catch (RuntimeException e) { - updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage()); - return; + @Override + public void process(long currentTimeMillis) { + if (Math.abs(currentTimeMillis - lastCommandReceivedTimestamp) > BUTTON_RELEASED_MILIS) { + reset(currentTimeMillis); } - - logger.debug("Impacted modules for {} = {}", thing.getUID(), impactedModules); + lastCommandReceivedTimestamp = currentTimeMillis; + processNext(currentTimeMillis); } - NikobusPcLinkHandler pcLink = getPcLink(); - if (pcLink != null) { - pcLink.addListener(getAddress(), this::commandReceived); - } + abstract protected void reset(long currentTimeMillis); + + abstract protected void processNext(long currentTimeMillis); } - @Override - public void dispose() { - super.dispose(); - - Utils.cancel(requestUpdateFuture); - requestUpdateFuture = null; - - NikobusPcLinkHandler pcLink = getPcLink(); - if (pcLink != null) { - pcLink.removeListener(getAddress()); - } + public static class TriggerButtonConfig { + public int threshold = 1000; } - @Override - public void handleCommand(ChannelUID channelUID, Command command) { - logger.debug("handleCommand '{}' '{}'", channelUID, command); + private class TriggerButton extends AbstractTriggerProcessor { + private long nextLongPressTimestamp = 0; + private @Nullable Future triggerShortPressFuture; - if (!CHANNEL_BUTTON.equals(channelUID.getId())) { - return; + TriggerButton(Channel channel) { + super(TriggerButtonConfig.class, channel); } - // Whenever the button receives an ON command, - // we send a simulated button press to the Nikobus. - if (command == OnOffType.ON) { - NikobusPcLinkHandler pcLink = getPcLink(); - if (pcLink != null) { - pcLink.sendCommand(new NikobusCommand(getAddress() + END_OF_TRANSMISSION)); + @Override + protected void reset(long currentTimeMillis) { + nextLongPressTimestamp = currentTimeMillis + config.threshold; + } + + @Override + protected void processNext(long currentTimeMillis) { + if (currentTimeMillis < nextLongPressTimestamp) { + Utils.cancel(triggerShortPressFuture); + triggerShortPressFuture = scheduler.schedule( + () -> triggerChannel(channelUID, CommonTriggerEvents.SHORT_PRESSED), BUTTON_RELEASED_MILIS, + TimeUnit.MILLISECONDS); + } else if (nextLongPressTimestamp != 0) { + Utils.cancel(triggerShortPressFuture); + nextLongPressTimestamp = 0; + triggerChannel(channelUID, CommonTriggerEvents.LONG_PRESSED); } } - } - private void commandReceived() { - if (thing.getStatus() != ThingStatus.ONLINE) { - updateStatus(ThingStatus.ONLINE); - } - - updateState(CHANNEL_BUTTON, OnOffType.ON); - - if (!impactedModules.isEmpty()) { - Utils.cancel(requestUpdateFuture); - requestUpdateFuture = scheduler.schedule(this::update, 400, TimeUnit.MILLISECONDS); + @Override + public String toString() { + return "TriggerButton '" + channelUID + "', config: threshold = " + config.threshold; } } - private void update() { - for (ImpactedModule module : impactedModules) { - NikobusModuleHandler switchModule = getModuleWithId(module.getThingUID()); - if (switchModule != null) { - switchModule.requestStatus(module.getGroup()); + public static class TriggerFilterConfig { + public @Nullable String command; + public int delay = 0; + public int period = -1; + } + + private class TriggerFilter extends AbstractTriggerProcessor { + private long nextTriggerTimestamp = 0; + + TriggerFilter(Channel channel) { + super(TriggerFilterConfig.class, channel); + } + + @Override + protected void reset(long currentTimeMillis) { + nextTriggerTimestamp = currentTimeMillis + config.delay; + } + + @Override + protected void processNext(long currentTimeMillis) { + if (currentTimeMillis >= nextTriggerTimestamp) { + nextTriggerTimestamp = (config.period < 0) ? Long.MAX_VALUE : currentTimeMillis + config.period; + String command = config.command; + if (command != null) { + triggerChannel(channelUID, command); + } else { + triggerChannel(channelUID); + } } } - } - private @Nullable NikobusModuleHandler getModuleWithId(ThingUID thingUID) { - Bridge bridge = getBridge(); - if (bridge == null) { - return null; + @Override + public String toString() { + return "TriggerFilter '" + channelUID + "', config: command = '" + config.command + "', delay = " + + config.delay + ", period = " + config.period; } - - Thing thing = bridge.getThing(thingUID); - if (thing == null) { - return null; - } - - ThingHandler thingHandler = thing.getHandler(); - if (thingHandler instanceof NikobusModuleHandler) { - return (NikobusModuleHandler) thingHandler; - } - return null; - } - - @Override - protected String getAddress() { - return "#N" + super.getAddress(); } } diff --git a/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/config/config.xml new file mode 100644 index 000000000..7418e61c5 --- /dev/null +++ b/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/config/config.xml @@ -0,0 +1,38 @@ + + + + + + + Command to send + + + + Delay in milliseconds before triggered + 0 + + + + Time in milliseconds between successive triggers + + + + + + + Long-press threshold in milliseconds + 1000 + + + + + + + The Nikobus address of the module + + + + diff --git a/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/dimmer-module.xml b/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/dimmer-module.xml new file mode 100644 index 000000000..86f7e0044 --- /dev/null +++ b/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/dimmer-module.xml @@ -0,0 +1,63 @@ + + + + + + + + + + Nikobus Dimmer module + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Dimmer + + Dimmer Module's Output + + + diff --git a/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/pc-link.xml b/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/pc-link.xml new file mode 100644 index 000000000..3825d1590 --- /dev/null +++ b/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/pc-link.xml @@ -0,0 +1,26 @@ + + + + + + PC-Link via serial connection + + + + + serial-port + false + The serial port used to connect to the Nikobus PC Link. + + + 60 + + Refresh interval in seconds. + + + + + diff --git a/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/push-button.xml b/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/push-button.xml new file mode 100644 index 000000000..f272ea7fe --- /dev/null +++ b/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/push-button.xml @@ -0,0 +1,56 @@ + + + + + + + + + + A single push button + + + + + + + + + The Nikobus address of the push-button. + + + + Comma separated list of impacted modules, i.e. switch-module:s1:1 + + + + + + Switch + + Fires when the button is pressed + + + + trigger + + + + + + + trigger + + + + + + + + + + + diff --git a/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/rollershutter-module.xml b/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/rollershutter-module.xml new file mode 100644 index 000000000..258f5cd55 --- /dev/null +++ b/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/rollershutter-module.xml @@ -0,0 +1,45 @@ + + + + + + + + + + Nikobus Rollershutter module + + + + + + + + + + + + + + + + + + + + + + + + + + + Rollershutter + + Rollershutter Module's Output + + + diff --git a/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/switch-module.xml b/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/switch-module.xml new file mode 100644 index 000000000..419ce16b9 --- /dev/null +++ b/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/switch-module.xml @@ -0,0 +1,63 @@ + + + + + + + + + + Nikobus Switch module + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Switch + + Switch Module's Output + + + diff --git a/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/thing-types.xml deleted file mode 100644 index 8c3d4ed5c..000000000 --- a/bundles/org.openhab.binding.nikobus/src/main/resources/OH-INF/thing/thing-types.xml +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - PC-Link via serial connection - - - - - serial-port - false - The serial port used to connect to the Nikobus PC Link. - - - 60 - - Refresh interval in seconds. - - - - - - - - - - - A single push button - - - - - - - - - The Nikobus address of the module - - - - Comma separated list of impacted modules, i.e. switch-module:s1:1 - - - - - - Switch - - Fires when the button is pressed - - - - - The Nikobus address of the module - - - - - - - - - - - Nikobus Switch module - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The Nikobus address of the module - - - - - - - - - - - Nikobus Dimmer module - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The Nikobus address of the module - - - - - - - - - - - Nikobus Rollershutter module - - - - - - - - - - - - - - - - - - - - - - - - - - The Nikobus address of the module - - - - - - Switch - - Switch Module's Output - - - - Dimmer - - Dimmer Module's Output - - - - Rollershutter - - Rollershutter Module's Output - - -