[intesis] - added IntesisBox support (#8694)

* Intesis Binding - added IntesisBox support

Signed-off-by: Hans-Jörg Merk <github@hmerk.de>
This commit is contained in:
Hans-Jörg Merk
2020-10-24 20:17:56 +02:00
committed by GitHub
parent 8b8b79cf04
commit 91fbe746e9
16 changed files with 900 additions and 43 deletions

View File

@@ -31,6 +31,7 @@ public class IntesisBindingConstants {
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_INTESISHOME = new ThingTypeUID(BINDING_ID, "intesisHome");
public static final ThingTypeUID THING_TYPE_INTESISBOX = new ThingTypeUID(BINDING_ID, "intesisBox");
// List of all Channel ids
public static final String CHANNEL_TYPE_POWER = "power";
@@ -41,4 +42,7 @@ public class IntesisBindingConstants {
public static final String CHANNEL_TYPE_TARGETTEMP = "targetTemperature";
public static final String CHANNEL_TYPE_AMBIENTTEMP = "ambientTemperature";
public static final String CHANNEL_TYPE_OUTDOORTEMP = "outdoorTemperature";
public static final String CHANNEL_TYPE_ERRORCODE = "errorCode";
public static final String CHANNEL_TYPE_ERRORSTATUS = "errorStatus";
public static final String CHANNEL_TYPE_RSSI = "wifiSignal";
}

View File

@@ -12,14 +12,17 @@
*/
package org.openhab.binding.intesis.internal;
import static org.openhab.binding.intesis.internal.IntesisBindingConstants.THING_TYPE_INTESISHOME;
import static org.openhab.binding.intesis.internal.IntesisBindingConstants.*;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.intesis.internal.handler.IntesisBoxHandler;
import org.openhab.binding.intesis.internal.handler.IntesisHomeHandler;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Thing;
@@ -48,7 +51,8 @@ public class IntesisHandlerFactory extends BaseThingHandlerFactory {
private final HttpClient httpClient;
private final IntesisDynamicStateDescriptionProvider intesisStateDescriptionProvider;
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_INTESISHOME);
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
.unmodifiableSet(Stream.of(THING_TYPE_INTESISHOME, THING_TYPE_INTESISBOX).collect(Collectors.toSet()));
@Activate
public IntesisHandlerFactory(@Reference HttpClientFactory httpClientFactory, ComponentContext componentContext,
@@ -69,10 +73,13 @@ public class IntesisHandlerFactory extends BaseThingHandlerFactory {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (THING_TYPE_INTESISHOME.equals(thingTypeUID)) {
logger.debug("Creating a IntesisHomeHandler for thing '{}'", thing.getUID());
return new IntesisHomeHandler(thing, httpClient, intesisStateDescriptionProvider);
}
if (THING_TYPE_INTESISBOX.equals(thingTypeUID)) {
return new IntesisBoxHandler(thing, intesisStateDescriptionProvider);
}
return null;
}
}

View File

@@ -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.intesis.internal.api;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.ThingStatus;
/**
* The {@link IntesisBoxChangeListener} is in interface for a IntesisBox changed consumer
*
* @author Hans-Jörg Merk - Initial contribution
*/
@NonNullByDefault
public interface IntesisBoxChangeListener {
/**
* This method will be called in case a message was received.
*
*/
void messageReceived(String messageLine);
/**
* This method will be called in case the connection status has changed.
*
*/
void connectionStatusChanged(ThingStatus status, @Nullable String message);
}

View File

@@ -0,0 +1,78 @@
/**
* 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.intesis.internal.api;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* @author Cody Cutrer - Initial contribution
*/
@NonNullByDefault
public class IntesisBoxMessage {
public static final String ID = "ID";
public static final String INFO = "INFO";
public static final String SET = "SET";
public static final String CHN = "CHN";
public static final String GET = "GET";
public static final String LOGIN = "LOGIN";
public static final String LOGOUT = "LOGOUT";
public static final String CFG = "CFG";
public static final String LIMITS = "LIMITS";
public static final String DISCOVER = "DISCOVER";
private static final Pattern REGEX = Pattern.compile("^([^,]+)(?:,(\\d+))?:([^,]+),([A-Z0-9.,\\[\\]]+)$");
@SuppressWarnings("unused")
private final String acNum;
private final String command;
private final String function;
private final String value;
private IntesisBoxMessage(String command, String acNum, String function, String value) {
this.command = command;
this.acNum = acNum;
this.function = function;
this.value = value;
}
public String getCommand() {
return command;
}
public String getFunction() {
return function;
}
public String getValue() {
return value;
}
public List<String> getLimitsValue() {
return Arrays.asList(value.substring(1, value.length() - 1).split(","));
}
public static @Nullable IntesisBoxMessage parse(String message) {
Matcher m = REGEX.matcher(message);
if (!m.find()) {
return null;
}
return new IntesisBoxMessage(m.group(1), m.group(2), m.group(3), m.group(4));
}
}

View File

@@ -0,0 +1,214 @@
/**
* 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.intesis.internal.api;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.intesis.internal.handler.IntesisBoxHandler;
import org.openhab.core.thing.ThingStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class handling the Socket connections.
*
* @author Cody Cutrer - Initial contribution
* @author Hans-Jörg Merk - Moved Socket to it's own class
*/
@NonNullByDefault
public class IntesisBoxSocketApi {
private final Logger logger = LoggerFactory.getLogger(IntesisBoxSocketApi.class);
private final String ipAddress;
private final int port;
private final String readerThreadName;
private @Nullable IntesisSocket tcpSocket = null;
private @Nullable OutputStreamWriter tcpOutput = null;
private @Nullable BufferedReader tcpInput = null;
private @Nullable IntesisBoxChangeListener changeListener;
private boolean connected = false;
public IntesisBoxSocketApi(final String ipAddress, final int port, final String readerThreadName) {
this.ipAddress = ipAddress;
this.port = port;
this.readerThreadName = readerThreadName;
}
private class IntesisSocket {
final Socket socket;
public IntesisSocket() throws UnknownHostException, IOException {
socket = new Socket();
SocketAddress tcpSocketAddress = new InetSocketAddress(ipAddress, port);
socket.connect(tcpSocketAddress);
}
public void close() throws IOException {
socket.close();
}
}
public void openConnection() throws IOException {
closeConnection();
IntesisBoxChangeListener listener = this.changeListener;
IntesisSocket localSocket = tcpSocket = new IntesisSocket();
tcpOutput = new OutputStreamWriter(localSocket.socket.getOutputStream(), StandardCharsets.US_ASCII);
tcpInput = new BufferedReader(
new InputStreamReader(localSocket.socket.getInputStream(), StandardCharsets.US_ASCII));
Thread tcpListener = new Thread(new TCPListener());
tcpListener.setName(readerThreadName);
tcpListener.setDaemon(true);
tcpListener.start();
setConnected(true);
if (listener != null) {
listener.connectionStatusChanged(ThingStatus.ONLINE, null);
}
}
public void closeConnection() {
try {
IntesisSocket localSocket = tcpSocket;
OutputStreamWriter localOutput = tcpOutput;
BufferedReader localInput = tcpInput;
if (localSocket != null) {
localSocket.close();
localSocket = null;
}
if (localInput != null) {
localInput.close();
localInput = null;
}
if (localOutput != null) {
localOutput.close();
localOutput = null;
}
setConnected(false);
} catch (IOException ioException) {
logger.debug("closeConnection(): Unable to close connection - {}", ioException.getMessage());
} catch (Exception exception) {
logger.debug("closeConnection(): Error closing connection - {}", exception.getMessage());
}
}
private class TCPListener implements Runnable {
/**
* Run method. Runs the MessageListener thread
*/
@Override
public void run() {
while (isConnected()) {
String message = read();
readMessage(message);
}
}
}
public void addIntesisBoxChangeListener(IntesisBoxChangeListener listener) {
if (this.changeListener == null) {
this.changeListener = listener;
}
}
private void write(String data) {
IntesisBoxChangeListener listener = this.changeListener;
try {
OutputStreamWriter localOutput = tcpOutput;
if (localOutput != null) {
localOutput.write(data);
localOutput.flush();
}
} catch (IOException ioException) {
setConnected(false);
if (listener != null) {
listener.connectionStatusChanged(ThingStatus.OFFLINE, ioException.getMessage());
}
}
}
public String read() {
String message = "";
try {
BufferedReader localInput = tcpInput;
if (localInput != null) {
message = localInput.readLine();
}
} catch (IOException ioException) {
setConnected(false);
}
return message;
}
public void readMessage(String message) {
IntesisBoxChangeListener listener = this.changeListener;
if (listener != null && !message.isEmpty()) {
listener.messageReceived(message);
}
}
public void sendAlive() {
write("GET,1:*\r\n");
}
public void sendId() {
write("ID\r\n");
}
public void sendLimitsQuery() {
write("LIMITS:*\r\n");
}
public void sendCommand(String function, String value) {
String data = String.format("SET,1:%s,%s\r\n", function, value);
write(data);
}
public void sendQuery(String function) {
String data = String.format("GET,1:%s\r\n", function);
write(data);
}
public boolean isConnected() {
return this.connected;
}
public void setConnected(boolean connected) {
this.connected = connected;
}
public void removeIntesisBoxChangeListener(IntesisBoxHandler intesisBoxHandler) {
if (this.changeListener != null) {
this.changeListener = null;
}
}
}

View File

@@ -23,7 +23,7 @@ import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader;
import org.openhab.binding.intesis.internal.IntesisConfiguration;
import org.openhab.binding.intesis.internal.config.IntesisHomeConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,7 +40,7 @@ public class IntesisHomeHttpApi {
private final Logger logger = LoggerFactory.getLogger(IntesisHomeHttpApi.class);
private final HttpClient httpClient;
public IntesisHomeHttpApi(IntesisConfiguration config, HttpClient httpClient) {
public IntesisHomeHttpApi(IntesisHomeConfiguration config, HttpClient httpClient) {
this.httpClient = httpClient;
}

View File

@@ -0,0 +1,39 @@
/**
* 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.intesis.internal.api;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link DataPointChangedEvent} is an event container for data point changes
*
* @author Hans-Jörg Merk - Initial contribution
*/
@NonNullByDefault
public class MessageReceivedEvent {
protected String message;
public MessageReceivedEvent(Object source, String message) {
this.message = message;
}
/**
* Gets the data-point of the event.
*
*/
@Nullable
public String getMessage() {
return this.message;
}
}

View File

@@ -0,0 +1,26 @@
/**
* 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.intesis.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link IntesisBoxConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Hans-Jörg Merk - Initial contribution
*/
@NonNullByDefault
public class IntesisBoxConfiguration {
public String ipAddress = "";
public int port;
}

View File

@@ -10,17 +10,17 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.intesis.internal;
package org.openhab.binding.intesis.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link IntesisConfiguration} class contains fields mapping thing configuration parameters.
* The {@link IntesisHomeConfiguration} class contains fields mapping thing configuration parameters.
*
* @author Hans-Jörg Merk - Initial contribution
*/
@NonNullByDefault
public class IntesisConfiguration {
public class IntesisHomeConfiguration {
public String ipAddress = "";
public String password = "";
}

View File

@@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.intesis.internal;
package org.openhab.binding.intesis.internal.enums;
import org.eclipse.jdt.annotation.NonNullByDefault;

View File

@@ -0,0 +1,394 @@
/**
* 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.intesis.internal.handler;
import static org.openhab.binding.intesis.internal.IntesisBindingConstants.*;
import static org.openhab.binding.intesis.internal.api.IntesisBoxMessage.*;
import static org.openhab.core.thing.Thing.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.intesis.internal.IntesisDynamicStateDescriptionProvider;
import org.openhab.binding.intesis.internal.api.IntesisBoxChangeListener;
import org.openhab.binding.intesis.internal.api.IntesisBoxMessage;
import org.openhab.binding.intesis.internal.api.IntesisBoxSocketApi;
import org.openhab.binding.intesis.internal.config.IntesisBoxConfiguration;
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.types.StringType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.thing.Channel;
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.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelKind;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.StateOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link IntesisBoxHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Cody Cutrer - Initial contribution
* @author Rocky Amatulli - additions to include id message handling, dynamic channel options based on limits.
* @author Hans-Jörg Merk - refactored for openHAB 3.0 compatibility
*
*/
@NonNullByDefault
public class IntesisBoxHandler extends BaseThingHandler implements IntesisBoxChangeListener {
private final Logger logger = LoggerFactory.getLogger(IntesisBoxHandler.class);
private @Nullable IntesisBoxSocketApi intesisBoxSocketApi;
private final Map<String, String> properties = new HashMap<>();
private final Map<String, List<String>> limits = new HashMap<>();
private final IntesisDynamicStateDescriptionProvider intesisStateDescriptionProvider;
private IntesisBoxConfiguration config = new IntesisBoxConfiguration();
private double minTemp = 0.0, maxTemp = 0.0;
private boolean hasProperties = false;
private @Nullable ScheduledFuture<?> pollingTask;
public IntesisBoxHandler(Thing thing, IntesisDynamicStateDescriptionProvider intesisStateDescriptionProvider) {
super(thing);
this.intesisStateDescriptionProvider = intesisStateDescriptionProvider;
}
@Override
public void initialize() {
config = getConfigAs(IntesisBoxConfiguration.class);
if (!config.ipAddress.isEmpty()) {
updateStatus(ThingStatus.UNKNOWN);
scheduler.submit(() -> {
String readerThreadName = "OH-binding-" + getThing().getUID().getAsString();
IntesisBoxSocketApi intesisLocalApi = intesisBoxSocketApi = new IntesisBoxSocketApi(config.ipAddress,
config.port, readerThreadName);
intesisLocalApi.addIntesisBoxChangeListener(this);
try {
intesisLocalApi.openConnection();
intesisLocalApi.sendId();
intesisLocalApi.sendLimitsQuery();
intesisLocalApi.sendAlive();
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
return;
}
updateStatus(ThingStatus.ONLINE);
});
pollingTask = scheduler.scheduleWithFixedDelay(this::polling, 3, 45, TimeUnit.SECONDS);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No IP address specified)");
}
}
@Override
public void dispose() {
final ScheduledFuture<?> pollingTask = this.pollingTask;
IntesisBoxSocketApi api = this.intesisBoxSocketApi;
if (pollingTask != null) {
pollingTask.cancel(true);
this.pollingTask = null;
}
if (api != null) {
api.closeConnection();
api.removeIntesisBoxChangeListener(this);
}
super.dispose();
}
private synchronized void polling() {
IntesisBoxSocketApi api = this.intesisBoxSocketApi;
if (api != null) {
if (!api.isConnected()) {
try {
api.openConnection();
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}
api.sendAlive();
api.sendId();
}
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
IntesisBoxSocketApi api = this.intesisBoxSocketApi;
if (api != null) {
if (!api.isConnected()) {
logger.trace("Sending command failed, not connected");
return;
}
if (command instanceof RefreshType) {
logger.trace("Refresh channel {}", channelUID.getId());
api.sendQuery(channelUID.getId());
return;
}
}
String value = "";
String function = "";
switch (channelUID.getId()) {
case CHANNEL_TYPE_POWER:
if (command instanceof OnOffType) {
function = "ONOFF";
value = command == OnOffType.ON ? "ON" : "OFF";
}
break;
case CHANNEL_TYPE_TARGETTEMP:
if (command instanceof QuantityType) {
QuantityType<?> celsiusTemperature = (QuantityType<?>) command;
celsiusTemperature = celsiusTemperature.toUnit(SIUnits.CELSIUS);
if (celsiusTemperature != null) {
double doubleValue = celsiusTemperature.doubleValue();
logger.trace("targetTemp double value = {}", doubleValue);
doubleValue = Math.max(minTemp, Math.min(maxTemp, doubleValue));
value = String.format("%.0f", doubleValue * 10);
function = "SETPTEMP";
logger.trace("targetTemp raw string = {}", value);
}
}
break;
case CHANNEL_TYPE_MODE:
function = "MODE";
value = command.toString();
break;
case CHANNEL_TYPE_FANSPEED:
function = "FANSP";
value = command.toString();
break;
case CHANNEL_TYPE_VANESUD:
function = "VANEUD";
value = command.toString();
break;
case CHANNEL_TYPE_VANESLR:
function = "VANELR";
value = command.toString();
break;
}
if (!value.isEmpty() || function.isEmpty()) {
if (api != null) {
logger.trace("Sending command {} to function {}", value, function);
api.sendCommand(function, value);
} else {
logger.warn("Sending command failed, could not get API");
}
}
}
private void populateProperties(String[] value) {
properties.put(PROPERTY_VENDOR, "Intesis");
properties.put(PROPERTY_MODEL_ID, value[0]);
properties.put(PROPERTY_MAC_ADDRESS, value[1]);
properties.put("ipAddress", value[2]);
properties.put("protocol", value[3]);
properties.put(PROPERTY_FIRMWARE_VERSION, value[4]);
properties.put("hostname", value[6]);
updateProperties(properties);
hasProperties = true;
}
private void receivedUpdate(String function, String receivedValue) {
String value = receivedValue;
logger.trace("receivedUpdate(): {} {}", function, value);
switch (function) {
case "ONOFF":
updateState(CHANNEL_TYPE_POWER, OnOffType.from(value));
break;
case "SETPTEMP":
if (value.equals("32768")) {
value = "0";
}
updateState(CHANNEL_TYPE_TARGETTEMP,
new QuantityType<Temperature>(Double.valueOf(value) / 10.0d, SIUnits.CELSIUS));
break;
case "AMBTEMP":
if (Double.valueOf(value).isNaN()) {
value = "0";
}
updateState(CHANNEL_TYPE_AMBIENTTEMP,
new QuantityType<Temperature>(Double.valueOf(value) / 10.0d, SIUnits.CELSIUS));
break;
case "MODE":
updateState(CHANNEL_TYPE_MODE, new StringType(value));
break;
case "FANSP":
updateState(CHANNEL_TYPE_FANSPEED, new StringType(value));
break;
case "VANEUD":
updateState(CHANNEL_TYPE_VANESUD, new StringType(value));
break;
case "VANELR":
updateState(CHANNEL_TYPE_VANESLR, new StringType(value));
break;
case "ERRCODE":
properties.put("errorCode", value);
updateProperties(properties);
break;
case "ERRSTATUS":
properties.put("errorStatus", value);
updateProperties(properties);
if ("ERR".equals(value)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"device reported an error");
}
break;
}
}
private void handleMessage(String data) {
logger.debug("handleMessage(): Message received - {}", data);
if (data.equals("ACK") || data.equals("")) {
return;
}
if (data.startsWith(ID + ':')) {
String[] value = data.substring(3).split(",");
if (!hasProperties) {
populateProperties(value);
}
DecimalType signalStrength = mapSignalStrength(Integer.parseInt(value[5]));
updateState(CHANNEL_TYPE_RSSI, signalStrength);
return;
}
IntesisBoxMessage message = IntesisBoxMessage.parse(data);
if (message != null) {
switch (message.getCommand()) {
case LIMITS:
logger.debug("handleMessage(): Limits received - {}", data);
String function = message.getFunction();
if (function.equals("SETPTEMP")) {
List<Double> limits = message.getLimitsValue().stream().map(l -> Double.valueOf(l) / 10.0d)
.collect(Collectors.toList());
if (limits.size() == 2) {
minTemp = limits.get(0);
maxTemp = limits.get(1);
}
logger.trace("Property target temperatures {} added", message.getValue());
properties.put("targetTemperature limits", "[" + minTemp + "," + maxTemp + "]");
addChannel(CHANNEL_TYPE_TARGETTEMP, "Number:Temperature");
} else {
switch (function) {
case "MODE":
properties.put("supported modes", message.getValue());
limits.put(CHANNEL_TYPE_MODE, message.getLimitsValue());
addChannel(CHANNEL_TYPE_MODE, "String");
break;
case "FANSP":
properties.put("supported fan levels", message.getValue());
limits.put(CHANNEL_TYPE_FANSPEED, message.getLimitsValue());
addChannel(CHANNEL_TYPE_FANSPEED, "String");
break;
case "VANEUD":
properties.put("supported vane up/down modes", message.getValue());
limits.put(CHANNEL_TYPE_VANESUD, message.getLimitsValue());
addChannel(CHANNEL_TYPE_VANESUD, "String");
break;
case "VANELR":
properties.put("supported vane left/right modes", message.getValue());
limits.put(CHANNEL_TYPE_VANESLR, message.getLimitsValue());
addChannel(CHANNEL_TYPE_VANESLR, "String");
break;
}
}
updateProperties(properties);
break;
case CHN:
receivedUpdate(message.getFunction(), message.getValue());
break;
}
}
}
public void addChannel(String channelId, String itemType) {
if (thing.getChannel(channelId) == null) {
logger.trace("Channel '{}' for UID to be added", channelId);
ThingBuilder thingBuilder = editThing();
final ChannelTypeUID channelTypeUID = new ChannelTypeUID(BINDING_ID, channelId);
Channel channel = ChannelBuilder.create(new ChannelUID(getThing().getUID(), channelId), itemType)
.withType(channelTypeUID).withKind(ChannelKind.STATE).build();
thingBuilder.withChannel(channel);
updateThing(thingBuilder.build());
if (limits.containsKey(channelId)) {
List<StateOption> options = new ArrayList<>();
for (String mode : limits.get(channelId)) {
options.add(new StateOption(mode,
mode.substring(0, 1).toUpperCase() + mode.substring(1).toLowerCase()));
}
intesisStateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), channelId),
options);
}
}
}
@Override
public void messageReceived(String messageLine) {
logger.trace("messageReceived() : {}", messageLine);
handleMessage(messageLine);
}
@Override
public void connectionStatusChanged(ThingStatus status, @Nullable String message) {
if (message != null) {
this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message);
}
this.updateStatus(status);
}
public static DecimalType mapSignalStrength(int dbm) {
int strength = -1;
if (dbm > -60) {
strength = 4;
} else if (dbm > -70) {
strength = 3;
} else if (dbm > -80) {
strength = 2;
} else if (dbm > -90) {
strength = 1;
} else {
strength = 0;
}
return new DecimalType(strength);
}
}

View File

@@ -29,10 +29,10 @@ import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.intesis.internal.IntesisConfiguration;
import org.openhab.binding.intesis.internal.IntesisDynamicStateDescriptionProvider;
import org.openhab.binding.intesis.internal.IntesisHomeModeEnum;
import org.openhab.binding.intesis.internal.api.IntesisHomeHttpApi;
import org.openhab.binding.intesis.internal.config.IntesisHomeConfiguration;
import org.openhab.binding.intesis.internal.enums.IntesisHomeModeEnum;
import org.openhab.binding.intesis.internal.gson.IntesisHomeJSonDTO.Data;
import org.openhab.binding.intesis.internal.gson.IntesisHomeJSonDTO.Datapoints;
import org.openhab.binding.intesis.internal.gson.IntesisHomeJSonDTO.Descr;
@@ -83,7 +83,7 @@ public class IntesisHomeHandler extends BaseThingHandler {
private final Gson gson = new Gson();
private IntesisConfiguration config = new IntesisConfiguration();
private IntesisHomeConfiguration config = new IntesisHomeConfiguration();
private @Nullable ScheduledFuture<?> refreshJob;
@@ -97,7 +97,7 @@ public class IntesisHomeHandler extends BaseThingHandler {
@Override
public void initialize() {
updateStatus(ThingStatus.UNKNOWN);
config = getConfigAs(IntesisConfiguration.class);
config = getConfigAs(IntesisHomeConfiguration.class);
if (config.ipAddress.isEmpty() && config.password.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "IP-Address and password not set");
return;
@@ -336,7 +336,7 @@ public class IntesisHomeHandler extends BaseThingHandler {
break;
}
}
properties.put("Supported modes", opModes.toString());
properties.put("supported modes", opModes.toString());
channelId = CHANNEL_TYPE_MODE;
addChannel(channelId, itemType, opModes);
break;
@@ -349,7 +349,7 @@ public class IntesisHomeHandler extends BaseThingHandler {
fanLevels.add(fanString);
}
}
properties.put("Supported fan levels", fanLevels.toString());
properties.put("supported fan levels", fanLevels.toString());
channelId = CHANNEL_TYPE_FANSPEED;
addChannel(channelId, itemType, fanLevels);
break;
@@ -372,12 +372,12 @@ public class IntesisHomeHandler extends BaseThingHandler {
switch (datapoint.uid) {
case 5:
channelId = CHANNEL_TYPE_VANESUD;
properties.put("Supported vane up/down modes", swingModes.toString());
properties.put("supported vane up/down modes", swingModes.toString());
addChannel(channelId, itemType, swingModes);
break;
case 6:
channelId = CHANNEL_TYPE_VANESLR;
properties.put("Supported vane left/right modes", swingModes.toString());
properties.put("supported vane left/right modes", swingModes.toString());
addChannel(channelId, itemType, swingModes);
break;
}

View File

@@ -29,7 +29,7 @@ channel-type.intesis.ambientTemperature.label = Ambient Temperature
channel-type.intesis.ambientTemperature.description = Shows actual room temperature.
channel-type.intesis.outdoorTemperature.label = Outdoor Temperature
channel-type.intesis.outdoorTemperature.description = Shows actual outdoor temperature.
channel-type.intesis.fanSpeed.label = Wind Speed
channel-type.intesis.fanSpeed.label = Fan Speed
channel-type.intesis.fanSpeed.description = Sets the fan speed on the Air conditioner.
channel-type.intesis.fanSpeed.state.option.auto = Auto
channel-type.intesis.vanesUpDown.label = Vertical Swing Mode
@@ -40,3 +40,7 @@ channel-type.intesis.vanes.option.auto = AUTO
channel-type.intesis.vanes.option.swing = Swing
channel-type.intesis.vanes.option.swirl = Swirl
channel-type.intesis.vanes.option.wide = Wide
channel-type.intesis.errorCode.label = Error Code
channel-type.intesis.errorCode.description = Shows the Air Conditioners error code if an error was found.
channel-type.intesis.errorStatus.label = Error Status
channel-type.intesis.errorStatus.description = Indicates if the Air Conditioner has encountered an error.

View File

@@ -40,3 +40,8 @@ channel-type.intesis.vanes.option.auto = Auto
channel-type.intesis.vanes.option.swing = Schwingen
channel-type.intesis.vanes.option.swirl = Pulsieren
channel-type.intesis.vanes.option.wide = Breit
channel-type.intesis.errorCode.label = Fehlercode
channel-type.intesis.errorCode.description = Zeigt im Fehlerzustand den Fehlercode an.
channel-type.intesis.errorStatus.label = Fehlerstatus
channel-type.intesis.errorStatus.description = Zeigt an, ob sich der Air Conditioner im Zustand "Fehler" befindet.

View File

@@ -23,4 +23,48 @@
</parameter>
</config-description>
</thing-type>
<thing-type id="intesisBox">
<label>IntesisBox Adapter</label>
<description>Represents a single IntesisBox adapter on the network, connected to an A/C unit.</description>
<channels>
<channel id="power" typeId="system.power"/>
<channel id="wifiSignal" typeId="system.signal-strength"/>
<channel id="ambientTemperature" typeId="ambientTemperature"/>
<channel id="errorCode" typeId="errorCode"/>
<channel id="errorStatus" typeId="errorStatus"/>
</channels>
<config-description>
<parameter name="ipAddress" type="text" required="true">
<label>@text/thing-type.config.intesis.ipAddress.label</label>
<description>@text/thing-type.config.intesis.ipAddress.description</description>
<context>network-address</context>
</parameter>
<parameter name="port" type="integer" required="true">
<label>Port</label>
<description>The TCP port to the IntesisBox.</description>
<default>3310</default>
</parameter>
</config-description>
</thing-type>
<channel-type id="ambientTemperature">
<item-type>Number:Temperature</item-type>
<label>@text/channel-type.intesis.ambientTemperature.label</label>
<description>@text/channel-type.intesis.ambientTemperature.description</description>
<state pattern="%d %unit%" readOnly="true"></state>
</channel-type>
<channel-type id="errorCode">
<item-type>String</item-type>
<label>@text/channel-type.intesis.errorCode.label</label>
<description>@text/channel-type.intesis.errorCode.description</description>
</channel-type>
<channel-type id="errorStatus">
<item-type>String</item-type>
<label>@text/channel-type.intesis.errorStatus.label</label>
<description>@text/channel-type.intesis.errorStatus.description</description>
</channel-type>
</thing:thing-descriptions>