added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -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>
|
||||
@@ -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)));
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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₂ 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³</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>
|
||||
Reference in New Issue
Block a user