added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.silvercrestwifisocket</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View 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

View File

@@ -0,0 +1,48 @@
# Silvercrest Wifi Plug Binding
This binding integrates the Silvercrest Wifi Socket SWS-A1 sold by Lidl and the EasyHome Wifi Socket DIS-124 sold by Aldi.
## Supported Things
- Silvercrest Wifi Socket SWS-A1 - [(Owner Manual)](https://www.lidl-service.com/static/118127777/103043_FI.pdf) -- Tested with firmware version: 1.41, 1.60, 1.70
- EasyHome Wifi Socket DIS-124 <https://www.aldi-sued.de/de/infos/aldi-sued-a-bis-z/s/serviceportal/ergebnisliste/sis/si/wifi-steckdose/>
## Discovery
The Discovery of Wifi Sockets is always running in the background.
If a command is sent to wifi socket using the Android/iOS app or if the physical button in the device is pressed, the device will be recognized and will be placed in the Inbox.
## Binding Configuration
The binding does not require any special configuration.
The Wifi Socket should be connected to the same wifi network.
## Thing Configuration
To configure a Wifi Socket manually the mac address and the vendor is required.
You can check the Wifi Socket mac address in your router or using some mobile app.
Supported vendors are either Silvercrest (Lidl) or EasyHome (Aldi).
Wifi Socket thing parameters:
| Parameter ID | Parameter Type | Mandatory | Description | Default |
|----------------|----------------|-----------|-------------------------------------------------------------------------------|------------------|
| macAddress | text | true | The socket MAC address | |
| hostAddress | text | false | The socket Host address. The binding is capable to discover the host address. | |
| updateInterval | integer | false | Update time interval in seconds to request the status of the socket. | 60 |
| vendor | text | true | The vendor of the system ("ALDI_EASYHOME" or "LIDL_SILVERCREST") | LIDL_SILVERCREST |
E.g.
```
Thing silvercrestwifisocket:wifiSocket:lamp [ macAddress="ACCF23343C50", vendor="ALDI_EASYHOME" ]
```
## Channels
The Silvercrest Wifi Socket support the following channel:
| Channel Type ID | Item Type | Description |
|-----------------|-----------|---------------------|
| switch | Switch | Wifi Socket Switch. |

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://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>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.silvercrestwifisocket</artifactId>
<name>openHAB Add-ons :: Bundles :: Silvercrest Wifi Socket Binding</name>
</project>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.silvercrestwifisocket-${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-silvercrestwifisocket" description="Silvercrest Wifi Plug Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.silvercrestwifisocket/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal;
import java.util.Collections;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.silvercrestwifisocket.internal.enums.SilvercrestWifiSocketVendor;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link SilvercrestWifiSocketBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Jaime Vaz - Initial contribution
* @author Christian Heimerl - for integration of EasyHome
*/
@NonNullByDefault
public class SilvercrestWifiSocketBindingConstants {
/**
* The binding id.
*/
public static final String BINDING_ID = "silvercrestwifisocket";
/**
* List of all Thing Type UIDs.
*/
public static final ThingTypeUID THING_TYPE_WIFI_SOCKET = new ThingTypeUID(BINDING_ID, "wifiSocket");
/**
* List of all Channel ids
*/
public static final String WIFI_SOCKET_CHANNEL_ID = "switch";
/**
* The supported thing types.
*/
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_WIFI_SOCKET);
// -------------- Configuration arguments ----------------
/**
* Mac address configuration argument key.
*/
public static final String MAC_ADDRESS_ARG = "macAddress";
/**
* Wifi socket update interval configuration argument key.
*/
public static final String UPDATE_INTERVAL_ARG = "updateInterval";
/**
* Host address configuration argument key.
*/
public static final String HOST_ADDRESS_ARG = "hostAddress";
/**
* Host address configuration argument key.
*/
public static final String VENDOR_ARG = "vendor";
// -------------- Default values ----------------
/**
* Default Wifi socket refresh interval.
*/
public static final long DEFAULT_REFRESH_INTERVAL = 60;
/**
* Default Wifi socket vendor.
*/
public static final SilvercrestWifiSocketVendor DEFAULT_VENDOR = SilvercrestWifiSocketVendor.LIDL_SILVERCREST;
/**
* Default Wifi socket default UDP port.
*/
public static final int WIFI_SOCKET_DEFAULT_UDP_PORT = 8530;
/**
* Discovery timeout in seconds.
*/
public static final int DISCOVERY_TIMEOUT_SECONDS = 4;
}

View File

@@ -0,0 +1,105 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal;
import java.util.Collections;
import java.util.Set;
import org.openhab.binding.silvercrestwifisocket.internal.exceptions.MacAddressNotValidException;
import org.openhab.binding.silvercrestwifisocket.internal.handler.SilvercrestWifiSocketHandler;
import org.openhab.binding.silvercrestwifisocket.internal.handler.SilvercrestWifiSocketMediator;
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;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SilvercrestWifiSocketHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Jaime Vaz - Initial contribution
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.silvercrestwifisocket")
public class SilvercrestWifiSocketHandlerFactory extends BaseThingHandlerFactory {
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
.singleton(SilvercrestWifiSocketBindingConstants.THING_TYPE_WIFI_SOCKET);
private final Logger logger = LoggerFactory.getLogger(SilvercrestWifiSocketHandlerFactory.class);
private SilvercrestWifiSocketMediator mediator;
/**
* Used by OSGI to inject the mediator in the handler factory.
*
* @param mediator the mediator
*/
@Reference
public void setMediator(final SilvercrestWifiSocketMediator mediator) {
logger.debug("Mediator has been injected on handler factory service.");
this.mediator = mediator;
}
/**
* Used by OSGI to unsets the mediator from the handler factory.
*
* @param mediator the mediator
*/
public void unsetMediator(final SilvercrestWifiSocketMediator mitsubishiMediator) {
logger.debug("Mediator has been unsetted from discovery service.");
this.mediator = null;
}
@Override
public boolean supportsThingType(final ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected ThingHandler createHandler(final Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(SilvercrestWifiSocketBindingConstants.THING_TYPE_WIFI_SOCKET)) {
SilvercrestWifiSocketHandler handler;
logger.debug("Creating a new SilvercrestWifiSocketHandler...");
try {
handler = new SilvercrestWifiSocketHandler(thing);
logger.debug("SilvercrestWifiSocketMediator will register the handler.");
if (this.mediator != null) {
this.mediator.registerThingAndWifiSocketHandler(thing, handler);
} else {
logger.error(
"The mediator is missing on Handler factory. Without one mediator the handler cannot work!");
return null;
}
return handler;
} catch (MacAddressNotValidException e) {
logger.debug("The mac address passed to WifiSocketHandler by configurations is not valid.");
}
}
return null;
}
@Override
public void unregisterHandler(final Thing thing) {
if (this.mediator != null) {
this.mediator.unregisterWifiSocketHandlerByThing(thing);
}
super.unregisterHandler(thing);
}
}

View File

@@ -0,0 +1,116 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.discovery;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.openhab.binding.silvercrestwifisocket.internal.SilvercrestWifiSocketBindingConstants;
import org.openhab.binding.silvercrestwifisocket.internal.handler.SilvercrestWifiSocketMediator;
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.config.discovery.DiscoveryService;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This is the {@link DiscoveryService} for the Silvercrest Items.
*
* @author Jaime Vaz - Initial contribution
*
*/
@Component(service = DiscoveryService.class, immediate = true, configurationPid = "discovery.silvercrestwifisocket")
public class SilvercrestWifiSocketDiscoveryService extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(SilvercrestWifiSocketDiscoveryService.class);
private SilvercrestWifiSocketMediator mediator;
/**
* Used by OSGI to inject the mediator in the discovery service.
*
* @param mediator the mediator
*/
@Reference
public void setMediator(final SilvercrestWifiSocketMediator mediator) {
logger.debug("Mediator has been injected on discovery service.");
this.mediator = mediator;
mediator.setDiscoveryService(this);
}
/**
* Used by OSGI to unset the mediator in the discovery service.
*
* @param mediator the mediator
*/
public void unsetMediator(final SilvercrestWifiSocketMediator mitsubishiMediator) {
logger.debug("Mediator has been unsetted from discovery service.");
this.mediator.setDiscoveryService(null);
this.mediator = null;
}
/**
* Constructor of the discovery service.
*
* @throws IllegalArgumentException if the timeout < 0
*/
public SilvercrestWifiSocketDiscoveryService() throws IllegalArgumentException {
super(SilvercrestWifiSocketBindingConstants.SUPPORTED_THING_TYPES_UIDS,
SilvercrestWifiSocketBindingConstants.DISCOVERY_TIMEOUT_SECONDS);
}
@Override
public Set<ThingTypeUID> getSupportedThingTypes() {
return SilvercrestWifiSocketBindingConstants.SUPPORTED_THING_TYPES_UIDS;
}
@Override
protected void startScan() {
logger.debug("Don't need to start new scan... background scanning in progress by mediator.");
}
/**
* Method called by mediator, when receive one packet from one unknown Wifi Socket.
*
* @param macAddress the mack address from the device.
* @param hostAddress the host address from the device.
*/
public void discoveredWifiSocket(final String macAddress, final String hostAddress) {
Map<String, Object> properties = new HashMap<>(2);
properties.put(SilvercrestWifiSocketBindingConstants.MAC_ADDRESS_ARG, macAddress);
properties.put(SilvercrestWifiSocketBindingConstants.HOST_ADDRESS_ARG, hostAddress);
ThingUID newThingId = new ThingUID(SilvercrestWifiSocketBindingConstants.THING_TYPE_WIFI_SOCKET, macAddress);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(newThingId).withProperties(properties)
.withLabel("Silvercrest Wifi Socket").withRepresentationProperty(macAddress).build();
logger.debug("Discovered new thing with mac address '{}' and host address '{}'", macAddress, hostAddress);
this.thingDiscovered(discoveryResult);
}
// SETTERS AND GETTERS
/**
* Gets the {@link SilvercrestWifiSocketMediator} of this binding.
*
* @return {@link SilvercrestWifiSocketMediator}.
*/
public SilvercrestWifiSocketMediator getMediator() {
return this.mediator;
}
}

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.entities;
import org.openhab.binding.silvercrestwifisocket.internal.enums.SilvercrestWifiSocketRequestType;
import org.openhab.binding.silvercrestwifisocket.internal.enums.SilvercrestWifiSocketVendor;
/**
* This POJO represents one Wifi Socket request.
*
* @author Jaime Vaz - Initial contribution
* @author Christian Heimerl - for integration of EasyHome
*
*/
public class SilvercrestWifiSocketRequest {
private String macAddress;
private SilvercrestWifiSocketRequestType type;
private SilvercrestWifiSocketVendor vendor;
/**
* Default constructor.
*
* @param macAddress the mac address
* @param type the {@link SilvercrestWifiSocketRequestType}
* @param vendor the {@link SilvercrestWifiSocketVendor}
*/
public SilvercrestWifiSocketRequest(final String macAddress, final SilvercrestWifiSocketRequestType type,
final SilvercrestWifiSocketVendor vendor) {
this.macAddress = macAddress;
this.type = type;
this.vendor = vendor;
}
public String getMacAddress() {
return this.macAddress;
}
public void setMacAddress(final String macAddress) {
this.macAddress = macAddress;
}
public SilvercrestWifiSocketRequestType getType() {
return this.type;
}
public void setType(final SilvercrestWifiSocketRequestType type) {
this.type = type;
}
public SilvercrestWifiSocketVendor getVendor() {
return vendor;
}
public void setVendor(SilvercrestWifiSocketVendor vendor) {
this.vendor = vendor;
}
}

View File

@@ -0,0 +1,92 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.entities;
import org.openhab.binding.silvercrestwifisocket.internal.enums.SilvercrestWifiSocketResponseType;
import org.openhab.binding.silvercrestwifisocket.internal.enums.SilvercrestWifiSocketVendor;
/**
* This POJO represents one Wifi Socket Response.
*
* @author Jaime Vaz - Initial contribution
* @author Christian Heimerl - for integration of EasyHome
*
*/
public class SilvercrestWifiSocketResponse {
private String macAddress;
private String hostAddress;
private SilvercrestWifiSocketResponseType type;
private SilvercrestWifiSocketVendor vendor;
/**
* Default constructor.
*
* @param macAddress the mac address
* @param hostAddress the host address
* @param type the {@link SilvercrestWifiSocketResponseType}
* @param vendor the vendor of the socket
*/
public SilvercrestWifiSocketResponse(final String macAddress, final String hostAddress,
final SilvercrestWifiSocketResponseType type, final SilvercrestWifiSocketVendor vendor) {
super();
this.macAddress = macAddress;
this.hostAddress = hostAddress;
this.type = type;
this.vendor = vendor;
}
/**
* Constructor.
*
* @param macAddress the mac address
* @param type the {@link SilvercrestWifiSocketResponseType}
* @param vendor the vendor of the socket
*/
public SilvercrestWifiSocketResponse(final String macAddress, final SilvercrestWifiSocketResponseType type,
final SilvercrestWifiSocketVendor vendor) {
this(macAddress, null, type, vendor);
}
public String getMacAddress() {
return this.macAddress;
}
public void setMacAddress(final String macAddress) {
this.macAddress = macAddress;
}
public SilvercrestWifiSocketResponseType getType() {
return this.type;
}
public void setType(final SilvercrestWifiSocketResponseType type) {
this.type = type;
}
public String getHostAddress() {
return this.hostAddress;
}
public void setHostAddress(final String hostAddress) {
this.hostAddress = hostAddress;
}
public SilvercrestWifiSocketVendor getVendor() {
return vendor;
}
public void setVendor(SilvercrestWifiSocketVendor vendor) {
this.vendor = vendor;
}
}

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.enums;
/**
* This enum represents the available Wifi Socket request types.
*
* @author Jaime Vaz - Initial contribution
*
*/
public enum SilvercrestWifiSocketRequestType {
/** Request ON. */
ON("010000FFFF04040404"),
/** Request OFF. */
OFF("01000000FF04040404"),
/** Request Status. */
GPIO_STATUS("020000000004040404"),
/** Discover socket. The command has one placeholder for the mac address. */
DISCOVERY("23%s0202");
private String command;
private SilvercrestWifiSocketRequestType(final String command) {
this.command = command;
}
/**
* Gets the hexadecimal command/format for include in request messages.
*
* @return the hexadecimal command/format
*/
public String getCommand() {
return this.command;
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.enums;
/**
* This enum represents the available Wifi Socket response types.
*
* @author Jaime Vaz - Initial contribution
*
*/
public enum SilvercrestWifiSocketResponseType {
/** Status changed to ON. */
ON,
/** Status changed to OFF. */
OFF,
/** ACKnowledgement. */
ACK,
/** Discovery request. */
DISCOVERY
}

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.enums;
/**
* This enum represents the available Wifi Socket vendors.
*
* @author Christian Heimerl - Initial contribution
*
*/
public enum SilvercrestWifiSocketVendor {
LIDL_SILVERCREST("C1", "7150"),
ALDI_EASYHOME("C2", "92DD");
private final String companyCode;
private String authenticationCode;
SilvercrestWifiSocketVendor(String companyCode, String authenticationCode) {
this.companyCode = companyCode;
this.authenticationCode = authenticationCode;
}
/**
* Gets the hexadecimal company code included in a request message.
*
* @return the hexadecimal company code
*/
public String getCompanyCode() {
return this.companyCode;
}
/**
* Gets the hexadecimal authentication code included in a request message
*
* @return the hexadecimal authentication code
*/
public String getAuthenticationCode() {
return this.authenticationCode;
}
/**
* Returns the SilvercrestWifiSocketVendor matching the given two digit company code.
*
* @param companyCode The two digit company code that should be searched for, e.g. C1
* @return A SilvercrestWifiSocketVendor or null if no vendor matches the company code.
*/
public static SilvercrestWifiSocketVendor fromCode(String companyCode) {
for (SilvercrestWifiSocketVendor v : SilvercrestWifiSocketVendor.values()) {
if (v.getCompanyCode().equals(companyCode)) {
return v;
}
}
return null;
}
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.exceptions;
/**
* Exception throwed when one Mac address is not valid.
*
* @author Jaime Vaz - Initial contribution
*
*/
public class MacAddressNotValidException extends Exception {
private static final long serialVersionUID = 6131138252323778017L;
private final String macAddress;
/**
* Default constructor.
*
* @param message the error message
* @param macAddress the wrong mac address.
*/
public MacAddressNotValidException(final String message, final String macAddress) {
super(message);
this.macAddress = macAddress;
}
// SETTERS AND GETTERS
public String getMacAddress() {
return this.macAddress;
}
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.exceptions;
/**
* Exception throwed when some packet is not one response packet.
*
* @author Jaime Vaz - Initial contribution
*
*/
public class NotOneResponsePacketException extends Exception {
private static final long serialVersionUID = -8531181654734497851L;
/**
* Default constructor.
*
* @param message the error message
*/
public NotOneResponsePacketException(final String message) {
super(message);
}
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.exceptions;
/**
* Exception throwed when some packet has one integrity error.
*
* @author Jaime Vaz - Initial contribution
*
*/
public class PacketIntegrityErrorException extends Exception {
private static final long serialVersionUID = -8531181654734497851L;
/**
* Default constructor.
*
* @param message the error message
*/
public PacketIntegrityErrorException(final String message) {
super(message);
}
}

View File

@@ -0,0 +1,384 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.handler;
import static org.openhab.binding.silvercrestwifisocket.internal.SilvercrestWifiSocketBindingConstants.WIFI_SOCKET_CHANNEL_ID;
import java.math.BigDecimal;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.openhab.binding.silvercrestwifisocket.internal.SilvercrestWifiSocketBindingConstants;
import org.openhab.binding.silvercrestwifisocket.internal.entities.SilvercrestWifiSocketRequest;
import org.openhab.binding.silvercrestwifisocket.internal.entities.SilvercrestWifiSocketResponse;
import org.openhab.binding.silvercrestwifisocket.internal.enums.SilvercrestWifiSocketRequestType;
import org.openhab.binding.silvercrestwifisocket.internal.enums.SilvercrestWifiSocketVendor;
import org.openhab.binding.silvercrestwifisocket.internal.exceptions.MacAddressNotValidException;
import org.openhab.binding.silvercrestwifisocket.internal.utils.NetworkUtils;
import org.openhab.binding.silvercrestwifisocket.internal.utils.ValidationUtils;
import org.openhab.binding.silvercrestwifisocket.internal.utils.WifiSocketPacketConverter;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SilvercrestWifiSocketHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Jaime Vaz - Initial contribution
* @author Christian Heimerl - for integration of EasyHome
*/
public class SilvercrestWifiSocketHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(SilvercrestWifiSocketHandler.class);
private String hostAddress;
private String macAddress;
private SilvercrestWifiSocketVendor vendor = SilvercrestWifiSocketBindingConstants.DEFAULT_VENDOR;
private Long updateInterval = SilvercrestWifiSocketBindingConstants.DEFAULT_REFRESH_INTERVAL;
private final WifiSocketPacketConverter converter = new WifiSocketPacketConverter();
private ScheduledFuture<?> keepAliveJob;
private long latestUpdate = -1;
/**
* Default constructor.
*
* @param thing the thing of the handler.
* @throws MacAddressNotValidException if the mac address isn't valid.
*/
public SilvercrestWifiSocketHandler(final Thing thing) throws MacAddressNotValidException {
super(thing);
this.saveMacAddressFromConfiguration(this.getConfig());
this.saveHostAddressFromConfiguration(this.getConfig());
this.saveUpdateIntervalFromConfiguration(this.getConfig());
this.saveVendorFromConfiguration(this.getConfig());
}
@Override
public void handleCommand(final ChannelUID channelUID, final Command command) {
if (channelUID.getId().equals(WIFI_SOCKET_CHANNEL_ID)) {
logger.debug("Silvercrest socket command received: {}", command);
if (command == OnOffType.ON) {
this.sendCommand(SilvercrestWifiSocketRequestType.ON);
} else if (command == OnOffType.OFF) {
this.sendCommand(SilvercrestWifiSocketRequestType.OFF);
} else if (command == RefreshType.REFRESH) {
this.sendCommand(SilvercrestWifiSocketRequestType.GPIO_STATUS);
}
}
}
@Override
public void handleRemoval() {
// stop update thread
this.keepAliveJob.cancel(true);
super.handleRemoval();
}
/**
* Starts one thread that querys the state of the socket, after the defined refresh interval.
*/
private void initGetStatusAndKeepAliveThread() {
if (this.keepAliveJob != null) {
this.keepAliveJob.cancel(true);
}
// try with handler port if is null
Runnable runnable = () -> {
logger.debug(
"Begin of Socket keep alive thread routine. Current configuration update interval: {} seconds.",
SilvercrestWifiSocketHandler.this.updateInterval);
long now = System.currentTimeMillis();
long timePassedFromLastUpdateInSeconds = (now - SilvercrestWifiSocketHandler.this.latestUpdate) / 1000;
logger.trace("Latest Update: {} Now: {} Delta: {} seconds", SilvercrestWifiSocketHandler.this.latestUpdate,
now, timePassedFromLastUpdateInSeconds);
logger.debug("It has been passed {} seconds since the last update on socket with mac address {}.",
timePassedFromLastUpdateInSeconds, SilvercrestWifiSocketHandler.this.macAddress);
boolean mustUpdateHostAddress = timePassedFromLastUpdateInSeconds > (SilvercrestWifiSocketHandler.this.updateInterval
* 2);
if (mustUpdateHostAddress) {
logger.debug("No updates have been received for a long time, search the mac address {} in network...",
SilvercrestWifiSocketHandler.this.getMacAddress());
SilvercrestWifiSocketHandler.this.lookupForSocketHostAddress();
}
boolean considerThingOffline = (SilvercrestWifiSocketHandler.this.latestUpdate < 0)
|| (timePassedFromLastUpdateInSeconds > (SilvercrestWifiSocketHandler.this.updateInterval * 4));
if (considerThingOffline) {
logger.debug(
"No updates have been received for a long long time will put the thing with mac address {} OFFLINE.",
SilvercrestWifiSocketHandler.this.getMacAddress());
SilvercrestWifiSocketHandler.this.updateStatus(ThingStatus.OFFLINE);
}
// request gpio status
SilvercrestWifiSocketHandler.this.sendCommand(SilvercrestWifiSocketRequestType.GPIO_STATUS);
};
this.keepAliveJob = this.scheduler.scheduleWithFixedDelay(runnable, 1,
SilvercrestWifiSocketHandler.this.updateInterval, TimeUnit.SECONDS);
}
@Override
public void initialize() {
this.initGetStatusAndKeepAliveThread();
updateStatus(ThingStatus.ONLINE);
this.saveConfigurationsUsingCurrentStates();
}
/**
* Lookup for socket host address, by sending one broadcast discovery message. Eventually the socket will respond to
* the message. When the mediator receives the message, it will set the host address in this handler, for future
* communications.
*/
private void lookupForSocketHostAddress() {
SilvercrestWifiSocketRequest requestPacket = new SilvercrestWifiSocketRequest(this.macAddress,
SilvercrestWifiSocketRequestType.DISCOVERY, this.vendor);
for (InetAddress broadcastAddressFound : NetworkUtils.getAllBroadcastAddresses()) {
logger.debug("Will query for device with mac address {} in network with broadcast address {}",
this.macAddress, broadcastAddressFound);
this.sendRequestPacket(requestPacket, broadcastAddressFound);
}
}
/**
* Method called by {@link SilvercrestWifiSocketMediator} when one new message has been received for this handler.
*
* @param receivedMessage the received {@link SilvercrestWifiSocketResponse}.
*/
public void newReceivedResponseMessage(final SilvercrestWifiSocketResponse receivedMessage) {
// if the host of the packet is different from the host address set in handler, update the host
// address.
if (!receivedMessage.getHostAddress().equals(this.hostAddress)) {
logger.debug(
"The host of the packet is different from the host address set in handler. "
+ "Will update the host address. handler of mac: {}. "
+ "Old host address: '{}' -> new host address: '{}'",
this.macAddress, this.hostAddress, receivedMessage.getHostAddress());
this.hostAddress = receivedMessage.getHostAddress();
this.saveConfigurationsUsingCurrentStates();
}
switch (receivedMessage.getType()) {
case ACK:
break;
case DISCOVERY:
break;
case OFF:
this.updateState(SilvercrestWifiSocketBindingConstants.WIFI_SOCKET_CHANNEL_ID, OnOffType.OFF);
break;
case ON:
this.updateState(SilvercrestWifiSocketBindingConstants.WIFI_SOCKET_CHANNEL_ID, OnOffType.ON);
break;
default:
logger.debug("Command not found!");
break;
}
this.updateStatus(ThingStatus.ONLINE);
this.latestUpdate = System.currentTimeMillis();
}
/**
* Saves the host address from configuration in field.
*
* @param configuration The {@link Configuration}
*/
private void saveHostAddressFromConfiguration(final Configuration configuration) {
if ((configuration != null)
&& (configuration.get(SilvercrestWifiSocketBindingConstants.HOST_ADDRESS_ARG) != null)) {
this.hostAddress = String
.valueOf(configuration.get(SilvercrestWifiSocketBindingConstants.HOST_ADDRESS_ARG));
}
}
/**
* Saves the host address from configuration in field.
*
* @param configuration The {@link Configuration}
*/
private void saveUpdateIntervalFromConfiguration(final Configuration configuration) {
this.updateInterval = SilvercrestWifiSocketBindingConstants.DEFAULT_REFRESH_INTERVAL;
if ((configuration != null)
&& (configuration.get(SilvercrestWifiSocketBindingConstants.UPDATE_INTERVAL_ARG) instanceof BigDecimal)
&& (((BigDecimal) configuration.get(SilvercrestWifiSocketBindingConstants.UPDATE_INTERVAL_ARG))
.longValue() > 0)) {
this.updateInterval = ((BigDecimal) configuration
.get(SilvercrestWifiSocketBindingConstants.UPDATE_INTERVAL_ARG)).longValue();
}
}
/**
* Saves the mac address from configuration in field.
*
* @param configuration The {@link Configuration}
*/
private void saveMacAddressFromConfiguration(final Configuration configuration) throws MacAddressNotValidException {
if ((configuration != null)
&& (configuration.get(SilvercrestWifiSocketBindingConstants.MAC_ADDRESS_ARG) != null)) {
String macAddress = String
.valueOf(configuration.get(SilvercrestWifiSocketBindingConstants.MAC_ADDRESS_ARG));
if (ValidationUtils.isMacNotValid(macAddress)) {
throw new MacAddressNotValidException("Mac address is not valid", macAddress);
}
this.macAddress = macAddress.replaceAll(":", "").toUpperCase();
}
if (this.macAddress == null) {
throw new MacAddressNotValidException("Mac address is not valid", this.macAddress);
}
}
/**
* Saves the vendor from configuration in field.
*
* @param configuration The {@link Configuration}
*/
private void saveVendorFromConfiguration(final Configuration configuration) {
if ((configuration != null) && (configuration.get(SilvercrestWifiSocketBindingConstants.VENDOR_ARG) != null)) {
this.vendor = SilvercrestWifiSocketVendor
.valueOf(String.valueOf(configuration.get(SilvercrestWifiSocketBindingConstants.VENDOR_ARG)));
}
}
/**
* Sends one command to the Wifi Socket. If the host address is not set, it will trigger the lookup of the
* host address and discard the command queried.
*
* @param type the {@link SilvercrestWifiSocketRequestType} of the command.
*/
private void sendCommand(final SilvercrestWifiSocketRequestType type) {
logger.debug("Send command for mac addr: {} with type: {} with hostaddress: {}", this.getMacAddress(),
type.name(), this.hostAddress);
if (this.hostAddress == null) {
logger.debug(
"Send command cannot proceed until one Host Address is set for mac address: {} Will invoke one mac address lookup!",
this.macAddress);
this.lookupForSocketHostAddress();
} else {
InetAddress address;
try {
address = InetAddress.getByName(this.hostAddress);
this.sendRequestPacket(new SilvercrestWifiSocketRequest(this.macAddress, type, this.vendor), address);
} catch (UnknownHostException e) {
logger.debug("Host Address not found: {}. Will lookup Mac address.", this.hostAddress);
this.hostAddress = null;
this.lookupForSocketHostAddress();
}
}
}
/**
* Sends {@link SilvercrestWifiSocketRequest} to the passed {@link InetAddress}.
*
* @param requestPacket the {@link SilvercrestWifiSocketRequest}.
* @param address the {@link InetAddress}.
*/
private void sendRequestPacket(final SilvercrestWifiSocketRequest requestPacket, final InetAddress address) {
DatagramSocket dsocket = null;
try {
if (address != null) {
byte[] message = this.converter.transformToByteMessage(requestPacket);
logger.trace("Preparing packet to send...");
// Initialize a datagram packet with data and address
DatagramPacket packet = new DatagramPacket(message, message.length, address,
SilvercrestWifiSocketBindingConstants.WIFI_SOCKET_DEFAULT_UDP_PORT);
// Create a datagram socket, send the packet through it, close it.
dsocket = new DatagramSocket();
dsocket.send(packet);
logger.debug("Sent packet to address: {} and port {}", address,
SilvercrestWifiSocketBindingConstants.WIFI_SOCKET_DEFAULT_UDP_PORT);
}
} catch (Exception exception) {
logger.debug("Something wrong happen sending the packet to address: {} and port {}... msg: {}", address,
SilvercrestWifiSocketBindingConstants.WIFI_SOCKET_DEFAULT_UDP_PORT, exception.getMessage());
} finally {
if (dsocket != null) {
dsocket.close();
}
}
}
@Override
protected void updateConfiguration(final Configuration configuration) {
try {
this.latestUpdate = -1;
this.saveMacAddressFromConfiguration(configuration);
this.hostAddress = null;
this.saveHostAddressFromConfiguration(configuration);
if (this.hostAddress == null) {
this.lookupForSocketHostAddress();
}
this.saveUpdateIntervalFromConfiguration(configuration);
this.saveVendorFromConfiguration(configuration);
this.initGetStatusAndKeepAliveThread();
this.saveConfigurationsUsingCurrentStates();
} catch (MacAddressNotValidException e) {
logger.error("The Mac address passed is not valid! {}", e.getMacAddress());
this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
}
}
/**
* Save the current runtime configuration of the handler in configuration mechanism.
*/
private void saveConfigurationsUsingCurrentStates() {
Map<String, Object> map = new HashMap<>();
map.put(SilvercrestWifiSocketBindingConstants.MAC_ADDRESS_ARG, this.macAddress);
map.put(SilvercrestWifiSocketBindingConstants.HOST_ADDRESS_ARG, this.hostAddress);
map.put(SilvercrestWifiSocketBindingConstants.VENDOR_ARG, this.vendor.toString());
map.put(SilvercrestWifiSocketBindingConstants.UPDATE_INTERVAL_ARG, this.updateInterval);
Configuration newConfiguration = new Configuration(map);
super.updateConfiguration(newConfiguration);
}
// SETTERS AND GETTERS
public String getHostAddress() {
return this.hostAddress;
}
public String getMacAddress() {
return this.macAddress;
}
public SilvercrestWifiSocketVendor getVendor() {
return this.vendor;
}
}

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.handler;
import java.util.Set;
import org.openhab.binding.silvercrestwifisocket.internal.discovery.SilvercrestWifiSocketDiscoveryService;
import org.openhab.binding.silvercrestwifisocket.internal.entities.SilvercrestWifiSocketResponse;
import org.openhab.binding.silvercrestwifisocket.internal.runnable.SilvercrestWifiSocketUpdateReceiverRunnable;
import org.openhab.core.thing.Thing;
/**
* The {@link SilvercrestWifiSocketMediator} is responsible for receiving all the UDP packets and route correctly to
* each handler.
*
* @author Jaime Vaz - Initial contribution
*/
public interface SilvercrestWifiSocketMediator {
/**
* This method is called by the {@link SilvercrestWifiSocketUpdateReceiverRunnable}, when one new message has been
* received.
*
* @param receivedMessage the {@link SilvercrestWifiSocketResponse} message.
*/
void processReceivedPacket(final SilvercrestWifiSocketResponse receivedMessage);
/**
* Registers a new {@link Thing} and the corresponding {@link SilvercrestWifiSocketHandler}.
*
* @param thing the {@link Thing}.
* @param handler the {@link SilvercrestWifiSocketHandler}.
*/
void registerThingAndWifiSocketHandler(final Thing thing, final SilvercrestWifiSocketHandler handler);
/**
* Unregisters a {@link SilvercrestWifiSocketHandler} by the corresponding {@link Thing}.
*
* @param thing the {@link Thing}.
*/
void unregisterWifiSocketHandlerByThing(final Thing thing);
/**
* Returns all the {@link Thing} registered.
*
* @returns all the {@link Thing}.
*/
Set<Thing> getAllThingsRegistred();
/**
* Sets the discovery service to inform the when one new thing has been found.
*
* @param discoveryService the discovery service.
*/
void setDiscoveryService(SilvercrestWifiSocketDiscoveryService discoveryService);
}

View File

@@ -0,0 +1,171 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.handler;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.openhab.binding.silvercrestwifisocket.internal.SilvercrestWifiSocketBindingConstants;
import org.openhab.binding.silvercrestwifisocket.internal.discovery.SilvercrestWifiSocketDiscoveryService;
import org.openhab.binding.silvercrestwifisocket.internal.entities.SilvercrestWifiSocketResponse;
import org.openhab.binding.silvercrestwifisocket.internal.runnable.SilvercrestWifiSocketUpdateReceiverRunnable;
import org.openhab.core.thing.Thing;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SilvercrestWifiSocketMediatorImpl} is responsible for receiving all the UDP packets and route correctly to
* each handler.
*
* @author Jaime Vaz - Initial contribution
*/
@Component(service = SilvercrestWifiSocketMediator.class, immediate = true)
public class SilvercrestWifiSocketMediatorImpl implements SilvercrestWifiSocketMediator {
private final Logger logger = LoggerFactory.getLogger(SilvercrestWifiSocketMediatorImpl.class);
private final Map<Thing, SilvercrestWifiSocketHandler> handlersRegistredByThing = new HashMap<>();
private SilvercrestWifiSocketUpdateReceiverRunnable receiver;
private Thread receiverThread;
private SilvercrestWifiSocketDiscoveryService silvercrestDiscoveryService;
/**
* Called at the service activation.
*
* @param componentContext the componentContext
*/
protected void activate(final ComponentContext componentContext) {
logger.debug("Mediator has been activated by OSGI.");
this.initMediatorWifiSocketUpdateReceiverRunnable();
}
/**
* Called at the service deactivation.
*
* @param componentContext the componentContext
*/
protected void deactivate(final ComponentContext componentContext) {
if (this.receiver != null) {
this.receiver.shutdown();
}
}
/**
* This method is called by the {@link SilvercrestWifiSocketUpdateReceiverRunnable}, when one new message has been
* received.
*
* @param receivedMessage the {@link SilvercrestWifiSocketResponse} message.
*/
@Override
public void processReceivedPacket(final SilvercrestWifiSocketResponse receivedMessage) {
logger.debug("Received packet from: {} with content: [{}]", receivedMessage.getHostAddress(),
receivedMessage.getType());
SilvercrestWifiSocketHandler handler = this.getHandlerRegistredByMac(receivedMessage.getMacAddress());
if (handler != null) {
// deliver message to handler.
handler.newReceivedResponseMessage(receivedMessage);
logger.debug("Received message delivered with success to handler of mac {}",
receivedMessage.getMacAddress());
} else {
logger.debug("There is no handler registered for mac address:{}", receivedMessage.getMacAddress());
// notify discovery service of thing found!
this.silvercrestDiscoveryService.discoveredWifiSocket(receivedMessage.getMacAddress(),
receivedMessage.getHostAddress());
}
}
/**
* Regists one new {@link Thing} and the corresponding {@link SilvercrestWifiSocketHandler}.
*
* @param thing the {@link Thing}.
* @param handler the {@link SilvercrestWifiSocketHandler}.
*/
@Override
public void registerThingAndWifiSocketHandler(final Thing thing, final SilvercrestWifiSocketHandler handler) {
this.handlersRegistredByThing.put(thing, handler);
}
/**
* Unregists one {@link SilvercrestWifiSocketHandler} by the corresponding {@link Thing}.
*
* @param thing the {@link Thing}.
*/
@Override
public void unregisterWifiSocketHandlerByThing(final Thing thing) {
SilvercrestWifiSocketHandler handler = this.handlersRegistredByThing.get(thing);
if (handler != null) {
this.handlersRegistredByThing.remove(thing);
}
}
/**
* Utilitary method to get the registered thing handler in mediator by the mac address.
*
* @param macAddress the mac address of the thing of the handler.
* @return {@link SilvercrestWifiSocketHandler} if found.
*/
private SilvercrestWifiSocketHandler getHandlerRegistredByMac(final String macAddress) {
SilvercrestWifiSocketHandler searchedHandler = null;
for (SilvercrestWifiSocketHandler handler : this.handlersRegistredByThing.values()) {
if (macAddress.equals(handler.getMacAddress())) {
searchedHandler = handler;
// don't spend more computation. Found the handler.
break;
}
}
return searchedHandler;
}
/**
* Inits the mediator WifiSocketUpdateReceiverRunnable thread. This thread is responsible to receive all
* packets from Wifi Socket devices, and redirect the messages to mediator.
*/
private void initMediatorWifiSocketUpdateReceiverRunnable() {
// try with handler port if is null
if ((this.receiver == null) || ((this.receiverThread != null)
&& (this.receiverThread.isInterrupted() || !this.receiverThread.isAlive()))) {
try {
this.receiver = new SilvercrestWifiSocketUpdateReceiverRunnable(this,
SilvercrestWifiSocketBindingConstants.WIFI_SOCKET_DEFAULT_UDP_PORT);
this.receiverThread = new Thread(this.receiver);
this.receiverThread.start();
logger.debug("Invoked the start of receiver thread.");
} catch (SocketException e) {
logger.debug("Cannot start the socket with default port...");
}
}
}
/**
* Returns all the {@link Thing} registered.
*
* @returns all the {@link Thing}.
*/
@Override
public Set<Thing> getAllThingsRegistred() {
return this.handlersRegistredByThing.keySet();
}
@Override
public void setDiscoveryService(final SilvercrestWifiSocketDiscoveryService discoveryService) {
this.silvercrestDiscoveryService = discoveryService;
}
}

View File

@@ -0,0 +1,133 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.runnable;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import org.openhab.binding.silvercrestwifisocket.internal.exceptions.NotOneResponsePacketException;
import org.openhab.binding.silvercrestwifisocket.internal.exceptions.PacketIntegrityErrorException;
import org.openhab.binding.silvercrestwifisocket.internal.handler.SilvercrestWifiSocketMediator;
import org.openhab.binding.silvercrestwifisocket.internal.utils.WifiSocketPacketConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This Thread is responsible to receive all Wifi Socket messages and redirect them to
* {@link SilvercrestWifiSocketMediator}.
*
* @author Jaime Vaz - Initial contribution
*
*/
public class SilvercrestWifiSocketUpdateReceiverRunnable implements Runnable {
private static final int TIMEOUT_TO_DATAGRAM_RECEPTION = 10000;
private final Logger logger = LoggerFactory.getLogger(SilvercrestWifiSocketUpdateReceiverRunnable.class);
private DatagramSocket datagramSocket;
private final SilvercrestWifiSocketMediator mediator;
private final WifiSocketPacketConverter packetConverter = new WifiSocketPacketConverter();
private boolean shutdown;
private int listeningPort;
/**
* Constructor of the receiver runnable thread.
*
* @param mediator the {@link SilvercrestWifiSocketMediator}
* @param listeningPort the listening UDP port
* @throws SocketException is some problem occurs opening the socket.
*/
public SilvercrestWifiSocketUpdateReceiverRunnable(final SilvercrestWifiSocketMediator mediator,
final int listeningPort) throws SocketException {
logger.debug("Starting Update Receiver Runnable...");
// Create a socket to listen on the port.
this.listeningPort = listeningPort;
this.mediator = mediator;
logger.debug("Opening socket and start listening UDP port: {}", listeningPort);
this.datagramSocket = new DatagramSocket(listeningPort);
this.datagramSocket.setSoTimeout(TIMEOUT_TO_DATAGRAM_RECEPTION);
logger.debug("Update Receiver Runnable and socket started with success...");
this.shutdown = false;
}
@Override
public void run() {
// Now loop forever, waiting to receive packets and redirect them to mediator.
while (!this.shutdown) {
datagramSocketHealthRoutine();
// Create a buffer to read datagrams into. If a
// packet is larger than this buffer, the
// excess will simply be discarded!
byte[] buffer = new byte[2048];
// Create a packet to receive data into the buffer
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// Wait to receive a datagram
try {
this.datagramSocket.receive(packet);
logger.debug("Received packet from: {}. Will process the packet...",
packet.getAddress().getHostAddress());
// Do mediator something with it
this.mediator.processReceivedPacket(this.packetConverter.decryptResponsePacket(packet));
logger.debug("Message delivered with success to mediator.");
} catch (SocketTimeoutException e) {
logger.trace("Socket Timeout receiving packet.");
} catch (IOException e) {
logger.debug("One exception has occurred: {} ", e.getMessage());
} catch (PacketIntegrityErrorException e) {
logger.debug("Packet has one integrity error: {}", e.getMessage());
} catch (NotOneResponsePacketException e) {
logger.debug(
"The message received is not one response. Probably the message received is one broadcast message looking for the socket.");
}
}
// close the socket
if (datagramSocket != null) {
datagramSocket.close();
}
}
private void datagramSocketHealthRoutine() {
if (datagramSocket == null || datagramSocket.isClosed()) {
logger.debug("Datagram Socket has been closed, will reconnect again...");
DatagramSocket newDatagramSocket = null;
try {
newDatagramSocket = new DatagramSocket(listeningPort);
newDatagramSocket.setSoTimeout(TIMEOUT_TO_DATAGRAM_RECEPTION);
datagramSocket = newDatagramSocket;
logger.debug("Datagram Socket reconnected.");
} catch (SocketException exception) {
logger.error("Problem creating one new socket on port {}. Error: {}", listeningPort,
exception.getLocalizedMessage());
}
}
}
/**
* Gracefully shutdown thread. Worst case takes TIMEOUT_TO_DATAGRAM_RECEPTION to shutdown.
*/
public void shutdown() {
this.shutdown = true;
}
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.utils;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
/**
* Utilitary static class to perform some network routines.
*
* @author Jaime Vaz - Initial contribution
*
*/
public final class NetworkUtils {
private NetworkUtils() {
// Avoid instantiation.
}
/**
* Gets all the broadcast address's from the machine.
*
* @return list with all the broadcast address's
*/
public static List<InetAddress> getAllBroadcastAddresses() {
List<InetAddress> listOfBroadcasts = new ArrayList<>();
Enumeration<NetworkInterface> list;
try {
list = NetworkInterface.getNetworkInterfaces();
while (list.hasMoreElements()) {
NetworkInterface iface = list.nextElement();
if (iface == null) {
continue;
}
if (!iface.isLoopback() && iface.isUp()) {
Iterator<InterfaceAddress> it = iface.getInterfaceAddresses().iterator();
while (it.hasNext()) {
InterfaceAddress address = it.next();
if (address == null) {
continue;
}
InetAddress broadcast = address.getBroadcast();
if (broadcast != null) {
listOfBroadcasts.add(broadcast);
}
}
}
}
} catch (SocketException ex) {
return new ArrayList<>();
}
return listOfBroadcasts;
}
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.utils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utilitary static class to perform some validations.
*
* @author Jaime Vaz - Initial contribution
*
*/
public final class ValidationUtils {
private ValidationUtils() {
// avoid instantiation.
}
public static final String MAC_PATTERN = "^([0-9A-Fa-f]{2}[:-]*){5}([0-9A-Fa-f]{2})$";
/**
* Validates if one Mac address is valid.
*
* @param mac the mac, with or without :
* @return true if is valid.
*/
public static boolean isMacValid(final String mac) {
Pattern pattern = Pattern.compile(ValidationUtils.MAC_PATTERN);
Matcher matcher = pattern.matcher(mac);
return matcher.matches();
}
/**
* Validates if one Mac address is not valid.
*
* @param mac the mac, with or without :
* @return true if is not valid.
*/
public static boolean isMacNotValid(final String macAddress) {
return !isMacValid(macAddress);
}
}

View File

@@ -0,0 +1,292 @@
/**
* Copyright (c) 2010-2020 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.silvercrestwifisocket.internal.utils;
import java.net.DatagramPacket;
import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.openhab.binding.silvercrestwifisocket.internal.entities.SilvercrestWifiSocketRequest;
import org.openhab.binding.silvercrestwifisocket.internal.entities.SilvercrestWifiSocketResponse;
import org.openhab.binding.silvercrestwifisocket.internal.enums.SilvercrestWifiSocketResponseType;
import org.openhab.binding.silvercrestwifisocket.internal.enums.SilvercrestWifiSocketVendor;
import org.openhab.binding.silvercrestwifisocket.internal.exceptions.NotOneResponsePacketException;
import org.openhab.binding.silvercrestwifisocket.internal.exceptions.PacketIntegrityErrorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Transforms the the received datagram packet to one
*
* @author Jaime Vaz - Initial contribution
* @author Christian Heimerl - for integration of EasyHome
*
*/
public class WifiSocketPacketConverter {
private final Logger logger = LoggerFactory.getLogger(WifiSocketPacketConverter.class);
private static final String REQUEST_PREFIX = "01";
private static final String RESPONSE_PREFIX = "0142";
private static final String LOCK_STATUS = "40";
/* encryptDataLength */
private static final String ENCRYPT_PREFIX = "00";
private static final String PACKET_NUMBER = "FFFF";
private static final String DEVICE_TYPE = "11";
private static final String ENCRIPTION_KEY = "0123456789abcdef";
private Cipher silvercrestEncryptCipher;
private Cipher silvercrestDecryptCipher;
/**
* START_OF_RECEIVED_PACKET.
* STX - pkt nbr - CompanyCode - device - authCode
*
* 00 -- 0029 -- C1 -- 11 -- 7150 (SilverCrest)
* 00 -- 0029 -- C2 -- 11 -- 92DD (EasyHome)
*/
private static final String REGEX_START_OF_RECEIVED_PACKET = "00([A-F0-9]{4})(?:C21192DD|C1117150)";
private static final String REGEX_HEXADECIMAL_PAIRS = "([A-F0-9]{2})*";
private static final String REGEX_START_OF_RECEIVED_PACKET_SEARCH_MAC_ADDRESS = REGEX_START_OF_RECEIVED_PACKET
+ "23" + REGEX_HEXADECIMAL_PAIRS;
private static final String REGEX_START_OF_RECEIVED_PACKET_HEART_BEAT = REGEX_START_OF_RECEIVED_PACKET + "61"
+ REGEX_HEXADECIMAL_PAIRS;
private static final String REGEX_START_OF_RECEIVED_PACKET_CMD_GPIO_EVENT = REGEX_START_OF_RECEIVED_PACKET + "06"
+ REGEX_HEXADECIMAL_PAIRS;
private static final String REGEX_START_OF_RECEIVED_PACKET_QUERY_STATUS = REGEX_START_OF_RECEIVED_PACKET + "02"
+ REGEX_HEXADECIMAL_PAIRS;
private static final String REGEX_START_OF_RECEIVED_PACKET_RESPONSE_GPIO_CHANGE_REQUEST = REGEX_START_OF_RECEIVED_PACKET
+ "01" + REGEX_HEXADECIMAL_PAIRS;
/**
* Default constructor of the packet converter.
*/
public WifiSocketPacketConverter() {
// init cipher
byte[] encriptionKeyBytes;
try {
encriptionKeyBytes = ENCRIPTION_KEY.getBytes(StandardCharsets.UTF_8);
SecretKeySpec secretKey = new SecretKeySpec(encriptionKeyBytes, "AES");
IvParameterSpec ivKey = new IvParameterSpec(encriptionKeyBytes);
this.silvercrestEncryptCipher = Cipher.getInstance("AES/CBC/NoPadding");
this.silvercrestEncryptCipher.init(Cipher.ENCRYPT_MODE, secretKey, ivKey);
this.silvercrestDecryptCipher = Cipher.getInstance("AES/CBC/NoPadding");
this.silvercrestDecryptCipher.init(Cipher.DECRYPT_MODE, secretKey, ivKey);
} catch (Exception exception) {
logger.debug(
"Failure on WifiSocketPacketConverter creation. There was a problem creating ciphers. Error: {}",
exception.getLocalizedMessage());
}
}
/**
* Method that transforms one {@link SilvercrestWifiSocketRequest} to one byte array to be ready to be transmitted.
*
* @param requestPacket the {@link SilvercrestWifiSocketRequest}.
* @return the byte array with the message.
*/
public byte[] transformToByteMessage(final SilvercrestWifiSocketRequest requestPacket) {
byte[] requestDatagram = null;
String fullCommand = ENCRYPT_PREFIX + PACKET_NUMBER + requestPacket.getVendor().getCompanyCode() + DEVICE_TYPE
+ requestPacket.getVendor().getAuthenticationCode()
+ String.format(requestPacket.getType().getCommand(), requestPacket.getMacAddress());
byte[] inputByte = hexStringToByteArray(fullCommand);
byte[] bEncrypted;
try {
bEncrypted = this.silvercrestEncryptCipher.doFinal(inputByte);
int encryptDataLength = bEncrypted.length;
logger.trace("Encrypted data={{}}", byteArrayToHexString(inputByte));
logger.trace("Decrypted data={{}}", byteArrayToHexString(bEncrypted));
String cryptedCommand = byteArrayToHexString(bEncrypted);
String packetString = REQUEST_PREFIX + LOCK_STATUS + requestPacket.getMacAddress()
+ Integer.toHexString(encryptDataLength) + cryptedCommand;
logger.trace("Request Packet: {}", packetString);
logger.trace("Request packet decrypted data: [{}] with lenght: {}", fullCommand, fullCommand.length());
requestDatagram = hexStringToByteArray(packetString);
} catch (BadPaddingException | IllegalBlockSizeException e) {
logger.debug("Failure processing the build of the request packet for mac '{}' and type '{}'",
requestPacket.getMacAddress(), requestPacket.getType());
}
return requestDatagram;
}
/**
* Decrypts one response {@link DatagramPacket}.
*
* @param packet the {@link DatagramPacket}
* @return the {@link SilvercrestWifiSocketResponse} is successfully decrypted.
* @throws PacketIntegrityErrorException if the message has some integrity error.
* @throws NotOneResponsePacketException if the message received is not one response packet.
*/
public SilvercrestWifiSocketResponse decryptResponsePacket(final DatagramPacket packet)
throws PacketIntegrityErrorException, NotOneResponsePacketException {
SilvercrestWifiSocketResponse responsePacket = this.decryptResponsePacket(
WifiSocketPacketConverter.byteArrayToHexString(packet.getData(), packet.getLength()));
responsePacket.setHostAddress(packet.getAddress().getHostAddress());
return responsePacket;
}
/**
* STX - pkt nbr - CompanyCode - device - authCode
*
* 00 -- 0029 -- C1 -- 11 -- 7150 (Silvercrest)
* 00 -- 0029 -- C2 -- 11 -- 92DD (EasyHome)
*
* @param hexPacket the hex packet to convert
* @return the converted response.
* @throws PacketIntegrityErrorException the packet passed is not recognized.
* @throws NotOneResponsePacketException the packet passed is not one response.
*/
private SilvercrestWifiSocketResponse decryptResponsePacket(final String hexPacket)
throws PacketIntegrityErrorException, NotOneResponsePacketException {
if (!Pattern.matches(RESPONSE_PREFIX + REGEX_HEXADECIMAL_PAIRS, hexPacket)) {
logger.trace("The packet received is not one response! \nPacket:[{}]", hexPacket);
throw new NotOneResponsePacketException("The packet received is not one response.");
}
logger.trace("Response packet: {}", hexPacket);
String macAddress = hexPacket.substring(4, 16);
logger.trace("The mac address of the sender of the packet is: {}", macAddress);
String decryptedData = this.decrypt(hexPacket.substring(18, hexPacket.length()));
logger.trace("Response packet decrypted data: [{}] with lenght: {}", decryptedData, decryptedData.length());
SilvercrestWifiSocketResponseType responseType;
// check packet integrity
if (Pattern.matches(REGEX_START_OF_RECEIVED_PACKET_SEARCH_MAC_ADDRESS, decryptedData)) {
responseType = SilvercrestWifiSocketResponseType.DISCOVERY;
logger.trace("Received answer of mac address search! lenght:{}", decryptedData.length());
} else if (Pattern.matches(REGEX_START_OF_RECEIVED_PACKET_HEART_BEAT, decryptedData)) {
responseType = SilvercrestWifiSocketResponseType.ACK;
logger.trace("Received heart beat!");
} else if (Pattern.matches(REGEX_START_OF_RECEIVED_PACKET_CMD_GPIO_EVENT, decryptedData)) {
logger.trace("Received gpio event!");
String status = decryptedData.substring(20, 22);
responseType = "FF".equalsIgnoreCase(status) ? SilvercrestWifiSocketResponseType.ON
: SilvercrestWifiSocketResponseType.OFF;
logger.trace("Socket status: {}", responseType);
} else if (Pattern.matches(REGEX_START_OF_RECEIVED_PACKET_RESPONSE_GPIO_CHANGE_REQUEST, decryptedData)) {
logger.trace("Received response from a gpio change request!");
String status = decryptedData.substring(20, 22);
responseType = "FF".equalsIgnoreCase(status) ? SilvercrestWifiSocketResponseType.ON
: SilvercrestWifiSocketResponseType.OFF;
logger.trace("Socket status: {}", responseType);
} else if (Pattern.matches(REGEX_START_OF_RECEIVED_PACKET_QUERY_STATUS, decryptedData)) {
logger.trace("Received response from status query!");
String status = decryptedData.substring(20, 22);
responseType = "FF".equalsIgnoreCase(status) ? SilvercrestWifiSocketResponseType.ON
: SilvercrestWifiSocketResponseType.OFF;
logger.trace("Socket status: {}", responseType);
} else {
throw new PacketIntegrityErrorException("The packet decrypted is with wrong format. \nPacket:[" + hexPacket
+ "] \nDecryptedPacket:[" + decryptedData + "]");
}
SilvercrestWifiSocketVendor vendor = SilvercrestWifiSocketVendor.fromCode(decryptedData.substring(6, 8));
if (vendor == null) {
throw new PacketIntegrityErrorException("Could not extract vendor from the decrypted packet. \nPacket:["
+ hexPacket + "] \nDecryptedPacket:[" + decryptedData + "]");
}
logger.trace("Decrypt success. Packet is from socket with mac address [{}] and type is [{}] and vendor is [{}]",
macAddress, responseType, vendor);
return new SilvercrestWifiSocketResponse(macAddress, responseType, vendor);
}
/**
* Decrypts one received message with the correct cypher.
*
* @param inputData the cyphered message
* @return the decrypted message.
*/
private String decrypt(final String inputData) {
byte[] inputByte = hexStringToByteArray(inputData);
byte[] bDecrypted;
try {
bDecrypted = this.silvercrestDecryptCipher.doFinal(inputByte);
logger.trace("Encrypted data={{}}", byteArrayToHexString(inputByte));
logger.trace("Decrypted data={{}}", byteArrayToHexString(bDecrypted));
return byteArrayToHexString(bDecrypted);
} catch (Exception e) {
logger.trace("Problem decrypting the input data. Bad reception?");
}
return null;
}
// String/Array/Hex manipulation
/**
* Converts one hexadecimal string to one byte array.
*
* @param str the string to convert.
* @return the byte array.
*/
private static byte[] hexStringToByteArray(final String str) {
byte[] b = new byte[str.length() / 2];
for (int i = 0; i < b.length; i++) {
int index = i * 2;
int v = Integer.parseInt(str.substring(index, index + 2), 16);
b[i] = (byte) v;
}
return b;
}
/**
* Converts one full byte array to one hexadecimal string.
*
* @param array the byte array to convert.
* @return the hexadecimal string.
*/
private static String byteArrayToHexString(final byte[] array) {
return byteArrayToHexString(array, array.length);
}
/**
* Converts one partial byte array to one hexadecimal string.
*
* @param array the byte array to convert.
* @param length the length to convert.
* @return the hexadecimal string.
*/
private static String byteArrayToHexString(final byte[] array, final int length) {
if ((array == null) || (array.length == 0)) {
return null;
}
StringBuilder builder = new StringBuilder();
String hex = "";
for (int i = 0; i < length; i++) {
hex = Integer.toHexString(0xFF & array[i]).toUpperCase();
if (hex.length() < 2) {
hex = "0" + hex;
}
builder.append(hex);
}
return builder.toString();
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="silvercrestwifisocket" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>Silvercrest Wifi Socket Binding</name>
<description>This is the binding for Silvercrest Wifi Socket sold by Lidl.</description>
<author>Jaime Vaz</author>
</binding:binding>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="silvercrestwifisocket"
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">
<!-- Socket Thing Type -->
<thing-type id="wifiSocket">
<label>Silvercrest Wifi Socket</label>
<description>Supports Silvercrest Wifi Socket SWS-A1.</description>
<channels>
<channel id="switch" typeId="switch"/>
</channels>
<config-description>
<parameter name="macAddress" type="text" required="true">
<label>MAC Address</label>
<description>The socket MAC address.</description>
</parameter>
<parameter name="hostAddress" type="text">
<label>Host Address</label>
<context>network-address</context>
<description>The socket Host address. The binding is able to discover the host address.</description>
</parameter>
<parameter name="updateInterval" type="integer" min="5">
<label>Update Interval</label>
<description>Update time interval in seconds to request the status of the socket.</description>
</parameter>
<parameter name="vendor" type="text" required="true">
<label>Vendor</label>
<description>The vendor selling the WiFi socket.</description>
<options>
<option value="LIDL_SILVERCREST">Lidl (Silvercrest)</option>
<option value="ALDI_EASYHOME">Aldi (EasyHome)</option>
</options>
</parameter>
</config-description>
</thing-type>
<!-- Socket type Channel Type -->
<channel-type id="switch">
<item-type>Switch</item-type>
<label>Socket Switch</label>
<description>Silvercrest Wifi Socket Switch to turn on or off.</description>
</channel-type>
</thing:thing-descriptions>