[bigassfan] Null annotations (#13903)

* Null annotations and some refactoring
* Fix synchronized block
* Fix remaining warnings

Signed-off-by: Leo Siepel <leosiepel@gmail.com>
This commit is contained in:
lsiepel 2023-01-05 23:08:07 +01:00 committed by GitHub
parent b91fc94bdb
commit cb460657eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 199 additions and 157 deletions

View File

@ -12,26 +12,29 @@
*/
package org.openhab.binding.bigassfan.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link BigAssFanConfig} is responsible for storing the BigAssFan thing configuration.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class BigAssFanConfig {
/**
* Name of the device
*/
private String label;
private String label = "";
/**
* IP address of the device
*/
private String ipAddress;
private String ipAddress = "";
/**
* MAC address of the device
*/
private String macAddress;
private String macAddress = "";
public String getLabel() {
return label;
@ -58,16 +61,7 @@ public class BigAssFanConfig {
}
public boolean isValid() {
if (label == null || label.isBlank()) {
return false;
}
if (ipAddress == null || ipAddress.isBlank()) {
return false;
}
if (macAddress == null || macAddress.isBlank()) {
return false;
}
return true;
return !label.isBlank() && !ipAddress.isBlank() && !macAddress.isBlank();
}
@Override

View File

@ -12,41 +12,44 @@
*/
package org.openhab.binding.bigassfan.internal.discovery;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link BigAssFanDevice} is responsible for storing information about a fan.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class BigAssFanDevice {
/**
* Name of device (e.g. Master Bedroom Fan)
*/
private String label;
private String label = "";
/**
* IP address of the device extracted from UDP packet
*/
private String ipAddress;
private String ipAddress = "";
/**
* MAC address of the device extracted from discovery message
*/
private String macAddress;
private String macAddress = "";
/**
* Type of device extracted from discovery message (e.g. FAN or SWITCH)
*/
private String type;
private String type = "";
/**
* Model of device extracted from discovery message (e.g. HSERIES)
*/
private String model;
private String model = "";
/**
* The raw discovery message
*/
private String discoveryMessage;
private String discoveryMessage = "";
public String getLabel() {
return label;

View File

@ -27,6 +27,8 @@ import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
@ -44,6 +46,7 @@ import org.slf4j.LoggerFactory;
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
@Component(service = DiscoveryService.class, configurationPid = "discovery.bigassfan")
public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(BigAssFanDiscoveryService.class);
@ -53,12 +56,9 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
// Our own thread pool for the long-running listener job
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
private ScheduledFuture<?> listenerJob;
DiscoveryListener discoveryListener;
private @Nullable ScheduledFuture<?> listenerJob;
private @Nullable DiscoveryListener discoveryListener;
private boolean terminate;
private final Pattern announcementPattern = Pattern.compile("[(](.*);DEVICE;ID;(.*);(.*)[)]");
private Runnable listenerRunnable = () -> {
@ -70,9 +70,9 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
};
// Frequency (in seconds) with which we poll for new devices
private final long POLL_FREQ = 300L;
private final long POLL_DELAY = 12L;
private ScheduledFuture<?> pollJob;
private static final long POLL_FREQ = 300L;
private static final long POLL_DELAY = 12L;
private @Nullable ScheduledFuture<?> pollJob;
public BigAssFanDiscoveryService() {
super(SUPPORTED_THING_TYPES_UIDS, 0, BACKGROUND_DISCOVERY_ENABLED);
@ -84,7 +84,7 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
}
@Override
protected void activate(Map<String, Object> configProperties) {
protected void activate(@Nullable Map<String, Object> configProperties) {
super.activate(configProperties);
logger.trace("BigAssFan discovery service ACTIVATED");
}
@ -97,7 +97,7 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
@Override
@Modified
protected void modified(Map<String, Object> configProperties) {
protected void modified(@Nullable Map<String, Object> configProperties) {
super.modified(configProperties);
}
@ -115,21 +115,22 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
cancelListenerJob();
}
private void startListenerJob() {
if (listenerJob == null) {
terminate = false;
private synchronized void startListenerJob() {
if (this.listenerJob == null) {
logger.debug("Starting discovery listener job in {} seconds", BACKGROUND_DISCOVERY_DELAY);
listenerJob = scheduledExecutorService.schedule(listenerRunnable, BACKGROUND_DISCOVERY_DELAY,
terminate = false;
this.listenerJob = scheduledExecutorService.schedule(listenerRunnable, BACKGROUND_DISCOVERY_DELAY,
TimeUnit.SECONDS);
}
}
private void cancelListenerJob() {
if (listenerJob != null) {
ScheduledFuture<?> localListenerJob = this.listenerJob;
if (localListenerJob != null) {
logger.debug("Canceling discovery listener job");
listenerJob.cancel(true);
localListenerJob.cancel(true);
terminate = true;
listenerJob = null;
this.listenerJob = null;
}
}
@ -143,9 +144,11 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
private synchronized void listen() {
logger.info("BigAssFan discovery service is running");
DiscoveryListener localDiscoveryListener;
try {
discoveryListener = new DiscoveryListener();
localDiscoveryListener = new DiscoveryListener();
discoveryListener = localDiscoveryListener;
} catch (SocketException se) {
logger.warn("Got Socket exception creating multicast socket: {}", se.getMessage(), se);
return;
@ -158,7 +161,7 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
while (!terminate) {
try {
// Wait for a discovery message
processMessage(discoveryListener.waitForMessage());
processMessage(localDiscoveryListener.waitForMessage());
} catch (SocketTimeoutException e) {
// Read on socket timed out; check for termination
continue;
@ -167,14 +170,11 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
break;
}
}
discoveryListener.shutdown();
localDiscoveryListener.shutdown();
logger.debug("DiscoveryListener job is exiting");
}
private void processMessage(BigAssFanDevice device) {
if (device == null) {
return;
}
Matcher matcher = announcementPattern.matcher(device.getDiscoveryMessage());
if (matcher.find()) {
logger.debug("Match: grp1={}, grp2={}, grp(3)={}", matcher.group(1), matcher.group(2), matcher.group(3));
@ -242,23 +242,30 @@ public class BigAssFanDiscoveryService extends AbstractDiscoveryService {
.withRepresentationProperty(THING_PROPERTY_MAC).withLabel(device.getLabel()).build());
}
private void schedulePollJob() {
logger.debug("Scheduling discovery poll job to run every {} seconds starting in {} sec", POLL_FREQ, POLL_DELAY);
private synchronized void schedulePollJob() {
cancelPollJob();
pollJob = scheduler.scheduleWithFixedDelay(() -> {
try {
discoveryListener.pollForDevices();
} catch (RuntimeException e) {
logger.warn("Poll job got unexpected exception: {}", e.getMessage(), e);
}
}, POLL_DELAY, POLL_FREQ, TimeUnit.SECONDS);
if (this.pollJob == null) {
logger.debug("Scheduling discovery poll job to run every {} seconds starting in {} sec", POLL_FREQ,
POLL_DELAY);
pollJob = scheduler.scheduleWithFixedDelay(() -> {
try {
DiscoveryListener localListener = discoveryListener;
if (localListener != null) {
localListener.pollForDevices();
}
} catch (RuntimeException e) {
logger.warn("Poll job got unexpected exception: {}", e.getMessage(), e);
}
}, POLL_DELAY, POLL_FREQ, TimeUnit.SECONDS);
}
}
private void cancelPollJob() {
if (pollJob != null) {
ScheduledFuture<?> localPollJob = pollJob;
if (localPollJob != null) {
logger.debug("Canceling poll job");
pollJob.cancel(true);
pollJob = null;
localPollJob.cancel(true);
this.pollJob = null;
}
}
}

View File

@ -23,6 +23,8 @@ import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -31,19 +33,23 @@ import org.slf4j.LoggerFactory;
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class DiscoveryListener {
private final Logger logger = LoggerFactory.getLogger(DiscoveryListener.class);
private final String BCAST_ADDRESS = "255.255.255.255";
private final int SOCKET_RECEIVE_TIMEOUT = 500;
private final String POLL_MESSAGE = "<ALL;DEVICE;ID;GET>";
private static final String BCAST_ADDRESS = "255.255.255.255";
private static final int SOCKET_RECEIVE_TIMEOUT = 500;
private static final String POLL_MESSAGE = "<ALL;DEVICE;ID;GET>";
@Nullable
DatagramSocket dSocket;
@Nullable
DatagramPacket rcvPacket;
byte[] rcvBuffer;
byte[] rcvBuffer = new byte[0];
@Nullable
InetAddress bcastAddress;
byte[] bcastBuffer;
byte[] bcastBuffer = new byte[0];
@Nullable
DatagramPacket bcastPacket;
BigAssFanDevice device;
@ -54,52 +60,66 @@ public class DiscoveryListener {
device = new BigAssFanDevice();
try {
// Create a socket on the UDP port and get send & receive buffers
dSocket = new DatagramSocket(BAF_PORT);
dSocket.setSoTimeout(SOCKET_RECEIVE_TIMEOUT);
dSocket.setBroadcast(true);
DatagramSocket localDatagramSocket = new DatagramSocket(BAF_PORT);
localDatagramSocket.setSoTimeout(SOCKET_RECEIVE_TIMEOUT);
localDatagramSocket.setBroadcast(true);
dSocket = localDatagramSocket;
rcvBuffer = new byte[256];
rcvPacket = new DatagramPacket(rcvBuffer, rcvBuffer.length);
bcastAddress = InetAddress.getByName(BCAST_ADDRESS);
bcastBuffer = POLL_MESSAGE.getBytes(StandardCharsets.US_ASCII);
bcastPacket = new DatagramPacket(bcastBuffer, bcastBuffer.length, bcastAddress, BAF_PORT);
} catch (UnknownHostException uhe) {
logger.warn("UnknownHostException sending poll request for fans: {}", uhe.getMessage(), uhe);
} catch (UnknownHostException | SocketException | SecurityException e) {
logger.warn("Unexpected exception sending poll request for fans: {}", e.getMessage(), e);
}
}
public BigAssFanDevice waitForMessage() throws IOException, SocketTimeoutException {
// Wait to receive a packet
rcvPacket.setLength(rcvBuffer.length);
dSocket.receive(rcvPacket);
DatagramPacket localPacket = rcvPacket;
DatagramSocket localDatagramSocket = dSocket;
// Process the received packet
device.reset();
device.setIpAddress(rcvPacket.getAddress().getHostAddress());
String message = (new String(rcvBuffer, 0, rcvPacket.getLength()));
device.setDiscoveryMessage(message);
logger.debug("RECEIVED packet of length {} from {}: {}", message.length(), device.getIpAddress(), message);
if (localPacket != null) {
localPacket.setLength(rcvBuffer.length);
}
if (localDatagramSocket != null && localPacket != null) {
localDatagramSocket.receive(localPacket);
// Process the received packet
device.reset();
String address = localPacket.getAddress().getHostAddress();
device.setIpAddress(address != null ? address : "");
String message = (new String(rcvBuffer, 0, localPacket.getLength()));
device.setDiscoveryMessage(message);
logger.debug("RECEIVED packet of length {} from {}: {}", message.length(), device.getIpAddress(), message);
}
return device;
}
public void pollForDevices() {
if (dSocket == null) {
DatagramSocket localDatagramSocket = dSocket;
if (localDatagramSocket == null) {
logger.debug("Socket is null in discoveryListener.pollForDevices()");
return;
}
logger.debug("Sending poll request for fans: {}", POLL_MESSAGE);
try {
dSocket.send(bcastPacket);
} catch (IOException ioe) {
logger.warn("IOException sending poll request for fans: {}", ioe.getMessage(), ioe);
localDatagramSocket.send(bcastPacket);
} catch (IllegalArgumentException | SecurityException | IOException e) {
logger.warn("Unexpected exception while sending poll request for fans: {}", e.getMessage(), e);
}
}
public void shutdown() {
logger.debug("DiscoveryListener closing socket");
if (dSocket != null) {
dSocket.close();
DatagramSocket localDatagramSocket = dSocket;
if (localDatagramSocket != null) {
localDatagramSocket.close();
dSocket = null;
}
}

View File

@ -23,6 +23,7 @@ import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.BufferOverflowException;
import java.nio.channels.IllegalBlockingModeException;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.ZoneId;
@ -40,6 +41,8 @@ import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.bigassfan.internal.BigAssFanConfig;
import org.openhab.binding.bigassfan.internal.utils.BigAssFanConverter;
import org.openhab.core.common.ThreadPoolManager;
@ -65,6 +68,7 @@ import org.slf4j.LoggerFactory;
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class BigAssFanHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(BigAssFanHandler.class);
@ -75,16 +79,15 @@ public class BigAssFanHandler extends BaseThingHandler {
private static final StringType COOLING = new StringType("COOLING");
private static final StringType HEATING = new StringType("HEATING");
private BigAssFanConfig config;
private String label = null;
private String ipAddress = null;
private String macAddress = null;
private String label = "";
private String ipAddress = "";
private String macAddress = "";
private FanListener fanListener;
private final FanListener fanListener;
protected Map<String, State> fanStateMap = Collections.synchronizedMap(new HashMap<>());
protected final Map<String, State> fanStateMap = Collections.synchronizedMap(new HashMap<>());
public BigAssFanHandler(Thing thing, String ipv4Address) {
public BigAssFanHandler(Thing thing, @Nullable String ipv4Address) {
super(thing);
this.thing = thing;
@ -96,18 +99,19 @@ public class BigAssFanHandler extends BaseThingHandler {
public void initialize() {
logger.debug("BigAssFanHandler for {} is initializing", thing.getUID());
config = getConfig().as(BigAssFanConfig.class);
logger.debug("BigAssFanHandler config for {} is {}", thing.getUID(), config);
BigAssFanConfig configuration = getConfig().as(BigAssFanConfig.class);
logger.debug("BigAssFanHandler config for {} is {}", thing.getUID(), configuration);
if (!config.isValid()) {
if (!configuration.isValid()) {
logger.debug("BigAssFanHandler config of {} is invalid. Check configuration", thing.getUID());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Invalid BigAssFan config. Check configuration.");
return;
}
label = config.getLabel();
ipAddress = config.getIpAddress();
macAddress = config.getMacAddress();
label = configuration.getLabel();
ipAddress = configuration.getIpAddress();
macAddress = configuration.getMacAddress();
fanListener.startFanListener();
}
@ -312,8 +316,9 @@ public class BigAssFanHandler extends BaseThingHandler {
private void adjustMaxSpeed(PercentType command, String channelId, String commandFragment) {
int newMin = command.intValue();
int currentMax = PercentType.ZERO.intValue();
if (fanStateMap.get(channelId) != null) {
currentMax = ((PercentType) fanStateMap.get(channelId)).intValue();
State fanState = fanStateMap.get(channelId);
if (fanState != null) {
currentMax = ((PercentType) fanState).intValue();
}
if (newMin > currentMax) {
updateState(CHANNEL_FAN_SPEED_MAX, command);
@ -324,8 +329,9 @@ public class BigAssFanHandler extends BaseThingHandler {
private void adjustMinSpeed(PercentType command, String channelId, String commandFragment) {
int newMax = command.intValue();
int currentMin = PercentType.HUNDRED.intValue();
if (fanStateMap.get(channelId) != null) {
currentMin = ((PercentType) fanStateMap.get(channelId)).intValue();
State fanSate = fanStateMap.get(channelId);
if (fanSate != null) {
currentMin = ((PercentType) fanSate).intValue();
}
if (newMax < currentMin) {
updateState(channelId, command);
@ -449,8 +455,9 @@ public class BigAssFanHandler extends BaseThingHandler {
private void adjustMaxLevel(PercentType command) {
int newMin = command.intValue();
int currentMax = PercentType.ZERO.intValue();
if (fanStateMap.get(CHANNEL_LIGHT_LEVEL_MAX) != null) {
currentMax = ((PercentType) fanStateMap.get(CHANNEL_LIGHT_LEVEL_MAX)).intValue();
State fanState = fanStateMap.get(CHANNEL_LIGHT_LEVEL_MAX);
if (fanState != null) {
currentMax = ((PercentType) fanState).intValue();
}
if (newMin > currentMax) {
updateState(CHANNEL_LIGHT_LEVEL_MAX, command);
@ -461,8 +468,9 @@ public class BigAssFanHandler extends BaseThingHandler {
private void adjustMinLevel(PercentType command) {
int newMax = command.intValue();
int currentMin = PercentType.HUNDRED.intValue();
if (fanStateMap.get(CHANNEL_LIGHT_LEVEL_MIN) != null) {
currentMin = ((PercentType) fanStateMap.get(CHANNEL_LIGHT_LEVEL_MIN)).intValue();
State fanState = fanStateMap.get(CHANNEL_LIGHT_LEVEL_MIN);
if (fanState != null) {
currentMin = ((PercentType) fanState).intValue();
}
if (newMax < currentMin) {
updateState(CHANNEL_LIGHT_LEVEL_MIN, command);
@ -483,11 +491,6 @@ public class BigAssFanHandler extends BaseThingHandler {
* Send a command to the fan
*/
private void sendCommand(String mac, String commandFragment) {
if (fanListener == null) {
logger.error("Unable to send message to {} because fanListener object is null!", thing.getUID());
return;
}
StringBuilder sb = new StringBuilder();
sb.append("<").append(mac).append(commandFragment).append(">");
String message = sb.toString();
@ -519,7 +522,7 @@ public class BigAssFanHandler extends BaseThingHandler {
}
}
private void markOfflineWithMessage(ThingStatusDetail statusDetail, String statusMessage) {
private void markOfflineWithMessage(ThingStatusDetail statusDetail, @Nullable String statusMessage) {
// If it's offline with no detail or if it's not offline, mark it offline with detailed status
if ((isOffline() && getDetail() == ThingStatusDetail.NONE) || !isOffline()) {
logger.debug("Changing status of {} from {}({}) to OFFLINE({})", thing.getUID(), getStatus(), getDetail(),
@ -556,9 +559,9 @@ public class BigAssFanHandler extends BaseThingHandler {
// Our own thread pool for the long-running listener job
private ScheduledExecutorService scheduledExecutorService = ThreadPoolManager
.getScheduledPool("bigassfanHandler" + "-" + thing.getUID());
private ScheduledFuture<?> listenerJob;
private @Nullable ScheduledFuture<?> listenerJob;
private final long FAN_LISTENER_DELAY = 2L;
private static final long FAN_LISTENER_DELAY = 2L;
private boolean terminate;
private final Pattern messagePattern = Pattern.compile("[(](.*)");
@ -573,7 +576,7 @@ public class BigAssFanHandler extends BaseThingHandler {
}
};
public FanListener(String ipv4Address) {
public FanListener(@Nullable String ipv4Address) {
conn = new ConnectionManager(ipv4Address);
}
@ -590,12 +593,14 @@ public class BigAssFanHandler extends BaseThingHandler {
}
public void stopFanListener() {
if (listenerJob != null) {
ScheduledFuture<?> localListenerJob = listenerJob;
if (localListenerJob != null) {
logger.debug("Stopping listener for {} at {}", thing.getUID(), ipAddress);
terminate = true;
listenerJob.cancel(true);
listenerJob = null;
localListenerJob.cancel(true);
this.listenerJob = null;
}
conn.cancelConnectionMonitorJob();
conn.disconnect();
}
@ -636,7 +641,7 @@ public class BigAssFanHandler extends BaseThingHandler {
logger.debug("Fan listener thread is exiting for {} at {}", thing.getUID(), ipAddress);
}
private String waitForMessage() throws IOException {
private @Nullable String waitForMessage() throws IOException {
if (!conn.isConnected()) {
if (logger.isTraceEnabled()) {
logger.trace("FanListener for {} can't receive message. No connection to fan", thing.getUID());
@ -650,7 +655,7 @@ public class BigAssFanHandler extends BaseThingHandler {
return readMessage();
}
private String readMessage() {
private @Nullable String readMessage() {
logger.trace("Waiting for message from {} at {}", thing.getUID(), ipAddress);
String message = conn.read();
if (message != null) {
@ -660,7 +665,7 @@ public class BigAssFanHandler extends BaseThingHandler {
return message;
}
private void processMessage(String incomingMessage) {
private void processMessage(@Nullable String incomingMessage) {
if (incomingMessage == null || incomingMessage.isEmpty()) {
return;
}
@ -739,10 +744,7 @@ public class BigAssFanHandler extends BaseThingHandler {
return true;
}
// Didn't match MAC address, check match for label
if (label.equalsIgnoreCase(idFromDevice)) {
return true;
}
return false;
return label.equalsIgnoreCase(idFromDevice);
}
private void updateFanPower(String[] messageParts) {
@ -1003,27 +1005,28 @@ public class BigAssFanHandler extends BaseThingHandler {
private boolean deviceIsConnected;
private InetAddress ifAddress;
private Socket fanSocket;
private Scanner fanScanner;
private DataOutputStream fanWriter;
private final int SOCKET_CONNECT_TIMEOUT = 1500;
private @Nullable InetAddress ifAddress;
private @Nullable Socket fanSocket;
private @Nullable Scanner fanScanner;
private @Nullable DataOutputStream fanWriter;
private static final int SOCKET_CONNECT_TIMEOUT = 1500;
ScheduledFuture<?> connectionMonitorJob;
private final long CONNECTION_MONITOR_FREQ = 120L;
private final long CONNECTION_MONITOR_DELAY = 30L;
private @Nullable ScheduledFuture<?> connectionMonitorJob;
private static final long CONNECTION_MONITOR_FREQ = 120L;
private static final long CONNECTION_MONITOR_DELAY = 30L;
Runnable connectionMonitorRunnable = () -> {
logger.trace("Performing connection check for {} at IP {}", thing.getUID(), ipAddress);
checkConnection();
};
public ConnectionManager(String ipv4Address) {
public ConnectionManager(@Nullable String ipv4Address) {
deviceIsConnected = false;
try {
ifAddress = InetAddress.getByName(ipv4Address);
logger.debug("Handler for {} using address {} on network interface {}", thing.getUID(),
ifAddress.getHostAddress(), NetworkInterface.getByInetAddress(ifAddress).getName());
logger.debug("Handler for {} using address {} on network interface {}", thing.getUID(), ipv4Address,
NetworkInterface.getByInetAddress(ifAddress).getName());
} catch (UnknownHostException e) {
logger.warn("Handler for {} got UnknownHostException getting local IPv4 net interface: {}",
thing.getUID(), e.getMessage(), e);
@ -1045,13 +1048,15 @@ public class BigAssFanHandler extends BaseThingHandler {
}
logger.trace("Connecting to {} at {}", thing.getUID(), ipAddress);
Socket localFanSocket = new Socket();
fanSocket = localFanSocket;
// Open socket
try {
fanSocket = new Socket();
fanSocket.bind(new InetSocketAddress(ifAddress, 0));
fanSocket.connect(new InetSocketAddress(ipAddress, BAF_PORT), SOCKET_CONNECT_TIMEOUT);
} catch (IOException e) {
logger.debug("IOException connecting to {} at {}: {}", thing.getUID(), ipAddress, e.getMessage());
localFanSocket.bind(new InetSocketAddress(ifAddress, 0));
localFanSocket.connect(new InetSocketAddress(ipAddress, BAF_PORT), SOCKET_CONNECT_TIMEOUT);
} catch (SecurityException | IllegalArgumentException | IOException e) {
logger.debug("Unexpected exception connecting to {} at {}: {}", thing.getUID(), ipAddress,
e.getMessage(), e);
markOfflineWithMessage(ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
disconnect();
return;
@ -1059,12 +1064,12 @@ public class BigAssFanHandler extends BaseThingHandler {
// Create streams
try {
fanWriter = new DataOutputStream(fanSocket.getOutputStream());
fanScanner = new Scanner(fanSocket.getInputStream());
fanScanner.useDelimiter("[)]");
} catch (IOException e) {
logger.warn("IOException getting streams for {} at {}: {}", thing.getUID(), ipAddress, e.getMessage(),
e);
fanWriter = new DataOutputStream(localFanSocket.getOutputStream());
Scanner localFanScanner = new Scanner(localFanSocket.getInputStream());
localFanScanner.useDelimiter("[)]");
fanScanner = localFanScanner;
} catch (IllegalBlockingModeException | IOException e) {
logger.warn("Exception getting streams for {} at {}: {}", thing.getUID(), ipAddress, e.getMessage(), e);
markOfflineWithMessage(ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
disconnect();
return;
@ -1081,17 +1086,22 @@ public class BigAssFanHandler extends BaseThingHandler {
logger.debug("Disconnecting from {} at {}", thing.getUID(), ipAddress);
try {
if (fanWriter != null) {
fanWriter.close();
DataOutputStream localFanWriter = fanWriter;
if (localFanWriter != null) {
localFanWriter.close();
fanWriter = null;
}
if (fanScanner != null) {
fanScanner.close();
Scanner localFanScanner = fanScanner;
if (localFanScanner != null) {
localFanScanner.close();
}
if (fanSocket != null) {
fanSocket.close();
Socket localFanSocket = fanSocket;
if (localFanSocket != null) {
localFanSocket.close();
fanSocket = null;
}
} catch (IOException e) {
logger.warn("IOException closing connection to {} at {}: {}", thing.getUID(), ipAddress, e.getMessage(),
} catch (IllegalStateException | IOException e) {
logger.warn("Exception closing connection to {} at {}: {}", thing.getUID(), ipAddress, e.getMessage(),
e);
}
deviceIsConnected = false;
@ -1101,15 +1111,18 @@ public class BigAssFanHandler extends BaseThingHandler {
markOffline();
}
public String read() {
public @Nullable String read() {
if (fanScanner == null) {
logger.warn("Scanner for {} is null when trying to scan from {}!", thing.getUID(), ipAddress);
return null;
}
String nextToken;
String nextToken = null;
try {
nextToken = fanScanner.next();
Scanner localFanScanner = fanScanner;
if (localFanScanner != null) {
nextToken = localFanScanner.next();
}
} catch (NoSuchElementException e) {
logger.debug("Scanner for {} threw NoSuchElementException; stream possibly closed", thing.getUID());
// Force a reconnect to the device
@ -1126,11 +1139,13 @@ public class BigAssFanHandler extends BaseThingHandler {
}
public void write(byte[] buffer) throws IOException {
if (fanWriter == null) {
DataOutputStream localFanWriter = fanWriter;
if (localFanWriter == null) {
logger.warn("fanWriter for {} is null when trying to write to {}!!!", thing.getUID(), ipAddress);
return;
} else {
localFanWriter.write(buffer, 0, buffer.length);
}
fanWriter.write(buffer, 0, buffer.length);
}
private boolean isConnected() {
@ -1140,7 +1155,7 @@ public class BigAssFanHandler extends BaseThingHandler {
/*
* Periodically validate the command connection to the device by executing a getversion command.
*/
private void scheduleConnectionMonitorJob() {
private synchronized void scheduleConnectionMonitorJob() {
if (connectionMonitorJob == null) {
logger.debug("Starting connection monitor job in {} seconds for {} at {}", CONNECTION_MONITOR_DELAY,
thing.getUID(), ipAddress);
@ -1150,9 +1165,10 @@ public class BigAssFanHandler extends BaseThingHandler {
}
private void cancelConnectionMonitorJob() {
if (connectionMonitorJob != null) {
ScheduledFuture<?> localConnectionMonitorJob = connectionMonitorJob;
if (localConnectionMonitorJob != null) {
logger.debug("Canceling connection monitor job for {} at {}", thing.getUID(), ipAddress);
connectionMonitorJob.cancel(true);
localConnectionMonitorJob.cancel(true);
connectionMonitorJob = null;
}
}

View File

@ -12,6 +12,7 @@
*/
package org.openhab.binding.bigassfan.internal.utils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.PercentType;
/**
@ -21,6 +22,7 @@ import org.openhab.core.library.types.PercentType;
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class BigAssFanConverter {
/*
* Conversion factor for fan range (0-7) to dimmer range (0-100).