mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[mynice] Binding for IT4Wifi module (Nice gate doors) (#12940)
Signed-off-by: clinique <gael@lhopital.org>
This commit is contained in:
parent
d130595f85
commit
3b5dfb11a0
@ -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
|
||||
|
@ -1081,6 +1081,11 @@
|
||||
<artifactId>org.openhab.binding.mybmw</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.mynice</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.myq</artifactId>
|
||||
|
13
bundles/org.openhab.binding.mynice/NOTICE
Normal file
13
bundles/org.openhab.binding.mynice/NOTICE
Normal file
@ -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
|
114
bundles/org.openhab.binding.mynice/README.md
Normal file
114
bundles/org.openhab.binding.mynice/README.md
Normal file
@ -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" <gate> (gMyniceSwing) ["Status","Opening"] {channel="mynice:swing:83eef09166:1:status"}
|
||||
String NiceIT4WIFI_Obstruction "Obstruction" <none> (gMyniceSwing) {channel="mynice:swing:83eef09166:1:obstruct"}
|
||||
Switch NiceIT4WIFI_Moving "Moving" <motion> (gMyniceSwing) ["Status","Vibration"] {channel="mynice:swing:83eef09166:1:moving"}
|
||||
String NiceIT4WIFI_Command "Command" <none> (gMyniceSwing) {channel="mynice:swing:83eef09166:1:command"}
|
||||
|
||||
```
|
17
bundles/org.openhab.binding.mynice/pom.xml
Normal file
17
bundles/org.openhab.binding.mynice/pom.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
|
||||
<version>4.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.mynice</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: MyNice Binding</name>
|
||||
|
||||
</project>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.mynice-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-mynice" description="MyNice Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.mynice/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
@ -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";
|
||||
}
|
@ -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<ThingTypeUID> 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;
|
||||
}
|
||||
}
|
@ -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 = "";
|
||||
}
|
@ -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<ThingTypeUID> 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();
|
||||
}
|
||||
}
|
@ -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<Device> 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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<It4WifiHandler> 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<Device> 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<String> 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;
|
||||
});
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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<MyNiceDataListener> dataListeners = new CopyOnWriteArrayList<>();
|
||||
private final MyNiceXStream xstream = new MyNiceXStream();
|
||||
|
||||
private @NonNullByDefault({}) RequestBuilder reqBuilder;
|
||||
private @Nullable It4WifiConnector connector;
|
||||
private @Nullable ScheduledFuture<?> keepAliveJob;
|
||||
private List<Device> devices = new ArrayList<>();
|
||||
private int handshakeAttempts = 0;
|
||||
|
||||
public It4WifiHandler(Bridge thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<? extends ThingHandlerService>> 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<String, String> 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<Device> 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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<Device> devices);
|
||||
}
|
@ -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 = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>";
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -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 = "<Request id=\"%s\" source=\"openhab\" target=\"%s\" gw=\"gwID\" protocolType=\"NHK\" protocolVersion=\"1.0\" type=\"%s\">\r\n";
|
||||
private static final String END_REQUEST = "%s%s</Request>";
|
||||
private static final String DOOR_ACTION = "<DoorAction>%s</DoorAction>";
|
||||
private static final String T4_ACTION = "<T4Action>%s</T4Action>";
|
||||
private static final String SIGN = "<Sign>%s</Sign>";
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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,
|
||||
"<Authentication username=\"" + USERNAME
|
||||
+ "\" cc=\"null\" CType=\"phone\" OSType=\"Android\" OSVer=\"6.0.1\"/>"),
|
||||
VERIFY(false, "<User username=\"" + USERNAME + "\"/>"),
|
||||
CONNECT(false, "<Authentication username=\"" + USERNAME + "\" cc=\"" + CLIENT_CHALLENGE + "\"/>"),
|
||||
INFO(true, ""),
|
||||
STATUS(true, ""),
|
||||
CHANGE(true, "<Devices><Device id=\"%s\"><Services>%s</Services></Device></Devices>");
|
||||
|
||||
public final boolean signNeeded;
|
||||
public final String body;
|
||||
|
||||
CommandType(boolean signNeeded, String body) {
|
||||
this.signNeeded = signNeeded;
|
||||
this.body = body;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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<Device> devices;
|
||||
|
||||
public @NonNull List<Device> getDevices() {
|
||||
List<Device> localDevices = devices;
|
||||
return localDevices == null ? List.of() : localDevices;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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<T4Command> fromBitmask(int bitmask) {
|
||||
return Stream.of(T4Command.values()).filter(command -> ((1 << command.bitPosition) & bitmask) != 0)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<addon:addon id="mynice" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:addon="https://openhab.org/schemas/addon/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/addon/v1.0.0 https://openhab.org/schemas/addon-1.0.0.xsd">
|
||||
|
||||
<type>binding</type>
|
||||
<name>MyNice Binding</name>
|
||||
<description>This binding lets you connect to your IT4Wifi Nice device.</description>
|
||||
|
||||
</addon:addon>
|
@ -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
|
@ -0,0 +1,166 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="mynice"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<bridge-type id="it4wifi">
|
||||
<label>IT4Wifi</label>
|
||||
<description>This thing connects to your IT4Wifi module</description>
|
||||
|
||||
<representation-property>macAddress</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="username" type="text">
|
||||
<label>Username</label>
|
||||
<description>User defined on the IT4Wifi for openHAB</description>
|
||||
</parameter>
|
||||
<parameter name="hostname" type="text" required="true">
|
||||
<context>network-address</context>
|
||||
<label>Hostname</label>
|
||||
<description>Hostname or IP address of the IT4Wifi</description>
|
||||
</parameter>
|
||||
<parameter name="macAddress" type="text" pattern="([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}(\s*#.*)*"
|
||||
required="true">
|
||||
<label>MAC Address</label>
|
||||
<description>The MAC address of the IT4Wifi</description>
|
||||
</parameter>
|
||||
<parameter name="password" type="text">
|
||||
<context>password</context>
|
||||
<label>Pairing Key</label>
|
||||
<description>Pairing Key needed to access the device, provided by the bridge itself</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
<thing-type id="swing">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="it4wifi"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Swing Gate</label>
|
||||
<description>A dual swing gate</description>
|
||||
|
||||
<channels>
|
||||
<channel id="status" typeId="doorstatus"/>
|
||||
<channel id="obstruct" typeId="obstruct"/>
|
||||
<channel id="moving" typeId="moving"/>
|
||||
<channel id="command" typeId="command"/>
|
||||
<channel id="t4command" typeId="t4command"/>
|
||||
</channels>
|
||||
|
||||
<representation-property>id</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="id" type="text" required="true">
|
||||
<label>ID</label>
|
||||
<description>ID of the gate on the TP4 bus connected to the bridge</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="sliding">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="it4wifi"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Sliding Gate</label>
|
||||
<description>A sliding gate</description>
|
||||
|
||||
<channels>
|
||||
<channel id="status" typeId="doorstatus"/>
|
||||
<channel id="obstruct" typeId="obstruct"/>
|
||||
<channel id="moving" typeId="moving"/>
|
||||
<channel id="command" typeId="command"/>
|
||||
<channel id="t4command" typeId="t4command"/>
|
||||
</channels>
|
||||
|
||||
<representation-property>id</representation-property>
|
||||
|
||||
<config-description>
|
||||
<parameter name="id" type="text" required="true">
|
||||
<label>ID</label>
|
||||
<description>ID of the gate on the TP4 bus connected to the bridge.</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
|
||||
<channel-type id="doorstatus">
|
||||
<item-type>String</item-type>
|
||||
<label>Gate Status</label>
|
||||
<description>Position of the gate or state if moving</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="open">Open</option>
|
||||
<option value="closed">Closed</option>
|
||||
<option value="opening">Opening</option>
|
||||
<option value="closing">Closing</option>
|
||||
<option value="stopped">Stopped</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="moving">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Moving</label>
|
||||
<description>Indicates if the device is currently operating a command</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="obstruct">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Obstruction</label>
|
||||
<description>Something prevented normal operation of the gate by crossing the infra-red barrier</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="command">
|
||||
<item-type>String</item-type>
|
||||
<label>Command</label>
|
||||
<description>Send a given command to the gate</description>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="stop">Stop</option>
|
||||
<option value="open">Open</option>
|
||||
<option value="close">Close</option>
|
||||
</options>
|
||||
</state>
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="t4command">
|
||||
<item-type>String</item-type>
|
||||
<label>T4 Command</label>
|
||||
<description>Send a T4 Command to the gate</description>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="MDAx">Step by Step</option>
|
||||
<option value="MDAy">Stop (as remote control)</option>
|
||||
<option value="MDAz">Open (as remote control)</option>
|
||||
<option value="MDA0">Close (as remote control)</option>
|
||||
<option value="MDA1">Partial opening 1</option>
|
||||
<option value="MDA2">Partial opening 2</option>
|
||||
<option value="MDA3">Partial opening 3</option>
|
||||
<option value="MDBi">Apartment Step by Step</option>
|
||||
<option value="MDBj">Step by Step high priority</option>
|
||||
<option value="MDBk">Open and block</option>
|
||||
<option value="MDBl">Close and block</option>
|
||||
<option value="MDBm">Block</option>
|
||||
<option value="MDEw">Release</option>
|
||||
<option value="MDEx">Courtesy light timer on</option>
|
||||
<option value="MDEy">Courtesy light on-off</option>
|
||||
<option value="MDEz">Step by Step master door</option>
|
||||
<option value="MDE0">Open master door</option>
|
||||
<option value="MDE1">Close master door</option>
|
||||
<option value="MDE2">Step by Step slave door</option>
|
||||
<option value="MDE3">Open slave door</option>
|
||||
<option value="MDE4">Close slave door</option>
|
||||
<option value="MDE5">Release and Open</option>
|
||||
<option value="MDFh">Release and Close</option>
|
||||
</options>
|
||||
</state>
|
||||
<autoUpdatePolicy>veto</autoUpdatePolicy>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
@ -250,6 +250,7 @@
|
||||
<module>org.openhab.binding.mqtt.homie</module>
|
||||
<module>org.openhab.binding.mybmw</module>
|
||||
<module>org.openhab.binding.mycroft</module>
|
||||
<module>org.openhab.binding.mynice</module>
|
||||
<module>org.openhab.binding.myq</module>
|
||||
<module>org.openhab.binding.mystrom</module>
|
||||
<module>org.openhab.binding.nanoleaf</module>
|
||||
|
Loading…
Reference in New Issue
Block a user