added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
33
bundles/org.openhab.binding.sinope/.classpath
Normal file
33
bundles/org.openhab.binding.sinope/.classpath
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<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.pde.core.requiredPlugins"/>
|
||||
<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.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
23
bundles/org.openhab.binding.sinope/.project
Normal file
23
bundles/org.openhab.binding.sinope/.project
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.binding.sinope</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>
|
||||
13
bundles/org.openhab.binding.sinope/NOTICE
Normal file
13
bundles/org.openhab.binding.sinope/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
|
||||
143
bundles/org.openhab.binding.sinope/README.md
Normal file
143
bundles/org.openhab.binding.sinope/README.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Sinopé Binding
|
||||
|
||||
The integration happens through the Sinopé (GT150) bridge, which acts as an IP gateway to the Sinopé devices on the 916 Mhz ISM band.
|
||||
|
||||
This binding supports multiple gateways with multiple devices.
|
||||
|
||||
## Supported Things
|
||||
|
||||
The Sinopé bridge is required as a "bridge" for accessing any other Sinopé devices.
|
||||
|
||||
Right now, only the thermostat devices (3000W and 4000W) (TH1120RF) are supported.
|
||||
|
||||
## Discovery
|
||||
|
||||
The Sinopé Gateway (bridge) discovery is not supported for now.
|
||||
It will be added in future release.
|
||||
The Sinopé devices discovery is implemented.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Bridge or the Sinopé Gateway
|
||||
|
||||
First, you will need to get your API key from your Sinopé gateway.
|
||||
|
||||
Grab the latest release of the [sinope-core library](<https://github.com/chaton78/sinope-core/releases>)
|
||||
|
||||
On Windows, you can run the SinopeProtocol.exe (in the zip release).
|
||||
The gateway parameter is written on the back of the SinopéGateway (example, 002f-c2c2-dd88-aaaa).
|
||||
The addr parameter is the IP given to your gateway.
|
||||
|
||||
```
|
||||
SinopeProtocol.exe -addr [YOUR_GATEWAY_IP_OR_HOSTNAME] -gateway [YOUR_GATEWAY_ID] -login
|
||||
Getting API Key - PRESS WEB Button
|
||||
Your api Key is: 0x12 0x57 0x55 0xD5 0xCD 0x4A 0xD5 0x33
|
||||
```
|
||||
|
||||
On other operating systems, using only a JVM, you can invoke directly the java command from the latest release of the [sinope-core library](<https://github.com/chaton78/sinope-core/releases>):
|
||||
|
||||
```
|
||||
java -jar core-0.0.3-shaded.jar -addr [YOUR_GATEWAY_IP_OR_HOSTNAME] -gateway [YOUR_GATEWAY_ID] -login
|
||||
Getting API Key - PRESS WEB Button
|
||||
Your api Key is: 0x12 0x57 0x55 0xD5 0xCD 0x4A 0xD5 0x33
|
||||
```
|
||||
|
||||
### Thing Discovery
|
||||
|
||||
You can use the same procedure to discover each device you want to use.
|
||||
You will need to provide the api key from the previous step.
|
||||
If you use spaces, please, use double quotes to pass the api key (i.e. "0x12 0x57 0x55 0xD5 0xCD 0x4A 0xD5 0x33")
|
||||
|
||||
Use the device procedure to discover it.
|
||||
For a thermostat, you need to push both buttons.
|
||||
The application will loop forever, press ctrl-c to exit.
|
||||
|
||||
```
|
||||
SinopeProtocol.exe -addr [YOUR_GATEWAY_IP_OR_HOSTNAME] -gateway [YOUR_GATEWAY_ID] -api "[YOUR_API_KEY]" -discover
|
||||
|
||||
It is now time to push both buttons on your device!
|
||||
Press crtl-c to exit!
|
||||
Your device id is: 0x00 0x00 0x35 0x86
|
||||
It is now time to push both buttons on your device!
|
||||
Press crtl-c to exit!
|
||||
```
|
||||
|
||||
On other operating systems, using only a JVM, you can invoke directly the java command:
|
||||
|
||||
```
|
||||
java -jar core-0.0.3-shaded.jar -addr [YOUR_GATEWAY_IP_OR_HOSTNAME] -gateway [YOUR_GATEWAY_ID] -api "[YOUR_API_KEY]" -discover
|
||||
|
||||
It is now time to push both buttons on your device!
|
||||
Press crtl-c to exit!
|
||||
Your device id is: 0x00 0x00 0x35 0x86
|
||||
It is now time to push both buttons on your device!
|
||||
Press crtl-c to exit!
|
||||
```
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
The Sinopé bridge requires the address, the gateway id and the API key in order for the binding to know where and how to access it.
|
||||
In the thing file, this looks e.g. like
|
||||
|
||||
```
|
||||
Bridge sinope:gateway:home [ hostname="[YOUR_GATEWAY_IP_OR_HOSTNAME]", gatewayId="[YOUR_GATEWAY_ID]", apiKey="0x1F 0x5D 0xC8 0xD5 0xCD 0x3A 0xD7 0x23"]
|
||||
```
|
||||
|
||||
The devices are identified by the ids that a Sinopé device returns when you have discovered it.
|
||||
|
||||
```
|
||||
thermostat room [ deviceId = "0x00 0x00 0x35 0x86" ]
|
||||
```
|
||||
|
||||
## Channels
|
||||
|
||||
Thermostat devices support some of the following channels:
|
||||
|
||||
Channel Type ID | Item Type | Description
|
||||
---------------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------|
|
||||
insideTemperature | Number (R) | Inside Temperature |
|
||||
outsideTemperature | Number (R) | Outside Temperature |
|
||||
setpointTemperature | Number (RW) | Set Point Temperature |
|
||||
setpointMode | String (RW) | Thermostat set point mode |
|
||||
heatingLevel | Number (R) | Heating Level |
|
||||
|
||||
## Full Example
|
||||
|
||||
In this example setup the Sinopé Gateway is represented as a Bridge **Home** with thermostat **Room**
|
||||
|
||||
### demo.things:
|
||||
|
||||
```
|
||||
Bridge sinope:gateway:home [ hostname="sinope", gatewayId="1234-4567-1234-1234", apiKey="0x12 0x34 0x56 0x78 0x9A 0xBC 0xDE 0xF0"] {
|
||||
thermostat room [ deviceId = "00003586" ]
|
||||
}
|
||||
```
|
||||
|
||||
### demo.items:
|
||||
|
||||
```
|
||||
Number Room_In "Room Temp. [%.2f °C]" <temperature> { channel="sinope:thermostat:home:room:insideTemperature" }
|
||||
Number Room_Out "Outside Temp. [%.2f °C]" <temperature> { channel="sinope:thermostat:home:room:outsideTemperature" }
|
||||
Number Room_SetPoint "Room Set Point [%.2f °C]" <temperature> { channel="sinope:thermostat:home:room:setpointTemperature" }
|
||||
Number Room_SetPointMode "Room Set Point Mode" { channel="sinope:thermostat:home:room:setpointMode" }
|
||||
Number Room_HeatLevel "Room Heating level [%d]" <heating> { channel="sinope:thermostat:home:room:heatingLevel" }
|
||||
```
|
||||
|
||||
### demo.sitemap:
|
||||
|
||||
```
|
||||
sitemap demo label="Main Menu"
|
||||
{
|
||||
Frame label="Sinope" {
|
||||
Text item=Room_In
|
||||
Text item=Room_Out
|
||||
Setpoint item=Room_SetPoint label="Set Point [%.1f °C]" step=0.5 minValue=5 maxValue=35
|
||||
Switch item=Room_SetPointMode mappings=[2=Manual, 3=Auto, 5=Away]
|
||||
Slider item=Room_HeatLevel
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### UI Example
|
||||
|
||||

|
||||
BIN
bundles/org.openhab.binding.sinope/doc/openhab.png
Normal file
BIN
bundles/org.openhab.binding.sinope/doc/openhab.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
17
bundles/org.openhab.binding.sinope/pom.xml
Normal file
17
bundles/org.openhab.binding.sinope/pom.xml
Normal 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.sinope</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: Sinope Binding</name>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.sinope-${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-sinope" description="Sinope Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<feature>openhab-transport-serial</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.sinope/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.sinope;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link sinopeBinding} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SinopeBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "sinope";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_THERMO = new ThingTypeUID(BINDING_ID, "thermostat");
|
||||
public static final ThingTypeUID THING_TYPE_GATEWAY = new ThingTypeUID(BINDING_ID, "gateway");
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String CHANNEL_HEATINGLEVEL = "heatingLevel";
|
||||
public static final String CHANNEL_SETTEMP = "setpointTemperature";
|
||||
public static final String CHANNEL_SETMODE = "setpointMode";
|
||||
public static final String CHANNEL_INTEMP = "insideTemperature";
|
||||
public static final String CHANNEL_OUTTEMP = "outsideTemperature";
|
||||
|
||||
public static final String CONFIG_PROPERTY_HOST = "ipAddress";
|
||||
public static final String CONFIG_PROPERTY_PORT = "ipPort";
|
||||
public static final String CONFIG_PROPERTY_GATEWAY_ID = "gatewayID";
|
||||
public static final String CONFIG_PROPERTY_API_KEY = "apiKey";
|
||||
public static final String CONFIG_PROPERTY_POLLING_INTERVAL = "pollingInterval";
|
||||
public static final String CONFIG_PROPERTY_DEVICE_ID = "deviceId";
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>();
|
||||
|
||||
static {
|
||||
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_GATEWAY);
|
||||
SUPPORTED_THING_TYPES_UIDS.add(THING_TYPE_THERMO);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
/**
|
||||
* 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.sinope.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
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.sinope.SinopeBindingConstants;
|
||||
import org.openhab.binding.sinope.internal.SinopeConfigStatusMessage;
|
||||
import org.openhab.binding.sinope.internal.config.SinopeConfig;
|
||||
import org.openhab.binding.sinope.internal.core.SinopeApiLoginAnswer;
|
||||
import org.openhab.binding.sinope.internal.core.SinopeApiLoginRequest;
|
||||
import org.openhab.binding.sinope.internal.core.SinopeDeviceReportAnswer;
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeAnswer;
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeDataAnswer;
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeDataRequest;
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeRequest;
|
||||
import org.openhab.binding.sinope.internal.discovery.SinopeThingsDiscoveryService;
|
||||
import org.openhab.binding.sinope.internal.util.ByteUtil;
|
||||
import org.openhab.core.config.core.status.ConfigStatusMessage;
|
||||
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.ConfigStatusBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link SinopeGatewayHandler} is responsible for handling commands for the Sinopé Gateway.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SinopeGatewayHandler extends ConfigStatusBridgeHandler {
|
||||
|
||||
private static final int FIRST_POLL_INTERVAL = 1; // In second
|
||||
private final Logger logger = LoggerFactory.getLogger(SinopeGatewayHandler.class);
|
||||
private @Nullable ScheduledFuture<?> pollFuture;
|
||||
private long refreshInterval; // In seconds
|
||||
private final List<SinopeThermostatHandler> thermostatHandlers = new CopyOnWriteArrayList<>();
|
||||
private int seq = 1;
|
||||
private @Nullable Socket clientSocket;
|
||||
private boolean searching; // In searching mode..
|
||||
private @Nullable ScheduledFuture<?> pollSearch;
|
||||
|
||||
public SinopeGatewayHandler(final Bridge bridge) {
|
||||
super(bridge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("Initializing Sinope Gateway");
|
||||
|
||||
try {
|
||||
SinopeConfig config = getConfigAs(SinopeConfig.class);
|
||||
refreshInterval = config.refresh;
|
||||
if (config.hostname == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Gateway hostname must be set");
|
||||
} else if (config.port == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Gateway port must be set");
|
||||
} else if (config.gatewayId == null || SinopeConfig.convert(config.gatewayId) == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Gateway Id must be set");
|
||||
} else if (config.apiKey == null || SinopeConfig.convert(config.apiKey) == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Api Key must be set");
|
||||
} else if (connectToBridge()) {
|
||||
schedulePoll();
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
return;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE,
|
||||
"Can't connect to gateway. Please make sure that another instance is not connected.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
stopPoll();
|
||||
if (clientSocket != null) {
|
||||
try {
|
||||
clientSocket.close();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Unexpected error when closing connection to gateway", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void schedulePoll() {
|
||||
if (searching) {
|
||||
return;
|
||||
}
|
||||
if (pollFuture != null) {
|
||||
pollFuture.cancel(false);
|
||||
}
|
||||
logger.debug("Scheduling poll for {} s out, then every {} s", FIRST_POLL_INTERVAL, refreshInterval);
|
||||
pollFuture = scheduler.scheduleWithFixedDelay(() -> poll(), FIRST_POLL_INTERVAL, refreshInterval,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
synchronized void stopPoll() {
|
||||
if (pollFuture != null && !pollFuture.isCancelled()) {
|
||||
pollFuture.cancel(true);
|
||||
pollFuture = null;
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void poll() {
|
||||
if (!thermostatHandlers.isEmpty()) {
|
||||
logger.debug("Polling for state");
|
||||
try {
|
||||
if (connectToBridge()) {
|
||||
logger.debug("Connected to bridge");
|
||||
for (SinopeThermostatHandler sinopeThermostatHandler : thermostatHandlers) {
|
||||
sinopeThermostatHandler.update();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
setCommunicationError(true);
|
||||
logger.debug("Polling issue", e);
|
||||
}
|
||||
} else {
|
||||
logger.debug("nothing to poll");
|
||||
}
|
||||
}
|
||||
|
||||
boolean connectToBridge() throws UnknownHostException, IOException {
|
||||
SinopeConfig config = getConfigAs(SinopeConfig.class);
|
||||
if (this.clientSocket == null || !this.clientSocket.isConnected() || this.clientSocket.isClosed()) {
|
||||
this.clientSocket = new Socket(config.hostname, config.port);
|
||||
SinopeApiLoginRequest loginRequest = new SinopeApiLoginRequest(SinopeConfig.convert(config.gatewayId),
|
||||
SinopeConfig.convert(config.apiKey));
|
||||
SinopeApiLoginAnswer loginAnswer = (SinopeApiLoginAnswer) execute(loginRequest);
|
||||
setCommunicationError(false);
|
||||
return loginAnswer.getStatus() == 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized byte[] newSeq() {
|
||||
return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(seq++).array();
|
||||
}
|
||||
|
||||
synchronized SinopeAnswer execute(SinopeRequest command) throws UnknownHostException, IOException {
|
||||
Socket clientSocket = this.getClientSocket();
|
||||
OutputStream outToServer = clientSocket.getOutputStream();
|
||||
InputStream inputStream = clientSocket.getInputStream();
|
||||
outToServer.write(command.getPayload());
|
||||
outToServer.flush();
|
||||
SinopeAnswer answ = command.getReplyAnswer(inputStream);
|
||||
|
||||
return answ;
|
||||
}
|
||||
|
||||
synchronized SinopeAnswer execute(SinopeDataRequest command) throws UnknownHostException, IOException {
|
||||
Socket clientSocket = this.getClientSocket();
|
||||
|
||||
OutputStream outToServer = clientSocket.getOutputStream();
|
||||
InputStream inputStream = clientSocket.getInputStream();
|
||||
if (logger.isDebugEnabled()) {
|
||||
int leftBytes = inputStream.available();
|
||||
if (leftBytes > 0) {
|
||||
logger.debug("Hum... some leftovers: {} bytes", leftBytes);
|
||||
}
|
||||
}
|
||||
outToServer.write(command.getPayload());
|
||||
|
||||
SinopeDataAnswer answ = command.getReplyAnswer(inputStream);
|
||||
|
||||
while (answ.getMore() == 0x01) {
|
||||
answ = command.getReplyAnswer(inputStream);
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
public boolean registerThermostatHandler(SinopeThermostatHandler thermostatHandler) {
|
||||
return thermostatHandlers.add(thermostatHandler);
|
||||
}
|
||||
|
||||
public boolean unregisterThermostatHandler(SinopeThermostatHandler thermostatHandler) {
|
||||
return thermostatHandlers.remove(thermostatHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigStatusMessage> getConfigStatus() {
|
||||
Collection<ConfigStatusMessage> configStatusMessages = new LinkedList<>();
|
||||
|
||||
SinopeConfig config = getConfigAs(SinopeConfig.class);
|
||||
if (config.hostname == null) {
|
||||
configStatusMessages.add(ConfigStatusMessage.Builder.error(SinopeBindingConstants.CONFIG_PROPERTY_HOST)
|
||||
.withMessageKeySuffix(SinopeConfigStatusMessage.HOST_MISSING.getMessageKey())
|
||||
.withArguments(SinopeBindingConstants.CONFIG_PROPERTY_HOST).build());
|
||||
}
|
||||
if (config.port == null) {
|
||||
configStatusMessages.add(ConfigStatusMessage.Builder.error(SinopeBindingConstants.CONFIG_PROPERTY_PORT)
|
||||
.withMessageKeySuffix(SinopeConfigStatusMessage.PORT_MISSING.getMessageKey())
|
||||
.withArguments(SinopeBindingConstants.CONFIG_PROPERTY_PORT).build());
|
||||
}
|
||||
|
||||
if (config.gatewayId == null || SinopeConfig.convert(config.gatewayId) == null) {
|
||||
configStatusMessages
|
||||
.add(ConfigStatusMessage.Builder.error(SinopeBindingConstants.CONFIG_PROPERTY_GATEWAY_ID)
|
||||
.withMessageKeySuffix(SinopeConfigStatusMessage.GATEWAY_ID_INVALID.getMessageKey())
|
||||
.withArguments(SinopeBindingConstants.CONFIG_PROPERTY_GATEWAY_ID).build());
|
||||
}
|
||||
if (config.apiKey == null || SinopeConfig.convert(config.apiKey) == null) {
|
||||
configStatusMessages.add(ConfigStatusMessage.Builder.error(SinopeBindingConstants.CONFIG_PROPERTY_API_KEY)
|
||||
.withMessageKeySuffix(SinopeConfigStatusMessage.API_KEY_INVALID.getMessageKey())
|
||||
.withArguments(SinopeBindingConstants.CONFIG_PROPERTY_API_KEY).build());
|
||||
}
|
||||
|
||||
return configStatusMessages;
|
||||
}
|
||||
|
||||
public void startSearch(final SinopeThingsDiscoveryService sinopeThingsDiscoveryService)
|
||||
throws UnknownHostException, IOException {
|
||||
// Stopping current polling
|
||||
stopPoll();
|
||||
this.searching = true;
|
||||
pollSearch = scheduler.schedule(() -> search(sinopeThingsDiscoveryService), FIRST_POLL_INTERVAL,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void search(final SinopeThingsDiscoveryService sinopeThingsDiscoveryService) {
|
||||
try {
|
||||
if (connectToBridge()) {
|
||||
logger.debug("Successful login");
|
||||
try {
|
||||
while (clientSocket != null && clientSocket.isConnected() && !clientSocket.isClosed()) {
|
||||
SinopeDeviceReportAnswer answ;
|
||||
answ = new SinopeDeviceReportAnswer(clientSocket.getInputStream());
|
||||
logger.debug("Got report answer: {}", answ);
|
||||
logger.debug("Your device id is: {}", ByteUtil.toString(answ.getDeviceId()));
|
||||
sinopeThingsDiscoveryService.newThermostat(answ.getDeviceId());
|
||||
}
|
||||
} finally {
|
||||
if (clientSocket != null && !clientSocket.isClosed()) {
|
||||
clientSocket.close();
|
||||
clientSocket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (UnknownHostException e) {
|
||||
logger.warn("Unexpected error when searching for new devices", e);
|
||||
} catch (IOException e) {
|
||||
logger.debug("Network connection error, expected when ending search", e);
|
||||
} finally {
|
||||
schedulePoll();
|
||||
}
|
||||
}
|
||||
|
||||
public void stopSearch() throws IOException {
|
||||
this.searching = false;
|
||||
if (this.pollSearch != null && !this.pollSearch.isCancelled()) {
|
||||
this.pollSearch.cancel(true);
|
||||
this.pollSearch = null;
|
||||
}
|
||||
if (this.clientSocket != null && this.clientSocket.isConnected()) {
|
||||
this.clientSocket.close();
|
||||
this.clientSocket = null;
|
||||
}
|
||||
|
||||
schedulePoll();
|
||||
}
|
||||
|
||||
public @Nullable Socket getClientSocket() throws UnknownHostException, IOException {
|
||||
if (connectToBridge()) {
|
||||
return clientSocket;
|
||||
}
|
||||
throw new IOException("Could not create a socket to the gateway. Check host/ip/gateway Id");
|
||||
}
|
||||
|
||||
public void setCommunicationError(boolean hasError) {
|
||||
if (hasError) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||
clientSocket = null;
|
||||
} else {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
schedulePoll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
/**
|
||||
* 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.sinope.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.sinope.SinopeBindingConstants;
|
||||
import org.openhab.binding.sinope.internal.config.SinopeConfig;
|
||||
import org.openhab.binding.sinope.internal.core.SinopeDataReadRequest;
|
||||
import org.openhab.binding.sinope.internal.core.SinopeDataWriteRequest;
|
||||
import org.openhab.binding.sinope.internal.core.appdata.SinopeHeatLevelData;
|
||||
import org.openhab.binding.sinope.internal.core.appdata.SinopeOutTempData;
|
||||
import org.openhab.binding.sinope.internal.core.appdata.SinopeRoomTempData;
|
||||
import org.openhab.binding.sinope.internal.core.appdata.SinopeSetPointModeData;
|
||||
import org.openhab.binding.sinope.internal.core.appdata.SinopeSetPointTempData;
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeDataAnswer;
|
||||
import org.openhab.binding.sinope.internal.util.ByteUtil;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.ThingStatusInfo;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link SinopeThermostatHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SinopeThermostatHandler extends BaseThingHandler {
|
||||
|
||||
private static final int DATA_ANSWER = 0x0A;
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(SinopeThermostatHandler.class);
|
||||
|
||||
private byte[] deviceId = new byte[0];
|
||||
|
||||
public SinopeThermostatHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (getSinopeGatewayHandler() != null) {
|
||||
try {
|
||||
if (SinopeBindingConstants.CHANNEL_SETTEMP.equals(channelUID.getId())
|
||||
&& command instanceof QuantityType) {
|
||||
setSetpointTemp(((QuantityType<?>) command).floatValue());
|
||||
}
|
||||
if (SinopeBindingConstants.CHANNEL_SETMODE.equals(channelUID.getId())
|
||||
&& command instanceof DecimalType) {
|
||||
setSetpointMode(((DecimalType) command).intValue());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("Cannot handle command for channel {} because of {}", channelUID.getId(),
|
||||
e.getLocalizedMessage());
|
||||
getSinopeGatewayHandler().setCommunicationError(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setSetpointTemp(float temp) throws UnknownHostException, IOException {
|
||||
int newTemp = (int) (temp * 100.0);
|
||||
|
||||
getSinopeGatewayHandler().stopPoll();
|
||||
try {
|
||||
if (getSinopeGatewayHandler().connectToBridge()) {
|
||||
logger.debug("Connected to bridge");
|
||||
|
||||
SinopeDataWriteRequest req = new SinopeDataWriteRequest(getSinopeGatewayHandler().newSeq(), deviceId,
|
||||
new SinopeSetPointTempData());
|
||||
((SinopeSetPointTempData) req.getAppData()).setSetPointTemp(newTemp);
|
||||
|
||||
SinopeDataAnswer answ = (SinopeDataAnswer) getSinopeGatewayHandler().execute(req);
|
||||
|
||||
if (answ.getStatus() == DATA_ANSWER) {
|
||||
logger.debug("Setpoint temp is now: {} C", temp);
|
||||
} else {
|
||||
logger.debug("Cannot Setpoint temp, status: {}", answ.getStatus());
|
||||
}
|
||||
} else {
|
||||
logger.debug("Could not connect to bridge to update Setpoint Temp");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot connect to bridge");
|
||||
}
|
||||
} finally {
|
||||
getSinopeGatewayHandler().schedulePoll();
|
||||
}
|
||||
}
|
||||
|
||||
private void setSetpointMode(int mode) throws UnknownHostException, IOException {
|
||||
getSinopeGatewayHandler().stopPoll();
|
||||
try {
|
||||
if (getSinopeGatewayHandler().connectToBridge()) {
|
||||
logger.debug("Connected to bridge");
|
||||
|
||||
SinopeDataWriteRequest req = new SinopeDataWriteRequest(getSinopeGatewayHandler().newSeq(), deviceId,
|
||||
new SinopeSetPointModeData());
|
||||
((SinopeSetPointModeData) req.getAppData()).setSetPointMode((byte) mode);
|
||||
|
||||
SinopeDataAnswer answ = (SinopeDataAnswer) getSinopeGatewayHandler().execute(req);
|
||||
|
||||
if (answ.getStatus() == DATA_ANSWER) {
|
||||
logger.debug("Setpoint mode is now : {}", mode);
|
||||
} else {
|
||||
logger.debug("Cannot Setpoint mode, status: {}", answ.getStatus());
|
||||
}
|
||||
} else {
|
||||
logger.debug("Could not connect to bridge to update Setpoint Temp");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot connect to bridge");
|
||||
}
|
||||
} finally {
|
||||
getSinopeGatewayHandler().schedulePoll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
|
||||
logger.debug("bridgeStatusChanged {}", bridgeStatusInfo);
|
||||
updateDeviceId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
logger.debug("initializeThing thing {}", getThing().getUID());
|
||||
updateDeviceId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateConfiguration(Configuration configuration) {
|
||||
super.updateConfiguration(configuration);
|
||||
updateDeviceId();
|
||||
}
|
||||
|
||||
public void updateOutsideTemp(double temp) {
|
||||
updateState(SinopeBindingConstants.CHANNEL_OUTTEMP, new QuantityType<>(temp, SIUnits.CELSIUS));
|
||||
}
|
||||
|
||||
public void updateRoomTemp(double temp) {
|
||||
updateState(SinopeBindingConstants.CHANNEL_INTEMP, new QuantityType<>(temp, SIUnits.CELSIUS));
|
||||
}
|
||||
|
||||
public void updateSetPointTemp(double temp) {
|
||||
updateState(SinopeBindingConstants.CHANNEL_SETTEMP, new QuantityType<>(temp, SIUnits.CELSIUS));
|
||||
}
|
||||
|
||||
public void updateSetPointMode(int mode) {
|
||||
updateState(SinopeBindingConstants.CHANNEL_SETMODE, new DecimalType(mode));
|
||||
}
|
||||
|
||||
public void updateHeatingLevel(int heatingLevel) {
|
||||
updateState(SinopeBindingConstants.CHANNEL_HEATINGLEVEL, new DecimalType(heatingLevel));
|
||||
}
|
||||
|
||||
public void update() throws UnknownHostException, IOException {
|
||||
if (this.deviceId.length > 0 && getSinopeGatewayHandler() != null) {
|
||||
if (isLinked(SinopeBindingConstants.CHANNEL_OUTTEMP)) {
|
||||
this.updateOutsideTemp(readOutsideTemp());
|
||||
}
|
||||
if (isLinked(SinopeBindingConstants.CHANNEL_INTEMP)) {
|
||||
this.updateRoomTemp(readRoomTemp());
|
||||
}
|
||||
if (isLinked(SinopeBindingConstants.CHANNEL_SETTEMP)) {
|
||||
this.updateSetPointTemp(readSetpointTemp());
|
||||
}
|
||||
if (isLinked(SinopeBindingConstants.CHANNEL_SETMODE)) {
|
||||
this.updateSetPointMode(readSetpointMode());
|
||||
}
|
||||
if (isLinked(SinopeBindingConstants.CHANNEL_HEATINGLEVEL)) {
|
||||
this.updateHeatingLevel(readHeatLevel());
|
||||
}
|
||||
} else {
|
||||
logger.error("Device id is null for Thing UID: {}", getThing().getUID());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private double readRoomTemp() throws UnknownHostException, IOException {
|
||||
logger.debug("Reading room temp for device id : {}", ByteUtil.toString(deviceId));
|
||||
SinopeDataReadRequest req = new SinopeDataReadRequest(getSinopeGatewayHandler().newSeq(), deviceId,
|
||||
new SinopeRoomTempData());
|
||||
SinopeDataAnswer answ = (SinopeDataAnswer) getSinopeGatewayHandler().execute(req);
|
||||
double temp = ((SinopeRoomTempData) answ.getAppData()).getRoomTemp() / 100.0;
|
||||
logger.debug("Room temp is : {} C", temp);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private double readOutsideTemp() throws UnknownHostException, IOException {
|
||||
SinopeDataReadRequest req = new SinopeDataReadRequest(getSinopeGatewayHandler().newSeq(), deviceId,
|
||||
new SinopeOutTempData());
|
||||
logger.debug("Reading outside temp for device id: {}", ByteUtil.toString(deviceId));
|
||||
SinopeDataAnswer answ = (SinopeDataAnswer) getSinopeGatewayHandler().execute(req);
|
||||
double temp = ((SinopeOutTempData) answ.getAppData()).getOutTemp() / 100.0;
|
||||
logger.debug("Outside temp is : {} C", temp);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private double readSetpointTemp() throws UnknownHostException, IOException {
|
||||
SinopeDataReadRequest req = new SinopeDataReadRequest(getSinopeGatewayHandler().newSeq(), deviceId,
|
||||
new SinopeSetPointTempData());
|
||||
logger.debug("Reading Set Point temp for device id: {}", ByteUtil.toString(deviceId));
|
||||
SinopeDataAnswer answ = (SinopeDataAnswer) getSinopeGatewayHandler().execute(req);
|
||||
double temp = ((SinopeSetPointTempData) answ.getAppData()).getSetPointTemp() / 100.0;
|
||||
logger.debug("Setpoint temp is : {} C", temp);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private int readSetpointMode() throws UnknownHostException, IOException {
|
||||
SinopeDataReadRequest req = new SinopeDataReadRequest(getSinopeGatewayHandler().newSeq(), deviceId,
|
||||
new SinopeSetPointModeData());
|
||||
logger.debug("Reading Set Point mode for device id: {}", ByteUtil.toString(deviceId));
|
||||
SinopeDataAnswer answ = (SinopeDataAnswer) getSinopeGatewayHandler().execute(req);
|
||||
int mode = ((SinopeSetPointModeData) answ.getAppData()).getSetPointMode();
|
||||
logger.debug("Setpoint mode is : {}", mode);
|
||||
return mode;
|
||||
}
|
||||
|
||||
private int readHeatLevel() throws UnknownHostException, IOException {
|
||||
SinopeDataReadRequest req = new SinopeDataReadRequest(getSinopeGatewayHandler().newSeq(), deviceId,
|
||||
new SinopeHeatLevelData());
|
||||
logger.debug("Reading Heat Level for device id: {}", ByteUtil.toString(deviceId));
|
||||
SinopeDataAnswer answ = (SinopeDataAnswer) getSinopeGatewayHandler().execute(req);
|
||||
int level = ((SinopeHeatLevelData) answ.getAppData()).getHeatLevel();
|
||||
logger.debug("Heating level is : {}", level);
|
||||
return level;
|
||||
}
|
||||
|
||||
private synchronized void updateDeviceId() {
|
||||
String sDeviceId = (String) getConfig().get(SinopeBindingConstants.CONFIG_PROPERTY_DEVICE_ID);
|
||||
this.deviceId = SinopeConfig.convert(sDeviceId);
|
||||
if (this.deviceId.length == 0) {
|
||||
logger.debug("Invalid Device id, cannot convert id: {}", sDeviceId);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid Device id");
|
||||
return;
|
||||
}
|
||||
|
||||
Bridge bridge = getBridge();
|
||||
if (bridge == null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
|
||||
return;
|
||||
}
|
||||
SinopeGatewayHandler handler = getSinopeGatewayHandler();
|
||||
if (handler != null) {
|
||||
handler.registerThermostatHandler(this);
|
||||
}
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
private @Nullable SinopeGatewayHandler getSinopeGatewayHandler() {
|
||||
Bridge bridge = this.getBridge();
|
||||
if (bridge != null) {
|
||||
return (SinopeGatewayHandler) bridge.getHandler();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.sinope.internal;
|
||||
|
||||
/**
|
||||
* The {@link SinopeConfigStatusMessage} defines
|
||||
* the keys to be used for {@link ConfigStatusMessage}s.
|
||||
*
|
||||
* @author Pascal Larin - Initial Contribution
|
||||
*
|
||||
*/
|
||||
public enum SinopeConfigStatusMessage {
|
||||
HOST_MISSING("missing-host-configuration"),
|
||||
PORT_MISSING("missing-port-configuration"),
|
||||
GATEWAY_ID_INVALID("invalid-gateway-id-configuration"),
|
||||
API_KEY_INVALID("invalid-api-key-configuration");
|
||||
|
||||
private String messageKey;
|
||||
|
||||
private SinopeConfigStatusMessage(String messageKey) {
|
||||
this.messageKey = messageKey;
|
||||
}
|
||||
|
||||
public String getMessageKey() {
|
||||
return messageKey;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* 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.sinope.internal;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.openhab.binding.sinope.SinopeBindingConstants;
|
||||
import org.openhab.binding.sinope.handler.SinopeGatewayHandler;
|
||||
import org.openhab.binding.sinope.handler.SinopeThermostatHandler;
|
||||
import org.openhab.binding.sinope.internal.discovery.SinopeThingsDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* {@link SinopeHandlerFactory} is a factory for {@link SinopeThermostatHandler}s and {@link SinopeGatewayHandler}s
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, immediate = true)
|
||||
public class SinopeHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = SinopeBindingConstants.SUPPORTED_THING_TYPES_UIDS;
|
||||
private Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (SinopeBindingConstants.THING_TYPE_GATEWAY.equals(thingTypeUID)) {
|
||||
SinopeGatewayHandler bridge = new SinopeGatewayHandler((Bridge) thing);
|
||||
registerDiscoveryService(bridge);
|
||||
return bridge;
|
||||
} else if (SinopeBindingConstants.THING_TYPE_THERMO.equals(thingTypeUID)) {
|
||||
return new SinopeThermostatHandler(thing);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private synchronized void registerDiscoveryService(SinopeGatewayHandler bridge) {
|
||||
SinopeThingsDiscoveryService discoveryService = new SinopeThingsDiscoveryService(bridge);
|
||||
|
||||
this.discoveryServiceRegs.put(bridge.getThing().getUID(),
|
||||
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void removeHandler(ThingHandler thingHandler) {
|
||||
if (thingHandler instanceof SinopeGatewayHandler) {
|
||||
ServiceRegistration<?> serviceReg = this.discoveryServiceRegs.get(thingHandler.getThing().getUID());
|
||||
if (serviceReg != null) {
|
||||
// remove discovery service, if bridge handler is removed
|
||||
serviceReg.unregister();
|
||||
discoveryServiceRegs.remove(thingHandler.getThing().getUID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.sinope.internal.config;
|
||||
|
||||
/**
|
||||
* Holds Config for the Sinope Gateway
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class SinopeConfig {
|
||||
/**
|
||||
* Hostname of the Sinope Gateway
|
||||
*/
|
||||
public String hostname;
|
||||
/**
|
||||
* ip port
|
||||
*/
|
||||
public Integer port;
|
||||
/**
|
||||
* Gateway ID
|
||||
*/
|
||||
public String gatewayId;
|
||||
/**
|
||||
* API Key returned by the Gateway
|
||||
*/
|
||||
public String apiKey;
|
||||
/**
|
||||
* The number of seconds between fetches from the sinope deivces
|
||||
*/
|
||||
public Integer refresh;
|
||||
|
||||
/**
|
||||
* Convert Hex Config String to byte
|
||||
*/
|
||||
public static byte[] convert(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String _value = value;
|
||||
|
||||
_value = _value.replace("-", "");
|
||||
_value = _value.replace("0x", "");
|
||||
_value = _value.replace(" ", "");
|
||||
|
||||
if (_value.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_value.length() % 2 == 0 && _value.length() > 1) {
|
||||
byte[] b = new byte[_value.length() / 2];
|
||||
|
||||
for (int i = 0; i < _value.length(); i = i + 2) {
|
||||
b[i / 2] = (byte) Integer.parseInt(_value.substring(i, i + 2), 16);
|
||||
}
|
||||
return b;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* 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.sinope.internal.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeAnswer;
|
||||
import org.openhab.binding.sinope.internal.util.ByteUtil;
|
||||
|
||||
/**
|
||||
* The Class SinopeApiLoginAnswer.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeApiLoginAnswer extends SinopeAnswer {
|
||||
|
||||
/** The Constant STATUS_SIZE. */
|
||||
protected static final int STATUS_SIZE = 1;
|
||||
|
||||
/** The Constant BACKOFF_SIZE. */
|
||||
protected static final int BACKOFF_SIZE = 2;
|
||||
|
||||
/** The Constant SW_REV_MAJ_SIZE. */
|
||||
protected static final int SW_REV_MAJ_SIZE = 1;
|
||||
|
||||
/** The Constant SW_REV_MIN_SIZE. */
|
||||
protected static final int SW_REV_MIN_SIZE = 1;
|
||||
|
||||
/** The Constant SW_REV_BUG_SIZE. */
|
||||
protected static final int SW_REV_BUG_SIZE = 1;
|
||||
|
||||
/** The Constant DEVICE_ID_SIZE. */
|
||||
protected static final int DEVICE_ID_SIZE = 4;
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope api login answer.
|
||||
*
|
||||
* @param r the r
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public SinopeApiLoginAnswer(InputStream r) throws IOException {
|
||||
super(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getCommand()
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getCommand() {
|
||||
return new byte[] { 0x01, 0x11 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status.
|
||||
*
|
||||
* @return the status
|
||||
*/
|
||||
public byte getStatus() {
|
||||
byte[] b = this.getFrameData();
|
||||
return b[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the backoff.
|
||||
*
|
||||
* @return the backoff
|
||||
*/
|
||||
public byte[] getBackoff() {
|
||||
byte[] b = this.getFrameData();
|
||||
b = Arrays.copyOfRange(b, STATUS_SIZE, STATUS_SIZE + BACKOFF_SIZE);
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sw rev maj.
|
||||
*
|
||||
* @return the sw rev maj
|
||||
*/
|
||||
public byte getSwRevMaj() {
|
||||
byte[] b = this.getFrameData();
|
||||
return b[STATUS_SIZE + BACKOFF_SIZE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sw rev min.
|
||||
*
|
||||
* @return the sw rev min
|
||||
*/
|
||||
public byte getSwRevMin() {
|
||||
byte[] b = this.getFrameData();
|
||||
return b[STATUS_SIZE + BACKOFF_SIZE + SW_REV_MAJ_SIZE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sw rev bug.
|
||||
*
|
||||
* @return the sw rev bug
|
||||
*/
|
||||
public byte getSwRevBug() {
|
||||
byte[] b = this.getFrameData();
|
||||
return b[STATUS_SIZE + BACKOFF_SIZE + SW_REV_MAJ_SIZE + SW_REV_MIN_SIZE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the device id.
|
||||
*
|
||||
* @return the device id
|
||||
*/
|
||||
public byte[] getDeviceId() {
|
||||
byte[] b = this.getFrameData();
|
||||
b = Arrays.copyOfRange(b, STATUS_SIZE + BACKOFF_SIZE + SW_REV_MAJ_SIZE + SW_REV_MIN_SIZE + SW_REV_BUG_SIZE,
|
||||
STATUS_SIZE + BACKOFF_SIZE + SW_REV_MAJ_SIZE + SW_REV_MIN_SIZE + SW_REV_BUG_SIZE + DEVICE_ID_SIZE);
|
||||
return ByteUtil.reverse(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(super.toString());
|
||||
sb.append(String.format("\nData: %s", ByteUtil.toString(getFrameData())));
|
||||
sb.append(String.format("\n\tStatus: 0x%02X ", getStatus()));
|
||||
sb.append(String.format("\n\tBackoff: %s", ByteUtil.toString(getBackoff())));
|
||||
sb.append(String.format("\n\tSwRevMaj: 0x%02X ", getSwRevMaj()));
|
||||
sb.append(String.format("\n\tSwRevMin: 0x%02X ", getSwRevMin()));
|
||||
sb.append(String.format("\n\tSwRevBug: 0x%02X ", getSwRevBug()));
|
||||
sb.append(String.format("\n\tDeviceID: %s", ByteUtil.toString(getDeviceId())));
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* 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.sinope.internal.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeRequest;
|
||||
import org.openhab.binding.sinope.internal.util.ByteUtil;
|
||||
|
||||
/**
|
||||
* The Class SinopeApiLoginRequest.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeApiLoginRequest extends SinopeRequest {
|
||||
|
||||
/** The api key. */
|
||||
private byte[] apiKey;
|
||||
|
||||
/** The id. */
|
||||
private byte[] id;
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope api login request.
|
||||
*
|
||||
* @param id the id
|
||||
* @param apiKey the api key
|
||||
*/
|
||||
public SinopeApiLoginRequest(byte[] id, byte[] apiKey) {
|
||||
this.id = id;
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getCommand()
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getCommand() {
|
||||
return new byte[] { 0x01, 0x10 };
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getFrameData()
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getFrameData() {
|
||||
byte[] b = new byte[id.length + apiKey.length];
|
||||
|
||||
ByteBuffer bb = ByteBuffer.wrap(b);
|
||||
|
||||
bb.put(ByteUtil.reverse(id));
|
||||
bb.put(ByteUtil.reverse(apiKey));
|
||||
|
||||
// System.out.println(toString(bb.array()));
|
||||
return bb.array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the id.
|
||||
*
|
||||
* @return the id
|
||||
*/
|
||||
public byte[] getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeRequest#getReplyAnswer(java.io.InputStream)
|
||||
*/
|
||||
@Override
|
||||
public SinopeApiLoginAnswer getReplyAnswer(InputStream r) throws IOException {
|
||||
return new SinopeApiLoginAnswer(r);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* 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.sinope.internal.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeAnswer;
|
||||
import org.openhab.binding.sinope.internal.util.ByteUtil;
|
||||
|
||||
/**
|
||||
* The Class SinopeAuthenticationKeyAnswer.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeAuthenticationKeyAnswer extends SinopeAnswer {
|
||||
|
||||
/** The Constant STATUS_SIZE. */
|
||||
protected static final int STATUS_SIZE = 1;
|
||||
|
||||
/** The Constant BACKOFF_SIZE. */
|
||||
protected static final int BACKOFF_SIZE = 2;
|
||||
|
||||
/** The Constant API_KEY_SIZE. */
|
||||
protected static final int API_KEY_SIZE = 8;
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope authentication key answer.
|
||||
*
|
||||
* @param r the r
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public SinopeAuthenticationKeyAnswer(InputStream r) throws IOException {
|
||||
super(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getCommand()
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getCommand() {
|
||||
return new byte[] { 0x01, 0x11 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status.
|
||||
*
|
||||
* @return the status
|
||||
*/
|
||||
public int getStatus() {
|
||||
byte[] b = this.getFrameData();
|
||||
return b[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the backoff.
|
||||
*
|
||||
* @return the backoff
|
||||
*/
|
||||
public byte[] getBackoff() {
|
||||
byte[] b = this.getFrameData();
|
||||
b = Arrays.copyOfRange(b, STATUS_SIZE, STATUS_SIZE + BACKOFF_SIZE);
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the api key.
|
||||
*
|
||||
* @return the api key
|
||||
*/
|
||||
public byte[] getApiKey() {
|
||||
byte[] b = this.getFrameData();
|
||||
b = Arrays.copyOfRange(b, STATUS_SIZE + BACKOFF_SIZE, STATUS_SIZE + BACKOFF_SIZE + API_KEY_SIZE);
|
||||
return ByteUtil.reverse(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(super.toString());
|
||||
sb.append(String.format("\nData: %s", ByteUtil.toString(getFrameData())));
|
||||
sb.append(String.format("\n\tStatus: 0x%02X ", getStatus()));
|
||||
sb.append(String.format("\n\tApi Key: %s", ByteUtil.toString(getApiKey())));
|
||||
sb.append(String.format("\n\tBackoff: %s", ByteUtil.toString(getBackoff())));
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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.sinope.internal.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeRequest;
|
||||
import org.openhab.binding.sinope.internal.util.ByteUtil;
|
||||
|
||||
/**
|
||||
* The Class SinopeAuthenticationKeyRequest.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeAuthenticationKeyRequest extends SinopeRequest {
|
||||
|
||||
/** The id. */
|
||||
private byte[] id;
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope authentication key request.
|
||||
*
|
||||
* @param id the id
|
||||
*/
|
||||
public SinopeAuthenticationKeyRequest(byte[] id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getCommand()
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getCommand() {
|
||||
return new byte[] { 0x01, 0x0a };
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getFrameData()
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getFrameData() {
|
||||
return ByteUtil.reverse(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeRequest#getReplyAnswer(java.io.InputStream)
|
||||
*/
|
||||
@Override
|
||||
public SinopeAuthenticationKeyAnswer getReplyAnswer(InputStream r) throws IOException {
|
||||
return new SinopeAuthenticationKeyAnswer(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the id.
|
||||
*
|
||||
* @return the id
|
||||
*/
|
||||
public byte[] getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 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.sinope.internal.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.openhab.binding.sinope.internal.core.appdata.SinopeAppData;
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeDataAnswer;
|
||||
|
||||
/**
|
||||
* The Class SinopeDataReadAnswer.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeDataReadAnswer extends SinopeDataAnswer {
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope data read answer.
|
||||
*
|
||||
* @param r the r
|
||||
* @param appData the app data
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public SinopeDataReadAnswer(InputStream r, SinopeAppData appData) throws IOException {
|
||||
super(r, appData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getCommand()
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getCommand() {
|
||||
return new byte[] { 0x02, 0x41 };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.sinope.internal.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.openhab.binding.sinope.internal.core.appdata.SinopeAppData;
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeDataRequest;
|
||||
|
||||
/**
|
||||
* The Class SinopeDataReadRequest.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeDataReadRequest extends SinopeDataRequest {
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope data read request.
|
||||
*
|
||||
* @param seq the seq
|
||||
* @param dstDeviceId the dst device id
|
||||
* @param appData the app data
|
||||
*/
|
||||
public SinopeDataReadRequest(byte[] seq, byte[] dstDeviceId, SinopeAppData appData) {
|
||||
super(seq, dstDeviceId, appData);
|
||||
// Read Request, as per spec.. zap data part
|
||||
appData.cleanData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getCommand()
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getCommand() {
|
||||
return new byte[] { 0x02, 0x40 };
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeDataRequest#getReplyAnswer(java.io.InputStream)
|
||||
*/
|
||||
@Override
|
||||
public SinopeDataReadAnswer getReplyAnswer(InputStream r) throws IOException {
|
||||
return new SinopeDataReadAnswer(r, this.getAppData());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 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.sinope.internal.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.openhab.binding.sinope.internal.core.appdata.SinopeAppData;
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeDataAnswer;
|
||||
|
||||
/**
|
||||
* The Class SinopeDataReadAnswer.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeDataWriteAnswer extends SinopeDataAnswer {
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope data read answer.
|
||||
*
|
||||
* @param r the r
|
||||
* @param appData the app data
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public SinopeDataWriteAnswer(InputStream r, SinopeAppData appData) throws IOException {
|
||||
super(r, appData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getCommand()
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getCommand() {
|
||||
return new byte[] { 0x02, 0x45 };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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.sinope.internal.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.openhab.binding.sinope.internal.core.appdata.SinopeAppData;
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeDataRequest;
|
||||
|
||||
/**
|
||||
* The Class SinopeDataReadRequest.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeDataWriteRequest extends SinopeDataRequest {
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope data read request.
|
||||
*
|
||||
* @param seq the seq
|
||||
* @param dstDeviceId the dst device id
|
||||
* @param appData the app data
|
||||
*/
|
||||
public SinopeDataWriteRequest(byte[] seq, byte[] dstDeviceId, SinopeAppData appData) {
|
||||
super(seq, dstDeviceId, appData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getCommand()
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getCommand() {
|
||||
return new byte[] { 0x02, 0x44 };
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeDataRequest#getReplyAnswer(java.io.InputStream)
|
||||
*/
|
||||
@Override
|
||||
public SinopeDataReadAnswer getReplyAnswer(InputStream r) throws IOException {
|
||||
return new SinopeDataReadAnswer(r, this.getAppData());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* 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.sinope.internal.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeAnswer;
|
||||
import org.openhab.binding.sinope.internal.util.ByteUtil;
|
||||
|
||||
/**
|
||||
* The Class SinopeDeviceReportAnswer.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeDeviceReportAnswer extends SinopeAnswer {
|
||||
|
||||
/** The Constant STATUS_SIZE. */
|
||||
protected static final int STATUS_SIZE = 1;
|
||||
|
||||
/** The Constant DEVICE_ID_SIZE. */
|
||||
protected static final int DEVICE_ID_SIZE = 4;
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope device report answer.
|
||||
*
|
||||
* @param r the r
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public SinopeDeviceReportAnswer(InputStream r) throws IOException {
|
||||
super(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getCommand()
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getCommand() {
|
||||
return new byte[] { 0x01, 0x16 };
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status.
|
||||
*
|
||||
* @return the status
|
||||
*/
|
||||
public byte getStatus() {
|
||||
byte[] b = this.getFrameData();
|
||||
return b[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the device id.
|
||||
*
|
||||
* @return the device id
|
||||
*/
|
||||
public byte[] getDeviceId() {
|
||||
byte[] b = this.getFrameData();
|
||||
b = Arrays.copyOfRange(b, STATUS_SIZE, STATUS_SIZE + DEVICE_ID_SIZE);
|
||||
return ByteUtil.reverse(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(super.toString());
|
||||
sb.append(String.format("\nStatus: 0x%02X ", getStatus()));
|
||||
sb.append(String.format("\n\tDeviceId: %s", ByteUtil.toString(getDeviceId())));
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* 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.sinope.internal.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeAnswer;
|
||||
|
||||
/**
|
||||
* The Class SinopePingAnswer.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopePingAnswer extends SinopeAnswer {
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope ping answer.
|
||||
*
|
||||
* @param r the r
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public SinopePingAnswer(InputStream r) throws IOException {
|
||||
super(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getCommand()
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getCommand() {
|
||||
return new byte[] { 0x00, 0x13 };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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.sinope.internal.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.openhab.binding.sinope.internal.core.base.SinopeRequest;
|
||||
|
||||
/**
|
||||
* The Class SinopePingRequest.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopePingRequest extends SinopeRequest {
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getCommand()
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getCommand() {
|
||||
return new byte[] { 0x00, 0x12 };
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getFrameData()
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getFrameData() {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeRequest#getReplyAnswer(java.io.InputStream)
|
||||
*/
|
||||
@Override
|
||||
public SinopePingAnswer getReplyAnswer(InputStream r) throws IOException {
|
||||
return new SinopePingAnswer(r);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* 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.sinope.internal.core.appdata;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.openhab.binding.sinope.internal.util.ByteUtil;
|
||||
|
||||
/**
|
||||
* The Class SinopeAppData.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeAppData {
|
||||
|
||||
/** The Constant DATA_ID_SIZE. */
|
||||
protected static final int DATA_ID_SIZE = 4;
|
||||
|
||||
/** The Constant DATA_SIZE. */
|
||||
protected static final int DATA_SIZE = 1;
|
||||
|
||||
/** The internal data. */
|
||||
protected byte[] internal_data; // Full App object
|
||||
|
||||
/** The data id. */
|
||||
private byte[] dataId;
|
||||
|
||||
/**
|
||||
* The data.
|
||||
* data == null ? Do not send data size and payload
|
||||
*/
|
||||
private byte[] data; // Data field of the app object
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope app data.
|
||||
*
|
||||
* @param dataId the data id
|
||||
* @param data the data
|
||||
*/
|
||||
public SinopeAppData(byte[] dataId, byte[] data) {
|
||||
this.dataId = dataId;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read.
|
||||
*
|
||||
* @param d the d
|
||||
*/
|
||||
public void read(byte[] d) {
|
||||
this.internal_data = d;
|
||||
if (d.length >= DATA_ID_SIZE) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(d);
|
||||
this.dataId = new byte[DATA_ID_SIZE];
|
||||
|
||||
bb.get(dataId);
|
||||
this.dataId = ByteUtil.reverse(dataId);
|
||||
if (d.length > DATA_ID_SIZE) {
|
||||
int len = bb.get() & 0xff;
|
||||
|
||||
this.data = new byte[len];
|
||||
bb.get(this.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the internal data.
|
||||
*
|
||||
* @return the internal data
|
||||
*/
|
||||
public byte[] getInternalData() {
|
||||
if (internal_data == null) {
|
||||
int len = data != null ? 1 + data.length : 0;
|
||||
byte[] b = new byte[DATA_ID_SIZE + len];
|
||||
|
||||
ByteBuffer bb = ByteBuffer.wrap(b);
|
||||
bb.put(ByteUtil.reverse(dataId));
|
||||
if (data != null) { // Special case, Data null, don't put app size
|
||||
bb.put((byte) data.length);
|
||||
bb.put(data);
|
||||
}
|
||||
this.internal_data = bb.array();
|
||||
}
|
||||
|
||||
return this.internal_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data.
|
||||
*
|
||||
* @return the data
|
||||
*/
|
||||
public byte[] getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
/*
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(String.format("\n\tData ID: %s", ByteUtil.toString(this.dataId)));
|
||||
if (data != null) {
|
||||
sb.append(String.format("\n\t\tData Size: 0x%02X ", getData().length));
|
||||
sb.append(String.format("\n\t\tData: %s", ByteUtil.toString(getData())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean data.
|
||||
* It nullifies the data field. Tells to not send data size and payload.
|
||||
*/
|
||||
public void cleanData() {
|
||||
this.data = null;
|
||||
}
|
||||
}
|
||||
@@ -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.sinope.internal.core.appdata;
|
||||
|
||||
/**
|
||||
* The Class SinopeRoomTempData.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeHeatLevelData extends SinopeAppData {
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope set point temp data.
|
||||
*/
|
||||
public SinopeHeatLevelData() {
|
||||
super(new byte[] { 0x00, 0x00, 0x02, 0x20 }, new byte[] { 0 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the room temp.
|
||||
*
|
||||
* @return the room temp
|
||||
*/
|
||||
public int getHeatLevel() {
|
||||
if (getData() != null) {
|
||||
return getData()[0] & 0xFF;
|
||||
}
|
||||
return -273;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.appdata.SinopeAppData#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(super.toString());
|
||||
if (getData() != null) {
|
||||
sb.append(String.format("\nHeat Level is %d %%", this.getHeatLevel()));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.sinope.internal.core.appdata;
|
||||
|
||||
/**
|
||||
* The Class SinopeLocalTimeData.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeLocalTimeData extends SinopeAppData {
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope local time data.
|
||||
*/
|
||||
public SinopeLocalTimeData() {
|
||||
super(new byte[] { 0x00, 0x00, 0x06, 0x00 }, new byte[] { 0, 0, 0 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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.sinope.internal.core.appdata;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* The Class SinopeOutTempData.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeOutTempData extends SinopeAppData {
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope out temp data.
|
||||
*/
|
||||
public SinopeOutTempData() {
|
||||
super(new byte[] { 0x00, 0x00, 0x02, 0x04 }, new byte[] { 0, 0 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the out temp.
|
||||
*
|
||||
* @return the out temp
|
||||
*/
|
||||
public int getOutTemp() {
|
||||
if (getData() != null) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(getData());
|
||||
bb.order(ByteOrder.LITTLE_ENDIAN);
|
||||
return bb.getShort();
|
||||
}
|
||||
return -273;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.appdata.SinopeAppData#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(super.toString());
|
||||
if (getData() != null) {
|
||||
sb.append(String.format("\n\tOutside temperature is %2.2f C", this.getOutTemp() / 100.0));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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.sinope.internal.core.appdata;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* The Class SinopeRoomTempData.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeRoomTempData extends SinopeAppData {
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope room temp data.
|
||||
*/
|
||||
public SinopeRoomTempData() {
|
||||
super(new byte[] { 0x00, 0x00, 0x02, 0x03 }, new byte[] { 0, 0 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the room temp.
|
||||
*
|
||||
* @return the room temp
|
||||
*/
|
||||
public int getRoomTemp() {
|
||||
if (getData() != null) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(getData());
|
||||
bb.order(ByteOrder.LITTLE_ENDIAN);
|
||||
return bb.getShort();
|
||||
}
|
||||
return -273;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.appdata.SinopeAppData#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(super.toString());
|
||||
if (getData() != null) {
|
||||
sb.append(String.format("\n\tRoom temperature is %2.2f C", this.getRoomTemp() / 100.0));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 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.sinope.internal.core.appdata;
|
||||
|
||||
/**
|
||||
* The Class SinopeRoomTempData.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeSetPointModeData extends SinopeAppData {
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope set point temp data.
|
||||
*/
|
||||
public SinopeSetPointModeData() {
|
||||
super(new byte[] { 0x00, 0x00, 0x02, 0x11 }, new byte[] { 0 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the room temp.
|
||||
*
|
||||
* @return the room temp
|
||||
*/
|
||||
public int getSetPointMode() {
|
||||
if (getData() != null) {
|
||||
return getData()[0] & 0xFF;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.appdata.SinopeAppData#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(super.toString());
|
||||
if (getData() != null) {
|
||||
sb.append(String.format("\nSet point mode is ", this.getSetPointMode()));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void setSetPointMode(byte mode) {
|
||||
getData()[0] = mode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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.sinope.internal.core.appdata;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
/**
|
||||
* The Class SinopeRoomTempData.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class SinopeSetPointTempData extends SinopeAppData {
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope set point temp data.
|
||||
*/
|
||||
public SinopeSetPointTempData() {
|
||||
super(new byte[] { 0x00, 0x00, 0x02, 0x08 }, new byte[] { 0, 0 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the room temp.
|
||||
*
|
||||
* @return the room temp
|
||||
*/
|
||||
public int getSetPointTemp() {
|
||||
if (getData() != null) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(getData());
|
||||
bb.order(ByteOrder.LITTLE_ENDIAN);
|
||||
return bb.getShort();
|
||||
}
|
||||
return -273;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.appdata.SinopeAppData#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(super.toString());
|
||||
if (getData() != null) {
|
||||
sb.append(String.format("\nSet point temperature is %2.2f C", this.getSetPointTemp() / 100.0));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void setSetPointTemp(int newTemp) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(getData());
|
||||
bb.order(ByteOrder.LITTLE_ENDIAN);
|
||||
bb.putShort((short) newTemp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 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.sinope.internal.core.base;
|
||||
|
||||
/**
|
||||
* The Class NotSupportedException.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class NotSupportedException extends RuntimeException {
|
||||
|
||||
/** The Constant serialVersionUID. */
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* 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.sinope.internal.core.base;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.openhab.binding.sinope.internal.util.ByteUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The Class SinopeAnswer.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public abstract class SinopeAnswer extends SinopeRequest {
|
||||
|
||||
/** The Constant logger. */
|
||||
private static final Logger logger = LoggerFactory.getLogger(SinopeAnswer.class);
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope answer.
|
||||
*
|
||||
* @param r the r
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public SinopeAnswer(InputStream r) throws IOException {
|
||||
byte[] header = new byte[SinopeFrame.PREAMBLE_SIZE + SinopeFrame.FRAME_CTL_SIZE + SinopeFrame.SIZE_SIZE];
|
||||
|
||||
r.read(header, 0, header.length);
|
||||
|
||||
if (header[0] != 0x55) {
|
||||
throw new IOException(String.format("Invalid header PREAMBLE: %02x", header[0]));
|
||||
}
|
||||
int startSizeIndex = SinopeFrame.PREAMBLE_SIZE + SinopeFrame.FRAME_CTL_SIZE;
|
||||
int endSizeIndex = startSizeIndex + SinopeFrame.SIZE_SIZE;
|
||||
byte[] sizeInByte = Arrays.copyOfRange(header, startSizeIndex, endSizeIndex);
|
||||
ByteBuffer bb = ByteBuffer.allocate(SIZE_SIZE);
|
||||
bb.order(ByteOrder.LITTLE_ENDIAN);
|
||||
bb.put(sizeInByte);
|
||||
short size = bb.getShort(0);
|
||||
|
||||
byte[] payload = new byte[SinopeRequest.HEADER_COMMAND_CRC_SIZE + size];
|
||||
byte[] remain = new byte[size + 1];
|
||||
r.read(remain, 0, size + 1);
|
||||
bb = ByteBuffer.wrap(payload);
|
||||
bb.put(header);
|
||||
bb.put(remain);
|
||||
|
||||
this.setInternal_payload(bb.array());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeRequest#getPayload()
|
||||
*/
|
||||
/*
|
||||
*
|
||||
*
|
||||
* @see ca.tulip.sinope.core.internal.SinopeRequest#getPayload()
|
||||
*/
|
||||
@Override
|
||||
public byte[] getPayload() {
|
||||
return getInternal_payload();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeRequest#getReplyAnswer(java.io.InputStream)
|
||||
*/
|
||||
/*
|
||||
*
|
||||
*
|
||||
* @see ca.tulip.sinope.core.internal.SinopeRequest#getReplyAnswer(java.io.InputStream)
|
||||
*/
|
||||
@Override
|
||||
public SinopeAnswer getReplyAnswer(InputStream r) {
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getFrameData()
|
||||
*/
|
||||
/*
|
||||
*
|
||||
*
|
||||
* @see ca.tulip.sinope.core.internal.SinopeFrame#getFrameData()
|
||||
*/
|
||||
@Override
|
||||
protected final byte[] getFrameData() {
|
||||
byte[] b = this.getInternal_payload();
|
||||
int headerSize = SinopeFrame.PREAMBLE_SIZE + SinopeFrame.FRAME_CTL_SIZE + SinopeFrame.COMMAND_SIZE
|
||||
+ SinopeFrame.SIZE_SIZE;
|
||||
return Arrays.copyOfRange(b, headerSize, b.length - SinopeFrame.CRC_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeRequest#setInternal_payload(byte[])
|
||||
*/
|
||||
@Override
|
||||
protected void setInternal_payload(byte[] internal_payload) {
|
||||
logger.debug("Answer Frame: {}", ByteUtil.toString(internal_payload));
|
||||
super.setInternal_payload(internal_payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* 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.sinope.internal.core.base;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.openhab.binding.sinope.internal.core.appdata.SinopeAppData;
|
||||
import org.openhab.binding.sinope.internal.util.ByteUtil;
|
||||
|
||||
/**
|
||||
* The Class SinopeDataAnswer.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public abstract class SinopeDataAnswer extends SinopeAnswer {
|
||||
|
||||
/** The Constant SEQ_SIZE. */
|
||||
protected static final int SEQ_SIZE = 4;
|
||||
|
||||
/** The Constant STATUS_SIZE. */
|
||||
protected static final int STATUS_SIZE = 1;
|
||||
|
||||
/** The Constant ATTEMPT_NBR_SIZE. */
|
||||
protected static final int ATTEMPT_NBR_SIZE = 1;
|
||||
|
||||
/** The Constant MORE_SIZE. */
|
||||
protected static final int MORE_SIZE = 1;
|
||||
|
||||
/** The Constant SRC_DEVICE_ID_SIZE. */
|
||||
protected static final int SRC_DEVICE_ID_SIZE = 4;
|
||||
|
||||
/** The Constant APP_DATA_SIZE_SIZE. */
|
||||
protected static final int APP_DATA_SIZE_SIZE = 1;
|
||||
|
||||
/** The app data. */
|
||||
private SinopeAppData appData;
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope data answer.
|
||||
*
|
||||
* @param r the r
|
||||
* @param appData the app data
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public SinopeDataAnswer(InputStream r, SinopeAppData appData) throws IOException {
|
||||
super(r);
|
||||
byte[] data = getData();
|
||||
this.appData = appData;
|
||||
this.appData.read(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the app data.
|
||||
*
|
||||
* @return the app data
|
||||
*/
|
||||
public SinopeAppData getAppData() {
|
||||
return appData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the seq.
|
||||
*
|
||||
* @return the seq
|
||||
*/
|
||||
public byte[] getSeq() {
|
||||
byte[] b = this.getFrameData();
|
||||
return Arrays.copyOfRange(b, 0, SEQ_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status.
|
||||
*
|
||||
* @return the status
|
||||
*/
|
||||
public byte getStatus() {
|
||||
byte[] b = this.getFrameData();
|
||||
return b[SEQ_SIZE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the attempt nbr.
|
||||
*
|
||||
* @return the attempt nbr
|
||||
*/
|
||||
public byte getAttemptNbr() {
|
||||
byte[] b = this.getFrameData();
|
||||
return b[SEQ_SIZE + STATUS_SIZE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the more.
|
||||
*
|
||||
* @return the more
|
||||
*/
|
||||
public byte getMore() {
|
||||
byte[] b = this.getFrameData();
|
||||
return b[SEQ_SIZE + STATUS_SIZE + ATTEMPT_NBR_SIZE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the src device id.
|
||||
*
|
||||
* @return the src device id
|
||||
*/
|
||||
public byte[] getSrcDeviceId() {
|
||||
byte[] b = this.getFrameData();
|
||||
int start = SEQ_SIZE + STATUS_SIZE + ATTEMPT_NBR_SIZE + MORE_SIZE;
|
||||
int end = start + SRC_DEVICE_ID_SIZE;
|
||||
return ByteUtil.reverse(Arrays.copyOfRange(b, start, end));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data.
|
||||
*
|
||||
* @return the data
|
||||
*/
|
||||
public byte[] getData() {
|
||||
byte[] b = this.getFrameData();
|
||||
int start = SEQ_SIZE + STATUS_SIZE + ATTEMPT_NBR_SIZE + MORE_SIZE + SRC_DEVICE_ID_SIZE;
|
||||
int end = start + 1 + (b[start] & 0xff);
|
||||
return Arrays.copyOfRange(b, start + 1, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#toString()
|
||||
*/
|
||||
/*
|
||||
*
|
||||
*
|
||||
* @see ca.tulip.sinope.core.internal.SinopeFrame#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(super.toString());
|
||||
sb.append(String.format("\nData: %s", ByteUtil.toString(getFrameData())));
|
||||
sb.append(String.format("\n\tSeq: %s", ByteUtil.toString(getSeq())));
|
||||
sb.append(String.format("\n\tStatus: 0x%02X ", getStatus()));
|
||||
sb.append(String.format("\n\tAttempt Nbr: 0x%02X ", getAttemptNbr()));
|
||||
sb.append(String.format("\n\tMore: 0x%02X ", getMore()));
|
||||
|
||||
sb.append(String.format("\n\tSrcDeviceId: %s", ByteUtil.toString(getSrcDeviceId())));
|
||||
|
||||
sb.append(appData);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* 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.sinope.internal.core.base;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.openhab.binding.sinope.internal.core.appdata.SinopeAppData;
|
||||
import org.openhab.binding.sinope.internal.util.ByteUtil;
|
||||
|
||||
/**
|
||||
* The Class SinopeDataRequest.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public abstract class SinopeDataRequest extends SinopeRequest {
|
||||
|
||||
/** The seq. */
|
||||
private byte[] seq;
|
||||
|
||||
/** The request type. */
|
||||
private byte requestType;
|
||||
|
||||
/** The res 1. */
|
||||
private byte res1;
|
||||
|
||||
/** The res 2. */
|
||||
private byte res2;
|
||||
|
||||
/** The res 3. */
|
||||
private byte[] res3;
|
||||
|
||||
/** The res 4. */
|
||||
private byte[] res4;
|
||||
|
||||
/** The dst device id. */
|
||||
private byte[] dstDeviceId;
|
||||
|
||||
/** The app data. */
|
||||
private SinopeAppData appData;
|
||||
|
||||
/**
|
||||
* Instantiates a new sinope data request.
|
||||
*
|
||||
* @param seq the seq
|
||||
* @param dstDeviceId the dst device id
|
||||
* @param appData the app data
|
||||
*/
|
||||
public SinopeDataRequest(byte[] seq, byte[] dstDeviceId, SinopeAppData appData) {
|
||||
super();
|
||||
this.seq = seq;
|
||||
this.requestType = 0;
|
||||
this.res1 = 0;
|
||||
this.res2 = 0;
|
||||
this.res3 = new byte[] { 0, 0 };
|
||||
this.res4 = new byte[] { 0, 0 };
|
||||
this.dstDeviceId = dstDeviceId;
|
||||
|
||||
this.appData = appData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the seq.
|
||||
*
|
||||
* @return the seq
|
||||
*/
|
||||
public byte[] getSeq() {
|
||||
return seq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the request type.
|
||||
*
|
||||
* @return the request type
|
||||
*/
|
||||
public byte getRequestType() {
|
||||
return requestType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the res 1.
|
||||
*
|
||||
* @return the res 1
|
||||
*/
|
||||
public byte getRes1() {
|
||||
return res1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the res 2.
|
||||
*
|
||||
* @return the res 2
|
||||
*/
|
||||
public byte getRes2() {
|
||||
return res2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the res 3.
|
||||
*
|
||||
* @return the res 3
|
||||
*/
|
||||
public byte[] getRes3() {
|
||||
return res3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the res 4.
|
||||
*
|
||||
* @return the res 4
|
||||
*/
|
||||
public byte[] getRes4() {
|
||||
return res4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the dst device id.
|
||||
*
|
||||
* @return the dst device id
|
||||
*/
|
||||
public byte[] getDstDeviceId() {
|
||||
return dstDeviceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the app data size.
|
||||
*
|
||||
* @return the app data size
|
||||
*/
|
||||
public int getAppDataSize() {
|
||||
return getAppData().getInternalData().length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the app data.
|
||||
*
|
||||
* @return the app data
|
||||
*/
|
||||
public SinopeAppData getAppData() {
|
||||
return appData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getFrameData()
|
||||
*/
|
||||
/*
|
||||
*
|
||||
*
|
||||
* @see ca.tulip.sinope.core.internal.SinopeFrame#getFrameData()
|
||||
*/
|
||||
@Override
|
||||
protected byte[] getFrameData() {
|
||||
int appDataLen = getAppDataSize();
|
||||
byte b[] = new byte[seq.length + 1 + 1 + 1 + res3.length + res4.length + dstDeviceId.length + 1 + appDataLen];
|
||||
|
||||
ByteBuffer bb = ByteBuffer.wrap(b);
|
||||
|
||||
bb.put(ByteUtil.reverse(seq));
|
||||
bb.put(requestType);
|
||||
bb.put(res1);
|
||||
bb.put(res2);
|
||||
bb.put(ByteUtil.reverse(res3));
|
||||
bb.put(ByteUtil.reverse(res4));
|
||||
bb.put(ByteUtil.reverse(dstDeviceId));
|
||||
bb.put((byte) appDataLen);
|
||||
bb.put(getAppData().getInternalData());
|
||||
//
|
||||
// System.out.println(toString(bb.array()));
|
||||
return bb.array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#toString()
|
||||
*/
|
||||
/*
|
||||
*
|
||||
*
|
||||
* @see ca.tulip.sinope.core.internal.SinopeFrame#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(String.format("\nData: %s", ByteUtil.toString(getFrameData())));
|
||||
sb.append(String.format("\n\tSeq: %s", ByteUtil.toString(getSeq())));
|
||||
sb.append(String.format("\n\tRequest Type: 0x%02X ", getRequestType()));
|
||||
sb.append(String.format("\n\tRes1: 0x%02X ", getRes1()));
|
||||
sb.append(String.format("\n\tRes2: 0x%02X ", getRes2()));
|
||||
sb.append(String.format("\n\tRes3: %s", ByteUtil.toString(getRes3())));
|
||||
sb.append(String.format("\n\tRes4: %s", ByteUtil.toString(getRes4())));
|
||||
sb.append(String.format("\n\tDstDeviceId: %s", ByteUtil.toString(getDstDeviceId())));
|
||||
sb.append(String.format("\n\tAppDataSize: 0x%02X ", getAppDataSize()));
|
||||
sb.append(String.format("\n\tAppData: %s", getAppData()));
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeRequest#getReplyAnswer(java.io.InputStream)
|
||||
*/
|
||||
/*
|
||||
*
|
||||
*
|
||||
* @see ca.tulip.sinope.core.internal.SinopeRequest#getReplyAnswer(java.io.InputStream)
|
||||
*/
|
||||
@Override
|
||||
public abstract SinopeDataAnswer getReplyAnswer(InputStream r) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* 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.sinope.internal.core.base;
|
||||
|
||||
import org.openhab.binding.sinope.internal.util.ByteUtil;
|
||||
import org.openhab.binding.sinope.internal.util.CRC8;
|
||||
|
||||
/**
|
||||
* The Class SinopeFrame.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
abstract class SinopeFrame {
|
||||
|
||||
/** The Constant PREAMBLE. */
|
||||
protected static final byte PREAMBLE = 0x55;
|
||||
|
||||
/** The Constant FRAME_CTL. */
|
||||
protected static final byte FRAME_CTL = 0x00;
|
||||
|
||||
/** The Constant PREAMBLE_SIZE. */
|
||||
protected static final int PREAMBLE_SIZE = 1;
|
||||
|
||||
/** The Constant FRAME_CTL_SIZE. */
|
||||
protected static final int FRAME_CTL_SIZE = 1;
|
||||
|
||||
/** The Constant CRC_SIZE. */
|
||||
protected static final int CRC_SIZE = 1;
|
||||
|
||||
/** The Constant SIZE_SIZE. */
|
||||
protected static final int SIZE_SIZE = 2;
|
||||
|
||||
/** The Constant COMMAND_SIZE. */
|
||||
protected static final byte COMMAND_SIZE = 2;
|
||||
|
||||
/** The crc 8. */
|
||||
private final CRC8 crc8 = new CRC8();
|
||||
|
||||
/** The internal payload. */
|
||||
protected byte[] internal_payload;
|
||||
|
||||
/**
|
||||
* Gets the command.
|
||||
*
|
||||
* @return the command
|
||||
*/
|
||||
protected abstract byte[] getCommand();
|
||||
|
||||
/**
|
||||
* Gets the frame data.
|
||||
*
|
||||
* @return the frame data
|
||||
*/
|
||||
protected abstract byte[] getFrameData();
|
||||
|
||||
/**
|
||||
* Gets the payload.
|
||||
*
|
||||
* @return the payload
|
||||
*/
|
||||
protected abstract byte[] getPayload();
|
||||
|
||||
/**
|
||||
* Gets the crc8.
|
||||
*
|
||||
* @param buffer the buffer
|
||||
* @return the crc8
|
||||
*/
|
||||
protected byte getCRC8(byte[] buffer) {
|
||||
crc8.reset();
|
||||
crc8.update(buffer, 0, buffer.length - 1);
|
||||
return (byte) (crc8.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
/*
|
||||
*
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
getPayload();
|
||||
sb.append(ByteUtil.toString(internal_payload));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the payload.
|
||||
*
|
||||
* @param payload the new payload
|
||||
*/
|
||||
public void setPayload(byte[] payload) {
|
||||
setInternal_payload(payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the internal payload.
|
||||
*
|
||||
* @return the internal payload
|
||||
*/
|
||||
protected byte[] getInternal_payload() {
|
||||
return internal_payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the internal payload.
|
||||
*
|
||||
* @param internal_payload the new internal payload
|
||||
*/
|
||||
protected void setInternal_payload(byte[] internal_payload) {
|
||||
this.internal_payload = internal_payload;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* 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.sinope.internal.core.base;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import org.openhab.binding.sinope.internal.util.ByteUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The Class SinopeRequest.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public abstract class SinopeRequest extends SinopeFrame {
|
||||
|
||||
/** The Constant HEADER_COMMAND_CRC_SIZE. */
|
||||
protected static final int HEADER_COMMAND_CRC_SIZE = SinopeFrame.PREAMBLE_SIZE + SinopeFrame.FRAME_CTL_SIZE
|
||||
+ SinopeFrame.SIZE_SIZE + SinopeFrame.COMMAND_SIZE + SinopeFrame.CRC_SIZE;
|
||||
|
||||
/** The Constant logger. */
|
||||
private static final Logger logger = LoggerFactory.getLogger(SinopeRequest.class);
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#getPayload()
|
||||
*/
|
||||
/*
|
||||
*
|
||||
*
|
||||
* @see ca.tulip.sinope.core.internal.SinopeFrame#getPayload()
|
||||
*/
|
||||
@Override
|
||||
public byte[] getPayload() {
|
||||
if (getInternal_payload() == null) {
|
||||
byte[] command = getCommand();
|
||||
byte[] data = getFrameData();
|
||||
int len = HEADER_COMMAND_CRC_SIZE + data.length;
|
||||
byte[] buffer = new byte[len];
|
||||
ByteBuffer bb = ByteBuffer.wrap(buffer);
|
||||
bb.put(PREAMBLE);
|
||||
bb.put(FRAME_CTL);
|
||||
bb.order(ByteOrder.LITTLE_ENDIAN);
|
||||
bb.putShort((short) (SinopeFrame.COMMAND_SIZE + data.length));
|
||||
|
||||
bb.put(ByteUtil.reverse(command));
|
||||
bb.put(data);
|
||||
|
||||
bb.put(getCRC8(bb.array()));
|
||||
|
||||
setInternal_payload(bb.array());
|
||||
}
|
||||
return getInternal_payload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the reply answer.
|
||||
*
|
||||
* @param r the r
|
||||
* @return the reply answer
|
||||
* @throws IOException Signals that an I/O exception has occurred.
|
||||
*/
|
||||
public abstract SinopeAnswer getReplyAnswer(InputStream r) throws IOException;
|
||||
|
||||
/**
|
||||
* @see org.openhab.binding.sinope.internal.core.base.SinopeFrame#setInternal_payload(byte[])
|
||||
*/
|
||||
@Override
|
||||
protected void setInternal_payload(byte[] internal_payload) {
|
||||
logger.debug("Request Frame: {}", ByteUtil.toString(internal_payload));
|
||||
super.setInternal_payload(internal_payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* 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.sinope.internal.discovery;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
import org.openhab.binding.sinope.SinopeBindingConstants;
|
||||
import org.openhab.binding.sinope.handler.SinopeGatewayHandler;
|
||||
import org.openhab.binding.sinope.internal.util.ByteUtil;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryListener;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link AbstractDiscoveryService} provides methods which handle the {@link DiscoveryListener}s.
|
||||
*
|
||||
* Subclasses do not have to care about adding and removing those listeners.
|
||||
* They can use the protected methods {@link #thingDiscovered(DiscoveryResult)} and {@link #thingRemoved(String)} in
|
||||
* order to notify the registered {@link DiscoveryListener}s.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class SinopeThingsDiscoveryService extends AbstractDiscoveryService {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(SinopeThingsDiscoveryService.class);
|
||||
|
||||
private static final int SEARCH_TIME = 120;
|
||||
|
||||
private SinopeGatewayHandler sinopeGatewayHandler;
|
||||
|
||||
public SinopeThingsDiscoveryService(SinopeGatewayHandler sinopeGatewayHandler) {
|
||||
super(SinopeBindingConstants.SUPPORTED_THING_TYPES_UIDS, SEARCH_TIME);
|
||||
this.sinopeGatewayHandler = sinopeGatewayHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypes() {
|
||||
return SinopeBindingConstants.SUPPORTED_THING_TYPES_UIDS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startScan() {
|
||||
logger.debug("Sinope Things starting scan");
|
||||
try {
|
||||
sinopeGatewayHandler.startSearch(this);
|
||||
} catch (IOException e) {
|
||||
logger.debug("Search failed with an exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void stopScan() {
|
||||
super.stopScan();
|
||||
removeOlderResults(getTimestampOfLastScan());
|
||||
try {
|
||||
sinopeGatewayHandler.stopSearch();
|
||||
} catch (IOException e) {
|
||||
logger.debug("Can't stop search with an exception", e);
|
||||
} finally {
|
||||
logger.debug("Sinope Things scan stopped");
|
||||
}
|
||||
}
|
||||
|
||||
public void newThermostat(byte[] deviceId) {
|
||||
logger.debug("Sinope Things service discovered a new device with id: {}", ByteUtil.toString(deviceId));
|
||||
ThingTypeUID thingTypeUID = SinopeBindingConstants.THING_TYPE_THERMO;
|
||||
ThingUID bridgeUID = sinopeGatewayHandler.getThing().getUID();
|
||||
ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, toUID(deviceId));
|
||||
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID)
|
||||
.withBridge(bridgeUID).withLabel("Device-Sinope")
|
||||
.withProperty(SinopeBindingConstants.CONFIG_PROPERTY_DEVICE_ID, ByteUtil.toString(deviceId)).build();
|
||||
|
||||
thingDiscovered(discoveryResult);
|
||||
}
|
||||
|
||||
private static String toUID(byte[] deviceId) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : deviceId) {
|
||||
sb.append(String.format("%02X", b));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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.sinope.internal.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* The Class ByteUtil.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class ByteUtil {
|
||||
|
||||
/**
|
||||
* Reverse.
|
||||
*
|
||||
* @param array to reverse
|
||||
* @return the reserved in byte[]
|
||||
*/
|
||||
public static byte[] reverse(byte[] array) {
|
||||
if (array == null) {
|
||||
return null;
|
||||
}
|
||||
byte[] r = Arrays.copyOf(array, array.length);
|
||||
int i = 0;
|
||||
int j = r.length - 1;
|
||||
byte tmp;
|
||||
while (j > i) {
|
||||
tmp = r[j];
|
||||
r[j] = r[i];
|
||||
r[i] = tmp;
|
||||
j--;
|
||||
i++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* To string.
|
||||
*
|
||||
* @param buf the buf
|
||||
* @return the string
|
||||
*/
|
||||
public static String toString(byte[] buf) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : buf) {
|
||||
sb.append(String.format("0x%02X ", b));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* 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.sinope.internal.util;
|
||||
|
||||
import java.util.zip.Checksum;
|
||||
|
||||
/**
|
||||
* The Class CRC8.
|
||||
*
|
||||
* @author Pascal Larin - Initial contribution
|
||||
*/
|
||||
public class CRC8 implements Checksum {
|
||||
|
||||
/** The init. */
|
||||
private final int init;
|
||||
|
||||
/** The Constant crcTable. */
|
||||
private static final int crcTable[] = { 0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31,
|
||||
0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53,
|
||||
0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd,
|
||||
0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0,
|
||||
0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe,
|
||||
0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a, 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c,
|
||||
0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42,
|
||||
0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6,
|
||||
0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8,
|
||||
0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a,
|
||||
0x43, 0x44, 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34,
|
||||
0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, 0x3e, 0x39,
|
||||
0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7,
|
||||
0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5,
|
||||
0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3 };
|
||||
|
||||
/** The value. */
|
||||
private int value;
|
||||
|
||||
/**
|
||||
* Instantiates a new crc8.
|
||||
*/
|
||||
public CRC8() {
|
||||
this.value = this.init = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.zip.Checksum#update(byte[], int, int)
|
||||
*/
|
||||
@Override
|
||||
public void update(byte[] buffer, int offset, int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
byte data = buffer[offset + i];
|
||||
value = crcTable[value ^ data & 0xff];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update.
|
||||
*
|
||||
* @param buffer the buffer
|
||||
*/
|
||||
public void update(byte[] buffer) {
|
||||
update(buffer, 0, buffer.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.zip.Checksum#update(int)
|
||||
*/
|
||||
@Override
|
||||
public void update(int b) {
|
||||
update(new byte[] { (byte) b }, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.zip.Checksum#getValue()
|
||||
*/
|
||||
@Override
|
||||
public long getValue() {
|
||||
return value & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.util.zip.Checksum#reset()
|
||||
*/
|
||||
@Override
|
||||
public void reset() {
|
||||
value = init;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="sinope" 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>Sinopé Binding</name>
|
||||
<description>This is the binding for Sinopé. Sinopé provides high-quality, energy-efficient products and with
|
||||
innovative technology solutions that will meet customer's current and future needs:
|
||||
low and high voltage thermostats,
|
||||
light switches, water leak detectors and load controllers.</description>
|
||||
<author>Pascal Larin</author>
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,4 @@
|
||||
config-status.error.missing-host-configuration=No host for the sinopé gateway has been provided.
|
||||
config-status.error.missing-port-configuration=No port for the sinopé gateway has been provided.
|
||||
invalid-gateway-id-configuration=Missing or invalid gateway id.
|
||||
invalid-api-key-configuration=Missing or invalid API key.
|
||||
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="sinope"
|
||||
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="gateway">
|
||||
<label>Sinopé Gateway</label>
|
||||
<description>A Sinopé Gateway</description>
|
||||
|
||||
<config-description>
|
||||
<parameter name="hostname" type="text">
|
||||
<label>Hostname</label>
|
||||
<description>Hostname of the Sinopé Gateway</description>
|
||||
<context>network-address</context>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
<parameter name="port" type="integer">
|
||||
<label>Port</label>
|
||||
<description>The port that the Sinopé Gateway listens on</description>
|
||||
<default>4550</default>
|
||||
</parameter>
|
||||
<parameter name="gatewayId" type="text">
|
||||
<label>Gateway ID</label>
|
||||
<description>The Sinopé gateway ID (as printed on the back-side, e.g. "xxxx xxxx xxxx xxxx")</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
<parameter name="apiKey" type="text">
|
||||
<label>API Key</label>
|
||||
<description>Use sinope-core application to generate your api key</description>
|
||||
<required>true</required>
|
||||
</parameter>
|
||||
<parameter name="refresh" type="integer" required="false">
|
||||
<label>Refresh Interval</label>
|
||||
<description>The number of seconds between fetches from the Sinopé Gateway.</description>
|
||||
<default>60</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
<thing-type id="thermostat">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="gateway"/>
|
||||
</supported-bridge-type-refs>
|
||||
<label>Sinopé Thermostat</label>
|
||||
<description>Sinopé Thermostat control</description>
|
||||
<channels>
|
||||
<channel id="insideTemperature" typeId="insideTemperature"/>
|
||||
<channel id="outsideTemperature" typeId="outsideTemperature"/>
|
||||
<channel id="setpointTemperature" typeId="setpointTemperature"/>
|
||||
<channel id="setpointMode" typeId="setpointMode"/>
|
||||
<channel id="heatingLevel" typeId="heatingLevel"/>
|
||||
</channels>
|
||||
<config-description>
|
||||
<parameter name="deviceId" type="text" required="true">
|
||||
<label>Thermostat Device ID</label>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="insideTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature</label>
|
||||
<description>Current inside temperature</description>
|
||||
<category>Temperature</category>
|
||||
<!-- As specified by manufacturer -->
|
||||
<state min="-10" max="70" step="0.01" pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="outsideTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature</label>
|
||||
<description>Current outside temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state step="0.01" pattern="%.2f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="setpointTemperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature</label>
|
||||
<description>Setpoint temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state min="5" max="30" step="0.01" pattern="%.2f %unit%" readOnly="false"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="setpointMode">
|
||||
<item-type>Number</item-type>
|
||||
<label>Setpoint Mode</label>
|
||||
<description>Thermostat setpoint mode</description>
|
||||
<category>Temperature</category>
|
||||
<state pattern="%s" readOnly="false">
|
||||
<options>
|
||||
<option value="0">Off</option>
|
||||
<option value="1">Freeze Protect</option>
|
||||
<option value="2">Manual</option>
|
||||
<option value="3">Auto</option>
|
||||
<option value="5">Away</option>
|
||||
<option value="129">Bypass Freeze Protect</option>
|
||||
<option value="131">Bypass Auto</option>
|
||||
<option value="133">Bypass Away</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="heatingLevel">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>Heat Level</label>
|
||||
<description>Heating Level</description>
|
||||
<category>Heating</category>
|
||||
<state min="0" max="100" step="1" pattern="%d %%" readOnly="true"/>
|
||||
</channel-type>
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user