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