fixup! [intellicenter2] feat: adds IntelliCenter2 binding for Pentair

This commit is contained in:
Valdis Rigdon 2024-12-06 11:01:33 -05:00
parent 512043d15f
commit 419b476cbe
28 changed files with 193 additions and 252 deletions

View File

@ -3,23 +3,23 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<scope>provided</scope>
<version>14.0.1</version>
</dependency>
</dependencies>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>4.2.0</version>
<version>4.3.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.intellicenter2</artifactId>
<name>openHAB Add-ons :: Bundles :: IntelliCenter2 Binding</name>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>14.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -25,7 +25,6 @@
*/
package org.openhab.binding.intellicenter2.internal;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
@ -34,7 +33,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
* @author Valdis Rigdon - Initial contribution
*/
@NonNullByDefault
@SuppressWarnings("PMD.ForbiddenPackageUsageCheck")
public class IntelliCenter2Configuration {
public String hostname = "";
@ -42,6 +40,6 @@ public class IntelliCenter2Configuration {
@Override
public String toString() {
return new ToStringBuilder(this).append("hostname", this.hostname).append("port", this.port).toString();
return getClass().getSimpleName() + "{hostname=" + hostname + ", port=" + port + "}";
}
}

View File

@ -43,6 +43,7 @@ import org.openhab.binding.intellicenter2.internal.protocol.ICResponse;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
@ -59,7 +60,6 @@ import com.google.common.annotations.VisibleForTesting;
*
* @see GetHardwareDefinition
*/
@SuppressWarnings({ "UnstableApiUsage", "PMD.ForbiddenPackageUsageCheck" })
@NonNullByDefault
public class IntelliCenter2DiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
@ -96,15 +96,18 @@ public class IntelliCenter2DiscoveryService extends AbstractDiscoveryService imp
@Override
@VisibleForTesting
public void startScan() {
if (bridgeHandler == null || !bridgeHandler.getProtocolFuture().isDone()) {
return;
IntelliCenter2BridgeHandler h = bridgeHandler;
if (h != null) {
if (!h.getProtocolFuture().isDone()) {
return;
}
startScan(h.getProtocol());
}
startScan(bridgeHandler.getProtocol());
}
private void startScan(final ICProtocol protocol) {
for (Argument discoveryArg : Argument.values()) {
logger.info("Looking for devices with argument {}", discoveryArg);
logger.trace("Looking for devices with argument {}", discoveryArg);
final Future<ICResponse> future = protocol.submit(discoveryArg.getRequest());
try {
final GetHardwareDefinition hardware = new GetHardwareDefinition(future.get());
@ -143,7 +146,7 @@ public class IntelliCenter2DiscoveryService extends AbstractDiscoveryService imp
}
}
} catch (Exception e) {
logger.error("Unable to discover IntelliCenter2 hardware for {}", discoveryArg, e);
logger.warn("Unable to discover IntelliCenter2 hardware for {}", discoveryArg, e);
}
}
try {
@ -157,7 +160,7 @@ public class IntelliCenter2DiscoveryService extends AbstractDiscoveryService imp
}
}
} catch (Exception e) {
logger.error("Unable to discover IntelliCenter2 hardware via GetConfiguration", e);
logger.warn("Unable to discover IntelliCenter2 hardware via GetConfiguration", e);
}
}
@ -189,15 +192,16 @@ public class IntelliCenter2DiscoveryService extends AbstractDiscoveryService imp
private void discoveryResult(ThingTypeUID bindingId, ResponseModel model, final Map<String, Object> properties) {
logger.debug("Discovered object {}", model);
if (bridgeHandler == null) {
final IntelliCenter2BridgeHandler bh = bridgeHandler;
if (bh == null) {
logger.error("discovered result with a null bridgeHandler {} {}", bindingId, model);
return;
}
final ThingUID bridgeUID = bridgeHandler.getThing().getUID();
final ThingUID bridgeUID = bh.getThing().getUID();
final ThingUID uid = new ThingUID(bindingId, bridgeUID, model.getObjectName());
properties.put("vendor", "Pentair");
properties.put("model", "IntelliCenter");
properties.put(Thing.PROPERTY_VENDOR, "Pentair");
properties.put(Thing.PROPERTY_MODEL_ID, "IntelliCenter");
properties.put(Attribute.OBJNAM.name(), model.getObjectName());
properties.put(Attribute.SUBTYP.name(), model.getSubType());
properties.put(Attribute.SNAME.name(), model.getSname());

View File

@ -1,13 +0,0 @@
/**
* Copyright (c) 2010-2024 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.intellicenter2.internal.discovery;

View File

@ -25,6 +25,7 @@ import org.openhab.binding.intellicenter2.internal.model.SystemInfo;
import org.openhab.binding.intellicenter2.internal.protocol.ICProtocol;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
@ -42,7 +43,6 @@ import com.google.common.util.concurrent.SettableFuture;
*
* @author Valdis Rigdon - Initial contribution
*/
@SuppressWarnings("UnstableApiUsage")
@NonNullByDefault
public class IntelliCenter2BridgeHandler extends BaseBridgeHandler {
@ -73,31 +73,30 @@ public class IntelliCenter2BridgeHandler extends BaseBridgeHandler {
final ICProtocol protocol = new ICProtocol(config);
protocolFuture.set(protocol);
systemInfo = protocol.getSystemInfo();
logger.info("Connected to IntelliCenter2 {}", systemInfo);
if (systemInfo != null) {
updateStatus(ThingStatus.ONLINE);
updateProperty("model", "IntelliCenter");
updateProperty("mode", systemInfo.getMode());
updateProperty("propertyName", systemInfo.getPropertyName());
updateProperty("version", systemInfo.getVersion());
logger.info("Bridge is ONLINE.");
if (!systemInfo.getIntellicenterVersion().equals("1.064")) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
"Running a non-supported version of IntelliCenter");
logger.debug("Connected to IntelliCenter2 {}", systemInfo);
SystemInfo si = systemInfo;
if (si != null) {
updateProperty(Thing.PROPERTY_MODEL_ID, "IntelliCenter");
updateProperty("mode", si.getMode());
updateProperty("propertyName", si.getPropertyName());
updateProperty("version", si.getVersion());
updateProperty("intellicenterVersion", si.getIntellicenterVersion());
if (!si.getIntellicenterVersion().equals("1.064")) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, String.format(
"Running a non-supported version of IntelliCenter: %s", si.getIntellicenterVersion()));
return;
}
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Unable to get system info from IntelliCenter2.");
}
} catch (UnknownHostException e) {
String msg = String.format("unknown host name: %s", config.hostname);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, msg);
updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.COMMUNICATION_ERROR,
String.format("unknown host name: %s", config.hostname));
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Unable to connect to host " + config.hostname);
} catch (Exception e) {
logger.error("Error initializing.", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR, e.getMessage());
updateStatus(ThingStatus.UNINITIALIZED, ThingStatusDetail.COMMUNICATION_ERROR,
String.format("Unable to connect to host %s", config.hostname));
}
});
}

View File

@ -31,8 +31,6 @@ import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handler for an IntelliCenter2 feature. Features are exposed as On/Off, and represent a Circuit.
@ -41,12 +39,9 @@ import org.slf4j.LoggerFactory;
*
* @see Circuit
*/
@SuppressWarnings("UnstableApiUsage")
@NonNullByDefault
public class IntelliCenter2FeatureHandler extends IntelliCenter2ThingHandler<Circuit> {
private final Logger logger = LoggerFactory.getLogger(IntelliCenter2FeatureHandler.class);
public IntelliCenter2FeatureHandler(Thing thing) {
super(thing);
}
@ -72,14 +67,17 @@ public class IntelliCenter2FeatureHandler extends IntelliCenter2ThingHandler<Cir
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
super.handleCommand(channelUID, command);
switch (channelUID.getId()) {
case CHANNEL_FEATURE_ON_OFF:
if (command instanceof OnOffType) {
var request = ICRequest.setParamList(
new RequestObject(getObjectName(), Map.of(Attribute.STATUS, command.toString())));
getProtocol().submit(request);
}
break;
final ICProtocol p = getProtocol();
if (p != null) {
switch (channelUID.getId()) {
case CHANNEL_FEATURE_ON_OFF:
if (command instanceof OnOffType) {
var request = ICRequest.setParamList(
new RequestObject(getObjectName(), Map.of(Attribute.STATUS, command.toString())));
p.submit(request);
}
break;
}
}
}

View File

@ -19,7 +19,6 @@ import static org.openhab.binding.intellicenter2.internal.IntelliCenter2BindingC
import java.util.Map;
import java.util.concurrent.Future;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.intellicenter2.internal.model.IntelliBrite;
@ -36,8 +35,6 @@ import org.openhab.core.library.types.PercentType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handler for an IntelliCenter2 IntelliBrite light.
@ -46,12 +43,9 @@ import org.slf4j.LoggerFactory;
*
* @see IntelliBrite
*/
@SuppressWarnings("UnstableApiUsage")
@NonNullByDefault
public class IntelliCenter2LightHandler extends IntelliCenter2ThingHandler<IntelliBrite> {
private final Logger logger = LoggerFactory.getLogger(IntelliCenter2LightHandler.class);
public IntelliCenter2LightHandler(Thing thing) {
super(thing);
}
@ -76,24 +70,27 @@ public class IntelliCenter2LightHandler extends IntelliCenter2ThingHandler<Intel
}
@Override
public void handleCommand(@NonNull ChannelUID channelUID, @NonNull Command command) {
public void handleCommand(ChannelUID channelUID, Command command) {
super.handleCommand(channelUID, command);
switch (channelUID.getId()) {
case CHANNEL_LIGHT_POWER:
if (command instanceof OnOffType) {
var request = ICRequest.setParamList(
new RequestObject(getObjectName(), Map.of(Attribute.STATUS, command.toString())));
getProtocol().submit(request);
}
break;
case CHANNEL_LIGHT_COLOR:
if (command instanceof HSBType) {
// ACT is the value to set, but USE is returned
var params = Map.of(Attribute.ACT, toColor(((HSBType) command)).intellicenterCode);
var request = ICRequest.setParamList(new RequestObject(getObjectName(), params));
getProtocol().submit(request);
}
break;
final ICProtocol p = getProtocol();
if (p != null) {
switch (channelUID.getId()) {
case CHANNEL_LIGHT_POWER:
if (command instanceof OnOffType) {
var request = ICRequest.setParamList(
new RequestObject(getObjectName(), Map.of(Attribute.STATUS, command.toString())));
p.submit(request);
}
break;
case CHANNEL_LIGHT_COLOR:
if (command instanceof HSBType) {
// ACT is the value to set, but USE is returned
var params = Map.of(Attribute.ACT, toColor(((HSBType) command)).intellicenterCode);
var request = ICRequest.setParamList(new RequestObject(getObjectName(), params));
p.submit(request);
}
break;
}
}
}

View File

@ -41,7 +41,6 @@ import org.slf4j.LoggerFactory;
*
* @see Body
*/
@SuppressWarnings("UnstableApiUsage")
@NonNullByDefault
public class IntelliCenter2PoolHandler extends IntelliCenter2ThingHandler<Body> {
@ -84,7 +83,7 @@ public class IntelliCenter2PoolHandler extends IntelliCenter2ThingHandler<Body>
updateState(channelUID, OnOffType.from(pool.isHeating()));
break;
default:
logger.error("Unable to update state for {}", channelUID);
logger.warn("Unable to update state for {}", channelUID);
}
}

View File

@ -19,10 +19,8 @@ import static org.openhab.binding.intellicenter2.internal.IntelliCenter2BindingC
import java.util.concurrent.Future;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.intellicenter2.internal.model.IntelliBrite;
import org.openhab.binding.intellicenter2.internal.model.Pump;
import org.openhab.binding.intellicenter2.internal.protocol.Attribute;
import org.openhab.binding.intellicenter2.internal.protocol.ICProtocol;
@ -30,11 +28,10 @@ import org.openhab.binding.intellicenter2.internal.protocol.ICRequest;
import org.openhab.binding.intellicenter2.internal.protocol.ICResponse;
import org.openhab.binding.intellicenter2.internal.protocol.ResponseObject;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handler for an IntelliCenter2 IntelliBrite light.
@ -43,12 +40,9 @@ import org.slf4j.LoggerFactory;
*
* @see IntelliBrite
*/
@SuppressWarnings("UnstableApiUsage")
@NonNullByDefault
public class IntelliCenter2PumpHandler extends IntelliCenter2ThingHandler<Pump> {
private final Logger logger = LoggerFactory.getLogger(IntelliCenter2PumpHandler.class);
public IntelliCenter2PumpHandler(Thing thing) {
super(thing);
}
@ -65,7 +59,7 @@ public class IntelliCenter2PumpHandler extends IntelliCenter2ThingHandler<Pump>
protected void updateState(Pump model) {
updateState(CHANNEL_PUMP_GPM, new DecimalType(model.getGPM()));
updateState(CHANNEL_PUMP_RPM, new DecimalType(model.getRPM()));
updateState(CHANNEL_PUMP_POWER, new DecimalType(model.getPowerConsumption()));
updateState(CHANNEL_PUMP_POWER, new QuantityType<>(model.getPowerConsumption(), Units.WATT));
}
@Override
@ -73,20 +67,6 @@ public class IntelliCenter2PumpHandler extends IntelliCenter2ThingHandler<Pump>
return new Pump(response);
}
@Override
public void handleCommand(@NonNull ChannelUID channelUID, @NonNull Command command) {
super.handleCommand(channelUID, command);
switch (channelUID.getId()) {
case CHANNEL_PUMP_GPM:
if (command instanceof DecimalType) {
// var request = ICRequest.setParamList(
// new RequestObject(getObjectName(), Map.of(Attribute.GPM, command.toString())));
// getProtocol().submit(request);
}
break;
}
}
@Override
protected void updateState(ChannelUID channelUID, Pump model) {
switch (channelUID.getId()) {
@ -97,7 +77,7 @@ public class IntelliCenter2PumpHandler extends IntelliCenter2ThingHandler<Pump>
updateState(channelUID, new DecimalType(model.getRPM()));
break;
case CHANNEL_PUMP_POWER:
updateState(channelUID, new DecimalType(model.getPowerConsumption()));
updateState(channelUID, new QuantityType<>(model.getPowerConsumption(), Units.WATT));
break;
default:
break;

View File

@ -16,16 +16,11 @@ import static com.google.common.util.concurrent.Futures.getUnchecked;
import static org.openhab.binding.intellicenter2.internal.IntelliCenter2BindingConstants.CHANNEL_SENSOR_CALIB;
import static org.openhab.binding.intellicenter2.internal.IntelliCenter2BindingConstants.CHANNEL_SENSOR_PROBE;
import static org.openhab.binding.intellicenter2.internal.IntelliCenter2BindingConstants.CHANNEL_SENSOR_SOURCE;
import static org.openhab.binding.intellicenter2.internal.protocol.Attribute.CALIB;
import static org.openhab.binding.intellicenter2.internal.protocol.Attribute.PROBE;
import static org.openhab.binding.intellicenter2.internal.protocol.Attribute.SOURCE;
import java.util.concurrent.Future;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.intellicenter2.internal.model.IntelliBrite;
import org.openhab.binding.intellicenter2.internal.model.Sensor;
import org.openhab.binding.intellicenter2.internal.protocol.Attribute;
import org.openhab.binding.intellicenter2.internal.protocol.ICProtocol;
@ -36,8 +31,6 @@ import org.openhab.core.library.types.DecimalType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handler for an IntelliCenter2 IntelliBrite light.
@ -46,12 +39,9 @@ import org.slf4j.LoggerFactory;
*
* @see IntelliBrite
*/
@SuppressWarnings("UnstableApiUsage")
@NonNullByDefault
public class IntelliCenter2SensorHandler extends IntelliCenter2ThingHandler<Sensor> {
private final Logger logger = LoggerFactory.getLogger(IntelliCenter2SensorHandler.class);
public IntelliCenter2SensorHandler(Thing thing) {
super(thing);
}
@ -77,7 +67,7 @@ public class IntelliCenter2SensorHandler extends IntelliCenter2ThingHandler<Sens
}
@Override
public void handleCommand(@NonNull ChannelUID channelUID, @NonNull Command command) {
public void handleCommand(ChannelUID channelUID, Command command) {
super.handleCommand(channelUID, command);
}

View File

@ -16,7 +16,6 @@ import static org.openhab.binding.intellicenter2.internal.protocol.Attribute.OBJ
import java.util.Objects;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.intellicenter2.internal.model.ResponseModel;
@ -24,6 +23,7 @@ import org.openhab.binding.intellicenter2.internal.protocol.Attribute;
import org.openhab.binding.intellicenter2.internal.protocol.ICProtocol;
import org.openhab.binding.intellicenter2.internal.protocol.NotifyListListener;
import org.openhab.binding.intellicenter2.internal.protocol.ResponseObject;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
@ -57,11 +57,11 @@ public abstract class IntelliCenter2ThingHandler<T extends ResponseModel> extend
// get an updated model from IC
final ICProtocol protocol = getProtocol();
if (protocol == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
return;
}
final T model = queryModel(protocol);
getProtocol().subscribe(this, model.asRequestObject());
protocol.subscribe(this, model.asRequestObject());
updateStatus(ThingStatus.ONLINE);
updateState(model);
} catch (Exception e) {
@ -75,35 +75,37 @@ public abstract class IntelliCenter2ThingHandler<T extends ResponseModel> extend
public void dispose() {
super.dispose();
scheduler.execute(() -> {
if (getProtocol() != null) {
getProtocol().unsubscribe(this);
final ICProtocol protocol = getProtocol();
if (protocol != null) {
protocol.unsubscribe(this);
}
updateStatus(ThingStatus.OFFLINE);
});
}
@Nullable
protected ICProtocol getProtocol() {
if (getBridgeHandler() != null) {
return getBridgeHandler().getProtocol();
final IntelliCenter2BridgeHandler bh = getBridgeHandler();
if (bh != null) {
return bh.getProtocol();
}
return null;
}
@Nullable
protected IntelliCenter2BridgeHandler getBridgeHandler() {
if (getBridge() == null) {
final Bridge b = getBridge();
if (b == null) {
return null;
}
return (IntelliCenter2BridgeHandler) getBridge().getHandler();
return (IntelliCenter2BridgeHandler) b.getHandler();
}
@Override
public void handleCommand(@NonNull ChannelUID channelUID, @NonNull Command command) {
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("Handling {} for {}", channelUID, command);
final ICProtocol protocol = getProtocol();
if (protocol == null) {
logger.error("handleCommand had a null protocol for {} {}", channelUID, command);
logger.warn("handleCommand had a null protocol for {} {}", channelUID, command);
return;
}
if (command instanceof RefreshType) {
@ -136,7 +138,7 @@ public abstract class IntelliCenter2ThingHandler<T extends ResponseModel> extend
}
});
} catch (Exception e) {
logger.error("Error handling onNotifyList", e);
logger.debug("Error handling onNotifyList", e);
}
}

View File

@ -1,13 +0,0 @@
/**
* Copyright (c) 2010-2024 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.intellicenter2.internal.handler;

View File

@ -38,6 +38,7 @@ public class GetConfiguration {
this.response = Objects.requireNonNull(response);
}
@SuppressWarnings("null")
public List<Circuit> getFeatureCircuits() {
return response.getAnswer().stream()
.filter(r -> CIRCUIT.toString().equals(r.getOptionalValueAsString(OBJTYP).orElse("")))
@ -46,6 +47,6 @@ public class GetConfiguration {
@Override
public String toString() {
return "GetConfiguration{" + "response=" + response + '}';
return getClass().getSimpleName() + "{" + "response=" + response + '}';
}
}

View File

@ -57,6 +57,7 @@ public class GetHardwareDefinition {
this.response = response;
}
@SuppressWarnings("null")
public List<Panel> getPanels() {
return response.getAnswer().stream().filter(r -> r.getValueAsString(OBJTYP).equals("PANEL")).map(Panel::new)
.collect(toList());

View File

@ -21,7 +21,6 @@ import static org.openhab.binding.intellicenter2.internal.protocol.Attribute.USE
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.intellicenter2.internal.protocol.Attribute;
import org.openhab.binding.intellicenter2.internal.protocol.RequestObject;
@ -51,7 +50,6 @@ public class IntelliBrite extends Circuit {
// borrowed from
// https://github.com/dustindclark/homebridge-pentair-intellicenter/blob/676b7dab2fbf5107443678c3ef5bc271108941f8/src/types.ts
@NonNullByDefault
public enum Color {
@SerializedName("WHITER")
@ -91,7 +89,6 @@ public class IntelliBrite extends Circuit {
}
}
@NonNull
public Color getColor() {
return getValueAsEnum(USE, Color.class);
}

View File

@ -51,21 +51,25 @@ public class Panel extends ResponseModel {
super(REQUEST_ATTRIBUTES, response);
}
@SuppressWarnings("null")
public List<Body> getBodies() {
return getModuleCircuits().filter(r -> BODY.toString().equals(r.getValueAsString(OBJTYP))).map(Body::new)
.collect(toList());
}
@SuppressWarnings("null")
public List<Circuit> getCircuits() {
return getModuleCircuits().filter(r -> CIRCUIT.toString().equals(r.getValueAsString(OBJTYP))).map(Circuit::new)
.collect(toList());
}
@SuppressWarnings("null")
public List<Pump> getPumps() {
return getValueAsResponseObjects(OBJLIST).stream()
.filter(r -> PUMP.toString().equals(r.getValueAsString(OBJTYP))).map(Pump::new).collect(toList());
}
@SuppressWarnings("null")
public List<Sensor> getSensors() {
return getValueAsResponseObjects(OBJLIST).stream()
.filter(r -> SENSE.toString().equals(r.getValueAsString(OBJTYP))).map(Sensor::new).collect(toList());

View File

@ -1,13 +0,0 @@
/**
* Copyright (c) 2010-2024 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.intellicenter2.internal;

View File

@ -51,7 +51,6 @@ import com.google.gson.GsonBuilder;
*
* @author Valdis Rigdon - Initial contribution
*/
@SuppressWarnings("UnstableApiUsage")
@NonNullByDefault
public class ICProtocol implements AutoCloseable {
@ -69,12 +68,12 @@ public class ICProtocol implements AutoCloseable {
@Nullable
private ICRead readRunnable;
private final AtomicReference<Socket> clientSocket = new AtomicReference<>();
private final AtomicReference<BufferedOutputStream> out = new AtomicReference<>();
private final AtomicReference<BufferedReader> in = new AtomicReference<>();
private final AtomicReference<@Nullable Socket> clientSocket = new AtomicReference<>();
private final AtomicReference<@Nullable BufferedOutputStream> out = new AtomicReference<>();
private final AtomicReference<@Nullable BufferedReader> in = new AtomicReference<>();
private final ConcurrentMap<String, ICRequest> requests = new ConcurrentHashMap<>(2);
private final ConcurrentMap<String, SettableFuture<ICResponse>> responses = new ConcurrentHashMap<>(2);
private final ConcurrentMap<String, @Nullable SettableFuture<ICResponse>> responses = new ConcurrentHashMap<>(2);
private final ConcurrentHashMap<String, List<NotifyListListener>> subscriptions = new ConcurrentHashMap<>();
private final ExecutorService listenersNotifier;
@ -87,17 +86,19 @@ public class ICProtocol implements AutoCloseable {
}
private void connect() throws IOException {
this.clientSocket.set(new Socket(config.hostname, config.port));
this.clientSocket.get().setKeepAlive(true);
this.out.set(new BufferedOutputStream(new DataOutputStream(clientSocket.get().getOutputStream())));
this.in.set(new BufferedReader(new InputStreamReader(clientSocket.get().getInputStream())));
final Socket socket = new Socket(config.hostname, config.port);
socket.setKeepAlive(true);
this.clientSocket.set(socket);
this.out.set(new BufferedOutputStream(new DataOutputStream(socket.getOutputStream())));
this.in.set(new BufferedReader(new InputStreamReader(socket.getInputStream())));
this.writerService = newSingleThreadExecutor(
new ThreadFactoryBuilder().setDaemon(true).setNameFormat("ic-protocol-writer-%s").build());
this.readerService = newSingleThreadExecutor(
var rs = newSingleThreadExecutor(
new ThreadFactoryBuilder().setDaemon(true).setNameFormat("ic-protocol-reader-%s").build());
this.readerService = rs;
this.readRunnable = new ICRead();
this.readerService.submit(readRunnable);
rs.submit(readRunnable);
}
private void reconnect() throws IOException {
@ -107,18 +108,21 @@ public class ICProtocol implements AutoCloseable {
@Override
public void close() throws IOException {
if (readRunnable != null) {
readRunnable.stop = true;
var rr = readRunnable;
if (rr != null) {
rr.stop = true;
}
safeClose(out.get());
safeClose(in.get());
safeClose(clientSocket.get());
try {
if (writerService != null) {
writerService.shutdownNow();
final var ws = writerService;
if (ws != null) {
ws.shutdownNow();
}
if (readerService != null) {
readerService.shutdownNow();
final var rs = readerService;
if (rs != null) {
rs.shutdownNow();
}
} catch (Exception ignored) {
}
@ -137,16 +141,18 @@ public class ICProtocol implements AutoCloseable {
return systemInfo;
}
@SuppressWarnings("null")
private SystemInfo querySystemInfo() {
return new SystemInfo(getUnchecked(submit(SystemInfo.REQUEST)).getObjectList().get(0));
}
@SuppressWarnings("null")
public synchronized void subscribe(NotifyListListener listener, RequestObject request) {
subscriptions.putIfAbsent(request.getObjectName(), new CopyOnWriteArrayList<>());
subscriptions.get(request.getObjectName()).add(listener);
String response = getUnchecked(submit(ICRequest.requestParamList(request))).getResponse();
if (!"200".equals(response)) {
logger.error("Error subscribing listener {} for request {}", listener, request);
logger.warn("Error subscribing listener {} for request {}", listener, request);
}
}
@ -162,6 +168,7 @@ public class ICProtocol implements AutoCloseable {
keysToRemove.forEach(subscriptions::remove);
}
@SuppressWarnings("null")
private void notifyListeners(final ICResponse response) {
final var runnable = new Runnable() {
@ -190,10 +197,11 @@ public class ICProtocol implements AutoCloseable {
requests.put(messageID, request);
final SettableFuture<ICResponse> future = SettableFuture.create();
responses.put(messageID, future);
if (writerService == null) {
var ws = writerService;
if (ws == null) {
throw new IllegalStateException("Null writerService.");
}
writerService.submit(new ICWrite(request));
ws.submit(new ICWrite(request));
return future;
}
@ -211,8 +219,7 @@ public class ICProtocol implements AutoCloseable {
try {
logger.trace("Sending request {}", jsonRequest);
write(jsonRequest);
} catch (Exception e) {
logger.error("Error writing request {}", jsonRequest);
} catch (IOException e) {
throw new RuntimeException("Error writing request", e);
}
}
@ -233,6 +240,7 @@ public class ICProtocol implements AutoCloseable {
}
}
@SuppressWarnings("null")
private void writeBytes(byte[] bytes) throws IOException {
out.get().write(bytes);
out.get().flush();
@ -261,7 +269,8 @@ public class ICProtocol implements AutoCloseable {
if (NOTIFY_LIST.toString().equals(response.getCommand())) {
notifyListeners(response);
} else {
logger.error("Got a non-NotifyList response w/o a corresponding messageID {}", response);
logger.debug("Got a non-NotifyList response without a corresponding messageID {}",
response);
}
} else {
future.set(response);
@ -269,15 +278,18 @@ public class ICProtocol implements AutoCloseable {
}
} catch (Exception e) {
if (!stop) {
logger.error("Error reading from socket", e);
logger.warn("Error reading from socket", e);
}
}
logger.info("Stopped socket read.");
}
@Nullable
private ICResponse readNext() throws IOException {
var jsonResponse = in.get().readLine();
var reader = in.get();
if (reader == null) {
return null;
}
var jsonResponse = reader.readLine();
logger.trace("Received response {}", jsonResponse);
if (jsonResponse == null) {
return null;

View File

@ -28,7 +28,9 @@ public class ICRequest {
private final String command;
private @Nullable final String condition;
@SuppressWarnings("unused")
private @Nullable final String queryName;
@SuppressWarnings("unused")
private @Nullable final String arguments;
private @Nullable List<RequestObject> objectList;

View File

@ -15,7 +15,6 @@ package org.openhab.binding.intellicenter2.internal.protocol;
import java.util.Collections;
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@ -36,7 +35,6 @@ public class ICResponse {
public ICResponse() {
}
@NonNull
public String getMessageID() {
return messageID;
}

View File

@ -33,8 +33,10 @@ public class RequestObject {
@SerializedName("objnam")
private final String objectName;
@SuppressWarnings("unused")
@Nullable
private final Collection<Attribute> keys;
@SuppressWarnings("unused")
@Nullable
private final Map<Attribute, String> params;

View File

@ -15,15 +15,13 @@ package org.openhab.binding.intellicenter2.internal.protocol;
import static java.util.Objects.requireNonNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
@ -33,13 +31,12 @@ import com.google.gson.annotations.SerializedName;
/**
* @author Valdis Rigdon - Initial contribution
*/
@SuppressWarnings("PMD.ForbiddenPackageUsageCheck")
@NonNullByDefault
public class ResponseObject {
@SerializedName("objnam")
private String objectName = "";
private Map<Attribute, Object> params = Map.of();
private Map<Attribute, @Nullable Object> params = Map.of();
protected ResponseObject() {
}
@ -49,13 +46,14 @@ public class ResponseObject {
@SuppressWarnings("unchecked")
var inputParams = (Map<String, Object>) m.get("params");
this.params = new HashMap<>();
inputParams.forEach((k, v) -> {
try {
params.put(Attribute.valueOf(k), v);
} catch (IllegalArgumentException e) {
System.err.println("No enum for '" + k + "'");
}
});
if (inputParams != null) {
inputParams.forEach((k, v) -> {
try {
params.put(Attribute.valueOf(k), v);
} catch (IllegalArgumentException e) {
}
});
}
}
public ResponseObject(@Nullable ResponseObject response) {
@ -69,7 +67,7 @@ public class ResponseObject {
return objectName;
}
public Map<Attribute, Object> getParams() {
public Map<Attribute, @Nullable Object> getParams() {
return params;
}
@ -92,11 +90,14 @@ public class ResponseObject {
public List<ResponseObject> getValueAsResponseObjects(Attribute key) {
@SuppressWarnings("unchecked")
final List<Map<?, ?>> list = (List<Map<?, ?>>) params.get(key);
final var responses = new ArrayList<ResponseObject>(list.size());
list.forEach(m -> {
responses.add(new ResponseObject(m));
});
return responses;
if (list != null) {
final var responses = new ArrayList<ResponseObject>(list.size());
list.forEach(m -> {
responses.add(new ResponseObject(m));
});
return responses;
}
return Collections.emptyList();
}
public <@NonNull T extends Enum<?>> T getValueAsEnum(Attribute a, Class<T> enumClass) {
@ -106,7 +107,7 @@ public class ResponseObject {
@Override
public String toString() {
return new ToStringBuilder(this).append("objectName", objectName).append("params", params).toString();
return getClass().getSimpleName() + "{objectName=" + objectName + "}";
}
@Override
@ -118,11 +119,11 @@ public class ResponseObject {
return false;
ResponseObject that = (ResponseObject) o;
return new EqualsBuilder().append(objectName, that.objectName).isEquals();
return this.objectName.equals(that.getObjectName());
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37).append(objectName).toHashCode();
return objectName.hashCode();
}
}

View File

@ -1,13 +0,0 @@
/**
* Copyright (c) 2010-2024 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.intellicenter2.internal.protocol;

View File

@ -18,9 +18,8 @@
<description>Hostname or IP address of the device</description>
</parameter>
<parameter name="port" type="integer" required="true">
<context>host</context>
<label>Port</label>
<description>Port of the device, default to 6681</description>
<description>Port of the device, defaults to 6681</description>
</parameter>
</config-description>
</bridge-type>
@ -34,16 +33,16 @@
<description>Pool for IntelliCenter2 Binding</description>
<channels>
<channel id="current-temperature" typeId="temperature">
<label>Current temperature</label>
<channel id="current-temperature" typeId="temperature-current">
<label>Current Temperature</label>
<description>The current pool temperature.</description>
</channel>
<channel id="target-temperature" typeId="temperature">
<label>Target temperature</label>
<channel id="target-temperature" typeId="temperature-target">
<label>Target Temperature</label>
<description>The target pool temperature.</description>
</channel>
<channel id="heater-status" typeId="heater-status">
<label>Heater status</label>
<label>Heater Status</label>
<description>On if the heater is running, off otherwise.</description>
</channel>
</channels>
@ -131,6 +130,20 @@
<state readOnly="true"/>
</channel-type>
<channel-type id="temperature-current">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<description>The current temperature value.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="temperature-target">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<description>The target temperature value.</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="heater-status">
<item-type>Switch</item-type>
<label>Pool Heater</label>
@ -159,10 +172,10 @@
</channel-type>
<channel-type id="pump-power">
<item-type>Number</item-type>
<item-type>Number:Power</item-type>
<label>Power Usage</label>
<description>Pump Power Usage in Watts</description>
<state readOnly="true"/>
<state readOnly="true" pattern="%.0f %unit%"/>
</channel-type>
<channel-type id="sensor-calib">

View File

@ -47,7 +47,7 @@ public class IntelliCenter2DiscoveryServiceTest {
bridgeHandler.getProtocol();
discovery.startScan();
// 2 pools and 4 features, 1 light, 2 pump (that are the same)
assertEquals(9, listener.discoveredResults.size());
// 2 pools and 4 features, 1 light, 2 pumps, 3 sensors (that are the same)
assertEquals(12, listener.discoveredResults.size());
}
}

View File

@ -16,7 +16,6 @@ import java.util.Collection;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.config.discovery.DiscoveryListener;
@ -43,8 +42,8 @@ public class SimpleDiscoveryListener implements DiscoveryListener {
}
@Override
public @Nullable Collection<@NonNull ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
@Nullable Collection<@NonNull ThingTypeUID> thingTypeUIDs, @Nullable ThingUID bridgeUID) {
public @Nullable Collection<ThingUID> removeOlderResults(DiscoveryService source, long timestamp,
@Nullable Collection<ThingTypeUID> thingTypeUIDs, @Nullable ThingUID bridgeUID) {
return null;
}
}

View File

@ -43,8 +43,6 @@ public class FeatureTest {
assertEquals(4, features.size());
System.err.println(features);
var array = features.toArray(new Circuit[0]);
assertEquals("Jets", array[0].getSname());
assertEquals("Air Blower", array[1].getSname());

View File

@ -47,8 +47,6 @@ public class GetHardwareDefinitionTest {
assertTrue(names.contains("B1101"));
assertTrue(names.contains("B1202"));
System.err.println(bodies);
}
@Test