diff --git a/CODEOWNERS b/CODEOWNERS
index f1f204fa412cd..69fc52c11ce4e 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -217,6 +217,7 @@
/bundles/org.openhab.binding.mqtt.homie/ @davidgraeff
/bundles/org.openhab.binding.mycroft/ @dalgwen
/bundles/org.openhab.binding.mybmw/ @weymann @ntruchsess
+/bundles/org.openhab.binding.mynice/ @clinique
/bundles/org.openhab.binding.myq/ @digitaldan
/bundles/org.openhab.binding.mystrom/ @pail23
/bundles/org.openhab.binding.nanoleaf/ @raepple @stefan-hoehn
diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml
index efe39a63389da..28893eca65d64 100644
--- a/bom/openhab-addons/pom.xml
+++ b/bom/openhab-addons/pom.xml
@@ -1081,6 +1081,11 @@
org.openhab.binding.mybmw
${project.version}
+
+ org.openhab.addons.bundles
+ org.openhab.binding.mynice
+ ${project.version}
+
org.openhab.addons.bundles
org.openhab.binding.myq
diff --git a/bundles/org.openhab.binding.mynice/NOTICE b/bundles/org.openhab.binding.mynice/NOTICE
new file mode 100644
index 0000000000000..38d625e349232
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/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.mynice/README.md b/bundles/org.openhab.binding.mynice/README.md
new file mode 100644
index 0000000000000..d4c7e65e70c35
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/README.md
@@ -0,0 +1,114 @@
+# MyNice Binding
+
+This binding implements the support of the IT4Wifi module through the NHK protocol and enables management of Nice gates actuators.
+IT4Wifi is a bridge between the TP4 bus of your gate and your Ethernet network.
+
+## Supported Things
+
+- `it4wifi`: The Bridge between openHAB and your module.
+- `swing`: A Thing representing a swinging (two rotating doors) gate.
+- `sliding`: A Thing representing a sliding gate.
+
+## Discovery
+
+The binding will auto-discover (by MDNS) your module, creating the associated `it4wifi` bridge.
+
+Once discovered, a user named “org.openhab.binding.mynice” will be created on it.
+You will have to grant him permissions using the MyNice app (Android or IOS).
+
+Once configuration of the bridge is completed, your gate(s) will also be auto-discovered and added to the Inbox.
+
+## Thing Configuration
+
+First configuration should be done via UI discovery, this will let you get automatically the password provided by the IT4Wifi module.
+Once done, you can also create your things via *.things file.
+
+### `it4wifi` Bridge Configuration
+
+| Name | Type | Description | Default | Required | Advanced |
+|------------|------|------------------------------------------------------------------------|---------|----------|----------|
+| hostname | text | Hostname or IP address of the device | N/A | yes | no |
+| password | text | Password to access the device | N/A | yes | no |
+| macAddress | text | The MAC address of the IT4Wifi | N/A | yes | no |
+| username | text | Pairing Key needed to access the device, provided by the bridge itself | N/A | yes | no |
+
+### Gates Thing Configuration
+
+| Name | Type | Description | Default | Required | Advanced |
+|------------|------|------------------------------------------------------------------------|---------|----------|----------|
+| id | text | ID of the gate on the TP4 bus connected to the bridge | N/A | yes | no |
+
+## Channels
+
+There is no channel associated with the bridge.
+
+Channels available for the gates are :
+
+| Channel | Type | Read/Write | Description |
+|-----------|--------|------------|----------------------------------------------------------|
+| status | String | R | Description of the current status of the door (1) |
+| obstruct | Switch | R | Flags an obstruction, blocking the door |
+| moving | Switch | R | Indicates if the device is currently operating a command |
+| command | String | W | Send a given command to the gate (2) |
+| t4command | String | W | Send a T4 Command to the gate |
+
+(1) : can be open, closed, opening, closing, stopped.
+(2) : must be "stop","open","close"
+
+### T4 Commands
+
+Depending upon your gate model and motor capabilities, some T4 commands can be used.
+The list of available commands for your model will be automatically discovered by the binding.
+This information is stored in the `allowedT4` property held by the gate Thing itself.
+
+Complete list of T4 Commands :
+
+| Command | Action |
+|---------|----------------------------|
+| MDAx | Step by Step |
+| MDAy | Stop (as remote control) |
+| MDAz | Open (as remote control) |
+| MDA0 | Close (as remote control) |
+| MDA1 | Partial opening 1 |
+| MDA2 | Partial opening 2 |
+| MDA3 | Partial opening 3 |
+| MDBi | Apartment Step by Step |
+| MDBj | Step by Step high priority |
+| MDBk | Open and block |
+| MDBl | Close and block |
+| MDBm | Block |
+| MDEw | Release |
+| MDEx | Courtesy ligh timer on |
+| MDEy | Courtesy light on-off |
+| MDEz | Step by Step master door |
+| MDE0 | Open master door |
+| MDE1 | Close master door |
+| MDE2 | Step by Step slave door |
+| MDE3 | Open slave door |
+| MDE4 | Close slave door |
+| MDE5 | Release and Open |
+| MDFh | Release and Close |
+
+## Full Example
+
+### things/mynice.things
+
+```java
+Bridge mynice:it4wifi:83eef09166 "Nice IT4WIFI" @ "portail" [
+ hostname="192.168.0.198",
+ macAddress="00:xx:zz:dd:ff:gg",
+ password="v***************************zU=",
+ username="neo_prod"] {
+ swing 1 "Nice POA3 Moteur Portail" @ "portail" [id="1"]
+}
+```
+
+### items/mynice.items
+
+```java
+String NiceIT4WIFI_GateStatus "Gate Status" (gMyniceSwing) ["Status","Opening"] {channel="mynice:swing:83eef09166:1:status"}
+String NiceIT4WIFI_Obstruction "Obstruction" (gMyniceSwing) {channel="mynice:swing:83eef09166:1:obstruct"}
+Switch NiceIT4WIFI_Moving "Moving" (gMyniceSwing) ["Status","Vibration"] {channel="mynice:swing:83eef09166:1:moving"}
+String NiceIT4WIFI_Command "Command" (gMyniceSwing) {channel="mynice:swing:83eef09166:1:command"}
+
+```
diff --git a/bundles/org.openhab.binding.mynice/pom.xml b/bundles/org.openhab.binding.mynice/pom.xml
new file mode 100644
index 0000000000000..47d08b3480372
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/pom.xml
@@ -0,0 +1,17 @@
+
+
+
+ 4.0.0
+
+
+ org.openhab.addons.bundles
+ org.openhab.addons.reactor.bundles
+ 4.0.0-SNAPSHOT
+
+
+ org.openhab.binding.mynice
+
+ openHAB Add-ons :: Bundles :: MyNice Binding
+
+
diff --git a/bundles/org.openhab.binding.mynice/src/main/feature/feature.xml b/bundles/org.openhab.binding.mynice/src/main/feature/feature.xml
new file mode 100644
index 0000000000000..001b78d9f57fb
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/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.mynice/${project.version}
+
+
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/MyNiceBindingConstants.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/MyNiceBindingConstants.java
new file mode 100644
index 0000000000000..b785ac6bb46d8
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/MyNiceBindingConstants.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.thing.ThingTypeUID;
+
+/**
+ * The {@link MyNiceBindingConstants} class defines common constants, which are used across the whole binding.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class MyNiceBindingConstants {
+ private static final String BINDING_ID = "mynice";
+
+ // List of all Channel ids
+ public static final String DOOR_STATUS = "status";
+ public static final String DOOR_OBSTRUCTED = "obstruct";
+ public static final String DOOR_MOVING = "moving";
+ public static final String DOOR_COMMAND = "command";
+ public static final String DOOR_T4_COMMAND = "t4command";
+
+ // List of all Thing Type UIDs
+ public static final ThingTypeUID BRIDGE_TYPE_IT4WIFI = new ThingTypeUID(BINDING_ID, "it4wifi");
+ public static final ThingTypeUID THING_TYPE_SWING = new ThingTypeUID(BINDING_ID, "swing");
+ public static final ThingTypeUID THING_TYPE_SLIDING = new ThingTypeUID(BINDING_ID, "sliding");
+
+ // Configuration element of a portal
+ public static final String DEVICE_ID = "id";
+
+ public static final String ALLOWED_T4 = "allowedT4";
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/MyNiceHandlerFactory.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/MyNiceHandlerFactory.java
new file mode 100644
index 0000000000000..a5fc54a3f1c35
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/MyNiceHandlerFactory.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal;
+
+import static org.openhab.binding.mynice.internal.MyNiceBindingConstants.*;
+
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.mynice.internal.handler.GateHandler;
+import org.openhab.binding.mynice.internal.handler.It4WifiHandler;
+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 MyNiceHandlerFactory} is responsible for creating thing handlers.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+@Component(configurationPid = "binding.mynice", service = ThingHandlerFactory.class)
+public class MyNiceHandlerFactory extends BaseThingHandlerFactory {
+ private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(BRIDGE_TYPE_IT4WIFI, THING_TYPE_SWING,
+ THING_TYPE_SLIDING);
+
+ @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 (BRIDGE_TYPE_IT4WIFI.equals(thingTypeUID)) {
+ return new It4WifiHandler((Bridge) thing);
+ } else if (THING_TYPE_SWING.equals(thingTypeUID)) {
+ return new GateHandler(thing);
+ } else if (THING_TYPE_SLIDING.equals(thingTypeUID)) {
+ return new GateHandler(thing);
+ }
+
+ return null;
+ }
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/config/It4WifiConfiguration.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/config/It4WifiConfiguration.java
new file mode 100644
index 0000000000000..6b92fd34a46d8
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/config/It4WifiConfiguration.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.config;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The {@link It4WifiConfiguration} class contains fields mapping thing configuration parameters.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class It4WifiConfiguration {
+ public static final String PASSWORD = "password";
+ public static final String HOSTNAME = "hostname";
+
+ public String username = "";
+ public String hostname = "";
+ public String macAddress = "";
+ public String password = "";
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/discovery/MyNiceDiscoveryParticipant.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/discovery/MyNiceDiscoveryParticipant.java
new file mode 100644
index 0000000000000..267d7c63500b9
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/discovery/MyNiceDiscoveryParticipant.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.discovery;
+
+import static org.openhab.binding.mynice.internal.MyNiceBindingConstants.BRIDGE_TYPE_IT4WIFI;
+import static org.openhab.binding.mynice.internal.config.It4WifiConfiguration.HOSTNAME;
+import static org.openhab.core.thing.Thing.PROPERTY_MAC_ADDRESS;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import javax.jmdns.ServiceInfo;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.config.discovery.DiscoveryResult;
+import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.ThingUID;
+import org.osgi.service.component.annotations.Component;
+
+/**
+ * The {@link MyNiceDiscoveryParticipant} is responsible for discovering the IT4Wifi bridge using mDNS discovery service
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@Component
+@NonNullByDefault
+public class MyNiceDiscoveryParticipant implements MDNSDiscoveryParticipant {
+ private static final String PROPERTY_MODEL = "model";
+ private static final String PROPERTY_DEVICE_ID = "deviceid";
+ private static final String MAC_REGEX = "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})|([0-9a-fA-F]{4}\\.[0-9a-fA-F]{4}\\.[0-9a-fA-F]{4})$";
+ private static final Pattern MAC_PATTERN = Pattern.compile(MAC_REGEX);
+
+ @Override
+ public Set getSupportedThingTypeUIDs() {
+ return Set.of(BRIDGE_TYPE_IT4WIFI);
+ }
+
+ @Override
+ public String getServiceType() {
+ return "_nap._tcp.local.";
+ }
+
+ @Override
+ public @Nullable DiscoveryResult createResult(ServiceInfo service) {
+ ThingUID thingUID = getThingUID(service);
+ String[] hostNames = service.getHostAddresses();
+ if (thingUID != null && hostNames.length > 0) {
+ String label = service.getPropertyString(PROPERTY_MODEL);
+ String macAddress = service.getPropertyString(PROPERTY_DEVICE_ID);
+
+ return DiscoveryResultBuilder.create(thingUID).withLabel(label)
+ .withRepresentationProperty(PROPERTY_MAC_ADDRESS).withThingType(BRIDGE_TYPE_IT4WIFI)
+ .withProperties(Map.of(HOSTNAME, hostNames[0], PROPERTY_MAC_ADDRESS, macAddress)).build();
+ }
+ return null;
+ }
+
+ @Override
+ public @Nullable ThingUID getThingUID(ServiceInfo service) {
+ String macAddress = service.getPropertyString(PROPERTY_DEVICE_ID);
+ if (macAddress != null && validate(macAddress)) {
+ macAddress = macAddress.replaceAll("[^a-fA-F0-9]", "").toLowerCase();
+ return new ThingUID(BRIDGE_TYPE_IT4WIFI, macAddress);
+ }
+ return null;
+ }
+
+ private boolean validate(String mac) {
+ return MAC_PATTERN.matcher(mac).find();
+ }
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/discovery/MyNiceDiscoveryService.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/discovery/MyNiceDiscoveryService.java
new file mode 100644
index 0000000000000..19dc182975e7b
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/discovery/MyNiceDiscoveryService.java
@@ -0,0 +1,118 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.discovery;
+
+import static org.openhab.binding.mynice.internal.MyNiceBindingConstants.*;
+
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.mynice.internal.handler.It4WifiHandler;
+import org.openhab.binding.mynice.internal.handler.MyNiceDataListener;
+import org.openhab.binding.mynice.internal.xml.dto.CommandType;
+import org.openhab.binding.mynice.internal.xml.dto.Device;
+import org.openhab.core.config.discovery.AbstractDiscoveryService;
+import org.openhab.core.config.discovery.DiscoveryResult;
+import org.openhab.core.config.discovery.DiscoveryResultBuilder;
+import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link MyNiceDiscoveryService} is responsible for discovering all things
+ * except the It4Wifi bridge itself
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class MyNiceDiscoveryService extends AbstractDiscoveryService
+ implements MyNiceDataListener, ThingHandlerService {
+
+ private static final int SEARCH_TIME = 5;
+ private final Logger logger = LoggerFactory.getLogger(MyNiceDiscoveryService.class);
+
+ private @Nullable It4WifiHandler bridgeHandler;
+
+ /**
+ * Creates a MyNiceDiscoveryService with background discovery disabled.
+ */
+ public MyNiceDiscoveryService() {
+ super(Set.of(THING_TYPE_SWING), SEARCH_TIME, false);
+ }
+
+ @Override
+ public void setThingHandler(ThingHandler handler) {
+ if (handler instanceof It4WifiHandler it4Handler) {
+ bridgeHandler = it4Handler;
+ }
+ }
+
+ @Override
+ public @Nullable ThingHandler getThingHandler() {
+ return bridgeHandler;
+ }
+
+ @Override
+ public void activate() {
+ super.activate(null);
+ It4WifiHandler handler = bridgeHandler;
+ if (handler != null) {
+ handler.registerDataListener(this);
+ }
+ }
+
+ @Override
+ public void deactivate() {
+ It4WifiHandler handler = bridgeHandler;
+ if (handler != null) {
+ handler.unregisterDataListener(this);
+ }
+ super.deactivate();
+ }
+
+ @Override
+ public void onDataFetched(List devices) {
+ It4WifiHandler handler = bridgeHandler;
+ if (handler != null) {
+ ThingUID bridgeUID = handler.getThing().getUID();
+ devices.stream().filter(device -> device.type != null).forEach(device -> {
+ ThingUID thingUID = switch (device.type) {
+ case SWING -> new ThingUID(THING_TYPE_SWING, bridgeUID, device.id);
+ case SLIDING -> new ThingUID(THING_TYPE_SLIDING, bridgeUID, device.id);
+ default -> null;
+ };
+
+ if (thingUID != null) {
+ DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID)
+ .withLabel(String.format("%s %s", device.manuf, device.prod))
+ .withRepresentationProperty(DEVICE_ID).withProperty(DEVICE_ID, device.id).build();
+ thingDiscovered(discoveryResult);
+ } else {
+ logger.info("`{}` type of device is not yet supported", device.type);
+ }
+ });
+ }
+ }
+
+ @Override
+ protected void startScan() {
+ It4WifiHandler handler = bridgeHandler;
+ if (handler != null) {
+ handler.sendCommand(CommandType.INFO);
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/handler/GateHandler.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/handler/GateHandler.java
new file mode 100644
index 0000000000000..8f5e9fb3b719c
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/handler/GateHandler.java
@@ -0,0 +1,129 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.handler;
+
+import static org.openhab.binding.mynice.internal.MyNiceBindingConstants.*;
+import static org.openhab.core.thing.Thing.*;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.mynice.internal.xml.dto.CommandType;
+import org.openhab.binding.mynice.internal.xml.dto.Device;
+import org.openhab.binding.mynice.internal.xml.dto.T4Command;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.library.types.StringType;
+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.binding.BaseThingHandler;
+import org.openhab.core.thing.binding.BridgeHandler;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class GateHandler extends BaseThingHandler implements MyNiceDataListener {
+ private static final String OPENING = "opening";
+ private static final String CLOSING = "closing";
+
+ private final Logger logger = LoggerFactory.getLogger(GateHandler.class);
+
+ private String id = "";
+
+ public GateHandler(Thing thing) {
+ super(thing);
+ }
+
+ @Override
+ public void initialize() {
+ id = (String) getConfig().get(DEVICE_ID);
+ getBridgeHandler().ifPresent(h -> h.registerDataListener(this));
+ }
+
+ @Override
+ public void dispose() {
+ getBridgeHandler().ifPresent(h -> h.unregisterDataListener(this));
+ }
+
+ private Optional getBridgeHandler() {
+ Bridge bridge = getBridge();
+ if (bridge != null) {
+ BridgeHandler handler = bridge.getHandler();
+ if (handler instanceof It4WifiHandler it4Handler) {
+ return Optional.of(it4Handler);
+ }
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ if (command instanceof RefreshType) {
+ return;
+ } else {
+ handleCommand(channelUID.getId(), command.toString());
+ }
+ }
+
+ private void handleCommand(String channelId, String command) {
+ if (DOOR_COMMAND.equals(channelId)) {
+ getBridgeHandler().ifPresent(handler -> handler.sendCommand(id, command));
+ } else if (DOOR_T4_COMMAND.equals(channelId)) {
+ String allowed = thing.getProperties().get(ALLOWED_T4);
+ if (allowed != null && allowed.contains(command)) {
+ getBridgeHandler().ifPresent(handler -> {
+ try {
+ T4Command t4 = T4Command.fromCode(command);
+ handler.sendCommand(id, t4);
+ } catch (IllegalArgumentException e) {
+ logger.warn("{} is not a valid T4 command", command);
+ }
+ });
+ } else {
+ logger.warn("This thing does not accept the T4 command '{}'", command);
+ }
+ }
+ }
+
+ @Override
+ public void onDataFetched(List devices) {
+ devices.stream().filter(d -> id.equals(d.id)).findFirst().map(device -> {
+ updateStatus(ThingStatus.ONLINE);
+ if (thing.getProperties().isEmpty()) {
+ int value = Integer.parseInt(device.properties.t4allowed.values, 16);
+ List t4Allowed = T4Command.fromBitmask(value).stream().map(Enum::name).toList();
+ updateProperties(Map.of(PROPERTY_VENDOR, device.manuf, PROPERTY_MODEL_ID, device.prod,
+ PROPERTY_SERIAL_NUMBER, device.serialNr, PROPERTY_HARDWARE_VERSION, device.versionHW,
+ PROPERTY_FIRMWARE_VERSION, device.versionFW, ALLOWED_T4, String.join(",", t4Allowed)));
+ }
+ if (device.prod != null) {
+ getBridgeHandler().ifPresent(h -> h.sendCommand(CommandType.STATUS));
+ } else {
+ String status = device.properties.doorStatus;
+ updateState(DOOR_STATUS, new StringType(status));
+ updateState(DOOR_OBSTRUCTED, OnOffType.from("1".equals(device.properties.obstruct)));
+ updateState(DOOR_MOVING, OnOffType.from(status.equals(CLOSING) || status.equals(OPENING)));
+ }
+ return true;
+ });
+ }
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/handler/It4WifiConnector.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/handler/It4WifiConnector.java
new file mode 100644
index 0000000000000..0a5a0c76499bf
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/handler/It4WifiConnector.java
@@ -0,0 +1,141 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.handler;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.io.net.http.TrustAllTrustManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link It4WifiConnector} is responsible for connecting reading, writing and disconnecting from the It4Wifi.
+ *
+ * @author Gaël L'hopital - Initial Contribution
+ */
+@NonNullByDefault
+public class It4WifiConnector extends Thread {
+ private static final int SERVER_PORT = 443;
+ private static final char ETX = '\u0003';
+ private static final char STX = '\u0002';
+
+ private final Logger logger = LoggerFactory.getLogger(It4WifiConnector.class);
+ private final It4WifiHandler handler;
+ private final SSLSocket sslsocket;
+
+ private @NonNullByDefault({}) InputStreamReader in;
+ private @NonNullByDefault({}) OutputStreamWriter out;
+
+ public It4WifiConnector(String hostname, It4WifiHandler handler) {
+ super(It4WifiConnector.class.getName());
+ this.handler = handler;
+ try {
+ SSLContext sslContext = SSLContext.getInstance("SSL");
+ sslContext.init(null, new TrustManager[] { TrustAllTrustManager.getInstance() }, null);
+ sslsocket = (SSLSocket) sslContext.getSocketFactory().createSocket(hostname, SERVER_PORT);
+ setDaemon(true);
+ } catch (NoSuchAlgorithmException | KeyManagementException | IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @Override
+ public void run() {
+ String buffer = "";
+ try {
+ connect();
+ while (!interrupted()) {
+ int data;
+ while ((data = in.read()) != -1) {
+ if (data == STX) {
+ buffer = "";
+ } else if (data == ETX) {
+ handler.received(buffer);
+ } else {
+ buffer += (char) data;
+ }
+ }
+ }
+ handler.connectorInterrupted("IT4WifiConnector interrupted");
+ dispose();
+ } catch (IOException e) {
+ handler.connectorInterrupted(e.getMessage());
+ }
+ }
+
+ public synchronized void sendCommand(String command) {
+ logger.debug("Sending ItT4Wifi :{}", command);
+ try {
+ out.write(STX + command + ETX);
+ out.flush();
+ } catch (IOException e) {
+ handler.connectorInterrupted(e.getMessage());
+ }
+ }
+
+ private void disconnect() {
+ logger.debug("Disconnecting");
+
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException ignore) {
+ }
+ }
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException ignore) {
+ }
+ }
+
+ in = null;
+ out = null;
+
+ logger.debug("Disconnected");
+ }
+
+ /**
+ * Stop the device thread
+ *
+ * @throws IOException
+ */
+ public void dispose() {
+ interrupt();
+ disconnect();
+ try {
+ sslsocket.close();
+ } catch (IOException e) {
+ logger.warn("Error closing sslsocket : {}", e.getMessage());
+ }
+ }
+
+ private void connect() throws IOException {
+ disconnect();
+ logger.debug("Initiating connection to IT4Wifi on port {}...", SERVER_PORT);
+
+ sslsocket.startHandshake();
+ in = new InputStreamReader(sslsocket.getInputStream());
+ out = new OutputStreamWriter(sslsocket.getOutputStream());
+ handler.handShaked();
+ }
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/handler/It4WifiHandler.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/handler/It4WifiHandler.java
new file mode 100644
index 0000000000000..b6535a5fb56e4
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/handler/It4WifiHandler.java
@@ -0,0 +1,244 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.handler;
+
+import static org.openhab.core.thing.Thing.*;
+import static org.openhab.core.types.RefreshType.REFRESH;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+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.binding.mynice.internal.config.It4WifiConfiguration;
+import org.openhab.binding.mynice.internal.discovery.MyNiceDiscoveryService;
+import org.openhab.binding.mynice.internal.xml.MyNiceXStream;
+import org.openhab.binding.mynice.internal.xml.RequestBuilder;
+import org.openhab.binding.mynice.internal.xml.dto.CommandType;
+import org.openhab.binding.mynice.internal.xml.dto.Device;
+import org.openhab.binding.mynice.internal.xml.dto.Event;
+import org.openhab.binding.mynice.internal.xml.dto.Response;
+import org.openhab.binding.mynice.internal.xml.dto.T4Command;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.thing.Bridge;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.binding.BaseBridgeHandler;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.openhab.core.types.Command;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link It4WifiHandler} is responsible for handling commands, which are
+ * sent to one of the channels.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class It4WifiHandler extends BaseBridgeHandler {
+ private static final int MAX_HANDSHAKE_ATTEMPTS = 3;
+ private static final int KEEPALIVE_DELAY_S = 235; // Timeout seems to be at 6 min
+
+ private final Logger logger = LoggerFactory.getLogger(It4WifiHandler.class);
+ private final List dataListeners = new CopyOnWriteArrayList<>();
+ private final MyNiceXStream xstream = new MyNiceXStream();
+
+ private @NonNullByDefault({}) RequestBuilder reqBuilder;
+ private @Nullable It4WifiConnector connector;
+ private @Nullable ScheduledFuture> keepAliveJob;
+ private List devices = new ArrayList<>();
+ private int handshakeAttempts = 0;
+
+ public It4WifiHandler(Bridge thing) {
+ super(thing);
+ }
+
+ @Override
+ public Collection> getServices() {
+ return Set.of(MyNiceDiscoveryService.class);
+ }
+
+ public void registerDataListener(MyNiceDataListener dataListener) {
+ dataListeners.add(dataListener);
+ notifyListeners(devices);
+ }
+
+ public void unregisterDataListener(MyNiceDataListener dataListener) {
+ dataListeners.remove(dataListener);
+ }
+
+ @Override
+ public void handleCommand(ChannelUID channelUID, Command command) {
+ if (REFRESH.equals(command)) {
+ sendCommand(CommandType.INFO);
+ }
+ }
+
+ @Override
+ public void initialize() {
+ if (getConfigAs(It4WifiConfiguration.class).username.isBlank()) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/conf-error-no-username");
+ } else {
+ updateStatus(ThingStatus.UNKNOWN);
+ scheduler.execute(() -> startConnector());
+ }
+ }
+
+ @Override
+ public void dispose() {
+ It4WifiConnector localConnector = connector;
+ if (localConnector != null) {
+ localConnector.dispose();
+ }
+ freeKeepAlive();
+ }
+
+ private void startConnector() {
+ It4WifiConfiguration config = getConfigAs(It4WifiConfiguration.class);
+ freeKeepAlive();
+ reqBuilder = new RequestBuilder(config.macAddress, config.username);
+ It4WifiConnector localConnector = new It4WifiConnector(config.hostname, this);
+ localConnector.start();
+ connector = localConnector;
+ }
+
+ private void freeKeepAlive() {
+ ScheduledFuture> keepAlive = keepAliveJob;
+ if (keepAlive != null) {
+ keepAlive.cancel(true);
+ }
+ keepAliveJob = null;
+ }
+
+ public void received(String command) {
+ logger.debug("Received : {}", command);
+ Event event = xstream.deserialize(command);
+ if (event.error != null) {
+ logger.warn("Error code {} received : {}", event.error.code, event.error.info);
+ } else {
+ if (event instanceof Response) {
+ handleResponse((Response) event);
+ } else {
+ notifyListeners(event.getDevices());
+ }
+ }
+ }
+
+ private void handleResponse(Response response) {
+ switch (response.type) {
+ case PAIR:
+ Configuration thingConfig = editConfiguration();
+ thingConfig.put(It4WifiConfiguration.PASSWORD, response.authentication.pwd);
+ updateConfiguration(thingConfig);
+ logger.info("Pairing key updated in Configuration.");
+ sendCommand(CommandType.VERIFY);
+ return;
+ case VERIFY:
+ if (keepAliveJob != null) { // means we are connected
+ return;
+ }
+ switch (response.authentication.perm) {
+ case admin:
+ case user:
+ sendCommand(CommandType.CONNECT);
+ return;
+ case wait:
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING,
+ "@text/conf-pending-validation");
+ scheduler.schedule(() -> handShaked(), 15, TimeUnit.SECONDS);
+ return;
+ default:
+ return;
+ }
+ case CONNECT:
+ String sc = response.authentication.sc;
+ It4WifiConfiguration config = getConfigAs(It4WifiConfiguration.class);
+ if (sc != null) {
+ reqBuilder.setChallenges(sc, response.authentication.id, config.password);
+ keepAliveJob = scheduler.scheduleWithFixedDelay(() -> sendCommand(CommandType.VERIFY),
+ KEEPALIVE_DELAY_S, KEEPALIVE_DELAY_S, TimeUnit.SECONDS);
+ sendCommand(CommandType.INFO);
+ }
+ return;
+ case INFO:
+ updateStatus(ThingStatus.ONLINE);
+ if (thing.getProperties().isEmpty()) {
+ Map properties = Map.of(PROPERTY_VENDOR, response.intf.manuf, PROPERTY_MODEL_ID,
+ response.intf.prod, PROPERTY_SERIAL_NUMBER, response.intf.serialNr,
+ PROPERTY_HARDWARE_VERSION, response.intf.versionHW, PROPERTY_FIRMWARE_VERSION,
+ response.intf.versionFW);
+ updateProperties(properties);
+ }
+ notifyListeners(response.getDevices());
+ return;
+ case STATUS:
+ notifyListeners(response.getDevices());
+ return;
+ case CHANGE:
+ logger.debug("Change command accepted");
+ return;
+ default:
+ logger.warn("Unhandled response type : {}", response.type);
+ }
+ }
+
+ public void handShaked() {
+ handshakeAttempts = 0;
+ It4WifiConfiguration config = getConfigAs(It4WifiConfiguration.class);
+ sendCommand(config.password.isBlank() ? CommandType.PAIR : CommandType.VERIFY);
+ }
+
+ private void notifyListeners(List list) {
+ devices = list;
+ dataListeners.forEach(listener -> listener.onDataFetched(devices));
+ }
+
+ private void sendCommand(String command) {
+ It4WifiConnector localConnector = connector;
+ if (localConnector != null) {
+ localConnector.sendCommand(command);
+ } else {
+ logger.warn("Tried to send a command when IT4WifiConnector is not initialized.");
+ }
+ }
+
+ public void sendCommand(CommandType command) {
+ sendCommand(reqBuilder.buildMessage(command));
+ }
+
+ public void sendCommand(String id, String command) {
+ sendCommand(reqBuilder.buildMessage(id, command.toLowerCase()));
+ }
+
+ public void sendCommand(String id, T4Command t4) {
+ sendCommand(reqBuilder.buildMessage(id, t4));
+ }
+
+ public void connectorInterrupted(@Nullable String message) {
+ if (handshakeAttempts++ <= MAX_HANDSHAKE_ATTEMPTS) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message);
+ startConnector();
+ } else {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "@text/error-handshake-limit");
+ connector = null;
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/handler/MyNiceDataListener.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/handler/MyNiceDataListener.java
new file mode 100644
index 0000000000000..aa2112c0ce19a
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/handler/MyNiceDataListener.java
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.handler;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.mynice.internal.xml.dto.Device;
+
+/**
+ * The {@link MyNiceDataListener} is notified by the bridge thing handler with updated data from
+ * the IP4Wifi.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public interface MyNiceDataListener {
+
+ public void onDataFetched(List devices);
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/MyNiceXStream.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/MyNiceXStream.java
new file mode 100644
index 0000000000000..e572f8811ecc0
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/MyNiceXStream.java
@@ -0,0 +1,63 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.xml;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.mynice.internal.xml.dto.Authentication;
+import org.openhab.binding.mynice.internal.xml.dto.Authentication.UserPerm;
+import org.openhab.binding.mynice.internal.xml.dto.CommandType;
+import org.openhab.binding.mynice.internal.xml.dto.Device;
+import org.openhab.binding.mynice.internal.xml.dto.Device.DeviceType;
+import org.openhab.binding.mynice.internal.xml.dto.Error;
+import org.openhab.binding.mynice.internal.xml.dto.Event;
+import org.openhab.binding.mynice.internal.xml.dto.Interface;
+import org.openhab.binding.mynice.internal.xml.dto.Properties;
+import org.openhab.binding.mynice.internal.xml.dto.Response;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.io.xml.StaxDriver;
+
+/**
+ * The {@link MyNiceXStream} class is a utility class that wraps an XStream object and provide additional
+ * functionality specific to the MyNice binding. It automatically load the correct converter classes and
+ * processes the XStream annotations used by the object classes.
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+
+@NonNullByDefault
+public class MyNiceXStream extends XStream {
+ public static final String XML_HEADER = "";
+
+ public MyNiceXStream() {
+ super(new StaxDriver());
+ allowTypesByWildcard(new String[] { Response.class.getPackageName() + ".**" });
+ setClassLoader(getClass().getClassLoader());
+ autodetectAnnotations(true);
+ ignoreUnknownElements();
+ alias("Response", Response.class);
+ alias("Event", Event.class);
+ alias("Authentication", Authentication.class);
+ alias("CommandType", CommandType.class);
+ alias("UserPerm", UserPerm.class);
+ alias("DeviceType", DeviceType.class);
+ alias("Error", Error.class);
+ alias("Interface", Interface.class);
+ alias("Device", Device.class);
+ alias("Properties", Properties.class);
+ }
+
+ public Event deserialize(String response) {
+ return (Event) fromXML(XML_HEADER + response);
+ }
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/RequestBuilder.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/RequestBuilder.java
new file mode 100644
index 0000000000000..da480fb6aebc0
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/RequestBuilder.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.xml;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Base64;
+import java.util.Base64.Encoder;
+import java.util.UUID;
+
+import javax.xml.bind.DatatypeConverter;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.mynice.internal.xml.dto.CommandType;
+import org.openhab.binding.mynice.internal.xml.dto.T4Command;
+
+/**
+ * The {@link RequestBuilder} is responsible for building a string request from the CommandType
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class RequestBuilder {
+ private static final Encoder BASE64_ENCODER = Base64.getEncoder();
+
+ public static final String USERNAME = "%un%";
+ public static final String CLIENT_CHALLENGE = "%cc%";
+ private static final String START_REQUEST = "\r\n";
+ private static final String END_REQUEST = "%s%s";
+ private static final String DOOR_ACTION = "%s";
+ private static final String T4_ACTION = "%s";
+ private static final String SIGN = "%s";
+
+ private final String clientChallenge = UUID.randomUUID().toString().substring(0, 8);
+ private final byte[] clientChallengeArr = invertArray(DatatypeConverter.parseHexBinary(clientChallenge));
+ private final MessageDigest digest;
+ private final String it4WifiMac;
+ private final String username;
+
+ private int sessionId = 0;
+ private int commandSequence = 0;
+ private byte[] sessionPassword = {};
+
+ public RequestBuilder(String it4WifiMac, String username) {
+ try {
+ this.digest = MessageDigest.getInstance("SHA-256");
+ this.it4WifiMac = it4WifiMac;
+ this.username = username;
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ private String buildSign(CommandType command, String message) {
+ if (command.signNeeded) {
+ byte[] msgHash = sha256(message.getBytes());
+ byte[] sign = sha256(msgHash, sessionPassword);
+ return String.format(SIGN, BASE64_ENCODER.encodeToString(sign));
+ }
+ return "";
+ }
+
+ public String buildMessage(String id, String command) {
+ return buildMessage(CommandType.CHANGE, id, String.format(DOOR_ACTION, command.toLowerCase()));
+ }
+
+ public String buildMessage(String id, T4Command t4) {
+ return buildMessage(CommandType.CHANGE, id, String.format(T4_ACTION, t4.name()));
+ }
+
+ public String buildMessage(CommandType command, Object... bodyParms) {
+ String startRequest = String.format(START_REQUEST, getCommandId(), it4WifiMac, command);
+ String body = startRequest + getBody(command, bodyParms);
+ String sign = buildSign(command, body);
+ return String.format(END_REQUEST, body, sign);
+ }
+
+ public String getBody(CommandType command, Object... bodyParms) {
+ String result = command.body;
+ if (result.length() != 0) {
+ result = result.replace(USERNAME, username);
+ result = result.replace(CLIENT_CHALLENGE, clientChallenge);
+ result = String.format(result, bodyParms);
+ }
+ return result;
+ }
+
+ public int getCommandId() {
+ return (commandSequence++ << 8) | sessionId;
+ }
+
+ public void setChallenges(String serverChallenge, int sessionId, String password) {
+ byte[] serverChallengeArr = invertArray(DatatypeConverter.parseHexBinary(serverChallenge));
+ byte[] pairingPassword = Base64.getDecoder().decode(password);
+ this.sessionPassword = sha256(pairingPassword, serverChallengeArr, clientChallengeArr);
+ this.sessionId = sessionId & 255;
+ }
+
+ private byte[] sha256(byte[]... values) {
+ for (byte[] data : values) {
+ digest.update(data);
+ }
+ return digest.digest();
+ }
+
+ private static byte[] invertArray(byte[] data) {
+ byte[] result = new byte[data.length];
+ int i = data.length - 1;
+ int c = 0;
+ while (i >= 0) {
+ int c2 = c + 1;
+ result[c] = data[i];
+ i--;
+ c = c2;
+ }
+ return result;
+ }
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Authentication.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Authentication.java
new file mode 100644
index 0000000000000..0c5be7dc5db00
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Authentication.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.xml.dto;
+
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+public class Authentication {
+ public enum UserPerm {
+ wait,
+ user,
+ admin;
+ }
+
+ @XStreamAsAttribute
+ public int id;
+ @XStreamAsAttribute
+ public String pwd;
+ @XStreamAsAttribute
+ private String username;
+ @XStreamAsAttribute
+ public UserPerm perm;
+ @XStreamAsAttribute
+ public boolean notify;
+ @XStreamAsAttribute
+ public String sc;
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/CommandType.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/CommandType.java
new file mode 100644
index 0000000000000..e99d75947ee33
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/CommandType.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.xml.dto;
+
+import static org.openhab.binding.mynice.internal.xml.RequestBuilder.*;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * The CommandType enum lists all handled command with according syntax
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public enum CommandType {
+ PAIR(false,
+ ""),
+ VERIFY(false, ""),
+ CONNECT(false, ""),
+ INFO(true, ""),
+ STATUS(true, ""),
+ CHANGE(true, "%s");
+
+ public final boolean signNeeded;
+ public final String body;
+
+ CommandType(boolean signNeeded, String body) {
+ this.signNeeded = signNeeded;
+ this.body = body;
+ }
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Device.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Device.java
new file mode 100644
index 0000000000000..e4a0b3b042ac9
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Device.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.xml.dto;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+public class Device {
+ public enum DeviceType {
+ SECTIONAL,
+ UP_AND_OVER,
+ SLIDING,
+ BARRIER,
+ SWING;
+ }
+
+ @XStreamAsAttribute
+ public String id;
+
+ @XStreamAlias("Type")
+ public DeviceType type;
+
+ @XStreamAlias("Manuf")
+ public String manuf;
+
+ @XStreamAlias("Prod")
+ public String prod;
+
+ @XStreamAlias("Desc")
+ public String desc;
+
+ @XStreamAlias("VersionHW")
+ public String versionHW;
+
+ @XStreamAlias("VersionFW")
+ public String versionFW;
+
+ @XStreamAlias("SerialNr")
+ public String serialNr;
+
+ @XStreamAlias("Properties")
+ public Properties properties;
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Error.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Error.java
new file mode 100644
index 0000000000000..4e753d1497227
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Error.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.xml.dto;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+public class Error {
+ @XStreamAlias("Code")
+ public int code;
+ @XStreamAlias("Info")
+ public String info;
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Event.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Event.java
new file mode 100644
index 0000000000000..3a55872cabf6f
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Event.java
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.xml.dto;
+
+import java.util.List;
+
+import org.eclipse.jdt.annotation.NonNull;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+public class Event {
+ @XStreamAsAttribute
+ private String id;
+ @XStreamAsAttribute
+ private String source;
+ @XStreamAsAttribute
+ private String target;
+ @XStreamAsAttribute
+ private String protocolType;
+ @XStreamAsAttribute
+ private String protocolVersion;
+ @XStreamAsAttribute
+ public CommandType type;
+ @XStreamAlias("Error")
+ public Error error;
+
+ @XStreamAlias("Devices")
+ private List devices;
+
+ public @NonNull List getDevices() {
+ List localDevices = devices;
+ return localDevices == null ? List.of() : localDevices;
+ }
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Interface.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Interface.java
new file mode 100644
index 0000000000000..02e15d0f8b293
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Interface.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.xml.dto;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+public class Interface {
+ @XStreamAlias("Zone")
+ public String zone;
+ @XStreamAlias("DST")
+ public String dst;
+ @XStreamAlias("VersionHW")
+ public String versionHW;
+ @XStreamAlias("VersionFW")
+ public String versionFW;
+ @XStreamAlias("Manuf")
+ public String manuf;
+ @XStreamAlias("Prod")
+ public String prod;
+ @XStreamAlias("SerialNr")
+ public String serialNr;
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Properties.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Properties.java
new file mode 100644
index 0000000000000..e90b6c0430874
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Properties.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.xml.dto;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@XStreamAlias("Properties")
+public class Properties {
+ @XStreamAlias("DoorStatus")
+ public String doorStatus;
+ @XStreamAlias("Obstruct")
+ public String obstruct;
+ @XStreamAlias("T4_allowed")
+ public Property t4allowed;
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Property.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Property.java
new file mode 100644
index 0000000000000..80589288624da
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Property.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.xml.dto;
+
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+public class Property {
+ @XStreamAsAttribute
+ public String type;
+ @XStreamAsAttribute
+ public String values;
+ @XStreamAsAttribute
+ public String perm;
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Response.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Response.java
new file mode 100644
index 0000000000000..f6ae5c4dc75fc
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/Response.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.xml.dto;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+
+/**
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+public class Response extends Event {
+ @XStreamAlias("Authentication")
+ public Authentication authentication;
+ @XStreamAlias("Interface")
+ public Interface intf;
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/T4Command.java b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/T4Command.java
new file mode 100644
index 0000000000000..3b4812d5b0eec
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/java/org/openhab/binding/mynice/internal/xml/dto/T4Command.java
@@ -0,0 +1,67 @@
+/**
+ * Copyright (c) 2010-2023 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.mynice.internal.xml.dto;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
+/**
+ * This enum lists all handled T4 commands
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public enum T4Command {
+ MDAx(1),
+ MDAy(2),
+ MDAz(3),
+ MDA0(4),
+ MDA1(5),
+ MDA2(6),
+ MDA3(7),
+ MDBi(11),
+ MDBj(12),
+ MDBk(13),
+ MDBl(14),
+ MDBm(15),
+ MDEw(16),
+ MDEx(17),
+ MDEy(18),
+ MDEz(19),
+ MDE0(20),
+ MDE1(21),
+ MDE2(22),
+ MDE3(23),
+ MDE4(24),
+ MDE5(25),
+ MDFh(26);
+
+ private int bitPosition;
+
+ private T4Command(int bitPosition) {
+ this.bitPosition = bitPosition;
+ }
+
+ public static T4Command fromCode(String commandCode) {
+ return Stream.of(T4Command.values()).filter(command -> command.name().equalsIgnoreCase(commandCode)).findFirst()
+ .orElseThrow(() -> new IllegalArgumentException("Unknown T4 command code (%s)".formatted(commandCode)));
+ }
+
+ public static List fromBitmask(int bitmask) {
+ return Stream.of(T4Command.values()).filter(command -> ((1 << command.bitPosition) & bitmask) != 0)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/bundles/org.openhab.binding.mynice/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.mynice/src/main/resources/OH-INF/addon/addon.xml
new file mode 100644
index 0000000000000..537e9cefc350b
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/resources/OH-INF/addon/addon.xml
@@ -0,0 +1,10 @@
+
+
+
+ binding
+ MyNice Binding
+ This binding lets you connect to your IT4Wifi Nice device.
+
+
diff --git a/bundles/org.openhab.binding.mynice/src/main/resources/OH-INF/i18n/mynice.properties b/bundles/org.openhab.binding.mynice/src/main/resources/OH-INF/i18n/mynice.properties
new file mode 100644
index 0000000000000..fe20f9f5288f1
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/resources/OH-INF/i18n/mynice.properties
@@ -0,0 +1,78 @@
+# add-on
+
+addon.mynice.name = MyNice Binding
+addon.mynice.description = This binding lets you connect to your IT4Wifi Nice device.
+
+# thing types
+
+thing-type.mynice.it4wifi.label = IT4Wifi
+thing-type.mynice.it4wifi.description = This thing connects to your IT4Wifi module
+thing-type.mynice.sliding.label = Sliding Gate
+thing-type.mynice.sliding.description = A sliding gate
+thing-type.mynice.swing.label = Swing Gate
+thing-type.mynice.swing.description = A dual swing gate
+
+# thing types config
+
+thing-type.config.mynice.it4wifi.hostname.label = Hostname
+thing-type.config.mynice.it4wifi.hostname.description = Hostname or IP address of the IT4Wifi
+thing-type.config.mynice.it4wifi.macAddress.label = MAC Address
+thing-type.config.mynice.it4wifi.macAddress.description = The MAC address of the IT4Wifi
+thing-type.config.mynice.it4wifi.password.label = Pairing Key
+thing-type.config.mynice.it4wifi.password.description = Pairing Key needed to access the device, provided by the bridge itself
+thing-type.config.mynice.it4wifi.username.label = Username
+thing-type.config.mynice.it4wifi.username.description = User defined on the IT4Wifi for openHAB
+thing-type.config.mynice.sliding.id.label = ID
+thing-type.config.mynice.sliding.id.description = ID of the gate on the TP4 bus connected to the bridge.
+thing-type.config.mynice.swing.id.label = ID
+thing-type.config.mynice.swing.id.description = ID of the gate on the TP4 bus connected to the bridge
+
+# channel types
+
+channel-type.mynice.command.label = Command
+channel-type.mynice.command.description = Send a given command to the gate
+channel-type.mynice.command.state.option.stop = Stop
+channel-type.mynice.command.state.option.open = Open
+channel-type.mynice.command.state.option.close = Close
+channel-type.mynice.doorstatus.label = Gate Status
+channel-type.mynice.doorstatus.description = Position of the gate or state if moving
+channel-type.mynice.doorstatus.state.option.open = Open
+channel-type.mynice.doorstatus.state.option.closed = Closed
+channel-type.mynice.doorstatus.state.option.opening = Opening
+channel-type.mynice.doorstatus.state.option.closing = Closing
+channel-type.mynice.doorstatus.state.option.stopped = Stopped
+channel-type.mynice.moving.label = Moving
+channel-type.mynice.moving.description = Indicates if the device is currently operating a command
+channel-type.mynice.obstruct.label = Obstruction
+channel-type.mynice.obstruct.description = Something prevented normal operation of the gate by crossing the infra-red barrier
+channel-type.mynice.t4command.label = T4 Command
+channel-type.mynice.t4command.description = Send a T4 Command to the gate
+channel-type.mynice.t4command.state.option.MDAx = Step by Step
+channel-type.mynice.t4command.state.option.MDAy = Stop (as remote control)
+channel-type.mynice.t4command.state.option.MDAz = Open (as remote control)
+channel-type.mynice.t4command.state.option.MDA0 = Close (as remote control)
+channel-type.mynice.t4command.state.option.MDA1 = Partial opening 1
+channel-type.mynice.t4command.state.option.MDA2 = Partial opening 2
+channel-type.mynice.t4command.state.option.MDA3 = Partial opening 3
+channel-type.mynice.t4command.state.option.MDBi = Apartment Step by Step
+channel-type.mynice.t4command.state.option.MDBj = Step by Step high priority
+channel-type.mynice.t4command.state.option.MDBk = Open and block
+channel-type.mynice.t4command.state.option.MDBl = Close and block
+channel-type.mynice.t4command.state.option.MDBm = Block
+channel-type.mynice.t4command.state.option.MDEw = Release
+channel-type.mynice.t4command.state.option.MDEx = Courtesy light timer on
+channel-type.mynice.t4command.state.option.MDEy = Courtesy light on-off
+channel-type.mynice.t4command.state.option.MDEz = Step by Step master door
+channel-type.mynice.t4command.state.option.MDE0 = Open master door
+channel-type.mynice.t4command.state.option.MDE1 = Close master door
+channel-type.mynice.t4command.state.option.MDE2 = Step by Step slave door
+channel-type.mynice.t4command.state.option.MDE3 = Open slave door
+channel-type.mynice.t4command.state.option.MDE4 = Close slave door
+channel-type.mynice.t4command.state.option.MDE5 = Release and Open
+channel-type.mynice.t4command.state.option.MDFh = Release and Close
+
+# error messages
+
+conf-error-no-username = Please define a username for this thing
+conf-pending-validation = Please validate the user on the MyNice application
+error-handshake-limit = Maximum handshake attempts reached
diff --git a/bundles/org.openhab.binding.mynice/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.mynice/src/main/resources/OH-INF/thing/thing-types.xml
new file mode 100644
index 0000000000000..4809d4f149b44
--- /dev/null
+++ b/bundles/org.openhab.binding.mynice/src/main/resources/OH-INF/thing/thing-types.xml
@@ -0,0 +1,166 @@
+
+
+
+
+
+ This thing connects to your IT4Wifi module
+
+ macAddress
+
+
+
+
+ User defined on the IT4Wifi for openHAB
+
+
+ network-address
+
+ Hostname or IP address of the IT4Wifi
+
+
+
+ The MAC address of the IT4Wifi
+
+
+ password
+
+ Pairing Key needed to access the device, provided by the bridge itself
+
+
+
+
+
+
+
+
+
+
+ A dual swing gate
+
+
+
+
+
+
+
+
+
+ id
+
+
+
+
+ ID of the gate on the TP4 bus connected to the bridge
+
+
+
+
+
+
+
+
+
+
+ A sliding gate
+
+
+
+
+
+
+
+
+
+ id
+
+
+
+
+ ID of the gate on the TP4 bus connected to the bridge.
+
+
+
+
+
+
+ String
+
+ Position of the gate or state if moving
+
+
+
+
+
+
+
+
+
+
+
+
+ Switch
+
+ Indicates if the device is currently operating a command
+
+
+
+
+ Switch
+
+ Something prevented normal operation of the gate by crossing the infra-red barrier
+
+
+
+
+ String
+
+ Send a given command to the gate
+
+
+
+
+
+
+
+ veto
+
+
+
+ String
+
+ Send a T4 Command to the gate
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ veto
+
+
+
diff --git a/bundles/pom.xml b/bundles/pom.xml
index e22d555988c61..23bf367cd0e9e 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -250,6 +250,7 @@
org.openhab.binding.mqtt.homie
org.openhab.binding.mybmw
org.openhab.binding.mycroft
+ org.openhab.binding.mynice
org.openhab.binding.myq
org.openhab.binding.mystrom
org.openhab.binding.nanoleaf