diff --git a/CODEOWNERS b/CODEOWNERS
index a74a451fe9807..5f1c6a0a1ccc3 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -67,6 +67,7 @@
/bundles/org.openhab.binding.digitalstrom/ @MichaelOchel @msiegele
/bundles/org.openhab.binding.dlinksmarthome/ @MikeJMajor
/bundles/org.openhab.binding.dmx/ @openhab/add-ons-maintainers
+/bundles/org.openhab.binding.dominoswiss/ @Friesoch
/bundles/org.openhab.binding.doorbird/ @mhilbush
/bundles/org.openhab.binding.draytonwiser/ @andrew-schofield
/bundles/org.openhab.binding.dscalarm/ @RSStephens
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index 67288274817e9..c2c284c08eb38 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -321,6 +321,11 @@
org.openhab.binding.dmx
${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.dominoswiss
+ ${project.version}
+
org.openhab.addons.bundles
org.openhab.binding.doorbird
diff --git a/bundles/org.openhab.binding.dominoswiss/NOTICE b/bundles/org.openhab.binding.dominoswiss/NOTICE
new file mode 100644
index 0000000000000..38d625e349232
--- /dev/null
+++ b/bundles/org.openhab.binding.dominoswiss/NOTICE
@@ -0,0 +1,13 @@
+This content is produced and maintained by the openHAB project.
+
+* Project home: https://www.openhab.org
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/openhab/openhab-addons
diff --git a/bundles/org.openhab.binding.dominoswiss/README.md b/bundles/org.openhab.binding.dominoswiss/README.md
new file mode 100644
index 0000000000000..fb2363d09ff33
--- /dev/null
+++ b/bundles/org.openhab.binding.dominoswiss/README.md
@@ -0,0 +1,100 @@
+# Dominoswiss Binding
+
+This binding allows the control of rollershutters, using an eGate as gateway and Dominoswiss radio receivers.
+The eGate-gateway is connected via ethernet to openHAB and sends its commands via radio to all rollershutters.
+See https://www.brelag.com/ for more information.
+
+
+## Supported Things
+
+eGate: Dominoswiss eGate Server. The eGate is the gateway which sends the commands to the connected rollershutters. The bridge-type ID is egate.
+Blind: represents one rollershutter, that can be controlled via eGate. The thing-type ID is blind.
+
+
+## Discovery
+
+Unfortunately no automatic discovery is possible due to protocol restrictions.
+Every rollershutter must be known by eGate and can be called by it's number of storage-place on eGate gateway.
+
+
+
+## Thing Configuration
+
+The bridge "eGate" has one channel "getconfig" which returns the config of this bridge.
+The eGate is configured with both an `ipAddress` and a port.
+
+|Property|Default|Required|Description|
+|--------|-------|--------|-----------|
+|ipAddress|none|Yes|The IP or host name of the Dominoswiss EGate Serve|
+|port|1318|Yes|Port interface of the Dominoswiss EGate Server|
+
+```
+Bridge dominoswiss:egate:myeGate "My eGate Server" @ "Home" [ ipAddres="localhost", port=5700 ]
+```
+
+
+The thing blind represents one blind on the eGate. Each blind is represented by an id set on your eGate.
+
+```
+Thing blind officeBlind "Office" @ "1stFloor" [ id="1"]
+```
+
+The blind-Thing has the following channels:
+
+|Channel Type ID|Item Type|Description|
+|---------------|---------|-----------|
+|pulseUp|Rollershutter|sends one pulse up to this blind.|
+|pulseDown|Rollershutter|sends one pulse down to this blind|
+|continuousUp|Rollershutter|sends a continuous up to this blind. The blind will automatically stop as it is fully up.|
+|continuousDown|Rollershutter|send a continous down to this blind. The blind will automatically stop as it is fully down.|
+|stop|Rollershutter|stop the action of the blind. The command will be imadiatly sent to the blind.|
+|shutter|Rollershutter|this is used to bind the channel to the shutter item. There are no further rules needed this channel will handel the up/down/stop commands. See example for usage.|
+|tilt|Rollershutter|same as shutter, this will handel all up/down/stop - tilt commands to be used with the shutter-item.|
+|tiltUp|Rollershutter|sends 3 pulse-up commands to this blind to tilt the blind. If your blind needs more than 3 pulse-up, create a rule yourself with three pluse-up commands. Between each pulse-up you should wait 150ms to let the command be processed.
+|tiltDown|Rollershutter|sends 3 pulse-down commands to this blind to tilt the blind. If your blind needs more than 3 pulse-down, create a rule yourself with three pluse-down commands. Between each pulse-down you should wait 150ms to let the command be processed. |
+
+## Full Example
+
+Sample things file:
+
+```
+Bridge dominoswiss:egate:myeGate "My eGate Server" @ "Home" [ ipAddres="localhost", port="5500" ]
+{
+ Thing blind officeBlind "Office" @ "1stFloor" [ id="1"]
+ Thing blind diningRoomBlind "Dining Room" @ "EG" [id="2"]
+ Thing blind kitchenBlind "Kitchen" @ "EG" [id="12"]
+}
+```
+
+Sample items file:
+
+```
+Rollershutter OfficeBlindShutter "Office blind" (g_blinds) { channel="dominoswiss:blind:myeGate:officeBlind:shutter"}
+
+Rollershutter OfficeBlindShutterTilt "Tilt Office" (g_blinds_tilt) { channel="dominoswiss:blind:meGgate:bueroBlind:tilt"}
+
+```
+
+Sample sitemap file
+
+```
+Switch item=OfficeBlindShutter
+Switch item=OfficeBlindShutterTilt
+```
+
+Sample rule file
+
+This example moves the blind of the office up as the sun passed 110 azimuth (so the sun is no longer shining directly into the office).
+
+```
+rule "OneSide up"
+when
+ Item Azimuth changed
+then
+ val azimuth = Math::round((Azimuth.state as DecimalType).intValue)
+ if (azimuth == 110)
+ {
+ OfficeBlindShutter.sendCommand(UP)
+ }
+end
+```
diff --git a/bundles/org.openhab.binding.dominoswiss/pom.xml b/bundles/org.openhab.binding.dominoswiss/pom.xml
new file mode 100644
index 0000000000000..b5c5b3cd2e0fd
--- /dev/null
+++ b/bundles/org.openhab.binding.dominoswiss/pom.xml
@@ -0,0 +1,15 @@
+
+
+
+ 4.0.0
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 3.2.0-SNAPSHOT
+
+ org.openhab.binding.dominoswiss
+
+ openHAB Add-ons :: Bundles :: Dominoswiss Binding
+
+
diff --git a/bundles/org.openhab.binding.dominoswiss/src/main/feature/feature.xml b/bundles/org.openhab.binding.dominoswiss/src/main/feature/feature.xml
new file mode 100644
index 0000000000000..61f2c28b1fe76
--- /dev/null
+++ b/bundles/org.openhab.binding.dominoswiss/src/main/feature/feature.xml
@@ -0,0 +1,9 @@
+
+
+ mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features
+
+
+ openhab-runtime-base
+ mvn:org.openhab.addons.bundles/org.openhab.binding.dominoswiss/${project.version}
+
+
diff --git a/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/BlindConfig.java b/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/BlindConfig.java
new file mode 100644
index 0000000000000..0deb53a1866ac
--- /dev/null
+++ b/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/BlindConfig.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.dominoswiss.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * Configuration of a blind.
+ *
+ * @author Frieso Aeschbacher - Initial contribution
+ */
+@NonNullByDefault
+public class BlindConfig {
+
+ /*
+ * The ID of that blind
+ */
+ public String id = "";
+}
diff --git a/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/BlindHandler.java b/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/BlindHandler.java
new file mode 100644
index 0000000000000..a2a1a140311f4
--- /dev/null
+++ b/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/BlindHandler.java
@@ -0,0 +1,186 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.dominoswiss.internal;
+
+import static org.openhab.binding.dominoswiss.internal.DominoswissBindingConstants.*;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link BlindHandler} is responsible for handling commands, which are
+ * sent to one of the channels.The class defines common constants, which are
+ * used across the whole binding
+ *
+ * @author Frieso Aeschbacher - Initial contribution
+ */
+@NonNullByDefault
+public class BlindHandler extends BaseThingHandler {
+
+ private Logger logger = LoggerFactory.getLogger(BlindHandler.class);
+
+ private @Nullable EGateHandler dominoswissHandler;
+
+ private String id = "";
+
+ public BlindHandler(Thing thing) {
+ super(thing);
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ logger.debug("Blind got command: {} and ChannelUID: {} ", command.toFullString(),
+ channelUID.getIdWithoutGroup());
+ Bridge bridge = getBridge();
+ EGateHandler localDominoswissHandler = dominoswissHandler;
+ if (bridge != null) {
+ localDominoswissHandler = (EGateHandler) bridge.getHandler();
+ }
+ if (localDominoswissHandler == null) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, "EGate not available");
+ logger.debug("Blind thing {} has no server configured, ignoring command: {}", getThing().getUID(), command);
+ return;
+ }
+
+ // Some of the code below is not designed to handle REFRESH
+ if (command == RefreshType.REFRESH) {
+ return;
+ }
+ switch (channelUID.getIdWithoutGroup()) {
+ case CHANNEL_PULSEUP:
+ if (command instanceof Number) {
+ localDominoswissHandler.pulseUp(id);
+ }
+ break;
+ case CHANNEL_PULSEDOWN:
+ if (command instanceof Number) {
+ localDominoswissHandler.pulseDown(id);
+ }
+ break;
+ case CHANNEL_CONTINOUSUP:
+ if (command instanceof Number) {
+ localDominoswissHandler.continuousUp(id);
+ }
+ break;
+ case CHANNEL_CONTINOUSDOWN:
+ if (command instanceof Number) {
+ localDominoswissHandler.continuousDown(id);
+ }
+ break;
+ case CHANNEL_STOP:
+ if (command instanceof Number) {
+ localDominoswissHandler.stop(id);
+ }
+ break;
+ case UP:
+ if (command instanceof Number) {
+ localDominoswissHandler.continuousUp(id);
+ }
+ break;
+ case DOWN:
+ if (command instanceof Number) {
+ localDominoswissHandler.continuousDown(id);
+ }
+ break;
+ case SHUTTER:
+ if (command.toFullString() == DOWN) {
+ localDominoswissHandler.continuousDown(id);
+ } else if (command.toFullString() == UP) {
+ localDominoswissHandler.continuousUp(id);
+ } else if (command.toFullString() == CHANNEL_STOP) {
+ localDominoswissHandler.stop(id);
+ } else {
+ logger.debug("Blind got command but nothing executed: {} and ChannelUID: {}",
+ command.toFullString(), channelUID.getIdWithoutGroup());
+ }
+
+ case TILTDOWN:
+ if (command instanceof Number) {
+ try {
+ localDominoswissHandler.tiltDown(id);
+ } catch (InterruptedException e) {
+ logger.debug("EGate tiltDown error: {} ", e.toString());
+ }
+ }
+ break;
+
+ case TILTUP:
+ if (command instanceof Number) {
+ try {
+ localDominoswissHandler.tiltUp(id);
+ } catch (InterruptedException e) {
+ logger.debug("EGate tiltUP error: {} ", e.toString());
+ }
+ }
+ break;
+
+ case SHUTTERTILT:
+ if (command.toFullString() == UP) {
+ localDominoswissHandler.pulseUp(id);
+ } else if (command.toFullString() == DOWN) {
+ localDominoswissHandler.pulseDown(id);
+ } else if (command.toFullString() == CHANNEL_STOP) {
+ localDominoswissHandler.stop(id);
+ } else {
+ logger.debug("Blind got command but nothing executed: {} and ChannelUID: {}",
+ command.toFullString(), channelUID.getIdWithoutGroup());
+ }
+
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void initialize() {
+ this.id = getConfig().as(BlindConfig.class).id;
+ Bridge bridge = getBridge();
+ if (bridge != null) {
+ dominoswissHandler = (EGateHandler) bridge.getHandler();
+ EGateHandler localDominoswissHandler = dominoswissHandler;
+ if (localDominoswissHandler != null) {
+ localDominoswissHandler.registerBlind(this.id, getThing().getUID());
+ try {
+ ThingStatus bridgeStatus = bridge.getStatus();
+ if (bridgeStatus == ThingStatus.ONLINE && getThing().getStatus() != ThingStatus.ONLINE) {
+ updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
+ localDominoswissHandler = (EGateHandler) bridge.getHandler();
+ } else if (bridgeStatus == ThingStatus.OFFLINE) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
+ }
+ } catch (Exception e) {
+ logger.debug("Could not update ThingStatus ", e);
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, e.toString());
+
+ }
+ }
+ }
+ }
+
+ /*
+ * Gets the ID of this Blind
+ */
+ public String getID() {
+ return this.id;
+ }
+}
diff --git a/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/DominoswissBindingConstants.java b/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/DominoswissBindingConstants.java
new file mode 100644
index 0000000000000..4c3d51373c5f2
--- /dev/null
+++ b/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/DominoswissBindingConstants.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.dominoswiss.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link DominoswissBindingConstants} class defines common constants, which are
+ * used across the whole binding.
+ *
+ * @author Frieso Aeschbacher - Initial contribution
+ */
+@NonNullByDefault
+public class DominoswissBindingConstants {
+
+ private static final String BINDING_ID = "dominoswiss";
+
+ // List of all Thing Type UIDs
+ public static final ThingTypeUID DOMINOSWISSBLINDS_THING_TYPE = new ThingTypeUID(BINDING_ID, "blind");
+ public static final ThingTypeUID DOMINOSWISSEGATE_THING_TYPE = new ThingTypeUID(BINDING_ID, "egate");
+
+ // List of all Channel ids
+ public static final String CHANNEL_PULSEUP = "pulseUp";
+ public static final String CHANNEL_PULSEDOWN = "pulseDown";
+ public static final String CHANNEL_CONTINOUSUP = "continousUp";
+ public static final String CHANNEL_CONTINOUSDOWN = "continousDown";
+ public static final String CHANNEL_STOP = "STOP";
+ public static final String UP = "UP";
+ public static final String DOWN = "DOWN";
+ public static final String SHUTTER = "shutter";
+ public static final String TILTUP = "tiltUp";
+ public static final String TILTDOWN = "tiltDown";
+ public static final String SHUTTERTILT = "shutterTilt";
+
+ public static final String GETCONFIG = "getConfig";
+
+ public static final String CR = "\r";
+}
diff --git a/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/DominoswissConfiguration.java b/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/DominoswissConfiguration.java
new file mode 100644
index 0000000000000..6ab19edbb8875
--- /dev/null
+++ b/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/DominoswissConfiguration.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.dominoswiss.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link DominoswissConfiguration} class contains fields mapping thing configuration parameters.
+ *
+ * @author Frieso Aeschbacher - Initial contribution
+ */
+@NonNullByDefault
+public class DominoswissConfiguration {
+
+ /**
+ * Server ip address
+ */
+ public String ipAddress = "localhost";
+ /**
+ * Server web port for REST calls
+ */
+ public int port = 1318;
+
+ /**
+ * Language for TTS has to be fix to EN as only English commands are allowed
+ */
+ public final String language = "EN";
+}
diff --git a/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/DominoswissHandlerFactory.java b/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/DominoswissHandlerFactory.java
new file mode 100644
index 0000000000000..5b38d1904e7f7
--- /dev/null
+++ b/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/DominoswissHandlerFactory.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.dominoswiss.internal;
+
+import static org.openhab.binding.dominoswiss.internal.DominoswissBindingConstants.*;
+
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.binding.BaseThingHandlerFactory;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerFactory;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * The {@link DominoswissHandlerFactory} is responsible for creating things and thing
+ * handlers.
+ *
+ * @author Frieso Aeschbacher - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.dominoswiss", service = ThingHandlerFactory.class)
+public class DominoswissHandlerFactory extends BaseThingHandlerFactory {
+
+ private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(DOMINOSWISSEGATE_THING_TYPE,
+ DOMINOSWISSBLINDS_THING_TYPE);
+
+ @Override
+ public boolean supportsThingType(ThingTypeUID thingTypeUID) {
+ return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
+ }
+
+ @Override
+ protected @Nullable ThingHandler createHandler(Thing thing) {
+ ThingTypeUID thingTypeUID = thing.getThingTypeUID();
+
+ if (thingTypeUID.equals(DOMINOSWISSEGATE_THING_TYPE)) {
+ return new EGateHandler((Bridge) thing);
+ }
+
+ if (thingTypeUID.equals(DOMINOSWISSBLINDS_THING_TYPE)) {
+ return new BlindHandler(thing);
+ }
+ return null;
+ }
+}
diff --git a/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/EGateHandler.java b/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/EGateHandler.java
new file mode 100644
index 0000000000000..7cbc66b2484da
--- /dev/null
+++ b/bundles/org.openhab.binding.dominoswiss/src/main/java/org/openhab/binding/dominoswiss/internal/EGateHandler.java
@@ -0,0 +1,316 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.dominoswiss.internal;
+
+import static org.openhab.binding.dominoswiss.internal.DominoswissBindingConstants.*;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.BaseBridgeHandler;
+import org.openhab.core.types.Command;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link EgateHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Frieso Aeschbacher - Initial contribution
+ */
+@NonNullByDefault
+public class EGateHandler extends BaseBridgeHandler {
+
+ private final Logger logger = LoggerFactory.getLogger(EGateHandler.class);
+ private @Nullable Socket egateSocket;
+
+ private int port;
+ private @Nullable String host;
+ private static final int SOCKET_TIMEOUT_SEC = 250;
+ private final Object lock = new Object();
+ private @Nullable BufferedWriter writer;
+ private @Nullable BufferedReader reader;
+ private @Nullable Future> refreshJob;
+ private Map registeredBlinds;
+ private @Nullable ScheduledFuture> pollingJob;
+
+ public EGateHandler(Bridge thing) {
+ super(thing);
+ registeredBlinds = new HashMap();
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ if (channelUID.getId().equals(GETCONFIG)) {
+ sendCommand("EthernetGet;\r");
+ }
+ }
+
+ @Override
+ public void initialize() {
+ DominoswissConfiguration config;
+ config = this.getConfigAs(DominoswissConfiguration.class);
+ host = config.ipAddress;
+ port = config.port;
+
+ if (host != null && port > 0) {
+ // Create a socket to eGate
+ try (Socket localEgateSocket = new Socket(host, port)) {
+ writer = new BufferedWriter(new OutputStreamWriter(localEgateSocket.getOutputStream()));
+ egateSocket = localEgateSocket;
+ } catch (IOException e) {
+ logger.debug("IOException in initialize: {} host {} port {}", e.toString(), host, port);
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.toString());
+ egateSocket = null;
+ }
+ pollingJob = scheduler.scheduleWithFixedDelay(this::pollingConfig, 0, 30, TimeUnit.SECONDS);
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
+ "Cannot connect to dominoswiss eGate gateway. host IP address or port are not set.");
+ }
+ }
+
+ @Override
+ public void dispose() {
+ try {
+ Socket localEgateSocket = egateSocket;
+ if (localEgateSocket != null) {
+ localEgateSocket.close();
+ }
+ Future> localRefreshJob = refreshJob;
+ if (localRefreshJob != null) {
+ localRefreshJob.cancel(true);
+ }
+ BufferedReader localReader = reader;
+ if (localReader != null) {
+ localReader.close();
+ }
+
+ BufferedWriter localWriter = writer;
+ if (localWriter != null) {
+ localWriter.close();
+ }
+ ScheduledFuture> localPollingJob = pollingJob;
+ if (localPollingJob != null) {
+ localPollingJob.cancel(true);
+ localPollingJob = null;
+ }
+ logger.debug("EGate Handler connection closed, disposing");
+ } catch (IOException e) {
+ logger.debug("EGate Handler Error on dispose: {} ", e.toString());
+ }
+ }
+
+ public synchronized boolean isConnected() {
+ Socket localEGateSocket = egateSocket;
+ if (localEGateSocket == null) {
+ return false;
+ }
+
+ // NOTE: isConnected() returns true once a connection is made and will
+ // always return true even after the socket is closed
+ // http://stackoverflow.com/questions/10163358/
+ return localEGateSocket.isConnected() && !localEGateSocket.isClosed();
+ }
+
+ /**
+ * Possible Instructions are:
+ * FssTransmit 1 Kommandoabsetzung (Controller > eGate > Dominoswiss)
+ * FssReceive 2 Empfangenes Funkpaket (Dominoswiss > eGate > Controller)
+ *
+ * @throws InterruptedException
+ *
+ */
+
+ public void tiltUp(String id) throws InterruptedException {
+ for (int i = 0; i < 3; i++) {
+ pulseUp(id);
+ Thread.sleep(150); // sleep to not confuse the blinds
+
+ }
+ }
+
+ public void tiltDown(String id) throws InterruptedException {
+ for (int i = 0; i < 3; i++) {
+ pulseDown(id);
+ Thread.sleep(150);// sleep to not confuse the blinds
+ }
+ }
+
+ public void pulseUp(String id) {
+ sendCommand("Instruction=1;ID=" + id + ";Command=1;Priority=1;CheckNr=3415347;" + CR);
+ }
+
+ public void pulseDown(String id) {
+ sendCommand("Instruction=1;ID=" + id + ";Command=2;Priority=1;CheckNr=2764516;" + CR);
+ }
+
+ public void continuousUp(String id) {
+ sendCommand("Instruction=1;ID=" + id + ";Command=3;Priority=1;CheckNr=2867016;" + CR, 20000);
+ }
+
+ public void continuousDown(String id) {
+ sendCommand("Instruction=1;ID=" + id + ";Command=4;Priority=1;CheckNr=973898;" + CR, 20000);
+ }
+
+ public void stop(String id) {
+ sendCommand("Instruction=1;ID=" + id + ";Command=5;Priority=1;CheckNr=5408219;" + CR);
+ }
+
+ public void registerBlind(String id, ThingUID uid) {
+ logger.debug("Registring Blind id {} with thingUID {}", id, uid);
+ registeredBlinds.put(id, uid);
+ }
+
+ /**
+ * Send a command to the eGate Server.
+ */
+
+ private void sendCommand(String command) {
+ sendCommand(command, SOCKET_TIMEOUT_SEC);
+ }
+
+ private synchronized void sendCommand(String command, int timeout) {
+ logger.debug("EGate got command: {}", command);
+ Socket localEGateSocket = egateSocket;
+ BufferedWriter localWriter = writer;
+ if (localEGateSocket == null || localWriter == null) {
+ return;
+ }
+ if (!isConnected()) {
+ logger.debug("no connection to Dominoswiss eGate server when trying to send command, returning...");
+ return;
+ }
+
+ // Send plain string to eGate Server,
+ try {
+ localEGateSocket.setSoTimeout(SOCKET_TIMEOUT_SEC);
+ localWriter.write(command);
+ localWriter.flush();
+ } catch (IOException e) {
+ logger.debug("Error while sending command {} to Dominoswiss eGate Server {} ", command, e.toString());
+ }
+ }
+
+ private void pollingConfig() {
+ if (!isConnected()) {
+ Socket localEGateSocket = egateSocket;
+ BufferedWriter localWriter = writer;
+ if (localEGateSocket == null || localWriter == null) {
+ return;
+ }
+ synchronized (lock) {
+ try {
+ localEGateSocket.connect(new InetSocketAddress(host, port));
+ localEGateSocket.setSoTimeout(SOCKET_TIMEOUT_SEC);
+ localWriter.write("SilenceModeSet;Value=0;" + CR);
+ localWriter.flush();
+ } catch (IOException e) {
+ logger.debug("IOException in pollingConfig: {} host {} port {}", e.toString(), host, port);
+ try {
+ localEGateSocket.close();
+ egateSocket = null;
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.toString());
+ } catch (IOException e1) {
+ logger.debug("EGate Socket not closed {}", e1.toString());
+ }
+ egateSocket = null;
+ }
+ if (egateSocket != null) {
+ updateStatus(ThingStatus.ONLINE);
+ startAutomaticRefresh();
+ logger.debug("EGate Handler started automatic refresh, status: {} ",
+ getThing().getStatus().toString());
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
+ }
+ }
+ }
+ }
+
+ private void startAutomaticRefresh() {
+ Runnable runnable = () -> {
+ try {
+
+ Socket localSocket = egateSocket;
+ if (localSocket == null) {
+ return;
+ }
+ BufferedReader localReader = reader;
+ if (localReader == null) {
+ reader = new BufferedReader(new InputStreamReader(localSocket.getInputStream()));
+ }
+ if (localReader != null && localReader.ready()) {
+ String input = localReader.readLine();
+ logger.debug("Reader got from EGATE: {}", input);
+ onData(input);
+ }
+ } catch (IOException e) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
+ "Error while reading command from Dominoswiss eGate Server " + e.toString());
+ }
+ };
+ refreshJob = scheduler.submit(runnable);
+ }
+
+ /**
+ * Finds and returns a child thing for a given UID of this bridge.
+ *
+ * @param uid uid of the child thing
+ * @return child thing with the given uid or null if thing was not found
+ */
+ public @Nullable Thing getThingByUID(ThingUID uid) {
+ Bridge bridge = getThing();
+
+ List things = bridge.getThings();
+
+ for (Thing thing : things) {
+ if (thing.getUID().equals(uid)) {
+ return thing;
+ }
+ }
+
+ return null;
+ }
+
+ protected void onData(String input) {
+ // Instruction=2;ID=19;Command=1;Value=0;Priority=0;
+ Map map = new HashMap();
+ // split on ;
+ String[] parts = input.split(";");
+ if (parts.length >= 2) {
+ for (int i = 0; i < parts.length; i += 2) {
+ map.put(parts[i], parts[i + 1]);
+ }
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.dominoswiss/src/main/resources/OH-INF/binding/binding.xml b/bundles/org.openhab.binding.dominoswiss/src/main/resources/OH-INF/binding/binding.xml
new file mode 100644
index 0000000000000..0c9d588efd6fe
--- /dev/null
+++ b/bundles/org.openhab.binding.dominoswiss/src/main/resources/OH-INF/binding/binding.xml
@@ -0,0 +1,8 @@
+
+
+
+ Dominoswiss Binding
+ The Dominoswiss Binding interacts with the Dominoswiss eGate G1 Gateway to control blinds
+
diff --git a/bundles/org.openhab.binding.dominoswiss/src/main/resources/OH-INF/i18n/dominoswiss_de_DE.properties b/bundles/org.openhab.binding.dominoswiss/src/main/resources/OH-INF/i18n/dominoswiss_de_DE.properties
new file mode 100644
index 0000000000000..73b2a40d902d8
--- /dev/null
+++ b/bundles/org.openhab.binding.dominoswiss/src/main/resources/OH-INF/i18n/dominoswiss_de_DE.properties
@@ -0,0 +1,3 @@
+# binding
+binding.dominoswiss.name = Dominoswiss
+binding.dominoswiss.description = Dominoswiss Binding zur Kontrolle der Jalousien
diff --git a/bundles/org.openhab.binding.dominoswiss/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.dominoswiss/src/main/resources/OH-INF/thing/thing-types.xml
new file mode 100644
index 0000000000000..2b06be3d50ad2
--- /dev/null
+++ b/bundles/org.openhab.binding.dominoswiss/src/main/resources/OH-INF/thing/thing-types.xml
@@ -0,0 +1,114 @@
+
+
+
+
+ This is a Dominoswiss EGate Server instance.
+
+
+
+ network-address
+ The IP or host name of the Dominoswiss EGate Server (192.168.1.100, localhost)
+
+
+
+ Port interface of the Dominoswiss EGate Server, default 1318
+ 1318
+
+
+
+
+
+
+
+
+
+ Provides various control commands for Dominoswiss receivers
+
+
+
+
+
+
+
+
+
+
+
+
+ Dominoswiss
+
+
+
+
+ Blinds are identified by their ID address
+
+
+
+
+
+ Rollershutter
+
+ Send one pulse up
+
+
+
+
+ Rollershutter
+
+ Send one pulse down
+
+
+
+
+ Rollershutter
+
+ Send continuous up command to blind
+
+
+
+
+ Rollershutter
+
+ Send continuous down command to blind
+
+
+
+
+ Rollershutter
+
+ Send stop impulse to stop the blinds
+
+
+
+
+ Rollershutter
+
+ Handle the commands up/down/stop
+
+
+
+
+ Rollershutter
+
+ Handle the commands tiltUp/tiltDown/stop
+
+
+
+
+ Rollershutter
+
+ Handle the command for 3 tilts up
+
+
+
+
+ Rollershutter
+
+ Handle the command for 3 tilts down
+
+
+
+
diff --git a/bundles/pom.xml b/bundles/pom.xml
index 345fc9daecd35..8c0e9e0d050d0 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -99,6 +99,7 @@
org.openhab.binding.digitalstrom
org.openhab.binding.dlinksmarthome
org.openhab.binding.dmx
+ org.openhab.binding.dominoswiss
org.openhab.binding.doorbird
org.openhab.binding.draytonwiser
org.openhab.binding.dscalarm