added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.airvisualnode-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-airvisualnode" description="AirVisual Node Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle dependency="true" start-level="80">mvn:org.samba.jcifs/jcifs/1.3.17</bundle>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.airvisualnode/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,59 @@
/**
* 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.airvisualnode.internal;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.DefaultSystemChannelTypeProvider;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link AirVisualNodeBindingConstants} class defines common constants, which are
* used across the whole binding.
*
* @author Victor Antonovich - Initial contribution
*/
@NonNullByDefault
public class AirVisualNodeBindingConstants {
public static final String BINDING_ID = "airvisualnode";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_AVNODE = new ThingTypeUID(BINDING_ID, "avnode");
// List of all Channel ids
public static final String CHANNEL_CO2 = "co2";
public static final String CHANNEL_HUMIDITY = "humidity";
public static final String CHANNEL_AQI_US = "aqi";
public static final String CHANNEL_PM_25 = "pm_25";
public static final String CHANNEL_TEMP_CELSIUS = "temperature";
public static final String CHANNEL_TIMESTAMP = "timestamp";
public static final String CHANNEL_USED_MEMORY = "used_memory";
public static final String CHANNEL_BATTERY_LEVEL = DefaultSystemChannelTypeProvider.SYSTEM_CHANNEL_BATTERY_LEVEL
.getUID().getId();
public static final String CHANNEL_WIFI_STRENGTH = DefaultSystemChannelTypeProvider.SYSTEM_CHANNEL_SIGNAL_STRENGTH
.getUID().getId();
// List of all supported Thing UIDs
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
.unmodifiableSet(new HashSet<>(Arrays.asList(THING_TYPE_AVNODE)));
// List of all supported Channel ids
public static final Set<String> SUPPORTED_CHANNEL_IDS = Collections.unmodifiableSet(new HashSet<>(
Arrays.asList(CHANNEL_CO2, CHANNEL_HUMIDITY, CHANNEL_AQI_US, CHANNEL_PM_25, CHANNEL_TEMP_CELSIUS,
CHANNEL_BATTERY_LEVEL, CHANNEL_WIFI_STRENGTH, CHANNEL_TIMESTAMP, CHANNEL_USED_MEMORY)));
}

View File

@@ -0,0 +1,50 @@
/**
* 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.airvisualnode.internal;
import static org.openhab.binding.airvisualnode.internal.AirVisualNodeBindingConstants.*;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.airvisualnode.internal.handler.AirVisualNodeHandler;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.service.component.annotations.Component;
/**
* The {@link AirVisualNodeHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Victor Antonovich - Initial contribution
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.airvisualnode")
public class AirVisualNodeHandlerFactory extends BaseThingHandlerFactory {
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(THING_TYPE_AVNODE)) {
return new AirVisualNodeHandler(thing);
}
return null;
}
}

View File

@@ -0,0 +1,33 @@
/**
* 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.airvisualnode.internal.config;
/**
* Configuration for AirVisual Node.
*
* @author Victor Antonovich - Initial contribution
*/
public class AirVisualNodeConfig {
public static final String ADDRESS = "address";
public String address;
public String username;
public String password;
public String share;
public long refresh;
}

View File

@@ -0,0 +1,132 @@
/**
* 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.airvisualnode.internal.discovery;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openhab.binding.airvisualnode.internal.AirVisualNodeBindingConstants;
import org.openhab.binding.airvisualnode.internal.config.AirVisualNodeConfig;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.ThingUID;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jcifs.netbios.NbtAddress;
import jcifs.smb.SmbFile;
/**
* Autodiscovery for AirVisual Node by searching for a host advertised with the NetBIOS name 'AVISUAL-<SerialNumber>'.
*
* @author Victor Antonovich - Initial contribution
*/
@Component(service = DiscoveryService.class, immediate = true)
public class AirVisualNodeDiscoveryService extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(AirVisualNodeDiscoveryService.class);
public static final String AVISUAL_WORKGROUP_NAME = "MSHOME";
private static final Pattern AVISUAL_NAME_PATTERN = Pattern.compile("^AVISUAL-([^/]+)$");
private ScheduledFuture<?> backgroundDiscoveryFuture;
public AirVisualNodeDiscoveryService() {
super(Collections.singleton(AirVisualNodeBindingConstants.THING_TYPE_AVNODE), 600, true);
}
@Override
protected void startScan() {
logger.debug("Starting scan");
scheduler.execute(this::scan);
}
@Override
protected void startBackgroundDiscovery() {
logger.debug("Starting background discovery");
backgroundDiscoveryFuture = scheduler.scheduleWithFixedDelay(this::scan, 0, 5, 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);
backgroundDiscoveryFuture = null;
}
}
private void scan() {
// Get all workgroup members
SmbFile[] workgroupMembers;
try {
String workgroupUrl = "smb://" + AVISUAL_WORKGROUP_NAME + "/";
workgroupMembers = new SmbFile(workgroupUrl).listFiles();
} catch (IOException e) {
// Can't get workgroup member list
return;
}
// Check found workgroup members for the Node devices
for (SmbFile s : workgroupMembers) {
String serverName = s.getServer();
// Check workgroup member for the Node device name match
Matcher m = AVISUAL_NAME_PATTERN.matcher(serverName);
if (!m.find()) {
// Workgroup member server name doesn't match the Node device name pattern
continue;
}
// Extract the Node serial number from device name
String nodeSerialNumber = m.group(1);
// The Node Thing UID is serial number converted to lower case
ThingUID thingUID = new ThingUID(AirVisualNodeBindingConstants.THING_TYPE_AVNODE,
nodeSerialNumber.toLowerCase());
try {
// Get the Node address by name
NbtAddress nodeNbtAddress = NbtAddress.getByName(serverName);
if (nodeNbtAddress == null) {
// The Node address not found by some reason, skip it
continue;
}
// 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);
} catch (UnknownHostException e) {
logger.debug("The Node address resolving failed ", e);
}
}
}
}

View File

@@ -0,0 +1,243 @@
/**
* 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.airvisualnode.internal.handler;
import static org.openhab.binding.airvisualnode.internal.AirVisualNodeBindingConstants.*;
import static org.openhab.core.library.unit.MetricPrefix.MICRO;
import static org.openhab.core.library.unit.SIUnits.CELSIUS;
import static org.openhab.core.library.unit.SIUnits.CUBIC_METRE;
import static org.openhab.core.library.unit.SIUnits.GRAM;
import static org.openhab.core.library.unit.SmartHomeUnits.ONE;
import static org.openhab.core.library.unit.SmartHomeUnits.PARTS_PER_MILLION;
import static org.openhab.core.library.unit.SmartHomeUnits.PERCENT;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.zone.ZoneRules;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.openhab.binding.airvisualnode.internal.config.AirVisualNodeConfig;
import org.openhab.binding.airvisualnode.internal.json.NodeData;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType;
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.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileInputStream;
/**
* The {@link AirVisualNodeHandler} is responsible for handling commands, which are
* sent to one of the channels.
*
* @author Victor Antonovich - Initial contribution
*/
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 final Gson gson;
private ScheduledFuture<?> pollFuture;
private long refreshInterval;
private String nodeAddress;
private String nodeUsername;
private String nodePassword;
private String nodeShareName;
private NodeData nodeData;
public AirVisualNodeHandler(Thing thing) {
super(thing);
gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
}
@Override
public void initialize() {
logger.debug("Initializing AirVisual Node handler");
AirVisualNodeConfig config = getConfigAs(AirVisualNodeConfig.class);
if (config.address == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Node address must be set");
return;
}
this.nodeAddress = config.address;
this.nodeUsername = config.username;
if (config.password == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Node password must be set");
return;
}
this.nodePassword = config.password;
this.nodeShareName = config.share;
this.refreshInterval = config.refresh * 1000L;
schedulePoll();
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
updateChannel(channelUID.getId(), true);
} else {
logger.debug("Can not handle command '{}'", command);
}
}
@Override
public void handleRemoval() {
super.handleRemoval();
stopPoll();
}
@Override
public void dispose() {
super.dispose();
stopPoll();
}
private synchronized void stopPoll() {
if (pollFuture != null && !pollFuture.isCancelled()) {
pollFuture.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);
}
private void poll() {
try {
logger.debug("Polling for state");
pollNode();
updateStatus(ThingStatus.ONLINE);
} catch (IOException e) {
logger.debug("Could not connect to Node", e);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
}
private void pollNode() throws IOException {
String jsonData = getNodeJsonData();
NodeData currentNodeData = gson.fromJson(jsonData, NodeData.class);
if (nodeData == null || currentNodeData.getStatus().getDatetime() > nodeData.getStatus().getDatetime()) {
nodeData = currentNodeData;
// Update all channels from the updated Node data
for (Channel channel : getThing().getChannels()) {
updateChannel(channel.getUID().getId(), false);
}
}
}
private String getNodeJsonData() throws IOException {
String url = "smb://" + nodeAddress + "/" + nodeShareName + "/" + NODE_JSON_FILE;
NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication(null, nodeUsername, nodePassword);
try (SmbFileInputStream in = new SmbFileInputStream(new SmbFile(url, auth))) {
return IOUtils.toString(in, StandardCharsets.UTF_8.name());
}
}
private void updateChannel(String channelId, boolean force) {
if (nodeData != null && (force || isLinked(channelId))) {
State state = getChannelState(channelId, nodeData);
logger.debug("Update channel {} with state {}", channelId, state);
updateState(channelId, state);
}
}
private State getChannelState(String channelId, NodeData nodeData) {
State state = UnDefType.UNDEF;
// Handle system channel IDs separately, because 'switch/case' expressions must be constant expressions
if (CHANNEL_BATTERY_LEVEL.equals(channelId)) {
state = new DecimalType(BigDecimal.valueOf(nodeData.getStatus().getBattery()).longValue());
} else if (CHANNEL_WIFI_STRENGTH.equals(channelId)) {
state = new DecimalType(
BigDecimal.valueOf(Math.max(0, nodeData.getStatus().getWifiStrength() - 1)).longValue());
} else {
// Handle binding-specific channel IDs
switch (channelId) {
case CHANNEL_CO2:
state = new QuantityType<>(nodeData.getMeasurements().getCo2Ppm(), PARTS_PER_MILLION);
break;
case CHANNEL_HUMIDITY:
state = new QuantityType<>(nodeData.getMeasurements().getHumidityRH(), PERCENT);
break;
case CHANNEL_AQI_US:
state = new QuantityType<>(nodeData.getMeasurements().getPm25AQIUS(), ONE);
break;
case CHANNEL_PM_25:
// PM2.5 is in ug/m3
state = new QuantityType<>(nodeData.getMeasurements().getPm25Ugm3(),
MICRO(GRAM).divide(CUBIC_METRE));
break;
case CHANNEL_TEMP_CELSIUS:
state = new QuantityType<>(nodeData.getMeasurements().getTemperatureC(), CELSIUS);
break;
case CHANNEL_TIMESTAMP:
// It seem the Node timestamp is Unix timestamp converted from UTC time plus timezone offset.
// Not sure about DST though, but it's best guess at now
Instant instant = Instant.ofEpochMilli(nodeData.getStatus().getDatetime() * 1000L);
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"));
ZoneId zoneId = ZoneId.of(nodeData.getSettings().getTimezone());
ZoneRules zoneRules = zoneId.getRules();
zonedDateTime.minus(Duration.ofSeconds(zoneRules.getOffset(instant).getTotalSeconds()));
if (zoneRules.isDaylightSavings(instant)) {
zonedDateTime.minus(Duration.ofSeconds(zoneRules.getDaylightSavings(instant).getSeconds()));
}
state = new DateTimeType(zonedDateTime);
break;
case CHANNEL_USED_MEMORY:
state = new DecimalType(BigDecimal.valueOf(nodeData.getStatus().getUsedMemory()).longValue());
break;
}
}
return state;
}
}

View File

@@ -0,0 +1,55 @@
/**
* 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.airvisualnode.internal.json;
/**
* Date and time / timestamp data.
*
* @author Victor Antonovich - Initial contribution
*/
public class DateAndTime {
private String date;
private String time;
private String timestamp;
public DateAndTime(String date, String time, String timestamp) {
this.date = date;
this.time = time;
this.timestamp = timestamp;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
}

View File

@@ -0,0 +1,113 @@
/**
* 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.airvisualnode.internal.json;
import com.google.gson.annotations.SerializedName;
/**
* Measurements data.
*
* @author Victor Antonovich - Initial contribution
*/
public class Measurements {
private int co2Ppm;
@SerializedName("humidity_RH")
private int humidityRH;
@SerializedName("pm25_AQICN")
private int pm25AQICN;
@SerializedName("pm25_AQIUS")
private int pm25AQIUS;
private float pm25Ugm3;
@SerializedName("temperature_C")
private float temperatureC;
@SerializedName("temperature_F")
private float temperatureF;
private int vocPpb;
public Measurements(int co2Ppm, int humidityRH, int pm25AQICN, int pm25AQIUS, float pm25Ugm3, float temperatureC,
float temperatureF, int vocPpb) {
this.co2Ppm = co2Ppm;
this.humidityRH = humidityRH;
this.pm25AQICN = pm25AQICN;
this.pm25AQIUS = pm25AQIUS;
this.pm25Ugm3 = pm25Ugm3;
this.temperatureC = temperatureC;
this.temperatureF = temperatureF;
this.vocPpb = vocPpb;
}
public int getCo2Ppm() {
return co2Ppm;
}
public void setCo2Ppm(int co2Ppm) {
this.co2Ppm = co2Ppm;
}
public int getHumidityRH() {
return humidityRH;
}
public void setHumidityRH(int humidityRH) {
this.humidityRH = humidityRH;
}
public int getPm25AQICN() {
return pm25AQICN;
}
public void setPm25AQICN(int pm25AQICN) {
this.pm25AQICN = pm25AQICN;
}
public int getPm25AQIUS() {
return pm25AQIUS;
}
public void setPm25AQIUS(int pm25AQIUS) {
this.pm25AQIUS = pm25AQIUS;
}
public float getPm25Ugm3() {
return pm25Ugm3;
}
public void setPm25Ugm3(float pm25Ugm3) {
this.pm25Ugm3 = pm25Ugm3;
}
public float getTemperatureC() {
return temperatureC;
}
public void setTemperatureC(float temperatureC) {
this.temperatureC = temperatureC;
}
public float getTemperatureF() {
return temperatureF;
}
public void setTemperatureF(float temperatureF) {
this.temperatureF = temperatureF;
}
public int getVocPpb() {
return vocPpb;
}
public void setVocPpb(int vocPpb) {
this.vocPpb = vocPpb;
}
}

View File

@@ -0,0 +1,76 @@
/**
* 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.airvisualnode.internal.json;
/**
* Top level object for AirVisual Node JSON data.
*
* @author Victor Antonovich - Initial contribution
*/
public class NodeData {
private DateAndTime dateAndTime;
private Measurements measurements;
private String serialNumber;
private Settings settings;
private Status status;
public NodeData(DateAndTime dateAndTime, Measurements measurements, String serialNumber, Settings settings,
Status status) {
this.dateAndTime = dateAndTime;
this.measurements = measurements;
this.serialNumber = serialNumber;
this.settings = settings;
this.status = status;
}
public DateAndTime getDateAndTime() {
return dateAndTime;
}
public void setDateAndTime(DateAndTime dateAndTime) {
this.dateAndTime = dateAndTime;
}
public Measurements getMeasurements() {
return measurements;
}
public void setMeasurements(Measurements measurements) {
this.measurements = measurements;
}
public String getSerialNumber() {
return serialNumber;
}
public void setSerialNumber(String serialNumber) {
this.serialNumber = serialNumber;
}
public Settings getSettings() {
return settings;
}
public void setSettings(Settings settings) {
this.settings = settings;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
}

View File

@@ -0,0 +1,61 @@
/**
* 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.airvisualnode.internal.json;
import java.util.List;
import com.google.gson.annotations.SerializedName;
/**
* Power saving data.
*
* @author Victor Antonovich - Initial contribution
*/
public class PowerSaving {
@SerializedName("2slots")
private List<PowerSavingTimeSlot> timeSlots = null;
private String mode;
@SerializedName("yes")
private List<PowerSavingTime> times = null;
public PowerSaving(List<PowerSavingTimeSlot> timeSlots, String mode, List<PowerSavingTime> times) {
this.mode = mode;
this.times = times;
this.timeSlots = timeSlots;
}
public List<PowerSavingTimeSlot> getTimeSlots() {
return timeSlots;
}
public void setTimeSlots(List<PowerSavingTimeSlot> timeSlots) {
this.timeSlots = timeSlots;
}
public List<PowerSavingTime> getTimes() {
return times;
}
public void setTimes(List<PowerSavingTime> times) {
this.times = times;
}
public String getMode() {
return mode;
}
public void setMode(String mode) {
this.mode = mode;
}
}

View File

@@ -0,0 +1,45 @@
/**
* 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.airvisualnode.internal.json;
/**
* Power saving time data.
*
* @author Victor Antonovich - Initial contribution
*/
public class PowerSavingTime {
private int hour;
private int minute;
public PowerSavingTime(int hour, int minute) {
this.hour = hour;
this.minute = minute;
}
public int getHour() {
return hour;
}
public void setHour(int hour) {
this.hour = hour;
}
public int getMinute() {
return minute;
}
public void setMinute(int minute) {
this.minute = minute;
}
}

View File

@@ -0,0 +1,45 @@
/**
* 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.airvisualnode.internal.json;
/**
* Power saving time slot data.
*
* @author Victor Antonovich - Initial contribution
*/
public class PowerSavingTimeSlot {
private int hourOff;
private int hourOn;
public PowerSavingTimeSlot(int hourOff, int hourOn) {
this.hourOff = hourOff;
this.hourOn = hourOn;
}
public int getHourOff() {
return hourOff;
}
public void setHourOff(int hourOff) {
this.hourOff = hourOff;
}
public int getHourOn() {
return hourOn;
}
public void setHourOn(int hourOn) {
this.hourOn = hourOn;
}
}

View File

@@ -0,0 +1,157 @@
/**
* 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.airvisualnode.internal.json;
/**
* Settings data.
*
* @author Victor Antonovich - Initial contribution
*/
public class Settings {
private String followedStation;
private boolean isAqiUsa;
private boolean isConcentrationShowed;
private boolean isIndoor;
private boolean isLcdOn;
private boolean isNetworkTime;
private boolean isTemperatureCelsius;
private String language;
private int lcdBrightness;
private String nodeName;
private PowerSaving powerSaving;
private String speedUnit;
private String timezone;
public Settings(String followedStation, boolean isAqiUsa, boolean isConcentrationShowed, boolean isIndoor,
boolean isLcdOn, boolean isNetworkTime, boolean isTemperatureCelsius, String language, int lcdBrightness,
String nodeName, PowerSaving powerSaving, String speedUnit, String timezone) {
this.followedStation = followedStation;
this.isAqiUsa = isAqiUsa;
this.isConcentrationShowed = isConcentrationShowed;
this.isIndoor = isIndoor;
this.isLcdOn = isLcdOn;
this.isNetworkTime = isNetworkTime;
this.isTemperatureCelsius = isTemperatureCelsius;
this.language = language;
this.lcdBrightness = lcdBrightness;
this.nodeName = nodeName;
this.powerSaving = powerSaving;
this.speedUnit = speedUnit;
this.timezone = timezone;
}
public String getFollowedStation() {
return followedStation;
}
public void setFollowedStation(String followedStation) {
this.followedStation = followedStation;
}
public boolean isIsAqiUsa() {
return isAqiUsa;
}
public void setIsAqiUsa(boolean isAqiUsa) {
this.isAqiUsa = isAqiUsa;
}
public boolean isIsConcentrationShowed() {
return isConcentrationShowed;
}
public void setIsConcentrationShowed(boolean isConcentrationShowed) {
this.isConcentrationShowed = isConcentrationShowed;
}
public boolean isIsIndoor() {
return isIndoor;
}
public void setIsIndoor(boolean isIndoor) {
this.isIndoor = isIndoor;
}
public boolean isIsLcdOn() {
return isLcdOn;
}
public void setIsLcdOn(boolean isLcdOn) {
this.isLcdOn = isLcdOn;
}
public boolean isIsNetworkTime() {
return isNetworkTime;
}
public void setIsNetworkTime(boolean isNetworkTime) {
this.isNetworkTime = isNetworkTime;
}
public boolean isIsTemperatureCelsius() {
return isTemperatureCelsius;
}
public void setIsTemperatureCelsius(boolean isTemperatureCelsius) {
this.isTemperatureCelsius = isTemperatureCelsius;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public int getLcdBrightness() {
return lcdBrightness;
}
public void setLcdBrightness(int lcdBrightness) {
this.lcdBrightness = lcdBrightness;
}
public String getNodeName() {
return nodeName;
}
public void setNodeName(String nodeName) {
this.nodeName = nodeName;
}
public PowerSaving getPowerSaving() {
return powerSaving;
}
public void setPowerSaving(PowerSaving powerSaving) {
this.powerSaving = powerSaving;
}
public String getSpeedUnit() {
return speedUnit;
}
public void setSpeedUnit(String speedUnit) {
this.speedUnit = speedUnit;
}
public String getTimezone() {
return timezone;
}
public void setTimezone(String timezone) {
this.timezone = timezone;
}
}

View File

@@ -0,0 +1,116 @@
/**
* 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.airvisualnode.internal.json;
/**
* Status data.
*
* @author Victor Antonovich - Initial contribution
*/
public class Status {
private String appVersion;
private int battery;
private long datetime;
private String model;
private String sensorPm25Serial;
private int syncTime;
private String systemVersion;
private int usedMemory;
private int wifiStrength;
public Status(String appVersion, int battery, long datetime, String model, String sensorPm25Serial, int syncTime,
String systemVersion, int usedMemory, int wifiStrength) {
this.appVersion = appVersion;
this.battery = battery;
this.datetime = datetime;
this.model = model;
this.sensorPm25Serial = sensorPm25Serial;
this.syncTime = syncTime;
this.systemVersion = systemVersion;
this.usedMemory = usedMemory;
this.wifiStrength = wifiStrength;
}
public String getAppVersion() {
return appVersion;
}
public void setAppVersion(String appVersion) {
this.appVersion = appVersion;
}
public int getBattery() {
return battery;
}
public void setBattery(int battery) {
this.battery = battery;
}
public long getDatetime() {
return datetime;
}
public void setDatetime(long datetime) {
this.datetime = datetime;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getSensorPm25Serial() {
return sensorPm25Serial;
}
public void setSensorPm25Serial(String sensorPm25Serial) {
this.sensorPm25Serial = sensorPm25Serial;
}
public int getSyncTime() {
return syncTime;
}
public void setSyncTime(int syncTime) {
this.syncTime = syncTime;
}
public String getSystemVersion() {
return systemVersion;
}
public void setSystemVersion(String systemVersion) {
this.systemVersion = systemVersion;
}
public int getUsedMemory() {
return usedMemory;
}
public void setUsedMemory(int usedMemory) {
this.usedMemory = usedMemory;
}
public int getWifiStrength() {
return wifiStrength;
}
public void setWifiStrength(int wifiStrength) {
this.wifiStrength = wifiStrength;
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="airvisualnode" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>AirVisual Node Binding</name>
<description>Binding for AirVisual Node air quality monitor</description>
<author>Victor Antonovich</author>
</binding:binding>

View File

@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="airvisualnode"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="avnode">
<label>AirVisual Node</label>
<description>AirVisual Node air quality monitor</description>
<!-- Channels -->
<channels>
<channel id="co2" typeId="Co2"/>
<channel id="humidity" typeId="Humidity"/>
<channel id="aqi" typeId="Aqi"/>
<channel id="pm_25" typeId="Pm_25"/>
<channel id="temperature" typeId="Temperature"/>
<channel id="timestamp" typeId="Timestamp"/>
<channel id="used_memory" typeId="Used_memory"/>
<channel id="signal-strength" typeId="system.signal-strength"/>
<channel id="battery-level" typeId="system.battery-level"/>
</channels>
<!-- Configuration parameters -->
<config-description>
<!-- Required parameters -->
<parameter name="address" type="text" required="true">
<context>network-address</context>
<label>Node Network Address</label>
<description>Node network address</description>
</parameter>
<parameter name="username" type="text">
<label>Node Username</label>
<description>Node network username</description>
<default>airvisual</default>
</parameter>
<parameter name="password" type="text" required="true">
<context>password</context>
<label>Node Password</label>
<description>Node network password</description>
</parameter>
<!-- Advanced parameters -->
<parameter name="share" type="text">
<label>Share Name</label>
<description>Node network share name</description>
<default>airvisual</default>
<advanced>true</advanced>
</parameter>
<parameter name="refresh" type="integer" min="30" unit="s">
<label>Refresh Interval</label>
<description>Node data fetches interval (in seconds)</description>
<default>60</default>
<unitLabel>s</unitLabel>
<advanced>true</advanced>
</parameter>
</config-description>
</thing-type>
<!-- Channel types -->
<channel-type id="Co2">
<item-type>Number:Dimensionless</item-type>
<label>CO₂ Level</label>
<description>CO&#8322; level, ppm</description>
<category>CarbonDioxide</category>
<state readOnly="true" pattern="%d %unit%"/>
</channel-type>
<channel-type id="Humidity">
<item-type>Number:Dimensionless</item-type>
<label>Humidity</label>
<description>Humidity, %</description>
<category>Humidity</category>
<state readOnly="true" pattern="%d %unit%"/>
</channel-type>
<channel-type id="Aqi">
<item-type>Number:Dimensionless</item-type>
<label>AQI</label>
<description>Air Quality Index (US)</description>
<state readOnly="true" pattern="%d"/>
</channel-type>
<channel-type id="Pm_25">
<item-type>Number:Density</item-type>
<label>PM2.5</label>
<description>PM2.5 level, µg/m&#179;</description>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="Temperature">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<description>Current temperature</description>
<category>Temperature</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="Timestamp">
<item-type>DateTime</item-type>
<label>Timestamp</label>
<description>Status timestamp</description>
<state readOnly="true"/>
</channel-type>
<channel-type id="Used_memory" advanced="true">
<item-type>Number</item-type>
<label>Used Memory</label>
<description>Used memory</description>
<state readOnly="true"/>
</channel-type>
</thing:thing-descriptions>