[airvisualnode] Add null annotations (#13895)

* Add null annotation

Signed-off-by: Leo Siepel <leosiepel@gmail.com>
This commit is contained in:
lsiepel 2022-12-26 17:00:53 +01:00 committed by GitHub
parent 88c0b720c6
commit 1c5b794145
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 114 additions and 90 deletions

View File

@ -14,6 +14,7 @@ package org.openhab.binding.airvisualnode.internal;
import static org.openhab.binding.airvisualnode.internal.AirVisualNodeBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.airvisualnode.internal.handler.AirVisualNodeHandler;
import org.openhab.core.thing.Thing;
@ -29,6 +30,7 @@ import org.osgi.service.component.annotations.Component;
*
* @author Victor Antonovich - Initial contribution
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.airvisualnode")
public class AirVisualNodeHandlerFactory extends BaseThingHandlerFactory {

View File

@ -12,22 +12,25 @@
*/
package org.openhab.binding.airvisualnode.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Configuration for AirVisual Node.
*
* @author Victor Antonovich - Initial contribution
*/
@NonNullByDefault
public class AirVisualNodeConfig {
public static final String ADDRESS = "address";
public String address;
public String address = "";
public String username;
public String username = "";
public String password;
public String password = "";
public String share;
public String share = "";
public long refresh;

View File

@ -20,6 +20,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.airvisualnode.internal.AirVisualNodeBindingConstants;
import org.openhab.binding.airvisualnode.internal.config.AirVisualNodeConfig;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
@ -39,16 +41,18 @@ import jcifs.smb.SmbFile;
*
* @author Victor Antonovich - Initial contribution
*/
@NonNullByDefault
@Component(service = DiscoveryService.class)
public class AirVisualNodeDiscoveryService extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(AirVisualNodeDiscoveryService.class);
private static final int REFRESH_MINUTES = 5;
public static final String AVISUAL_WORKGROUP_NAME = "MSHOME";
private static final Pattern AVISUAL_NAME_PATTERN = Pattern.compile("^AVISUAL-([^/]+)$");
private ScheduledFuture<?> backgroundDiscoveryFuture;
private @Nullable ScheduledFuture<?> backgroundDiscoveryFuture;
public AirVisualNodeDiscoveryService() {
super(Collections.singleton(AirVisualNodeBindingConstants.THING_TYPE_AVNODE), 600, true);
@ -63,19 +67,20 @@ public class AirVisualNodeDiscoveryService extends AbstractDiscoveryService {
@Override
protected void startBackgroundDiscovery() {
logger.debug("Starting background discovery");
backgroundDiscoveryFuture = scheduler.scheduleWithFixedDelay(this::scan, 0, 5, TimeUnit.MINUTES);
ScheduledFuture<?> localDiscoveryFuture = backgroundDiscoveryFuture;
if (localDiscoveryFuture == null || localDiscoveryFuture.isCancelled()) {
backgroundDiscoveryFuture = scheduler.scheduleWithFixedDelay(this::scan, 0, REFRESH_MINUTES,
TimeUnit.MINUTES);
}
}
@Override
protected void stopBackgroundDiscovery() {
logger.debug("Stopping background discovery");
cancelBackgroundDiscoveryFuture();
super.stopBackgroundDiscovery();
}
private void cancelBackgroundDiscoveryFuture() {
if (backgroundDiscoveryFuture != null && !backgroundDiscoveryFuture.isDone()) {
backgroundDiscoveryFuture.cancel(true);
ScheduledFuture<?> localDiscoveryFuture = backgroundDiscoveryFuture;
if (localDiscoveryFuture != null) {
localDiscoveryFuture.cancel(true);
backgroundDiscoveryFuture = null;
}
}
@ -87,7 +92,7 @@ public class AirVisualNodeDiscoveryService extends AbstractDiscoveryService {
String workgroupUrl = "smb://" + AVISUAL_WORKGROUP_NAME + "/";
workgroupMembers = new SmbFile(workgroupUrl).listFiles();
} catch (IOException e) {
// Can't get workgroup member list
logger.debug("IOException while trying to get workgroup member list", e);
return;
}
@ -105,6 +110,10 @@ public class AirVisualNodeDiscoveryService extends AbstractDiscoveryService {
// Extract the Node serial number from device name
String nodeSerialNumber = m.group(1);
if (nodeSerialNumber != null) {
logger.debug("Extracting the Node serial number failed");
return;
}
// The Node Thing UID is serial number converted to lower case
ThingUID thingUID = new ThingUID(AirVisualNodeBindingConstants.THING_TYPE_AVNODE,
nodeSerialNumber.toLowerCase());
@ -119,14 +128,19 @@ public class AirVisualNodeDiscoveryService extends AbstractDiscoveryService {
// Create discovery result
String nodeAddress = nodeNbtAddress.getInetAddress().getHostAddress();
DiscoveryResult result = DiscoveryResultBuilder.create(thingUID)
.withProperty(AirVisualNodeConfig.ADDRESS, nodeAddress)
.withRepresentationProperty(AirVisualNodeConfig.ADDRESS)
.withLabel("AirVisual Node (" + nodeSerialNumber + ")").build();
thingDiscovered(result);
if (nodeAddress != null) {
DiscoveryResult result = DiscoveryResultBuilder.create(thingUID)
.withProperty(AirVisualNodeConfig.ADDRESS, nodeAddress)
.withRepresentationProperty(AirVisualNodeConfig.ADDRESS)
.withLabel("AirVisual Node (" + nodeSerialNumber + ")").build();
thingDiscovered(result);
} else {
logger.debug("Getting the node address from the host failed");
}
} catch (UnknownHostException e) {
logger.debug("The Node address resolving failed ", e);
}
}
}
}

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json;
package org.openhab.binding.airvisualnode.internal.dto;
/**
* Date and time / timestamp data.

View File

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

View File

@ -10,11 +10,11 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json;
package org.openhab.binding.airvisualnode.internal.dto;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.airvisualnode.internal.json.airvisual.Settings;
import org.openhab.binding.airvisualnode.internal.json.airvisual.Status;
import org.openhab.binding.airvisualnode.internal.dto.airvisual.Settings;
import org.openhab.binding.airvisualnode.internal.dto.airvisual.Status;
/**
* Interface for AirVisual and AirVisual Pro models

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json;
package org.openhab.binding.airvisualnode.internal.dto;
/**
* Power saving time data.

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json;
package org.openhab.binding.airvisualnode.internal.dto;
/**
* Power saving time slot data.

View File

@ -10,9 +10,9 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json.airvisual;
package org.openhab.binding.airvisualnode.internal.dto.airvisual;
import org.openhab.binding.airvisualnode.internal.json.MeasurementsInterface;
import org.openhab.binding.airvisualnode.internal.dto.MeasurementsInterface;
import com.google.gson.annotations.SerializedName;

View File

@ -10,17 +10,19 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json.airvisual;
package org.openhab.binding.airvisualnode.internal.dto.airvisual;
import org.openhab.binding.airvisualnode.internal.json.DateAndTime;
import org.openhab.binding.airvisualnode.internal.json.MeasurementsInterface;
import org.openhab.binding.airvisualnode.internal.json.NodeDataInterface;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.airvisualnode.internal.dto.DateAndTime;
import org.openhab.binding.airvisualnode.internal.dto.MeasurementsInterface;
import org.openhab.binding.airvisualnode.internal.dto.NodeDataInterface;
/**
* Top level object for AirVisual Node JSON data.
*
* @author Victor Antonovich - Initial contribution
*/
@NonNullByDefault
public class NodeData implements NodeDataInterface {
private DateAndTime dateAndTime;

View File

@ -10,12 +10,12 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json.airvisual;
package org.openhab.binding.airvisualnode.internal.dto.airvisual;
import java.util.List;
import org.openhab.binding.airvisualnode.internal.json.PowerSavingTime;
import org.openhab.binding.airvisualnode.internal.json.PowerSavingTimeSlot;
import org.openhab.binding.airvisualnode.internal.dto.PowerSavingTime;
import org.openhab.binding.airvisualnode.internal.dto.PowerSavingTimeSlot;
import com.google.gson.annotations.SerializedName;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json.airvisual;
package org.openhab.binding.airvisualnode.internal.dto.airvisual;
/**
* Settings data.

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json.airvisual;
package org.openhab.binding.airvisualnode.internal.dto.airvisual;
/**
* Status data.

View File

@ -10,9 +10,9 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json.airvisualpro;
package org.openhab.binding.airvisualnode.internal.dto.airvisualpro;
import org.openhab.binding.airvisualnode.internal.json.MeasurementsInterface;
import org.openhab.binding.airvisualnode.internal.dto.MeasurementsInterface;
import com.google.gson.annotations.SerializedName;
@ -54,7 +54,6 @@ public class Measurements implements MeasurementsInterface {
public Measurements(int co2Ppm, int humidityRH, int pm25AQICN, int pm25AQIUS, float pm01Ugm3, float pm10Ugm3,
float pm25Ugm3, float temperatureC, float temperatureF, int vocPpb) {
this.co2Ppm = co2Ppm;
this.humidityRH = humidityRH;
this.pm25AQICN = pm25AQICN;

View File

@ -10,12 +10,12 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json.airvisualpro;
package org.openhab.binding.airvisualnode.internal.dto.airvisualpro;
import java.util.List;
import org.openhab.binding.airvisualnode.internal.json.PowerSavingTime;
import org.openhab.binding.airvisualnode.internal.json.PowerSavingTimeSlot;
import org.openhab.binding.airvisualnode.internal.dto.PowerSavingTime;
import org.openhab.binding.airvisualnode.internal.dto.PowerSavingTimeSlot;
import com.google.gson.annotations.SerializedName;

View File

@ -10,21 +10,23 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json.airvisualpro;
package org.openhab.binding.airvisualnode.internal.dto.airvisualpro;
import java.util.List;
import org.openhab.binding.airvisualnode.internal.json.DateAndTime;
import org.openhab.binding.airvisualnode.internal.json.MeasurementsInterface;
import org.openhab.binding.airvisualnode.internal.json.NodeDataInterface;
import org.openhab.binding.airvisualnode.internal.json.airvisual.Settings;
import org.openhab.binding.airvisualnode.internal.json.airvisual.Status;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.airvisualnode.internal.dto.DateAndTime;
import org.openhab.binding.airvisualnode.internal.dto.MeasurementsInterface;
import org.openhab.binding.airvisualnode.internal.dto.NodeDataInterface;
import org.openhab.binding.airvisualnode.internal.dto.airvisual.Settings;
import org.openhab.binding.airvisualnode.internal.dto.airvisual.Status;
/**
* Top level object for AirVisual Node JSON data.
*
* @author Victor Antonovich - Initial contribution
*/
@NonNullByDefault
public class ProNodeData implements NodeDataInterface {
private DateAndTime dateAndTime;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json.airvisualpro;
package org.openhab.binding.airvisualnode.internal.dto.airvisualpro;
/**
* Sensor Usage/Life data

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json.airvisualpro;
package org.openhab.binding.airvisualnode.internal.dto.airvisualpro;
/**
* Sensor Operating Mode

View File

@ -10,9 +10,9 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json.airvisualpro;
package org.openhab.binding.airvisualnode.internal.dto.airvisualpro;
import org.openhab.binding.airvisualnode.internal.json.airvisual.PowerSaving;
import org.openhab.binding.airvisualnode.internal.dto.airvisual.PowerSaving;
/**
* Settings data.
@ -41,7 +41,6 @@ public class Settings {
boolean isIndoor, boolean isLcdOn, boolean isNetworkTime, boolean isTemperatureCelsius, String language,
int lcdBrightness, String nodeName, PowerSaving powerSaving, SensorMode sensorMode, String speedUnit,
String timezone) {
this.followMode = followMode;
this.followedStation = followedStation;
this.isAqiUsa = isAqiUsa;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.airvisualnode.internal.json.airvisualpro;
package org.openhab.binding.airvisualnode.internal.dto.airvisualpro;
/**
* Status data.

View File

@ -35,11 +35,13 @@ import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.airvisualnode.internal.config.AirVisualNodeConfig;
import org.openhab.binding.airvisualnode.internal.json.MeasurementsInterface;
import org.openhab.binding.airvisualnode.internal.json.NodeDataInterface;
import org.openhab.binding.airvisualnode.internal.json.airvisual.NodeData;
import org.openhab.binding.airvisualnode.internal.json.airvisualpro.ProNodeData;
import org.openhab.binding.airvisualnode.internal.dto.MeasurementsInterface;
import org.openhab.binding.airvisualnode.internal.dto.NodeDataInterface;
import org.openhab.binding.airvisualnode.internal.dto.airvisual.NodeData;
import org.openhab.binding.airvisualnode.internal.dto.airvisualpro.ProNodeData;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType;
@ -71,29 +73,23 @@ import jcifs.smb.SmbFileInputStream;
*
* @author Victor Antonovich - Initial contribution
*/
@NonNullByDefault
public class AirVisualNodeHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(AirVisualNodeHandler.class);
public static final String NODE_JSON_FILE = "latest_config_measurements.json";
private static final long DELAY_IN_MS = 500;
private final Gson gson;
private ScheduledFuture<?> pollFuture;
private @Nullable ScheduledFuture<?> pollFuture;
private long refreshInterval;
private String nodeAddress;
private String nodeUsername;
private String nodePassword;
private String nodeShareName;
private NodeDataInterface nodeData;
private boolean isProVersion;
private String nodeAddress = "";
private String nodeUsername = "";
private String nodePassword = "";
private String nodeShareName = "";
private @Nullable NodeDataInterface nodeData;
private boolean isProVersion = false;
public AirVisualNodeHandler(Thing thing) {
super(thing);
@ -106,22 +102,19 @@ public class AirVisualNodeHandler extends BaseThingHandler {
AirVisualNodeConfig config = getConfigAs(AirVisualNodeConfig.class);
if (config.address == null) {
if (config.address.isBlank()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Node address must be set");
return;
}
this.nodeAddress = config.address;
this.nodeUsername = config.username;
if (config.password == null) {
if (config.password.isBlank()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Node password must be set");
return;
}
this.nodeAddress = config.address;
this.nodeUsername = config.username;
this.nodePassword = config.password;
this.nodeShareName = config.share;
this.refreshInterval = config.refresh * 1000L;
try {
@ -141,10 +134,17 @@ public class AirVisualNodeHandler extends BaseThingHandler {
private void removeProChannels() {
List<Channel> channels = new ArrayList<>(getThing().getChannels());
channels.removeIf(channel -> channel.getLabel().equals("PM0.1") || channel.getLabel().equals("PM10"));
channels.removeIf(channel -> isProChannel(channel.getLabel()));
replaceChannels(channels);
}
private boolean isProChannel(@Nullable String channelLabel) {
if (channelLabel == null || channelLabel.isBlank()) {
return false;
}
return "PM0.1".equals(channelLabel) || "PM10".equals(channelLabel);
}
private void replaceChannels(List<Channel> channels) {
ThingBuilder thingBuilder = editThing();
thingBuilder.withChannels(channels);
@ -173,14 +173,15 @@ public class AirVisualNodeHandler extends BaseThingHandler {
}
private synchronized void stopPoll() {
if (pollFuture != null && !pollFuture.isCancelled()) {
pollFuture.cancel(false);
ScheduledFuture<?> localFuture = pollFuture;
if (localFuture != null) {
localFuture.cancel(false);
}
}
private synchronized void schedulePoll() {
logger.debug("Scheduling poll for 500ms out, then every {} ms", refreshInterval);
pollFuture = scheduler.scheduleWithFixedDelay(this::poll, 500, refreshInterval, TimeUnit.MILLISECONDS);
logger.debug("Scheduling poll for {}}ms out, then every {} ms", DELAY_IN_MS, refreshInterval);
pollFuture = scheduler.scheduleWithFixedDelay(this::poll, DELAY_IN_MS, refreshInterval, TimeUnit.MILLISECONDS);
}
private void poll() {
@ -203,8 +204,9 @@ public class AirVisualNodeHandler extends BaseThingHandler {
} else {
currentNodeData = gson.fromJson(jsonData, NodeData.class);
}
if (nodeData == null || currentNodeData.getStatus().getDatetime() > nodeData.getStatus().getDatetime()) {
NodeDataInterface localNodeDate = nodeData;
if (localNodeDate == null
|| currentNodeData.getStatus().getDatetime() > localNodeDate.getStatus().getDatetime()) {
nodeData = currentNodeData;
// Update all channels from the updated Node data
for (Channel channel : getThing().getChannels()) {
@ -222,8 +224,9 @@ public class AirVisualNodeHandler extends BaseThingHandler {
}
private void updateChannel(String channelId, boolean force) {
if (nodeData != null && (force || isLinked(channelId))) {
State state = getChannelState(channelId, nodeData);
NodeDataInterface localnodeData = nodeData;
if (localnodeData != null && (force || isLinked(channelId))) {
State state = getChannelState(channelId, localnodeData);
logger.debug("Update channel {} with state {}", channelId, state);
updateState(channelId, state);
}