mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 07:02:02 +01:00
[ventaair] New VentaAir binding for air humidifiers (#9979)
* [ventaair] New VentaAir binding for air humidifiers New binding that implements support for air humidifier from Venta Air. Closes #9922 Signed-off-by: Stefan Triller <github@stefantriller.de>
This commit is contained in:
parent
95cdc3cb35
commit
08602c04b4
@ -286,6 +286,7 @@
|
||||
/bundles/org.openhab.binding.velbus/ @cedricboon
|
||||
/bundles/org.openhab.binding.velux/ @gs4711
|
||||
/bundles/org.openhab.binding.venstarthermostat/ @hww3 @digitaldan
|
||||
/bundles/org.openhab.binding.ventaair/ @t2000
|
||||
/bundles/org.openhab.binding.verisure/ @jannegpriv
|
||||
/bundles/org.openhab.binding.vigicrues/ @clinique
|
||||
/bundles/org.openhab.binding.vitotronic/ @steand
|
||||
|
@ -1411,6 +1411,11 @@
|
||||
<artifactId>org.openhab.binding.venstarthermostat</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.ventaair</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openhab.addons.bundles</groupId>
|
||||
<artifactId>org.openhab.binding.verisure</artifactId>
|
||||
|
13
bundles/org.openhab.binding.ventaair/NOTICE
Normal file
13
bundles/org.openhab.binding.ventaair/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
|
127
bundles/org.openhab.binding.ventaair/README.md
Normal file
127
bundles/org.openhab.binding.ventaair/README.md
Normal file
@ -0,0 +1,127 @@
|
||||
# VentaAir Binding
|
||||
|
||||
This binding is for air humidifiers from Venta Air.
|
||||
Thankfully the vendor allows for communicating within the local network without needing any internet access or accounts.
|
||||
This is even stated in the official manual.
|
||||
Hence this binding communicates locally with the humidifier and is able to read out the current measurements and settings, as well as changing the settings.
|
||||
|
||||
## Supported Things
|
||||
|
||||
It currently supports the LW60-T device (`ThingType`: "lw60t") as well as a `ThingType` ("generic") for other models.
|
||||
For now the generic `ThingType` only adds the "boost" channel, but in the status reply from the device there is more which could be added in the future by someone who owns a different device.
|
||||
|
||||
## Discovery
|
||||
|
||||
This binding supports an automatic discovery for humidifiers that are connected to the local network and which are in the same broadcast domain.
|
||||
To do so, the binding listens to UDP port 48000 for data and creates `DiscoveryResult`s based on the received data from the device.
|
||||
This comes in handy for getting the MAC address for the device for example.
|
||||
Once the `DiscoveryResult` is added as a `Thing`, a connection to the device will be created and it will beep, showing a confirmation screen that the device "openHAB" would like to get access.
|
||||
After confirming this request, the user can link its items to receive data or control the device.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
There are three mandatory configuration parameters for a thing: `ipAddress`, `macAddress` and `deviceType`.
|
||||
|
||||
| parameter | required | description |
|
||||
|----------|------------|-------------------------------------------|
|
||||
| ipAddress | Y | The IP Address or hostname of the device. |
|
||||
| macAddress | Y | The MAC address of the device. |
|
||||
| deviceType | Y | Defines the type of device. It is an integer value and its best to use the automatic discovery to obtain it from the device. |
|
||||
| pollingTime | N | The time interval in seconds in which the data should be polled from the device, default is 10 seconds. |
|
||||
| hash | N | It is a negative integer value and it is used by the device to identify a connection to a client, like the App from the vendor for example. (*) |
|
||||
|
||||
(*) I do not know whether there are devices which are restricted to only one client, so I added this parameter to allow the user to set the same value as his App on the phone (can be obtained via sniffing the network).
|
||||
However, the LW60-T allows for multiple connections to different clients, identified by different `hash` values at the same time without issues.
|
||||
By default the binding uses "-42", so a new ID that is not known to the device and hence it asks for confirmation, see the Discovery section.
|
||||
|
||||
Example Thing configuration:
|
||||
|
||||
```
|
||||
Thing ventaair:lw60t:humidifier [ ipAddress="192.168.42.69", macAddress="f8:f0:05:a6:4e:03", deviceType=4, pollingTime=10, hash=-42]
|
||||
```
|
||||
|
||||
## Channels
|
||||
|
||||
These are the channels that are currently supported:
|
||||
|
||||
|
||||
| channel | type (RO=read-only) | description |
|
||||
|----------|--------|------------------------------|
|
||||
| power | Switch | This is the power on/off channel |
|
||||
| fanSpeed | Number | This is the channel to control the steps (in range 0-5 where 0 means "off") for the speed of the fan |
|
||||
| targetHumidity | Number | This channel sets the target humidity (in percent) that should be tried to reach by the device (allowed values: 30-70) |
|
||||
| timer | Number | This channel sets the power off timer to the set value in hours, i.e. 3 = turn off in 3 hours from now (allowed values: 0-9 where 0 means "off") |
|
||||
| sleepMode | Switch | This channel controls the sleep mode of the device (dims the display and slows down the fan) |
|
||||
| childLock | Switch | This is the control channel for the child lock |
|
||||
| automatic | Switch | This is the control channel to start the automatic operation mode of the device |
|
||||
| cleanMode | Switch (RO) | This is the channel that indicates if the device is in the cleaning mode |
|
||||
| temperature | Number:Temperature (RO) | This channel provides the current measured temperature in Celsius or Fahrenheit as configured on the device |
|
||||
| humidity | Number:Dimensionless (RO) | This channel provides the humidity measured by the device in percent |
|
||||
| waterLevel | Number (RO) | This channel indicates the water level of the tank where 1 is equal to the yellow "refill tank" warning on the device/App |
|
||||
| fanRPM | Number (RO) | This channel provides the speed of the ventilation fan |
|
||||
| timerTimePassed | Number:Time (RO) | If a timer has been set, this channel provides the minutes since when the timer was started |
|
||||
| operationTime | Number:Time (RO) | This channel provides the operation time of the device in hours |
|
||||
| discReplaceTime | Number:Time (RO) | This channel provides the time in how many hours the cleaning disc should be replaced |
|
||||
| cleaningTime | Number:Time (RO) | This channel provides the time in how many hours the device should be cleaned |
|
||||
| boost | Switch | This is the control channel for the boost mode (on some devices that supports it) |
|
||||
|
||||
## Full Example
|
||||
|
||||
Things:
|
||||
|
||||
```
|
||||
Thing ventaair:lw60t:humidifier [ ipAddress="192.168.42.69", macAddress="f8:f0:05:a6:4e:03", deviceType=4, pollingTime=10, hash=-42]
|
||||
```
|
||||
|
||||
Items:
|
||||
|
||||
```
|
||||
Group gHumidifier "Air Humidifier" <humidity>
|
||||
|
||||
Switch Humidifier_Power "Power: [%s]" (gHumidifier) { channel="ventaair:lw60t:humidifier:power" }
|
||||
Number Humidifier_FanSpeed "FanSpeed: [%s]" (gHumidifier) { channel="ventaair:lw60t:humidifier:fanSpeed" }
|
||||
Number Humidifier_TargetHum "Target Humidity: [%s]" (gHumidifier) { channel="ventaair:lw60t:humidifier:targetHumidity" }
|
||||
Number Humidifier_Timer "Timer: [%s]" (gHumidifier) { channel="ventaair:lw60t:humidifier:timer" }
|
||||
|
||||
Switch Humidifier_SleepMode "SleepMode:" (gHumidifier) { channel="ventaair:lw60t:humidifier:sleepMode" }
|
||||
Switch Humidifier_ChildLock "ChildLock:" (gHumidifier) { channel="ventaair:lw60t:humidifier:childLock" }
|
||||
Switch Humidifier_Automatic "Automatic:" (gHumidifier) { channel="ventaair:lw60t:humidifier:automatic" }
|
||||
|
||||
Switch Humidifier_CleaningMode "Cleaning mode:" (gHumidifier) { channel="ventaair:lw60t:humidifier:cleanMode" }
|
||||
|
||||
Number:Temperature Humidifier_Temperature "Temp: [%.1f %unit%]" (gHumidifier) { channel="ventaair:lw60t:humidifier:temperature" }
|
||||
Number:Temperature Humidifier_temperatureF "Temp: [%.1f °F]" (gHumidifier) { channel="ventaair:lw60t:humidifier:temperature" }
|
||||
Number Humidifier_Humidity "Humidity: [%.1f %%]" (gHumidifier) { channel="ventaair:lw60t:humidifier:humidity" }
|
||||
|
||||
Number Humidifier_WaterLevel "WaterLevel: [%d]" (gHumidifier) { channel="ventaair:lw60t:humidifier:waterLevel" }
|
||||
Number Humidifier_FanRPM "Fan RPM: [%d]" (gHumidifier) { channel="ventaair:lw60t:humidifier:fanRPM" }
|
||||
|
||||
Number Humidifier_TimerTime "Timer time: [%d]" (gHumidifier) { channel="ventaair:lw60t:humidifier:timerTimePassed" }
|
||||
Number Humidifier_OpTime "Operation Time: [%d]" (gHumidifier) { channel="ventaair:lw60t:humidifier:operationTime" }
|
||||
Number Humidifier_ReplaceTime "Disc replace in (h): [%d]" (gHumidifier) { channel="ventaair:lw60t:humidifier:discReplaceTime" }
|
||||
Number Humidifier_CleaningTime "Cleaning in (h): [%d]" (gHumidifier) { channel="ventaair:lw60t:humidifier:cleaningTime" }
|
||||
|
||||
//for generic devices:
|
||||
Switch boost "Boost:" { channel="ventaair:generic:humidifier:boost" }
|
||||
```
|
||||
|
||||
Sitemap:
|
||||
|
||||
```
|
||||
Text item=Humidifier_Humidity
|
||||
Text item=Humidifier_Temperature
|
||||
Switch item=Humidifier_Power
|
||||
Switch item=Humidifier_SleepMode
|
||||
Switch item=Humidifier_FanSpeed icon="fan" mappings=[0="0", 1="1", 2="2", 3="3", 4="4", 5="5"]
|
||||
Switch item=Humidifier_TargetHum mappings=[30="30", 35="35", 40="40", 45="45", 50="50", 55="55", 60="60", 65="65", 70="70"]
|
||||
Switch item=Humidifier_Timer mappings=[0="0", 1="1", 3="3", 5="5", 7="7", 9="9"]
|
||||
Text item=Humidifier_WaterLevel
|
||||
Text item=Humidifier_FanRPM
|
||||
Text item=Humidifier_OpTime
|
||||
Text item=Humidifier_ReplaceTime
|
||||
Text item=Humidifier_CleaningTime
|
||||
Text item=Humidifier_TimerTime
|
||||
Switch item=Humidifier_CleaningModeActive
|
||||
Switch item=Humidifier_ChildLock
|
||||
Switch item=Humidifier_Automatic
|
||||
```
|
17
bundles/org.openhab.binding.ventaair/pom.xml
Normal file
17
bundles/org.openhab.binding.ventaair/pom.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 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.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.binding.ventaair</artifactId>
|
||||
|
||||
<name>openHAB Add-ons :: Bundles :: VentaAir Binding</name>
|
||||
|
||||
</project>
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.ventaair-${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-ventaair" description="VentaAir Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.ventaair/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
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.ventaair.internal.VentaThingHandler.StateUpdatedCallback;
|
||||
import org.openhab.binding.ventaair.internal.message.action.Action;
|
||||
import org.openhab.binding.ventaair.internal.message.dto.CommandMessage;
|
||||
import org.openhab.binding.ventaair.internal.message.dto.DeviceInfoMessage;
|
||||
import org.openhab.binding.ventaair.internal.message.dto.Header;
|
||||
import org.openhab.binding.ventaair.internal.message.dto.Message;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* The {@link Communicator} is responsible for sending/receiving commands to/from the device
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class Communicator {
|
||||
private static final Duration COMMUNICATION_TIMEOUT = Duration.ofSeconds(5);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(Communicator.class);
|
||||
|
||||
private @Nullable String ipAddress;
|
||||
private Header header;
|
||||
private int pollingTimeInSeconds;
|
||||
private StateUpdatedCallback callback;
|
||||
|
||||
private Gson gson = new Gson();
|
||||
|
||||
private @Nullable ScheduledFuture<?> pollingJob;
|
||||
|
||||
public Communicator(@Nullable String ipAddress, Header header, @Nullable BigDecimal pollingTime,
|
||||
StateUpdatedCallback callback) {
|
||||
this.ipAddress = ipAddress;
|
||||
this.header = header;
|
||||
if (pollingTime != null) {
|
||||
this.pollingTimeInSeconds = pollingTime.intValue();
|
||||
} else {
|
||||
this.pollingTimeInSeconds = 60;
|
||||
}
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request message to the device, reads the reply and informs the listener about the current device data
|
||||
*/
|
||||
public void pollDataFromDevice() {
|
||||
String messageJson = gson.toJson(new Message(header));
|
||||
|
||||
try (Socket socket = new Socket(ipAddress, VentaAirBindingConstants.PORT)) {
|
||||
socket.setSoTimeout((int) COMMUNICATION_TIMEOUT.toMillis());
|
||||
InputStream input = socket.getInputStream();
|
||||
OutputStream output = socket.getOutputStream();
|
||||
|
||||
byte[] dataToSend = buildMessageBytes(messageJson, "GET", "Complete");
|
||||
// we write these lines to the log in order to help users with new/other venta devices, so they only need to
|
||||
// enable debug logging
|
||||
logger.debug("Sending request data message (String):\n{}", new String(dataToSend));
|
||||
logger.debug("Sending request data message (bytes): [{}]", HexUtils.bytesToHex(dataToSend, ", "));
|
||||
output.write(dataToSend);
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
String reply = "";
|
||||
while ((reply = br.readLine()) != null) {
|
||||
if (reply.startsWith("{")) {
|
||||
// remove padding byte(s) after JSON data
|
||||
String data = String.valueOf(reply.toCharArray(), 0, reply.length() - 1);
|
||||
// we write this line to the log in order to help users with new/other venta devices, so they only
|
||||
// need to enable debug logging
|
||||
logger.debug("Got Data from device: {}", data);
|
||||
|
||||
DeviceInfoMessage deviceInfoMessage = gson.fromJson(data, DeviceInfoMessage.class);
|
||||
if (deviceInfoMessage != null) {
|
||||
callback.stateUpdated(deviceInfoMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
br.close();
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
callback.communicationProblem();
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] buildMessageBytes(String message, String method, String endpoint) throws IOException {
|
||||
ByteArrayOutputStream getInfoOutputStream = new ByteArrayOutputStream();
|
||||
getInfoOutputStream
|
||||
.write(createMessageHeader(method, endpoint, message.length()).getBytes(StandardCharsets.UTF_8));
|
||||
getInfoOutputStream.write(message.getBytes(StandardCharsets.UTF_8));
|
||||
getInfoOutputStream.write(new byte[] { 0x1c, 0x00 });
|
||||
return getInfoOutputStream.toByteArray();
|
||||
}
|
||||
|
||||
private String createMessageHeader(String method, String endPoint, int contentLength) {
|
||||
return method + " /" + endPoint + "\n" + "Content-Length: " + contentLength + "\n" + "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends and {@link Action} to the device to set for example the FanSpeed or TargetHumidity
|
||||
*
|
||||
* @param action - The action to be send to the device
|
||||
*/
|
||||
public void sendActionToDevice(Action action) throws IOException {
|
||||
CommandMessage message = new CommandMessage(action, header);
|
||||
|
||||
String messageJson = gson.toJson(message);
|
||||
|
||||
try (Socket socket = new Socket(ipAddress, VentaAirBindingConstants.PORT)) {
|
||||
OutputStream output = socket.getOutputStream();
|
||||
|
||||
byte[] dataToSend = buildMessageBytes(messageJson, "POST", "Action");
|
||||
|
||||
// we write these lines to the log in order to help users with new/other venta devices, so they only need to
|
||||
// enable debug logging
|
||||
logger.debug("sending: {}", new String(dataToSend));
|
||||
logger.debug("sendingArray: {}", Arrays.toString(dataToSend));
|
||||
|
||||
output.write(dataToSend);
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the polling job to fetch the current device data
|
||||
*
|
||||
* @param scheduler - The scheduler of the {@link ThingHandler}
|
||||
*/
|
||||
public void startPollDataFromDevice(ScheduledExecutorService scheduler) {
|
||||
stopPollDataFromDevice();
|
||||
pollingJob = scheduler.scheduleWithFixedDelay(this::pollDataFromDevice, 2, pollingTimeInSeconds,
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the polling for device data
|
||||
*/
|
||||
public void stopPollDataFromDevice() {
|
||||
ScheduledFuture<?> localPollingJob = pollingJob;
|
||||
if (localPollingJob != null && !localPollingJob.isCancelled()) {
|
||||
localPollingJob.cancel(true);
|
||||
}
|
||||
logger.debug("Setting polling job to null");
|
||||
pollingJob = null;
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link VentaAirBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class VentaAirBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "ventaair";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_LW60T = new ThingTypeUID(BINDING_ID, "lw60t");
|
||||
public static final ThingTypeUID THING_TYPE_GENERIC = new ThingTypeUID(BINDING_ID, "generic");
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String CHANNEL_POWER = "power";
|
||||
public static final String CHANNEL_FAN_SPEED = "fanSpeed";
|
||||
public static final String CHANNEL_TARGET_HUMIDITY = "targetHumidity";
|
||||
public static final String CHANNEL_TIMER = "timer";
|
||||
public static final String CHANNEL_SLEEP_MODE = "sleepMode";
|
||||
public static final String CHANNEL_BOOST = "boost";
|
||||
public static final String CHANNEL_CHILD_LOCK = "childLock";
|
||||
public static final String CHANNEL_AUTOMATIC = "automatic";
|
||||
public static final String CHANNEL_TEMPERATURE = "temperature";
|
||||
public static final String CHANNEL_HUMIDITY = "humidity";
|
||||
public static final String CHANNEL_WATERLEVEL = "waterLevel";
|
||||
public static final String CHANNEL_FAN_RPM = "fanRPM";
|
||||
public static final String CHANNEL_CLEAN_MODE = "cleanMode";
|
||||
public static final String CHANNEL_OPERATION_TIME = "operationTime";
|
||||
public static final String CHANNEL_DISC_REPLACE_TIME = "discReplaceTime";
|
||||
public static final String CHANNEL_CLEANING_TIME = "cleaningTime";
|
||||
public static final String CHANNEL_TIMER_TIME_PASSED = "timerTimePassed";
|
||||
|
||||
public static final int PORT = 48000;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link VentaAirDeviceConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class VentaAirDeviceConfiguration {
|
||||
public String ipAddress = "";
|
||||
public String macAddress = "";
|
||||
public BigDecimal deviceType = BigDecimal.ZERO;
|
||||
// we all know that 42 is the answer to everything, so let's pick this one ;)
|
||||
public BigDecimal hash = new BigDecimal("-42");
|
||||
public BigDecimal pollingTime = BigDecimal.TEN;
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal;
|
||||
|
||||
import static org.openhab.binding.ventaair.internal.VentaAirBindingConstants.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
|
||||
/**
|
||||
* The {@link VentaAirHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.ventaair", service = ThingHandlerFactory.class)
|
||||
public class VentaAirHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_LW60T, THING_TYPE_GENERIC);
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (THING_TYPE_LW60T.equals(thingTypeUID) || THING_TYPE_GENERIC.equals(thingTypeUID)) {
|
||||
return new VentaThingHandler(thing);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,333 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.measure.Unit;
|
||||
import javax.measure.quantity.Dimensionless;
|
||||
import javax.measure.quantity.Temperature;
|
||||
import javax.measure.quantity.Time;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.ventaair.internal.message.action.Action;
|
||||
import org.openhab.binding.ventaair.internal.message.action.AllActions;
|
||||
import org.openhab.binding.ventaair.internal.message.action.AutomaticAction;
|
||||
import org.openhab.binding.ventaair.internal.message.action.BoostAction;
|
||||
import org.openhab.binding.ventaair.internal.message.action.ChildLockAction;
|
||||
import org.openhab.binding.ventaair.internal.message.action.FanAction;
|
||||
import org.openhab.binding.ventaair.internal.message.action.HumidityAction;
|
||||
import org.openhab.binding.ventaair.internal.message.action.PowerAction;
|
||||
import org.openhab.binding.ventaair.internal.message.action.SleepModeAction;
|
||||
import org.openhab.binding.ventaair.internal.message.action.TimerAction;
|
||||
import org.openhab.binding.ventaair.internal.message.dto.DeviceInfoMessage;
|
||||
import org.openhab.binding.ventaair.internal.message.dto.Header;
|
||||
import org.openhab.binding.ventaair.internal.message.dto.Info;
|
||||
import org.openhab.binding.ventaair.internal.message.dto.Measurements;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link VentaThingHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class VentaThingHandler extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(VentaThingHandler.class);
|
||||
|
||||
private VentaAirDeviceConfiguration config = new VentaAirDeviceConfiguration();
|
||||
|
||||
private @Nullable Communicator communicator;
|
||||
|
||||
private Map<String, State> channelValueCache = new HashMap<>();
|
||||
|
||||
public VentaThingHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
logger.debug("Handle command={} for channel={} with channelID={}", command, channelUID, channelUID.getId());
|
||||
if (command instanceof RefreshType) {
|
||||
refreshChannelFromCache(channelUID);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (channelUID.getId()) {
|
||||
case VentaAirBindingConstants.CHANNEL_POWER:
|
||||
if (command instanceof OnOffType) {
|
||||
dispatchActionToDevice(new PowerAction(command == OnOffType.ON));
|
||||
}
|
||||
break;
|
||||
case VentaAirBindingConstants.CHANNEL_FAN_SPEED:
|
||||
if (command instanceof DecimalType) {
|
||||
int fanStage = ((DecimalType) command).intValue();
|
||||
dispatchActionToDevice(new FanAction(fanStage));
|
||||
}
|
||||
break;
|
||||
case VentaAirBindingConstants.CHANNEL_TARGET_HUMIDITY:
|
||||
if (command instanceof DecimalType) {
|
||||
int targetHumidity = ((DecimalType) command).intValue();
|
||||
dispatchActionToDevice(new HumidityAction(targetHumidity));
|
||||
}
|
||||
break;
|
||||
case VentaAirBindingConstants.CHANNEL_TIMER:
|
||||
if (command instanceof DecimalType) {
|
||||
int timer = ((DecimalType) command).intValue();
|
||||
dispatchActionToDevice(new TimerAction(timer));
|
||||
}
|
||||
break;
|
||||
case VentaAirBindingConstants.CHANNEL_SLEEP_MODE:
|
||||
if (command instanceof OnOffType) {
|
||||
dispatchActionToDevice(new SleepModeAction(command == OnOffType.ON));
|
||||
}
|
||||
break;
|
||||
case VentaAirBindingConstants.CHANNEL_BOOST:
|
||||
if (command instanceof OnOffType) {
|
||||
dispatchActionToDevice(new BoostAction(command == OnOffType.ON));
|
||||
}
|
||||
break;
|
||||
case VentaAirBindingConstants.CHANNEL_CHILD_LOCK:
|
||||
if (command instanceof OnOffType) {
|
||||
dispatchActionToDevice(new ChildLockAction(command == OnOffType.ON));
|
||||
}
|
||||
break;
|
||||
case VentaAirBindingConstants.CHANNEL_AUTOMATIC:
|
||||
if (command instanceof OnOffType) {
|
||||
dispatchActionToDevice(new AutomaticAction(command == OnOffType.ON));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
config = getConfigAs(VentaAirDeviceConfiguration.class);
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
String configErrorMessage;
|
||||
if ((configErrorMessage = validateConfig()) != null) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, configErrorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
Header header = new Header(config.macAddress, config.deviceType.intValue(), config.hash.toString(), "openHAB");
|
||||
|
||||
communicator = new Communicator(config.ipAddress, header, config.pollingTime, new StateUpdatedCallback());
|
||||
communicator.startPollDataFromDevice(scheduler);
|
||||
}
|
||||
|
||||
private @Nullable String validateConfig() {
|
||||
if (config.ipAddress.isEmpty()) {
|
||||
return "IP address not set";
|
||||
}
|
||||
if (config.macAddress.isEmpty()) {
|
||||
return "Mac Address not set, use discovery to find the correct one";
|
||||
}
|
||||
if (config.deviceType == BigDecimal.ZERO) {
|
||||
return "Device Type not set, use discovery to find the correct one";
|
||||
}
|
||||
if (config.pollingTime.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return "Polling time has to be larger than 0 seconds";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void dispatchActionToDevice(Action action) {
|
||||
Communicator localCommunicator = communicator;
|
||||
if (localCommunicator != null) {
|
||||
logger.debug("Dispatching Action={} to the device", action.getClass());
|
||||
try {
|
||||
localCommunicator.sendActionToDevice(action);
|
||||
} catch (IOException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
return;
|
||||
}
|
||||
localCommunicator.pollDataFromDevice();
|
||||
} else {
|
||||
logger.error("Should send action={} to device but communicator is not available.", action.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshChannelFromCache(ChannelUID channelUID) {
|
||||
State cachedState = channelValueCache.get(channelUID.getId());
|
||||
if (cachedState != null) {
|
||||
updateState(channelUID, cachedState);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateProperties(Info info) {
|
||||
Thing thing = getThing();
|
||||
thing.setProperty("SWDisplay", info.getSwDisplay());
|
||||
thing.setProperty("SWPower", info.getSwPower());
|
||||
thing.setProperty("SWTouch", info.getSwTouch());
|
||||
thing.setProperty("SWWIFI", info.getSwWIFI());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
Communicator localCommunicator = communicator;
|
||||
if (localCommunicator != null) {
|
||||
localCommunicator.stopPollDataFromDevice();
|
||||
}
|
||||
communicator = null;
|
||||
}
|
||||
|
||||
class StateUpdatedCallback {
|
||||
/**
|
||||
* Method to pass the data received from the device to the handler
|
||||
*
|
||||
* @param message - message containing the parsed data from the device
|
||||
*/
|
||||
public void stateUpdated(DeviceInfoMessage message) {
|
||||
if (messageIsEmpty(message)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING,
|
||||
"Please allow openHAB to access your device");
|
||||
return;
|
||||
}
|
||||
|
||||
AllActions actions = message.getCurrentActions();
|
||||
|
||||
Unit<Temperature> temperatureUnit = SIUnits.CELSIUS;
|
||||
|
||||
if (actions != null) {
|
||||
OnOffType powerState = OnOffType.from(actions.isPower());
|
||||
updateState(VentaAirBindingConstants.CHANNEL_POWER, powerState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_POWER, powerState);
|
||||
|
||||
DecimalType fanspeedState = new DecimalType(actions.getFanSpeed());
|
||||
updateState(VentaAirBindingConstants.CHANNEL_FAN_SPEED, fanspeedState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_FAN_SPEED, fanspeedState);
|
||||
|
||||
DecimalType targetHumState = new DecimalType(actions.getTargetHum());
|
||||
updateState(VentaAirBindingConstants.CHANNEL_TARGET_HUMIDITY, targetHumState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_TARGET_HUMIDITY, targetHumState);
|
||||
|
||||
DecimalType timerState = new DecimalType(actions.getTimer());
|
||||
updateState(VentaAirBindingConstants.CHANNEL_TIMER, timerState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_TIMER, timerState);
|
||||
|
||||
OnOffType sleepModeState = OnOffType.from(actions.isSleepMode());
|
||||
updateState(VentaAirBindingConstants.CHANNEL_SLEEP_MODE, sleepModeState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_SLEEP_MODE, sleepModeState);
|
||||
|
||||
OnOffType boostState = OnOffType.from(actions.isBoost());
|
||||
updateState(VentaAirBindingConstants.CHANNEL_BOOST, boostState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_BOOST, boostState);
|
||||
|
||||
OnOffType childLockState = OnOffType.from(actions.isChildLock());
|
||||
updateState(VentaAirBindingConstants.CHANNEL_CHILD_LOCK, childLockState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_CHILD_LOCK, childLockState);
|
||||
|
||||
OnOffType automaticState = OnOffType.from(actions.isAutomatic());
|
||||
updateState(VentaAirBindingConstants.CHANNEL_AUTOMATIC, automaticState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_AUTOMATIC, automaticState);
|
||||
|
||||
temperatureUnit = actions.getTempUnit() == 0 ? SIUnits.CELSIUS : ImperialUnits.FAHRENHEIT;
|
||||
}
|
||||
|
||||
Measurements measurements = message.getMeasurements();
|
||||
|
||||
if (measurements != null) {
|
||||
QuantityType<Temperature> temperatureState = new QuantityType<>(measurements.getTemperature(),
|
||||
temperatureUnit);
|
||||
updateState(VentaAirBindingConstants.CHANNEL_TEMPERATURE, temperatureState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_TEMPERATURE, temperatureState);
|
||||
|
||||
QuantityType<Dimensionless> humidityState = new QuantityType<>(measurements.getHumidity(),
|
||||
Units.PERCENT);
|
||||
updateState(VentaAirBindingConstants.CHANNEL_HUMIDITY, humidityState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_HUMIDITY, humidityState);
|
||||
|
||||
DecimalType waterLevelState = new DecimalType(measurements.getWaterLevel());
|
||||
updateState(VentaAirBindingConstants.CHANNEL_WATERLEVEL, waterLevelState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_WATERLEVEL, waterLevelState);
|
||||
|
||||
DecimalType fanRPMstate = new DecimalType(measurements.getFanRpm());
|
||||
updateState(VentaAirBindingConstants.CHANNEL_FAN_RPM, fanRPMstate);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_FAN_RPM, fanRPMstate);
|
||||
}
|
||||
|
||||
Info info = message.getInfo();
|
||||
if (info != null) {
|
||||
int opHours = info.getOperationT() * 5 / 60;
|
||||
int discReplaceHours = info.getDiscIonT() * 5 / 60;
|
||||
int cleaningHours = info.getCleaningT() * 5 / 60;
|
||||
|
||||
QuantityType<Time> opHoursState = new QuantityType<Time>(opHours, Units.HOUR);
|
||||
updateState(VentaAirBindingConstants.CHANNEL_OPERATION_TIME, opHoursState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_OPERATION_TIME, opHoursState);
|
||||
|
||||
QuantityType<Time> discReplaceHoursState = new QuantityType<Time>(2200 - discReplaceHours, Units.HOUR);
|
||||
updateState(VentaAirBindingConstants.CHANNEL_DISC_REPLACE_TIME, discReplaceHoursState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_DISC_REPLACE_TIME, discReplaceHoursState);
|
||||
|
||||
QuantityType<Time> cleaningHoursState = new QuantityType<Time>(4400 - cleaningHours, Units.HOUR);
|
||||
updateState(VentaAirBindingConstants.CHANNEL_CLEANING_TIME, cleaningHoursState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_CLEANING_TIME, cleaningHoursState);
|
||||
|
||||
OnOffType cleanModeState = info.isCleanMode() ? OnOffType.ON : OnOffType.OFF;
|
||||
updateState(VentaAirBindingConstants.CHANNEL_CLEAN_MODE, cleanModeState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_CLEAN_MODE, cleanModeState);
|
||||
|
||||
QuantityType<Time> timerTimePassedState = new QuantityType<Time>(info.getTimerT(), Units.MINUTE);
|
||||
updateState(VentaAirBindingConstants.CHANNEL_TIMER_TIME_PASSED, timerTimePassedState);
|
||||
channelValueCache.put(VentaAirBindingConstants.CHANNEL_TIMER_TIME_PASSED, timerTimePassedState);
|
||||
|
||||
updateProperties(info);
|
||||
}
|
||||
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
private boolean messageIsEmpty(DeviceInfoMessage message) {
|
||||
if (message.getCurrentActions() == null && message.getInfo() == null && message.getMeasurements() == null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to inform the handler about a communication issue
|
||||
*/
|
||||
public void communicationProblem() {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.discovery;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.SocketException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
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.ventaair.internal.VentaAirBindingConstants;
|
||||
import org.openhab.binding.ventaair.internal.message.dto.Header;
|
||||
import org.openhab.binding.ventaair.internal.message.dto.Message;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
* Discovers Venta Air humidifier and cleaner devices by listening for UDP messages
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = DiscoveryService.class, configurationPid = "discovery.ventaair")
|
||||
public class VentaDeviceDiscovery extends AbstractDiscoveryService {
|
||||
private static final String REPRESENTATION_PROPERTY = "macAddress";
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
|
||||
.singleton(VentaAirBindingConstants.THING_TYPE_LW60T);
|
||||
// defined as int, because AbstractDiscoveryService wants and int and not long as provided by Duration.getSeconds()
|
||||
private static final int MANUAL_DISCOVERY_TIME = 30;
|
||||
private static final Duration TIME_BETWEEN_SCANS = Duration.ofSeconds(30);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(VentaDeviceDiscovery.class);
|
||||
|
||||
private @Nullable ScheduledFuture<?> scanJob = null;
|
||||
|
||||
public VentaDeviceDiscovery() {
|
||||
super(SUPPORTED_THING_TYPES_UIDS, MANUAL_DISCOVERY_TIME, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startScan() {
|
||||
findDevices();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startBackgroundDiscovery() {
|
||||
super.startBackgroundDiscovery();
|
||||
|
||||
ScheduledFuture<?> localScanJob = scanJob;
|
||||
if (localScanJob != null) {
|
||||
localScanJob.cancel(true);
|
||||
}
|
||||
|
||||
scanJob = scheduler.scheduleWithFixedDelay(this::findDevices, 5, TIME_BETWEEN_SCANS.getSeconds(),
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopBackgroundDiscovery() {
|
||||
super.stopBackgroundDiscovery();
|
||||
|
||||
ScheduledFuture<?> localScanJob = scanJob;
|
||||
if (localScanJob != null) {
|
||||
localScanJob.cancel(true);
|
||||
}
|
||||
scanJob = null;
|
||||
}
|
||||
|
||||
private void findDevices() {
|
||||
byte[] buf = new byte[512];
|
||||
try (DatagramSocket socket = new DatagramSocket(VentaAirBindingConstants.PORT)) {
|
||||
DatagramPacket packet = new DatagramPacket(buf, buf.length);
|
||||
socket.receive(packet);
|
||||
|
||||
Message m = parseDiscoveryPaket(packet.getData());
|
||||
if (m == null) {
|
||||
logger.debug("Received broken discovery packet data={}", HexUtils.bytesToHex(packet.getData(), ", "));
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Found device with: IP={} Mac={} and device type={}", m.getHeader().getIpAdress(),
|
||||
m.getHeader().getMacAdress(), m.getHeader().getDeviceType());
|
||||
|
||||
ThingTypeUID thingTypeUID;
|
||||
switch (m.getHeader().getDeviceType()) {
|
||||
case 4:
|
||||
thingTypeUID = VentaAirBindingConstants.THING_TYPE_LW60T;
|
||||
break;
|
||||
default:
|
||||
thingTypeUID = VentaAirBindingConstants.THING_TYPE_GENERIC;
|
||||
break;
|
||||
}
|
||||
createDiscoveryResult(thingTypeUID, m.getHeader());
|
||||
} catch (SocketException e) {
|
||||
logger.warn("Could not open port {} to scan for Venta devices in the network.",
|
||||
VentaAirBindingConstants.PORT);
|
||||
} catch (IOException e) {
|
||||
// swallow, since we already log the broken packet above
|
||||
}
|
||||
}
|
||||
|
||||
private @Nullable Message parseDiscoveryPaket(byte[] packet) {
|
||||
Gson gson = new Gson();
|
||||
Message msg = null;
|
||||
|
||||
String packetAsString = new String(packet, StandardCharsets.UTF_8);
|
||||
|
||||
String[] lines = packetAsString.split("\n");
|
||||
if (lines.length >= 3) {
|
||||
String input = lines[2];
|
||||
int end = input.lastIndexOf("}"); // strip padding bytes added by the device
|
||||
if (end > 0) {
|
||||
String rawJSONstring = input.substring(0, end + 1);
|
||||
try {
|
||||
msg = gson.fromJson(rawJSONstring, Message.class);
|
||||
} catch (JsonSyntaxException e) {
|
||||
logger.debug("Received invalid JSON data={}", rawJSONstring, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
private void createDiscoveryResult(ThingTypeUID thingTypeUID, Header messageHeader) {
|
||||
String ipAddress = messageHeader.getIpAdress();
|
||||
String macAddress = messageHeader.getMacAdress();
|
||||
int deviceType = messageHeader.getDeviceType();
|
||||
|
||||
ThingUID uid = new ThingUID(thingTypeUID, ipAddress.replace(".", "_"));
|
||||
HashMap<String, Object> properties = new HashMap<>();
|
||||
properties.put("ipAddress", ipAddress);
|
||||
properties.put(REPRESENTATION_PROPERTY, macAddress);
|
||||
properties.put("deviceType", deviceType);
|
||||
|
||||
String typeLabel = thingTypeUID.getId().toUpperCase();
|
||||
|
||||
DiscoveryResult result = DiscoveryResultBuilder.create(uid).withRepresentationProperty(REPRESENTATION_PROPERTY)
|
||||
.withProperties(properties).withLabel(typeLabel + " (IP=" + ipAddress + ")").build();
|
||||
|
||||
this.thingDiscovered(result);
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.action;
|
||||
|
||||
/**
|
||||
* Marker interface for Actions that can be send to the device
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public interface Action {
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.action;
|
||||
|
||||
/**
|
||||
* Actions send by the device, containing information about the current device settings
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class AllActions implements Action {
|
||||
private boolean Power = true;
|
||||
private int FanSpeed;
|
||||
private int TargetHum = 65;
|
||||
private int Timer;
|
||||
private boolean Boost;
|
||||
private boolean SleepMode;
|
||||
private boolean ChildLock;
|
||||
private boolean Automatic;
|
||||
private int SysLanguage; // 3?
|
||||
private int CleanLanguage; // 0?
|
||||
private int TempUnit; // 0=Celsius, 1=Fahrenheit?
|
||||
private int DisplayLeft;
|
||||
private int DisplayRight;
|
||||
private int Reset;
|
||||
private int ConINet;
|
||||
private boolean DelUser; // default false
|
||||
|
||||
public boolean isPower() {
|
||||
return Power;
|
||||
}
|
||||
|
||||
public int getFanSpeed() {
|
||||
return FanSpeed;
|
||||
}
|
||||
|
||||
public int getTargetHum() {
|
||||
return TargetHum;
|
||||
}
|
||||
|
||||
public int getTimer() {
|
||||
return Timer;
|
||||
}
|
||||
|
||||
public boolean isBoost() {
|
||||
return Boost;
|
||||
}
|
||||
|
||||
public boolean isSleepMode() {
|
||||
return SleepMode;
|
||||
}
|
||||
|
||||
public boolean isChildLock() {
|
||||
return ChildLock;
|
||||
}
|
||||
|
||||
public boolean isAutomatic() {
|
||||
return Automatic;
|
||||
}
|
||||
|
||||
public int getSysLanguage() {
|
||||
return SysLanguage;
|
||||
}
|
||||
|
||||
public int getCleanLanguage() {
|
||||
return CleanLanguage;
|
||||
}
|
||||
|
||||
public int getTempUnit() {
|
||||
return TempUnit;
|
||||
}
|
||||
|
||||
public int getDisplayLeft() {
|
||||
return DisplayLeft;
|
||||
}
|
||||
|
||||
public int getDisplayRight() {
|
||||
return DisplayRight;
|
||||
}
|
||||
|
||||
public int getReset() {
|
||||
return Reset;
|
||||
}
|
||||
|
||||
public int getConINet() {
|
||||
return ConINet;
|
||||
}
|
||||
|
||||
public boolean isDelUser() {
|
||||
return DelUser;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.action;
|
||||
|
||||
/**
|
||||
* Action to enable the automatic mode of the device
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class AutomaticAction implements Action {
|
||||
@SuppressWarnings("unused")
|
||||
private boolean Automatic;
|
||||
|
||||
public AutomaticAction(boolean automatic) {
|
||||
this.Automatic = automatic;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.action;
|
||||
|
||||
/**
|
||||
* Action to enable the boost mode of the device
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class BoostAction implements Action {
|
||||
@SuppressWarnings("unused")
|
||||
private boolean Boost;
|
||||
|
||||
public BoostAction(boolean boost) {
|
||||
this.Boost = boost;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.action;
|
||||
|
||||
/**
|
||||
* Action to enable the child lock mode of the device
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class ChildLockAction implements Action {
|
||||
@SuppressWarnings("unused")
|
||||
private boolean ChildLock;
|
||||
|
||||
public ChildLockAction(boolean childLockOn) {
|
||||
this.ChildLock = childLockOn;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.action;
|
||||
|
||||
/**
|
||||
* Action to control the fan speed of the device
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class FanAction implements Action {
|
||||
@SuppressWarnings("unused")
|
||||
private int FanSpeed;
|
||||
|
||||
public FanAction(int fanSpeed) {
|
||||
this.FanSpeed = fanSpeed;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.action;
|
||||
|
||||
/**
|
||||
* Action to set the target humidity of the device
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class HumidityAction implements Action {
|
||||
@SuppressWarnings("unused")
|
||||
private int TargetHum;
|
||||
|
||||
public HumidityAction(int targetHumidity) {
|
||||
this.TargetHum = targetHumidity;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.action;
|
||||
|
||||
/**
|
||||
* Action to power on/off the device
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class PowerAction implements Action {
|
||||
@SuppressWarnings("unused")
|
||||
private boolean Power;
|
||||
|
||||
public PowerAction(boolean on) {
|
||||
this.Power = on;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.action;
|
||||
|
||||
/**
|
||||
* Action to enable the sleep mode of the device
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class SleepModeAction implements Action {
|
||||
@SuppressWarnings("unused")
|
||||
private boolean SleepMode;
|
||||
|
||||
public SleepModeAction(boolean sleepModeOn) {
|
||||
this.SleepMode = sleepModeOn;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.action;
|
||||
|
||||
/**
|
||||
* Action to change the temperature unit of the device
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class TemperatureUnitAction implements Action {
|
||||
@SuppressWarnings("unused")
|
||||
// 0=Celsius, 1=Fahrenheit
|
||||
private int TempUnit;
|
||||
|
||||
public TemperatureUnitAction(int temperatureUnit) {
|
||||
this.TempUnit = temperatureUnit;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.action;
|
||||
|
||||
/**
|
||||
* Action to set an off timer on the device
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class TimerAction implements Action {
|
||||
@SuppressWarnings("unused")
|
||||
private int Timer;
|
||||
|
||||
public TimerAction(int timer) {
|
||||
this.Timer = timer;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.dto;
|
||||
|
||||
import org.openhab.binding.ventaair.internal.message.action.Action;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Message containing a command to be send to the device
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class CommandMessage extends Message {
|
||||
@SerializedName(value = "Action")
|
||||
private Action action;
|
||||
|
||||
public CommandMessage(Action action, Header header) {
|
||||
super(header);
|
||||
this.action = action;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.dto;
|
||||
|
||||
import org.openhab.binding.ventaair.internal.message.action.AllActions;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Message send by the device, containing information about its current state
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class DeviceInfoMessage extends Message {
|
||||
|
||||
public DeviceInfoMessage(Header header) {
|
||||
super(header);
|
||||
}
|
||||
|
||||
@SerializedName(value = "Action")
|
||||
private AllActions currentActions;
|
||||
|
||||
@SerializedName(value = "Info")
|
||||
private Info info;
|
||||
|
||||
@SerializedName(value = "Measure")
|
||||
private Measurements measurements;
|
||||
|
||||
public AllActions getCurrentActions() {
|
||||
return currentActions;
|
||||
}
|
||||
|
||||
public Info getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
public Measurements getMeasurements() {
|
||||
return measurements;
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.dto;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Header which is part of a message to/from a device
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class Header {
|
||||
|
||||
@SerializedName(value = "MacAdress")
|
||||
private String macAdress;
|
||||
|
||||
@SerializedName(value = "IpAdress")
|
||||
private String ipAdress;
|
||||
|
||||
@SerializedName(value = "DeviceType")
|
||||
private int deviceType;
|
||||
|
||||
@SerializedName(value = "Hash")
|
||||
private String hash;
|
||||
|
||||
@SerializedName(value = "DeviceName")
|
||||
private String deviceName;
|
||||
|
||||
public Header(String mac, int devType, String hash, String devName) {
|
||||
this.macAdress = mac;
|
||||
this.deviceType = devType;
|
||||
this.hash = hash;
|
||||
this.deviceName = devName;
|
||||
}
|
||||
|
||||
public String getMacAdress() {
|
||||
return macAdress;
|
||||
}
|
||||
|
||||
public void setMacAdress(String macAdress) {
|
||||
this.macAdress = macAdress;
|
||||
}
|
||||
|
||||
public String getIpAdress() {
|
||||
return ipAdress;
|
||||
}
|
||||
|
||||
public void setIpAdress(String ipAdress) {
|
||||
this.ipAdress = ipAdress;
|
||||
}
|
||||
|
||||
public int getDeviceType() {
|
||||
return deviceType;
|
||||
}
|
||||
|
||||
public void setDeviceType(int deviceType) {
|
||||
this.deviceType = deviceType;
|
||||
}
|
||||
|
||||
public String getHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
public void setHash(String hash) {
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
public String getDeviceName() {
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
public void setDeviceName(String deviceName) {
|
||||
this.deviceName = deviceName;
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.dto;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Part of the {@link DeviceInfoMessage} containing details about the device state
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class Info {
|
||||
@SerializedName(value = "SWDisplay")
|
||||
private String swDisplay;
|
||||
|
||||
@SerializedName(value = "SWPower")
|
||||
private String swPower;
|
||||
|
||||
@SerializedName(value = "SWTouch")
|
||||
private String swTouch;
|
||||
|
||||
@SerializedName(value = "SWWIFI")
|
||||
private String swWIFI;
|
||||
|
||||
@SerializedName(value = "CleanMode")
|
||||
private boolean cleanMode; // default false?
|
||||
|
||||
@SerializedName(value = "RelState")
|
||||
private boolean[] relState; // [true,true,false,false]
|
||||
|
||||
@SerializedName(value = "TimerT")
|
||||
private int timerT;
|
||||
|
||||
@SerializedName(value = "OperationT")
|
||||
private int operationT;
|
||||
|
||||
@SerializedName(value = "DiscIonT")
|
||||
private int discIonT;
|
||||
|
||||
@SerializedName(value = "CleaningT")
|
||||
private int cleaningT;
|
||||
|
||||
@SerializedName(value = "FilterT")
|
||||
private int filterT;
|
||||
|
||||
@SerializedName(value = "UVCOnT")
|
||||
private int uvCOnT;
|
||||
|
||||
@SerializedName(value = "UVCOffT")
|
||||
private int uvCOffT;
|
||||
|
||||
@SerializedName(value = "CleaningR")
|
||||
private int cleaningR;
|
||||
|
||||
@SerializedName(value = "Warnings")
|
||||
private int warnings;
|
||||
|
||||
public String getSwDisplay() {
|
||||
return swDisplay;
|
||||
}
|
||||
|
||||
public String getSwPower() {
|
||||
return swPower;
|
||||
}
|
||||
|
||||
public String getSwTouch() {
|
||||
return swTouch;
|
||||
}
|
||||
|
||||
public String getSwWIFI() {
|
||||
return swWIFI;
|
||||
}
|
||||
|
||||
public boolean isCleanMode() {
|
||||
return cleanMode;
|
||||
}
|
||||
|
||||
public boolean[] getRelState() {
|
||||
return relState;
|
||||
}
|
||||
|
||||
public int getTimerT() {
|
||||
return timerT;
|
||||
}
|
||||
|
||||
public int getOperationT() {
|
||||
return operationT;
|
||||
}
|
||||
|
||||
public int getDiscIonT() {
|
||||
return discIonT;
|
||||
}
|
||||
|
||||
public int getCleaningT() {
|
||||
return cleaningT;
|
||||
}
|
||||
|
||||
public int getFilterT() {
|
||||
return filterT;
|
||||
}
|
||||
|
||||
public int getUvCOnT() {
|
||||
return uvCOnT;
|
||||
}
|
||||
|
||||
public int getUvCOffT() {
|
||||
return uvCOffT;
|
||||
}
|
||||
|
||||
public int getCleaningR() {
|
||||
return cleaningR;
|
||||
}
|
||||
|
||||
public int getWarnings() {
|
||||
return warnings;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.dto;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Part of the {@link DeviceInfoMessage} containing the measurements of the device
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class Measurements {
|
||||
@SerializedName(value = "Temperature")
|
||||
private double temperature;
|
||||
|
||||
@SerializedName(value = "Humidity")
|
||||
private double humidity;
|
||||
|
||||
@SerializedName(value = "Dust")
|
||||
private int dust;
|
||||
|
||||
@SerializedName(value = "WaterLevel")
|
||||
private int waterLevel;
|
||||
|
||||
@SerializedName(value = "FanRpm")
|
||||
private int fanRpm;
|
||||
|
||||
public double getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public double getHumidity() {
|
||||
return humidity;
|
||||
}
|
||||
|
||||
public int getDust() {
|
||||
return dust;
|
||||
}
|
||||
|
||||
public int getWaterLevel() {
|
||||
return waterLevel;
|
||||
}
|
||||
|
||||
public int getFanRpm() {
|
||||
return fanRpm;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.ventaair.internal.message.dto;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Base class for messages
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class Message {
|
||||
|
||||
@SerializedName(value = "Header")
|
||||
protected Header header;
|
||||
|
||||
public Message(Header header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
public Header getHeader() {
|
||||
return header;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="ventaair" 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>VentaAir Binding</name>
|
||||
<description>This is the binding for Venta Air - Air cleaning and humidifying devices</description>
|
||||
|
||||
</binding:binding>
|
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config-description:config-descriptions
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
|
||||
|
||||
<config-description uri="thing-type:ventaair:humidifier">
|
||||
<parameter name="ipAddress" type="text" required="true">
|
||||
<label>IP Address</label>
|
||||
<context>network-address</context>
|
||||
<description>IP Address or hostname of the device</description>
|
||||
</parameter>
|
||||
<parameter name="macAddress" type="text" required="true">
|
||||
<label>MAC Address</label>
|
||||
<description>MAC Address of the device</description>
|
||||
</parameter>
|
||||
<parameter name="deviceType" type="integer" required="true">
|
||||
<label>Device Type</label>
|
||||
<description>Type of the device as integer</description>
|
||||
</parameter>
|
||||
<parameter name="pollingTime" type="integer" required="false" unit="s" min="1" max="86400">
|
||||
<label>Polling Interval</label>
|
||||
<default>10</default>
|
||||
<description>Time in seconds between fetching data from the device</description>
|
||||
</parameter>
|
||||
<parameter name="hash" type="integer" required="false" max="-1">
|
||||
<label>Hash</label>
|
||||
<description>Optional negative number that relates to a connection (like from the VentaApp) to the device</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</config-description:config-descriptions>
|
@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="ventaair"
|
||||
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">
|
||||
|
||||
<channel-type id="fanSpeed">
|
||||
<item-type>Number</item-type>
|
||||
<label>Fan Speed</label>
|
||||
<description>Speed of the ventilation fan (0-5)</description>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="0">Off</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="targetHumidity">
|
||||
<item-type>Number</item-type>
|
||||
<label>Target Humidity</label>
|
||||
<description>Target Humidity (30-70)</description>
|
||||
<category>Humidity</category>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="30">30 %</option>
|
||||
<option value="35">35 %</option>
|
||||
<option value="40">40 %</option>
|
||||
<option value="45">45 %</option>
|
||||
<option value="50">50 %</option>
|
||||
<option value="55">55 %</option>
|
||||
<option value="60">60 %</option>
|
||||
<option value="65">65 %</option>
|
||||
<option value="70">70 %</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="timer">
|
||||
<item-type>Number</item-type>
|
||||
<label>Timer</label>
|
||||
<description>Timer (0,1,3,5,7,9h)</description>
|
||||
<state readOnly="false">
|
||||
<options>
|
||||
<option value="0">Off</option>
|
||||
<option value="1">1 hour</option>
|
||||
<option value="3">3 hours</option>
|
||||
<option value="5">5 hours</option>
|
||||
<option value="7">7 hours</option>
|
||||
<option value="9">9 hours</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="timerTimePassed">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Timer Time Passed</label>
|
||||
<description>Time that has passed since set by the Timer</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="sleepMode">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Sleep Mode</label>
|
||||
<description>Sleep Mode</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="boost">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Boost</label>
|
||||
<description>Boost</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="childLock">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Child Lock</label>
|
||||
<description>Child Lock</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="automatic">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Automatic</label>
|
||||
<description>Automatic</description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="temperature">
|
||||
<item-type>Number:Temperature</item-type>
|
||||
<label>Temperature</label>
|
||||
<description>Current Temperature</description>
|
||||
<category>Temperature</category>
|
||||
<state pattern="%.1f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="humidity">
|
||||
<item-type>Number:Dimenionsless</item-type>
|
||||
<label>Humidity</label>
|
||||
<description>Current Humidity</description>
|
||||
<category>Humidity</category>
|
||||
<state pattern="%.1f %unit%" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="waterLevel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Water Level</label>
|
||||
<description>Water Level</description>
|
||||
<state readOnly="true">
|
||||
<options>
|
||||
<option value="0">Critical</option>
|
||||
<option value="1">Refill tank</option>
|
||||
<option value="2">OK</option>
|
||||
<option value="3">Full</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="fanRPM">
|
||||
<item-type>Number</item-type>
|
||||
<label>Fan RPM</label>
|
||||
<description>Fan RPM</description>
|
||||
<state pattern="%d RPM" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="cleanMode">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Cleaning Mode</label>
|
||||
<description>Device is in cleaning mode (ON)</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="operationTime">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Operation Time</label>
|
||||
<description>Operation Time since the device was first started (in hours)</description>
|
||||
<state pattern="%d" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="discReplaceTime">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Hygiene Disc Replacement</label>
|
||||
<description>Time until the Hygiene Disc should be replaced (in hours)</description>
|
||||
<state pattern="%d" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="cleaningTime">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Cleaning Time</label>
|
||||
<description>Time until next cleaning (in hours)</description>
|
||||
<state pattern="%d" readOnly="true"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="ventaair"
|
||||
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">
|
||||
|
||||
<thing-type id="lw60t">
|
||||
<label>LW60-T VentaAir Humidifier</label>
|
||||
<description>Thing for Venta Air LW60-T Humidifiers</description>
|
||||
|
||||
<channels>
|
||||
<channel id="power" typeId="system.power"/>
|
||||
<channel id="fanSpeed" typeId="fanSpeed"/>
|
||||
<channel id="targetHumidity" typeId="targetHumidity"/>
|
||||
<channel id="timer" typeId="timer"/>
|
||||
<channel id="sleepMode" typeId="sleepMode"/>
|
||||
<channel id="childLock" typeId="childLock"/>
|
||||
<channel id="automatic" typeId="automatic"/>
|
||||
<channel id="temperature" typeId="temperature"/>
|
||||
<channel id="humidity" typeId="humidity"/>
|
||||
<channel id="waterLevel" typeId="waterLevel"/>
|
||||
<channel id="fanRPM" typeId="fanRPM"/>
|
||||
<channel id="cleanMode" typeId="cleanMode"/>
|
||||
<channel id="timerTimePassed" typeId="timerTimePassed"/>
|
||||
<channel id="operationTime" typeId="operationTime"/>
|
||||
<channel id="discReplaceTime" typeId="discReplaceTime"/>
|
||||
<channel id="cleaningTime" typeId="cleaningTime"/>
|
||||
</channels>
|
||||
|
||||
<representation-property>macAddress</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:ventaair:humidifier"/>
|
||||
|
||||
</thing-type>
|
||||
|
||||
<!-- Generic type has boost channel and maybe in the future other channels -->
|
||||
<thing-type id="generic">
|
||||
<label>Generic Humidifier/Cleaner</label>
|
||||
<description>Thing for Venta Air Humidifiers/Cleaners</description>
|
||||
|
||||
<channels>
|
||||
<channel id="power" typeId="system.power"/>
|
||||
<channel id="fanSpeed" typeId="fanSpeed"/>
|
||||
<channel id="targetHumidity" typeId="targetHumidity"/>
|
||||
<channel id="timer" typeId="timer"/>
|
||||
<channel id="sleepMode" typeId="sleepMode"/>
|
||||
<channel id="boost" typeId="boost"/>
|
||||
<channel id="childLock" typeId="childLock"/>
|
||||
<channel id="automatic" typeId="automatic"/>
|
||||
<channel id="temperature" typeId="temperature"/>
|
||||
<channel id="humidity" typeId="humidity"/>
|
||||
<channel id="waterLevel" typeId="waterLevel"/>
|
||||
<channel id="fanRPM" typeId="fanRPM"/>
|
||||
<channel id="cleanMode" typeId="cleanMode"/>
|
||||
<channel id="timerTimePassed" typeId="timerTimePassed"/>
|
||||
<channel id="operationTime" typeId="operationTime"/>
|
||||
<channel id="discReplaceTime" typeId="discReplaceTime"/>
|
||||
<channel id="cleaningTime" typeId="cleaningTime"/>
|
||||
</channels>
|
||||
|
||||
<representation-property>macAddress</representation-property>
|
||||
|
||||
<config-description-ref uri="thing-type:ventaair:humidifier"/>
|
||||
|
||||
</thing-type>
|
||||
|
||||
</thing:thing-descriptions>
|
@ -317,6 +317,7 @@
|
||||
<module>org.openhab.binding.velbus</module>
|
||||
<module>org.openhab.binding.velux</module>
|
||||
<module>org.openhab.binding.venstarthermostat</module>
|
||||
<module>org.openhab.binding.ventaair</module>
|
||||
<module>org.openhab.binding.verisure</module>
|
||||
<module>org.openhab.binding.vigicrues</module>
|
||||
<module>org.openhab.binding.vitotronic</module>
|
||||
|
Loading…
Reference in New Issue
Block a user