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.jeelink-${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-jeelink" description="Jeelink Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<feature>openhab-transport-serial</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.jeelink/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,26 @@
/**
* 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.jeelink.internal;
/**
* Converter that simply ignores input.
*
* @author Volker Bier - Initial contribution
*/
public class IgnoringConverter implements JeeLinkReadingConverter<Reading> {
@Override
public Reading createReading(String inputLine) {
return null;
}
}

View File

@@ -0,0 +1,92 @@
/**
* 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.jeelink.internal;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
* Defines common constants, which are used across the whole binding.
*
* @author Volker Bier - Initial contribution
*/
@NonNullByDefault
public class JeeLinkBindingConstants {
private JeeLinkBindingConstants() {
}
public static final String BINDING_ID = "jeelink";
// List of all Thing Type UIDs
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>();
public static final Set<ThingTypeUID> SUPPORTED_SENSOR_THING_TYPES_UIDS = new HashSet<>();
public static final ThingTypeUID JEELINK_USB_STICK_THING_TYPE = new ThingTypeUID(BINDING_ID, "jeelinkUsb");
public static final ThingTypeUID JEELINK_TCP_STICK_THING_TYPE = new ThingTypeUID(BINDING_ID, "jeelinkTcp");
public static final ThingTypeUID LGW_USB_STICK_THING_TYPE = new ThingTypeUID(BINDING_ID, "lgwUsb");
public static final ThingTypeUID LGW_TCP_STICK_THING_TYPE = new ThingTypeUID(BINDING_ID, "lgwTcp");
public static final ThingTypeUID LACROSSE_SENSOR_THING_TYPE = new ThingTypeUID(BINDING_ID, "lacrosse");
public static final ThingTypeUID EC3000_SENSOR_THING_TYPE = new ThingTypeUID(BINDING_ID, "ec3k");
public static final ThingTypeUID PCA301_SENSOR_THING_TYPE = new ThingTypeUID(BINDING_ID, "pca301");
public static final ThingTypeUID TX22_SENSOR_THING_TYPE = new ThingTypeUID(BINDING_ID, "tx22");
public static final ThingTypeUID REVOLT_SENSOR_THING_TYPE = new ThingTypeUID(BINDING_ID, "revolt");
public static final ThingTypeUID LGW_SENSOR_THING_TYPE = new ThingTypeUID(BINDING_ID, "lgw");
// List of all channel ids for lacrosse sensor things
public static final String TEMPERATURE_CHANNEL = "temperature";
public static final String HUMIDITY_CHANNEL = "humidity";
public static final String BATTERY_NEW_CHANNEL = "batteryNew";
public static final String BATTERY_LOW_CHANNEL = "batteryLow";
public static final String PROPERTY_SENSOR_ID = "sensorId";
// List of all additional channel ids for ec3k sensor things
public static final String CURRENT_POWER_CHANNEL = "currentPower";
public static final String MAX_POWER_CHANNEL = "maxPower";
public static final String CONSUMPTION_CHANNEL = "consumptionTotal";
public static final String APPLIANCE_TIME_CHANNEL = "applianceTime";
public static final String SENSOR_TIME_CHANNEL = "sensorTime";
public static final String RESETS_CHANNEL = "resets";
// List of all additional channel ids for pca301 sensor things
public static final String SWITCHING_STATE_CHANNEL = "switchingState";
// List of all additional channel ids for revolt sensor things
public static final String POWER_FACTOR_CHANNEL = "powerFactor";
public static final String ELECTRIC_CURRENT_CHANNEL = "electricCurrent";
public static final String ELECTRIC_POTENTIAL_CHANNEL = "electricPotential";
public static final String FREQUENCY_CHANNEL = "powerFrequency";
// List of all additional channel ids for tx22 sensor things
public static final String PRESSURE_CHANNEL = "pressure";
public static final String RAIN_CHANNEL = "rain";
public static final String WIND_STENGTH_CHANNEL = "windStrength";
public static final String WIND_ANGLE_CHANNEL = "windAngle";
public static final String GUST_STRENGTH_CHANNEL = "gustStrength";
static {
for (SensorDefinition<?> def : SensorDefinition.getDefinitions()) {
SUPPORTED_SENSOR_THING_TYPES_UIDS.add(def.getThingTypeUID());
}
SUPPORTED_THING_TYPES_UIDS.add(JeeLinkBindingConstants.JEELINK_USB_STICK_THING_TYPE);
SUPPORTED_THING_TYPES_UIDS.add(JeeLinkBindingConstants.JEELINK_TCP_STICK_THING_TYPE);
SUPPORTED_THING_TYPES_UIDS.add(JeeLinkBindingConstants.LGW_USB_STICK_THING_TYPE);
SUPPORTED_THING_TYPES_UIDS.add(JeeLinkBindingConstants.LGW_TCP_STICK_THING_TYPE);
SUPPORTED_THING_TYPES_UIDS.addAll(SUPPORTED_SENSOR_THING_TYPES_UIDS);
}
}

View File

@@ -0,0 +1,290 @@
/**
* 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.jeelink.internal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.openhab.binding.jeelink.internal.config.JeeLinkConfig;
import org.openhab.binding.jeelink.internal.connection.ConnectionListener;
import org.openhab.binding.jeelink.internal.connection.JeeLinkConnection;
import org.openhab.binding.jeelink.internal.connection.JeeLinkSerialConnection;
import org.openhab.binding.jeelink.internal.connection.JeeLinkTcpConnection;
import org.openhab.core.io.transport.serial.SerialPortIdentifier;
import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.BridgeHandler;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handler for a JeeLink USB Receiver thing.
*
* @author Volker Bier - Initial contribution
*/
public class JeeLinkHandler extends BaseBridgeHandler implements BridgeHandler, ConnectionListener {
private final Logger logger = LoggerFactory.getLogger(JeeLinkHandler.class);
private final List<JeeLinkReadingConverter<?>> converters = new ArrayList<>();
private final Map<String, JeeLinkReadingConverter<?>> sensorTypeConvertersMap = new HashMap<>();
private final Map<Class<?>, Set<ReadingHandler<? extends Reading>>> readingClassHandlerMap = new HashMap<>();
private final SerialPortManager serialPortManager;
private JeeLinkConnection connection;
private AtomicBoolean connectionInitialized = new AtomicBoolean(false);
private ScheduledFuture<?> connectJob;
private ScheduledFuture<?> initJob;
private long lastReadingTime;
private ScheduledFuture<?> monitorJob;
public JeeLinkHandler(Bridge bridge, SerialPortManager serialPortManager) {
super(bridge);
this.serialPortManager = serialPortManager;
}
@Override
public void initialize() {
JeeLinkConfig cfg = getConfig().as(JeeLinkConfig.class);
if (cfg.serialPort != null && cfg.baudRate != null) {
SerialPortIdentifier serialPortIdentifier = serialPortManager.getIdentifier(cfg.serialPort);
if (serialPortIdentifier == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Port not found: " + cfg.serialPort);
return;
}
connection = new JeeLinkSerialConnection(serialPortIdentifier, cfg.baudRate, this);
connection.openConnection();
} else if (cfg.ipAddress != null && cfg.port != null) {
connection = new JeeLinkTcpConnection(cfg.ipAddress + ":" + cfg.port, scheduler, this);
connection.openConnection();
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Connection configuration incomplete");
}
}
@Override
public void connectionOpened() {
logger.debug("Connection to port {} opened.", connection.getPort());
updateStatus(ThingStatus.ONLINE);
if (connectJob != null) {
logger.debug("Connection to port {} established. Reconnect cancelled.", connection.getPort());
connectJob.cancel(true);
connectJob = null;
}
JeeLinkConfig cfg = getConfig().as(JeeLinkConfig.class);
initJob = scheduler.schedule(() -> {
intializeConnection();
}, cfg.initDelay, TimeUnit.SECONDS);
logger.debug("Init commands scheduled in {} seconds.", cfg.initDelay);
if (cfg.reconnectInterval > 0) {
monitorJob = scheduler.scheduleWithFixedDelay(new Runnable() {
private long lastMonitorTime;
@Override
public void run() {
if (getThing().getStatus() == ThingStatus.ONLINE && lastReadingTime < lastMonitorTime) {
logger.debug("Monitoring job for port {} detected missing readings. Triggering reconnect...",
connection.getPort());
connection.closeConnection();
updateStatus(ThingStatus.OFFLINE);
connection.openConnection();
}
lastMonitorTime = System.currentTimeMillis();
}
}, cfg.reconnectInterval, cfg.reconnectInterval, TimeUnit.SECONDS);
logger.debug("Monitoring job started.");
}
}
@Override
public void connectionClosed() {
logger.debug("Connection to port {} closed.", connection.getPort());
updateStatus(ThingStatus.OFFLINE);
connectionInitialized.set(false);
if (initJob != null) {
initJob.cancel(true);
}
if (monitorJob != null) {
monitorJob.cancel(true);
}
}
@Override
public void connectionAborted(String cause) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, cause);
if (monitorJob != null) {
monitorJob.cancel(true);
}
if (initJob != null) {
initJob.cancel(true);
}
connectionInitialized.set(false);
connectJob = scheduler.schedule(() -> {
connection.openConnection();
}, 10, TimeUnit.SECONDS);
logger.debug("Connection to port {} aborted ({}). Reconnect scheduled.", connection.getPort(), cause);
}
public void addReadingHandler(ReadingHandler<? extends Reading> h) {
synchronized (readingClassHandlerMap) {
Set<ReadingHandler<? extends Reading>> handlers = readingClassHandlerMap.get(h.getReadingClass());
if (handlers == null) {
handlers = new HashSet<>();
// this is the first handler for this reading class => also setup converter
readingClassHandlerMap.put(h.getReadingClass(), handlers);
if (SensorDefinition.ALL_TYPE == h.getSensorType()) {
converters.addAll(SensorDefinition.getDiscoveryConverters());
} else {
JeeLinkReadingConverter<?> c = SensorDefinition.getConverter(h.getSensorType());
if (c != null) {
converters.add(c);
sensorTypeConvertersMap.put(h.getSensorType(), c);
}
}
}
if (!handlers.contains(h)) {
logger.debug("Adding reading handler for class {}: {}", h.getReadingClass(), h);
handlers.add(h);
}
}
}
public void removeReadingHandler(ReadingHandler<? extends Reading> h) {
synchronized (readingClassHandlerMap) {
Set<ReadingHandler<? extends Reading>> handlers = readingClassHandlerMap.get(h.getReadingClass());
if (handlers != null) {
logger.debug("Removing reading handler for class {}: {}", h.getReadingClass(), h);
handlers.remove(h);
if (handlers.isEmpty()) {
// this was the last handler for this reading class => also remove converter
readingClassHandlerMap.remove(h.getReadingClass());
if (SensorDefinition.ALL_TYPE == h.getSensorType()) {
converters.removeAll(SensorDefinition.getDiscoveryConverters());
} else {
JeeLinkReadingConverter<?> c = SensorDefinition.getConverter(h.getSensorType());
if (c != null) {
converters.remove(c);
}
}
}
}
}
}
@Override
public void handleCommand(ChannelUID channelUid, Command command) {
}
@Override
public void handleInput(String input) {
lastReadingTime = System.currentTimeMillis();
// try all associated converters to find the correct one
for (JeeLinkReadingConverter<?> c : converters) {
Reading r = c.createReading(input);
if (r != null) {
// this converter is responsible
intializeConnection();
// propagate to the appropriate sensor handler
synchronized (readingClassHandlerMap) {
Set<ReadingHandler<? extends Reading>> handlers = getAllHandlers(r.getClass());
for (ReadingHandler h : handlers) {
h.handleReading(r);
}
}
break;
}
}
}
private Set<ReadingHandler<? extends Reading>> getAllHandlers(Class<? extends Reading> readingClass) {
Set<ReadingHandler<? extends Reading>> handlers = new HashSet<>();
Set<ReadingHandler<? extends Reading>> typeHandlers = readingClassHandlerMap.get(readingClass);
if (typeHandlers != null) {
handlers.addAll(typeHandlers);
}
Set<ReadingHandler<? extends Reading>> discoveryHandlers = readingClassHandlerMap.get(Reading.class);
if (discoveryHandlers != null) {
handlers.addAll(discoveryHandlers);
}
return handlers;
}
private void intializeConnection() {
if (!connectionInitialized.getAndSet(true)) {
JeeLinkConfig cfg = getConfig().as(JeeLinkConfig.class);
String initCommands = cfg.initCommands;
if (initCommands != null && !initCommands.trim().isEmpty()) {
logger.debug("Sending init commands for port {}: {}", connection.getPort(), initCommands);
connection.sendCommands(initCommands);
}
}
}
@Override
public void dispose() {
if (connectJob != null) {
connectJob.cancel(true);
connectJob = null;
}
if (connection != null) {
connection.closeConnection();
}
super.dispose();
}
public JeeLinkConnection getConnection() {
return connection;
}
}

View File

@@ -0,0 +1,99 @@
/**
* 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.jeelink.internal;
import static org.openhab.binding.jeelink.internal.JeeLinkBindingConstants.*;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.openhab.binding.jeelink.internal.discovery.SensorDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link JeeLinkHandlerFactory} is responsible for creating things and thing handlers.
*
* @author Volker Bier - Initial contribution
*/
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.jeelink")
public class JeeLinkHandlerFactory extends BaseThingHandlerFactory {
private final Logger logger = LoggerFactory.getLogger(JeeLinkHandlerFactory.class);
private final Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
private final SerialPortManager serialPortManager;
@Activate
public JeeLinkHandlerFactory(final @Reference SerialPortManager serialPortManager) {
this.serialPortManager = serialPortManager;
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUid) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUid);
}
@Override
protected ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUid = thing.getThingTypeUID();
ThingHandler handler = null;
if (thingTypeUid.equals(JEELINK_USB_STICK_THING_TYPE) || thingTypeUid.equals(JEELINK_TCP_STICK_THING_TYPE)
|| thingTypeUid.equals(LGW_TCP_STICK_THING_TYPE) || thingTypeUid.equals(LGW_USB_STICK_THING_TYPE)) {
logger.debug("creating JeeLinkHandler for thing {}...", thing.getUID().getId());
handler = new JeeLinkHandler((Bridge) thing, serialPortManager);
registerSensorDiscoveryService((JeeLinkHandler) handler);
} else {
handler = SensorDefinition.createHandler(thingTypeUid, thing);
if (handler == null) {
logger.debug("skipping creation of unknown handler for thing {} with type {}...",
thing.getUID().getId(), thing.getThingTypeUID().getId());
}
}
return handler;
}
@Override
protected synchronized void removeHandler(ThingHandler thingHandler) {
if (thingHandler instanceof JeeLinkHandler) {
ServiceRegistration<?> serviceReg = this.discoveryServiceRegs.remove(thingHandler.getThing().getUID());
if (serviceReg != null) {
serviceReg.unregister();
}
}
}
private synchronized void registerSensorDiscoveryService(JeeLinkHandler bridgeHandler) {
logger.debug("registering sensor discovery service...");
SensorDiscoveryService discoveryService = new SensorDiscoveryService(bridgeHandler);
discoveryServiceRegs.put(bridgeHandler.getThing().getUID(),
bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
}
}

View File

@@ -0,0 +1,22 @@
/**
* 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.jeelink.internal;
/**
* Interface for converting input read from a JeeLinkConnection to a Reading.
*
* @author Volker Bier - Initial contribution
*/
public interface JeeLinkReadingConverter<R extends Reading> {
public R createReading(String inputLine);
}

View File

@@ -0,0 +1,109 @@
/**
* 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.jeelink.internal;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.openhab.binding.jeelink.internal.config.JeeLinkSensorConfig;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
/**
* Abstract thing handler for sensors connected to a JeeLink.
*
* @author Volker Bier - Initial contribution
*/
public abstract class JeeLinkSensorHandler<R extends Reading> extends BaseThingHandler implements ReadingHandler<R> {
protected String id;
protected final String sensorType;
private ReadingPublisher<R> publisher;
private long secsSinceLastReading;
private ScheduledFuture<?> statusUpdateJob;
public JeeLinkSensorHandler(Thing thing, String sensorType) {
super(thing);
this.sensorType = sensorType;
}
public abstract ReadingPublisher<R> createPublisher();
@Override
public String getSensorType() {
return sensorType;
}
@Override
public void handleReading(R r) {
if (r != null && id.equals(r.getSensorId())) {
secsSinceLastReading = 0;
updateStatus(ThingStatus.ONLINE);
if (publisher != null) {
publisher.publish(r);
}
}
}
@Override
public synchronized void handleCommand(ChannelUID channelUid, Command command) {
}
@Override
public synchronized void initialize() {
JeeLinkHandler jlh = (JeeLinkHandler) getBridge().getHandler();
jlh.addReadingHandler(this);
JeeLinkSensorConfig cfg = getConfigAs(JeeLinkSensorConfig.class);
id = cfg.sensorId;
statusUpdateJob = createStatusUpdateJob(scheduler, cfg.sensorTimeout);
publisher = createPublisher();
updateStatus(ThingStatus.UNKNOWN);
}
@Override
public synchronized void dispose() {
id = null;
JeeLinkHandler jlh = (JeeLinkHandler) getBridge().getHandler();
jlh.removeReadingHandler(this);
if (statusUpdateJob != null) {
statusUpdateJob.cancel(true);
statusUpdateJob = null;
}
if (publisher != null) {
publisher.dispose();
publisher = null;
}
super.dispose();
}
private ScheduledFuture<?> createStatusUpdateJob(ScheduledExecutorService execService, final int sensorTimeout) {
return execService.scheduleWithFixedDelay(() -> {
if (secsSinceLastReading++ > sensorTimeout) {
updateStatus(ThingStatus.OFFLINE);
}
}, sensorTimeout, 1, TimeUnit.SECONDS);
}
}

View File

@@ -0,0 +1,26 @@
/**
* 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.jeelink.internal;
/**
* Interface for a Reading from a Sensor. Addiionally provided basic arithmetic operations needed
* for computing average values for readings.
*
* @author Volker Bier - Initial contribution
*/
public interface Reading {
/**
* @return the sensor ID of the sensor that provided this reading.
*/
String getSensorId();
}

View File

@@ -0,0 +1,26 @@
/**
* 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.jeelink.internal;
/**
* Interface for classes that handle Readings.
*
* @author Volker Bier - Initial contribution
*/
public interface ReadingHandler<R extends Reading> {
public void handleReading(R r);
public Class<R> getReadingClass();
public String getSensorType();
}

View File

@@ -0,0 +1,24 @@
/**
* 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.jeelink.internal;
/**
* Interface for classes that publish readings.
*
* @author Volker Bier - Initial contribution
*/
public interface ReadingPublisher<R extends Reading> {
public void publish(R reading);
public void dispose();
}

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.jeelink.internal;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* Computes a rolling average of readings that is passed on to the next publisher
* after a given time frame.
*
* @author Volker Bier - Initial contribution
*/
public abstract class RollingAveragePublisher<R extends Reading> implements ReadingPublisher<R> {
private final ReadingPublisher<R> publisher;
private ScheduledFuture<?> valueUpdateJob;
private RollingReadingAverage<R> rollingAvg;
public RollingAveragePublisher(int bufferSize, int interval, ReadingPublisher<R> p,
ScheduledExecutorService execService) {
publisher = p;
valueUpdateJob = createUpdateJob(execService, interval);
rollingAvg = createRollingReadingAverage(bufferSize);
}
public abstract RollingReadingAverage<R> createRollingReadingAverage(int bufferSize);
@Override
public void publish(R reading) {
rollingAvg.add(reading);
}
@Override
public void dispose() {
if (valueUpdateJob != null) {
valueUpdateJob.cancel(true);
valueUpdateJob = null;
}
publisher.dispose();
}
private ScheduledFuture<?> createUpdateJob(ScheduledExecutorService execService, final int updateInterval) {
return execService.scheduleWithFixedDelay(() -> {
publisher.publish(rollingAvg.getAverage());
}, updateInterval, updateInterval, TimeUnit.SECONDS);
}
}

View File

@@ -0,0 +1,62 @@
/**
* 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.jeelink.internal;
/**
* Computes a rolling average of readings.
*
* @author Volker Bier - Initial contribution
*/
public abstract class RollingReadingAverage<R extends Reading> {
private int size = 0;
private int maxSize;
private R total = null;
private int index = 0;
private R[] samples;
public RollingReadingAverage(R[] array) {
maxSize = array.length;
samples = array;
}
public void add(R reading) {
if (size < maxSize) {
size++;
}
if (total == null) {
total = reading;
} else {
total = add(total, reading);
total = substract(total, samples[index]);
}
samples[index] = reading;
if (++index == maxSize) {
index = 0;
}
}
public R getAverage() {
if (total == null) {
return null;
}
return divide(total, size);
}
protected abstract R add(R value1, R value2);
protected abstract R substract(R from, R value);
protected abstract R divide(R value, int count);
}

View File

@@ -0,0 +1,111 @@
/**
* 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.jeelink.internal;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.openhab.binding.jeelink.internal.ec3k.Ec3kSensorDefinition;
import org.openhab.binding.jeelink.internal.lacrosse.LaCrosseSensorDefinition;
import org.openhab.binding.jeelink.internal.lacrosse.LgwSensorDefinition;
import org.openhab.binding.jeelink.internal.lacrosse.Tx22SensorDefinition;
import org.openhab.binding.jeelink.internal.pca301.Pca301SensorDefinition;
import org.openhab.binding.jeelink.internal.revolt.RevoltSensorDefinition;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.ThingHandler;
/**
* Base class for sensor definitions.
*
* @author Volker Bier - Initial contribution
*
* @param <R> the Reading type this sensor provides.
*/
public abstract class SensorDefinition<R extends Reading> {
public static final String ALL_TYPE = "All";
private static final Set<SensorDefinition<?>> SENSOR_DEFS = Stream
.of(new LaCrosseSensorDefinition(), new Ec3kSensorDefinition(), new Pca301SensorDefinition(),
new Tx22SensorDefinition(), new RevoltSensorDefinition(), new LgwSensorDefinition())
.collect(Collectors.toSet());
private static final Set<JeeLinkReadingConverter<?>> CONVERTERS = SENSOR_DEFS.stream()
.map(SensorDefinition::createConverter).collect(Collectors.toSet());
protected final String type;
final ThingTypeUID thingTypeUid;
final String name;
public SensorDefinition(ThingTypeUID thingTypeUid, String name, String type) {
this.thingTypeUid = thingTypeUid;
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public ThingTypeUID getThingTypeUID() {
return thingTypeUid;
}
public abstract Class<R> getReadingClass();
public abstract JeeLinkSensorHandler<R> createHandler(Thing thing);
public abstract JeeLinkReadingConverter<R> createConverter();
public static SensorDefinition<?> getSensorDefinition(Reading reading) {
for (SensorDefinition<?> sensor : SENSOR_DEFS) {
if (sensor.getReadingClass().equals(reading.getClass())) {
return sensor;
}
}
return null;
}
public static Set<SensorDefinition<?>> getDefinitions() {
return SENSOR_DEFS;
}
public static ThingHandler createHandler(ThingTypeUID thingTypeUid, Thing thing) {
for (SensorDefinition<?> sensor : SENSOR_DEFS) {
if (sensor.getThingTypeUID().equals(thingTypeUid)) {
return sensor.createHandler(thing);
}
}
return null;
}
public static JeeLinkReadingConverter<?> getConverter(String sensorType) {
for (SensorDefinition<?> sensor : SENSOR_DEFS) {
if (sensor.getSensorType().equals(sensorType)) {
return sensor.createConverter();
}
}
return null;
}
public static Set<JeeLinkReadingConverter<?>> getDiscoveryConverters() {
return CONVERTERS;
}
private String getSensorType() {
return type;
}
}

View File

@@ -0,0 +1,23 @@
/**
* 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.jeelink.internal.config;
/**
* Configuration for a Handler that is able to buffer values.
*
* @author Volker Bier - Initial contribution
*/
public class BufferedSensorConfig extends JeeLinkSensorConfig {
public int updateInterval;
public int bufferSize;
}

View File

@@ -0,0 +1,28 @@
/**
* 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.jeelink.internal.config;
/**
* Configuration for a JeeLinkHandler.
*
* @author Volker Bier - Initial contribution
*/
public class JeeLinkConfig {
public String ipAddress;
public Integer port;
public String serialPort;
public Integer baudRate;
public String initCommands;
public Integer initDelay;
public Integer reconnectInterval;
}

View File

@@ -0,0 +1,23 @@
/**
* 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.jeelink.internal.config;
/**
* Configuration for a JeeLinkSensorHandler.
*
* @author Volker Bier - Initial contribution
*/
public class JeeLinkSensorConfig {
public String sensorId;
public int sensorTimeout;
}

View File

@@ -0,0 +1,24 @@
/**
* 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.jeelink.internal.config;
/**
* Configuration for a LaCrossTemperatureSensorHandler.
*
* @author Volker Bier - Initial contribution
*/
public class LaCrosseTemperatureSensorConfig extends BufferedSensorConfig {
public float minTemp;
public float maxTemp;
public float maxDiff;
}

View File

@@ -0,0 +1,22 @@
/**
* 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.jeelink.internal.config;
/**
* Configuration for a Pca301SensorHandler.
*
* @author Volker Bier - Initial contribution
*/
public class Pca301SensorConfig extends JeeLinkSensorConfig {
public int sendCount;
}

View File

@@ -0,0 +1,100 @@
/**
* 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.jeelink.internal.connection;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract base class for a connection to a JeeLink.
* Manages ReadingListeners, finds out the sketch name and allows to propagate read lines.
*
* @author Volker Bier - Initial contribution
*/
public abstract class AbstractJeeLinkConnection implements JeeLinkConnection {
private final Logger logger = LoggerFactory.getLogger(AbstractJeeLinkConnection.class);
protected final ConnectionListener connectionListener;
protected String port;
private AtomicBoolean initialized = new AtomicBoolean(false);
public AbstractJeeLinkConnection(String port, ConnectionListener listener) {
this.port = port;
connectionListener = listener;
}
@Override
public String getPort() {
return port;
}
/**
* returns the stream that can be used to write the init commands to the receiver.
*/
protected abstract OutputStream getInitStream() throws IOException;
protected void notifyOpen() {
connectionListener.connectionOpened();
}
protected void notifyClosed() {
connectionListener.connectionClosed();
}
protected void notifyAbort(String cause) {
connectionListener.connectionAborted(cause);
initialized.set(false);
}
public void propagateLine(String line) throws IOException {
logger.trace("Read line from port {}: {}", port, line);
connectionListener.handleInput(line);
}
@Override
public void sendCommands(String commands) {
try {
if (commands != null && !commands.trim().isEmpty()) {
// do not create in try-with-resources as this will
// close the undelying socket for TCP connections
OutputStream initStream = getInitStream();
if (initStream == null) {
throw new IOException(
"Connection on port " + port + " did not provide an init stream for writing init commands");
}
// do not close the writer as this closes the underlying stream, and
// in case of tcp connections, the underlying socket
OutputStreamWriter w = new OutputStreamWriter(initStream);
for (String cmd : commands.split(";")) {
logger.debug("Writing to device on port {}: {} ", port, cmd);
w.write(cmd + "\n");
}
w.flush();
}
} catch (IOException ex) {
logger.debug("Error writing to output stream!", ex);
closeConnection();
notifyAbort("propagate: " + ex.getMessage());
}
}
}

View File

@@ -0,0 +1,43 @@
/**
* 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.jeelink.internal.connection;
/**
* Listener that is notified on connection status changes of JeeLinkConnections
* as well as when input has been read from the connection.
*
* @author Volker Bier - Initial contribution
*/
public interface ConnectionListener {
/**
* Called when the connection has been opened.
*/
void connectionOpened();
/**
* Called when the connection has been closed.
*/
void connectionClosed();
/**
* Called when the connection has been aborted.
*
* @param cause a text describing the cause of the abort.
*/
void connectionAborted(String cause);
/**
* Called whenever input has been read from the connection.
*/
public void handleInput(String input);
}

View File

@@ -0,0 +1,40 @@
/**
* 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.jeelink.internal.connection;
/**
* Interface for connections to JeeLink USB Receivers.
*
* @author Volker Bier - Initial contribution
*/
public interface JeeLinkConnection {
/**
* closes the connection to the receiver.
*/
void closeConnection();
/**
* opens the connection to the receiver.
*/
void openConnection();
/**
* returns port to which the receiver is connected.
*/
String getPort();
/**
* sends the specified commands to the receiver (commands are semicolon separated)
*/
void sendCommands(String initCommands);
}

View File

@@ -0,0 +1,109 @@
/**
* 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.jeelink.internal.connection;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.TooManyListenersException;
import org.openhab.core.io.transport.serial.PortInUseException;
import org.openhab.core.io.transport.serial.SerialPort;
import org.openhab.core.io.transport.serial.SerialPortEvent;
import org.openhab.core.io.transport.serial.SerialPortEventListener;
import org.openhab.core.io.transport.serial.SerialPortIdentifier;
import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Reads lines from serial port and propagates them to registered InputListeners.
*
* @author Volker Bier - Initial contribution
*/
public class JeeLinkSerialConnection extends AbstractJeeLinkConnection {
private final Logger logger = LoggerFactory.getLogger(JeeLinkSerialConnection.class);
private final int baudRate;
private SerialPort serialPort;
private final SerialPortIdentifier serialPortIdentifier;
private boolean open;
public JeeLinkSerialConnection(SerialPortIdentifier serialPortIdentifier, int baudRate,
ConnectionListener listener) {
super(serialPortIdentifier.getName(), listener);
logger.debug("Creating serial connection for port {} with baud rate {}...", port, baudRate);
this.baudRate = baudRate;
this.serialPortIdentifier = serialPortIdentifier;
}
@Override
public synchronized void closeConnection() {
if (open) {
logger.debug("Closing serial connection to port {}...", port);
serialPort.notifyOnDataAvailable(false);
serialPort.removeEventListener();
serialPort.close();
notifyClosed();
open = false;
}
}
@Override
public synchronized void openConnection() {
try {
if (!open) {
logger.debug("Opening serial connection to port {} with baud rate {}...", port, baudRate);
serialPort = serialPortIdentifier.open("openhab", 3000);
open = true;
serialPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
final BufferedReader input = new BufferedReader(new InputStreamReader(serialPort.getInputStream()));
serialPort.addEventListener(new SerialPortEventListener() {
@Override
public void serialEvent(SerialPortEvent event) {
try {
if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
propagateLine(input.readLine());
}
} catch (IOException ex) {
logger.debug("Error reading from serial port!", ex);
closeConnection();
notifyAbort("propagate: " + ex.getMessage());
}
}
});
serialPort.notifyOnDataAvailable(true);
notifyOpen();
}
} catch (UnsupportedCommOperationException | IOException | TooManyListenersException ex) {
closeConnection();
notifyAbort(ex.getMessage());
} catch (PortInUseException ex) {
notifyAbort("Port in use: " + port);
}
}
@Override
public OutputStream getInitStream() throws IOException {
return open ? serialPort.getOutputStream() : null;
}
}

View File

@@ -0,0 +1,151 @@
/**
* 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.jeelink.internal.connection;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.ScheduledExecutorService;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Reads lines from TCP port and propagates them to registered InputListeners.
*
* @author Volker Bier - Initial contribution
*/
public class JeeLinkTcpConnection extends AbstractJeeLinkConnection {
private static final Pattern IP_PORT_PATTERN = Pattern.compile("([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+):([0-9]+)");
private final Logger logger = LoggerFactory.getLogger(JeeLinkTcpConnection.class);
private ScheduledExecutorService scheduler;
private Reader reader;
private Socket socket;
public JeeLinkTcpConnection(String port, ScheduledExecutorService scheduler, ConnectionListener l) {
super(port, l);
this.scheduler = scheduler;
}
@Override
public synchronized void closeConnection() {
if (reader != null) {
logger.debug("Closing TCP connection to port {}...", port);
reader.close();
reader = null;
closeSocketSilently();
socket = null;
notifyClosed();
}
}
@Override
public synchronized void openConnection() {
if (reader != null) {
logger.debug("TCP connection to port {} is already open!", port);
return;
}
Matcher ipm = IP_PORT_PATTERN.matcher(port);
if (!ipm.matches()) {
notifyAbort("Invalid TCP port specification: " + port);
}
String hostName = ipm.group(1);
int portNumber = Integer.parseInt(ipm.group(2));
logger.debug("Opening TCP connection to host {} port {}...", hostName, portNumber);
try {
logger.debug("Creating TCP socket to {}...", port);
socket = new Socket(hostName, portNumber);
socket.setKeepAlive(true);
logger.debug("TCP socket created.");
reader = new Reader(socket);
scheduler.execute(reader);
notifyOpen();
} catch (IOException ex) {
if (socket != null) {
closeSocketSilently();
}
notifyAbort(ex.getMessage());
}
}
private void closeSocketSilently() {
try {
socket.close();
} catch (IOException e) {
logger.debug("Failed to close socket.", e);
}
}
@Override
public OutputStream getInitStream() throws IOException {
return socket == null ? null : socket.getOutputStream();
}
private class Reader implements Runnable {
private Socket socket;
private BufferedReader inputReader;
private volatile boolean isRunning = true;
private Reader(Socket socket) throws IOException {
this.socket = socket;
inputReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
@Override
public void run() {
String line;
logger.debug("Reader for TCP port {} starting...", port);
try {
while (isRunning) {
line = inputReader.readLine();
if (line == null) {
throw new IOException("Got EOF on port " + port);
}
propagateLine(line);
}
} catch (IOException ex) {
if (isRunning) {
closeConnection();
notifyAbort(ex.getMessage());
}
} finally {
logger.debug("Reader for TCP port {} finished...", port);
}
}
public void close() {
logger.debug("Shutting down reader for TCP port {}...", port);
try {
isRunning = false;
socket.close();
inputReader.close();
} catch (IOException ex) {
logger.debug("Failed to close TCP port {}!", port, ex);
}
}
}
}

View File

@@ -0,0 +1,147 @@
/**
* 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.jeelink.internal.discovery;
import static org.openhab.binding.jeelink.internal.JeeLinkBindingConstants.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.openhab.binding.jeelink.internal.JeeLinkHandler;
import org.openhab.binding.jeelink.internal.Reading;
import org.openhab.binding.jeelink.internal.ReadingHandler;
import org.openhab.binding.jeelink.internal.SensorDefinition;
import org.openhab.binding.jeelink.internal.config.JeeLinkSensorConfig;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Discovery service for sensors connected to a JeeLink USB Receiver.
*
* @author Volker Bier - Initial contribution
*/
public class SensorDiscoveryService extends AbstractDiscoveryService implements ReadingHandler<Reading> {
private static final int DISCOVER_TIMEOUT_SECONDS = 30;
private final Logger logger = LoggerFactory.getLogger(SensorDiscoveryService.class);
JeeLinkHandler bridge;
AtomicBoolean capture = new AtomicBoolean();
/**
* Creates the discovery service for the given handler and converter.
*/
public SensorDiscoveryService(JeeLinkHandler jeeLinkHandler) {
super(SUPPORTED_SENSOR_THING_TYPES_UIDS, DISCOVER_TIMEOUT_SECONDS, true);
bridge = jeeLinkHandler;
}
@Override
protected synchronized void startScan() {
if (!capture.getAndSet(true)) {
logger.debug("discovery started for bridge {}", bridge.getThing().getUID());
// start listening for new sensor values
bridge.addReadingHandler(this);
capture.set(true);
}
}
@Override
protected void startBackgroundDiscovery() {
startScan();
}
@Override
protected synchronized void stopScan() {
if (capture.getAndSet(false)) {
bridge.removeReadingHandler(this);
logger.debug("discovery stopped for bridge {}", bridge.getThing().getUID());
}
}
@Override
protected void stopBackgroundDiscovery() {
stopScan();
}
private boolean idExistsAtBridge(String id) {
List<Thing> existingThings = bridge.getThing().getThings();
boolean idExists = false;
for (Thing t : existingThings) {
idExists = idExists || t.getUID().getId().equals(id);
}
return idExists;
}
@Override
public void handleReading(Reading reading) {
final String id = reading.getSensorId();
List<Thing> existingThings = bridge.getThing().getThings();
boolean sensorThingExists = false;
for (Thing t : existingThings) {
sensorThingExists = sensorThingExists
|| id.equals(t.getConfiguration().as(JeeLinkSensorConfig.class).sensorId);
}
ThingUID bridgeUID = bridge.getThing().getUID();
if (!sensorThingExists) {
SensorDefinition<?> def = SensorDefinition.getSensorDefinition(reading);
logger.debug("discovery for bridge {} found unknown sensor of type {} with id {}", bridgeUID,
def.getThingTypeUID(), id);
boolean idExists = idExistsAtBridge(id);
String newId = id;
if (idExists) {
logger.debug("bridge {} already has a connected sensor with thing id {}", bridgeUID, id);
int idx = 1;
while (idExists) {
newId = id + "-" + idx++;
idExists = idExistsAtBridge(newId);
}
logger.debug("Bridge {} uses thing id {} instead of {}", bridgeUID, newId, id);
}
ThingUID sensorThing = new ThingUID(def.getThingTypeUID(), bridgeUID, newId);
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(sensorThing).withLabel(def.getName())
.withBridge(bridgeUID).withRepresentationProperty("id").withProperty(PROPERTY_SENSOR_ID, id)
.build();
thingDiscovered(discoveryResult);
} else {
logger.debug("discovery for bridge {} found already known sensor id {}", bridgeUID, id);
}
}
@Override
public Class<Reading> getReadingClass() {
return Reading.class;
}
@Override
public String getSensorType() {
return SensorDefinition.ALL_TYPE;
}
}

View File

@@ -0,0 +1,77 @@
/**
* 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.jeelink.internal.ec3k;
import org.openhab.binding.jeelink.internal.Reading;
/**
* Reading of a EC3000 sensor.
*
* @author Volker Bier - Initial contribution
*/
public class Ec3kReading implements Reading {
private float currentWatt;
private float maxWatt;
private long consumptionTotal;
private long applianceTime;
private long sensorTime;
private String sensorId;
private int resets;
public Ec3kReading(String sensorId, float currentWatt, float maxWatt, long consumptionTotal, long applianceTime,
long sensorTime, int resets) {
this.currentWatt = currentWatt;
this.maxWatt = maxWatt;
this.consumptionTotal = consumptionTotal;
this.applianceTime = applianceTime;
this.sensorTime = sensorTime;
this.sensorId = sensorId;
this.resets = resets;
}
@Override
public String toString() {
return "sensorId=" + sensorId + ": currWatt=" + currentWatt + ", maxWatt=" + maxWatt + ", consumption="
+ consumptionTotal + ", applianceTime=" + applianceTime + ", sensorTime=" + sensorTime + ", resets="
+ resets;
}
public float getCurrentWatt() {
return currentWatt;
}
@Override
public String getSensorId() {
return sensorId;
}
public float getMaxWatt() {
return maxWatt;
}
public long getConsumptionTotal() {
return consumptionTotal;
}
public long getApplianceTime() {
return applianceTime;
}
public long getSensorTime() {
return sensorTime;
}
public int getResets() {
return resets;
}
}

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.jeelink.internal.ec3k;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openhab.binding.jeelink.internal.JeeLinkReadingConverter;
/**
* Converter for converting a line read from a ec3kSerial sketch to a Ec3kReading.
*
* @author Volker Bier - Initial contribution
*/
public class Ec3kReadingConverter implements JeeLinkReadingConverter<Ec3kReading> {
private static final Pattern LINE_P = Pattern
.compile("OK\\s+22\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)"
+ "\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)"
+ "\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)");
@Override
public Ec3kReading createReading(String inputLine) {
if (inputLine != null) {
Matcher matcher = LINE_P.matcher(inputLine);
if (matcher.matches()) {
/*
* OK 22 188 129 0 209 209 102 0 174 89 187 0 1 123 102 0 0 10 117 2 0 (ID = BC81)
*/
long id1 = Long.parseLong(matcher.group(1));
long id2 = Long.parseLong(matcher.group(2));
String id = String.format("%02X%02X", id1, id2);
long secTot1 = Long.parseLong(matcher.group(3));
long secTot2 = Long.parseLong(matcher.group(4));
long secTot3 = Long.parseLong(matcher.group(5));
long secTot4 = Long.parseLong(matcher.group(6));
long secondsTotal = (secTot1 << 24) + (secTot2 << 16) + (secTot3 << 8) + secTot4;
long secOn1 = Long.parseLong(matcher.group(7));
long secOn2 = Long.parseLong(matcher.group(8));
long secOn3 = Long.parseLong(matcher.group(9));
long secOn4 = Long.parseLong(matcher.group(10));
long secondsOn = (secOn1 << 24) + (secOn2 << 16) + (secOn3 << 8) + secOn4;
long con1 = Long.parseLong(matcher.group(11));
long con2 = Long.parseLong(matcher.group(12));
long con3 = Long.parseLong(matcher.group(13));
long con4 = Long.parseLong(matcher.group(14));
long consumptionTotal = ((con1 << 24) + (con2 << 16) + (con3 << 8) + con4) / 1000;
long cur1 = Long.parseLong(matcher.group(15));
long cur2 = Long.parseLong(matcher.group(16));
float currentWatt = ((cur1 << 8) + cur2) / 10f;
long max1 = Long.parseLong(matcher.group(17));
long max2 = Long.parseLong(matcher.group(18));
float maxWatt = ((max1 << 8) + max2) / 10f;
int resets = Integer.parseInt(matcher.group(19));
return new Ec3kReading(id, currentWatt, maxWatt, consumptionTotal, secondsOn, secondsTotal, resets);
}
}
return null;
}
}

View File

@@ -0,0 +1,56 @@
/**
* 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.jeelink.internal.ec3k;
import org.openhab.binding.jeelink.internal.RollingReadingAverage;
/**
* Computes a rolling average of readings.
*
* @author Volker Bier - Initial contribution
*/
public class Ec3kRollingReadingAverage extends RollingReadingAverage<Ec3kReading> {
public Ec3kRollingReadingAverage(int bufferSize) {
super(new Ec3kReading[bufferSize]);
}
@Override
protected Ec3kReading add(Ec3kReading value1, Ec3kReading value2) {
if (value2 != null) {
return new Ec3kReading(value2.getSensorId(), value1.getCurrentWatt() + value2.getCurrentWatt(),
value2.getMaxWatt(), value2.getConsumptionTotal(), value2.getApplianceTime(),
value2.getSensorTime(), value2.getResets());
}
return new Ec3kReading(value1.getSensorId(), value1.getCurrentWatt(), value1.getMaxWatt(),
value1.getConsumptionTotal(), value1.getApplianceTime(), value1.getSensorTime(), value1.getResets());
}
@Override
protected Ec3kReading substract(Ec3kReading from, Ec3kReading value) {
float newCurrWatt = from.getCurrentWatt();
if (value != null) {
newCurrWatt -= value.getCurrentWatt();
}
return new Ec3kReading(from.getSensorId(), newCurrWatt, from.getMaxWatt(), from.getConsumptionTotal(),
from.getApplianceTime(), from.getSensorTime(), from.getResets());
}
@Override
protected Ec3kReading divide(Ec3kReading value, int number) {
return new Ec3kReading(value.getSensorId(), value.getCurrentWatt() / number, value.getMaxWatt(),
value.getConsumptionTotal(), value.getApplianceTime(), value.getSensorTime(), value.getResets());
}
}

View File

@@ -0,0 +1,46 @@
/**
* 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.jeelink.internal.ec3k;
import org.openhab.binding.jeelink.internal.JeeLinkBindingConstants;
import org.openhab.binding.jeelink.internal.JeeLinkReadingConverter;
import org.openhab.binding.jeelink.internal.JeeLinkSensorHandler;
import org.openhab.binding.jeelink.internal.SensorDefinition;
import org.openhab.core.thing.Thing;
/**
* Sensor Defintion of a EC3000 Power Monitor.
*
* @author Volker Bier - Initial contribution
*/
public class Ec3kSensorDefinition extends SensorDefinition<Ec3kReading> {
public Ec3kSensorDefinition() {
super(JeeLinkBindingConstants.EC3000_SENSOR_THING_TYPE, "EnergyCount 3000 Power Monitor", "22");
}
@Override
public JeeLinkReadingConverter<Ec3kReading> createConverter() {
return new Ec3kReadingConverter();
}
@Override
public Class<Ec3kReading> getReadingClass() {
return Ec3kReading.class;
}
@Override
public JeeLinkSensorHandler<Ec3kReading> createHandler(Thing thing) {
return new Ec3kSensorHandler(thing, type);
}
}

View File

@@ -0,0 +1,93 @@
/**
* 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.jeelink.internal.ec3k;
import static org.openhab.binding.jeelink.internal.JeeLinkBindingConstants.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import org.openhab.binding.jeelink.internal.JeeLinkSensorHandler;
import org.openhab.binding.jeelink.internal.ReadingPublisher;
import org.openhab.binding.jeelink.internal.RollingAveragePublisher;
import org.openhab.binding.jeelink.internal.RollingReadingAverage;
import org.openhab.binding.jeelink.internal.config.BufferedSensorConfig;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handler for a EC3000 sensor thing.
*
* @author Volker Bier - Initial contribution
*/
public class Ec3kSensorHandler extends JeeLinkSensorHandler<Ec3kReading> {
private final Logger logger = LoggerFactory.getLogger(Ec3kSensorHandler.class);
public Ec3kSensorHandler(Thing thing, String sensorType) {
super(thing, sensorType);
}
@Override
public Class<Ec3kReading> getReadingClass() {
return Ec3kReading.class;
}
@Override
public ReadingPublisher<Ec3kReading> createPublisher() {
ReadingPublisher<Ec3kReading> publisher = new ReadingPublisher<Ec3kReading>() {
@Override
public void publish(Ec3kReading reading) {
if (reading != null && getThing().getStatus() == ThingStatus.ONLINE) {
BigDecimal currentWatt = new BigDecimal(reading.getCurrentWatt()).setScale(1, RoundingMode.HALF_UP);
BigDecimal maxWatt = new BigDecimal(reading.getMaxWatt()).setScale(1, RoundingMode.HALF_UP);
logger.debug(
"updating states for thing {}: currWatt={} ({}), maxWatt={}, consumption={}, secondsOn={}, secondsTotal={}",
getThing().getUID().getId(), currentWatt, reading.getCurrentWatt(), maxWatt,
reading.getConsumptionTotal(), reading.getApplianceTime(), reading.getSensorTime());
updateState(CURRENT_POWER_CHANNEL, new QuantityType<>(currentWatt, SmartHomeUnits.WATT));
updateState(MAX_POWER_CHANNEL, new QuantityType<>(maxWatt, SmartHomeUnits.WATT));
updateState(CONSUMPTION_CHANNEL,
new QuantityType<>(reading.getConsumptionTotal(), SmartHomeUnits.WATT_HOUR));
updateState(APPLIANCE_TIME_CHANNEL,
new QuantityType<>(reading.getApplianceTime(), SmartHomeUnits.HOUR));
updateState(SENSOR_TIME_CHANNEL, new QuantityType<>(reading.getSensorTime(), SmartHomeUnits.HOUR));
updateState(RESETS_CHANNEL, new DecimalType(reading.getResets()));
}
}
@Override
public void dispose() {
}
};
BufferedSensorConfig cfg = getConfigAs(BufferedSensorConfig.class);
if (cfg.bufferSize > 1 && cfg.updateInterval > 0) {
publisher = new RollingAveragePublisher<Ec3kReading>(cfg.bufferSize, cfg.updateInterval, publisher,
scheduler) {
@Override
public RollingReadingAverage<Ec3kReading> createRollingReadingAverage(int bufferSize) {
return new Ec3kRollingReadingAverage(bufferSize);
}
};
}
return publisher;
}
}

View File

@@ -0,0 +1,51 @@
/**
* 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.jeelink.internal.lacrosse;
import org.openhab.binding.jeelink.internal.ReadingPublisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Checks that the given temperature is in range before passing it on to the next publisher.
*
* @author Volker Bier - Initial contribution
*/
public class BoundsCheckingPublisher implements ReadingPublisher<LaCrosseTemperatureReading> {
private final Logger logger = LoggerFactory.getLogger(BoundsCheckingPublisher.class);
private final ReadingPublisher<LaCrosseTemperatureReading> publisher;
private final float minTemp;
private final float maxTemp;
public BoundsCheckingPublisher(float min, float max, ReadingPublisher<LaCrosseTemperatureReading> p) {
minTemp = min;
maxTemp = max;
publisher = p;
}
@Override
public void publish(LaCrosseTemperatureReading reading) {
if (reading.getTemperature() >= minTemp && reading.getTemperature() <= maxTemp) {
publisher.publish(reading);
} else {
logger.debug("Ignoring out of bounds reading {}", reading.getTemperature());
}
}
@Override
public void dispose() {
publisher.dispose();
}
}

View File

@@ -0,0 +1,54 @@
/**
* 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.jeelink.internal.lacrosse;
import org.openhab.binding.jeelink.internal.ReadingPublisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Checks that the given temperature does not differ too much from the last temperature
* before passing it on to the next publisher.
*
* @author Volker Bier - Initial contribution
*/
public class DifferenceCheckingPublisher implements ReadingPublisher<LaCrosseTemperatureReading> {
private final Logger logger = LoggerFactory.getLogger(DifferenceCheckingPublisher.class);
private final ReadingPublisher<LaCrosseTemperatureReading> publisher;
private final float allowedDifference;
private LaCrosseTemperatureReading lastReading;
public DifferenceCheckingPublisher(float difference, ReadingPublisher<LaCrosseTemperatureReading> p) {
allowedDifference = difference;
publisher = p;
}
@Override
public void publish(LaCrosseTemperatureReading reading) {
if (lastReading == null
|| Math.abs(reading.getTemperature() - lastReading.getTemperature()) < allowedDifference) {
publisher.publish(reading);
} else {
logger.debug("Ignoring reading {} differing too much from previous value", reading.getTemperature());
}
lastReading = reading;
}
@Override
public void dispose() {
publisher.dispose();
}
}

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.jeelink.internal.lacrosse;
import org.openhab.binding.jeelink.internal.RollingReadingAverage;
/**
* Computes a rolling average of readings.
*
* @author Volker Bier - Initial contribution
*/
public class LaCrosseRollingReadingAverage extends RollingReadingAverage<LaCrosseTemperatureReading> {
public LaCrosseRollingReadingAverage(int bufferSize) {
super(new LaCrosseTemperatureReading[bufferSize]);
}
@Override
protected LaCrosseTemperatureReading add(LaCrosseTemperatureReading value1, LaCrosseTemperatureReading value2) {
if (value2 != null) {
return new LaCrosseTemperatureReading(value2.getSensorId(), value2.getSensorType(), value2.getChannel(),
value1.getTemperature() + value2.getTemperature(), value1.getHumidity() + value2.getHumidity(),
value2.isBatteryNew(), value2.isBatteryLow());
}
return new LaCrosseTemperatureReading(value1.getSensorId(), value1.getSensorType(), value1.getChannel(),
value1.getTemperature(), value1.getHumidity(), value1.isBatteryNew(), value1.isBatteryLow());
}
@Override
protected LaCrosseTemperatureReading substract(LaCrosseTemperatureReading from, LaCrosseTemperatureReading value) {
float newTemp = from.getTemperature();
int newHum = from.getHumidity();
if (value != null) {
newTemp -= value.getTemperature();
newHum -= value.getHumidity();
}
return new LaCrosseTemperatureReading(from.getSensorId(), from.getSensorType(), from.getChannel(), newTemp,
newHum, from.isBatteryNew(), from.isBatteryLow());
}
@Override
protected LaCrosseTemperatureReading divide(LaCrosseTemperatureReading value, int number) {
return new LaCrosseTemperatureReading(value.getSensorId(), value.getSensorType(), value.getChannel(),
value.getTemperature() / number, value.getHumidity() / number, value.isBatteryNew(),
value.isBatteryLow());
}
}

View File

@@ -0,0 +1,46 @@
/**
* 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.jeelink.internal.lacrosse;
import org.openhab.binding.jeelink.internal.JeeLinkBindingConstants;
import org.openhab.binding.jeelink.internal.JeeLinkReadingConverter;
import org.openhab.binding.jeelink.internal.JeeLinkSensorHandler;
import org.openhab.binding.jeelink.internal.SensorDefinition;
import org.openhab.core.thing.Thing;
/**
* Sensor Defintion of a LaCrosse Temperature Sensor
*
* @author Volker Bier - Initial contribution
*/
public class LaCrosseSensorDefinition extends SensorDefinition<LaCrosseTemperatureReading> {
public LaCrosseSensorDefinition() {
super(JeeLinkBindingConstants.LACROSSE_SENSOR_THING_TYPE, "LaCrosse Temperature Sensor", "9");
}
@Override
public JeeLinkReadingConverter<LaCrosseTemperatureReading> createConverter() {
return new LaCrosseTemperatureReadingConverter();
}
@Override
public Class<LaCrosseTemperatureReading> getReadingClass() {
return LaCrosseTemperatureReading.class;
}
@Override
public JeeLinkSensorHandler<LaCrosseTemperatureReading> createHandler(Thing thing) {
return new LaCrosseTemperatureSensorHandler(thing, type);
}
}

View File

@@ -0,0 +1,81 @@
/**
* 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.jeelink.internal.lacrosse;
import org.openhab.binding.jeelink.internal.Reading;
/**
* Reading of a LaCrosse Temperature Sensor.
*
* @author Volker Bier - Initial contribution
*/
public class LaCrosseTemperatureReading implements Reading {
private String sensorId;
private int sensorType;
private int channel;
private Float temp;
private Integer humidity;
private boolean batteryNew;
private boolean batteryLow;
public LaCrosseTemperatureReading(int sensorId, int sensorType, int channel, Float temp, Integer humidity,
boolean batteryNew, boolean batteryLow) {
this(String.valueOf(sensorId), sensorType, channel, temp, humidity, batteryNew, batteryLow);
}
public LaCrosseTemperatureReading(String sensorId, int sensorType, int channel, Float temp, Integer humidity,
boolean batteryNew, boolean batteryLow) {
this.sensorId = sensorId;
this.sensorType = sensorType;
this.channel = channel;
this.temp = temp;
this.humidity = humidity;
this.batteryNew = batteryNew;
this.batteryLow = batteryLow;
}
@Override
public String getSensorId() {
return sensorId;
}
public int getSensorType() {
return sensorType;
}
public Float getTemperature() {
return temp;
}
public Integer getHumidity() {
return humidity;
}
public boolean isBatteryLow() {
return batteryLow;
}
@Override
public String toString() {
return "sensorId=" + sensorId + ": channel=" + channel + ", temp=" + temp + ", hum=" + humidity + ", batLow="
+ batteryLow + ", batNew=" + batteryNew;
}
public boolean isBatteryNew() {
return batteryNew;
}
public int getChannel() {
return channel;
}
}

View File

@@ -0,0 +1,77 @@
/**
* 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.jeelink.internal.lacrosse;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openhab.binding.jeelink.internal.JeeLinkReadingConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Converter for converting a line read from a LaCrosseITPlusReader sketch to a LaCrosseTemperatureReading.
*
* @author Volker Bier - Initial contribution
*/
public class LaCrosseTemperatureReadingConverter implements JeeLinkReadingConverter<LaCrosseTemperatureReading> {
private static final Pattern LINE_P = Pattern
.compile("OK\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)");
private final Logger logger = LoggerFactory.getLogger(LaCrosseTemperatureReadingConverter.class);
@Override
public LaCrosseTemperatureReading createReading(String inputLine) {
// parse lines only if we have registered listeners
if (inputLine != null) {
Matcher matcher = LINE_P.matcher(inputLine);
if (matcher.matches()) {
// Format
//
// OK 9 56 1 4 156 37 (ID = 56 T: 18.0 H: 37 no NewBatt)
// OK 9 49 1 4 182 54 (ID = 49 T: 20.6 H: 54 no NewBatt)
// OK 9 55 129 4 192 56 (ID = 55 T: 21.6 H: 56 WITH NewBatt)
// OK 9 ID XXX XXX XXX XXX
// | | | | | | |
// | | | | | | --- Humidity incl. WeakBatteryFlag
// | | | | | |------ Temp * 10 + 1000 LSB
// | | | | |---------- Temp * 10 + 1000 MSB
// | | | |-------------- Sensor type (1 or 2) +128 if NewBatteryFlag
// | | |----------------- Sensor ID
// | |------------------- fix "9"
// |---------------------- fix "OK"
logger.trace("Creating reading from: {}", inputLine);
int sensorId = Integer.parseInt(matcher.group(2));
int int3 = Integer.parseInt(matcher.group(3));
int batteryNewInt = (int3 & 0x80) >> 7;
int type = (int3 & 0x70) >> 4;
int channel = int3 & 0x0F;
float temperature = (float) (Integer.parseInt(matcher.group(4)) * 256
+ Integer.parseInt(matcher.group(5)) - 1000) / 10;
int humidity = Integer.parseInt(matcher.group(6)) & 0x7f;
int batteryLowInt = (Integer.parseInt(matcher.group(6)) & 0x80) >> 7;
boolean batteryLow = batteryLowInt == 1;
boolean batteryNew = batteryNewInt == 1;
return new LaCrosseTemperatureReading(sensorId, type, channel, temperature, humidity, batteryNew,
batteryLow);
}
}
return null;
}
}

View File

@@ -0,0 +1,173 @@
/**
* 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.jeelink.internal.lacrosse;
import static org.openhab.binding.jeelink.internal.JeeLinkBindingConstants.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.openhab.binding.jeelink.internal.JeeLinkSensorHandler;
import org.openhab.binding.jeelink.internal.ReadingPublisher;
import org.openhab.binding.jeelink.internal.RollingAveragePublisher;
import org.openhab.binding.jeelink.internal.RollingReadingAverage;
import org.openhab.binding.jeelink.internal.config.LaCrosseTemperatureSensorConfig;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.SmartHomeUnits;
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.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handler for a LaCrosse Temperature Sensor thing.
*
* @author Volker Bier - Initial contribution
*/
public class LaCrosseTemperatureSensorHandler extends JeeLinkSensorHandler<LaCrosseTemperatureReading> {
private final Logger logger = LoggerFactory.getLogger(LaCrosseTemperatureSensorHandler.class);
public LaCrosseTemperatureSensorHandler(Thing thing, String sensorType) {
super(thing, sensorType);
}
@Override
public Class<LaCrosseTemperatureReading> getReadingClass() {
return LaCrosseTemperatureReading.class;
}
@Override
public ReadingPublisher<LaCrosseTemperatureReading> createPublisher() {
return new ReadingPublisher<LaCrosseTemperatureReading>() {
private final Map<Integer, ReadingPublisher<LaCrosseTemperatureReading>> channelPublishers = new HashMap<>();
@Override
public void publish(LaCrosseTemperatureReading reading) {
if (reading != null) {
int channelNo = reading.getChannel();
ReadingPublisher<LaCrosseTemperatureReading> publisher;
synchronized (channelPublishers) {
publisher = channelPublishers.get(channelNo);
if (publisher == null) {
publisher = createPublisherForChannel(channelNo);
channelPublishers.put(channelNo, publisher);
createMissingChannels(reading.getChannel());
}
}
publisher.publish(reading);
}
}
private void createMissingChannels(int channelNo) {
List<Channel> missingChannels = new ArrayList<>();
String idSuffix = channelNo > 1 ? String.valueOf(channelNo) : "";
String labelSuffix = channelNo > 1 ? " " + channelNo : "";
for (String channelName : new String[] { TEMPERATURE_CHANNEL, HUMIDITY_CHANNEL }) {
if (getThing().getChannel(channelName + idSuffix) == null) {
missingChannels.add(ChannelBuilder
.create(new ChannelUID(getThing().getUID(), channelName + idSuffix), "Number")
.withType(new ChannelTypeUID(getThing().getThingTypeUID().getBindingId(), channelName))
.withLabel(StringUtils.capitalize(channelName + labelSuffix)).build());
}
}
missingChannels.addAll(getThing().getChannels());
if (!missingChannels.isEmpty()) {
ThingBuilder thingBuilder = editThing();
thingBuilder.withChannels(missingChannels);
updateThing(thingBuilder.build());
}
}
@Override
public void dispose() {
synchronized (channelPublishers) {
for (ReadingPublisher<LaCrosseTemperatureReading> p : channelPublishers.values()) {
p.dispose();
}
channelPublishers.clear();
}
}
};
}
public ReadingPublisher<LaCrosseTemperatureReading> createPublisherForChannel(int channelNo) {
ReadingPublisher<LaCrosseTemperatureReading> publisher = new ReadingPublisher<LaCrosseTemperatureReading>() {
@Override
public void publish(LaCrosseTemperatureReading reading) {
if (reading != null && getThing().getStatus() == ThingStatus.ONLINE) {
BigDecimal temp = new BigDecimal(reading.getTemperature()).setScale(1, RoundingMode.HALF_UP);
if (channelNo == 1) {
logger.debug(
"updating states for thing {} ({}): temp={} ({}), humidity={}, batteryNew={}, batteryLow={}",
getThing().getLabel(), getThing().getUID().getId(), temp, reading.getTemperature(),
reading.getHumidity(), reading.isBatteryNew(), reading.isBatteryLow());
updateState(TEMPERATURE_CHANNEL, new QuantityType<>(temp, SIUnits.CELSIUS));
updateState(HUMIDITY_CHANNEL,
new QuantityType<>(reading.getHumidity(), SmartHomeUnits.PERCENT));
updateState(BATTERY_NEW_CHANNEL, reading.isBatteryNew() ? OnOffType.ON : OnOffType.OFF);
updateState(BATTERY_LOW_CHANNEL, reading.isBatteryLow() ? OnOffType.ON : OnOffType.OFF);
} else {
logger.debug("updating states for channel {} of thing {} ({}): temp={} ({}), humidity={}",
reading.getChannel(), getThing().getLabel(), getThing().getUID().getId(), temp,
reading.getTemperature(), reading.getHumidity());
updateState(TEMPERATURE_CHANNEL + reading.getChannel(),
new QuantityType<>(temp, SIUnits.CELSIUS));
updateState(HUMIDITY_CHANNEL + reading.getChannel(),
new QuantityType<>(reading.getHumidity(), SmartHomeUnits.PERCENT));
}
}
}
@Override
public void dispose() {
}
};
LaCrosseTemperatureSensorConfig cfg = getConfigAs(LaCrosseTemperatureSensorConfig.class);
if (cfg.bufferSize > 1 && cfg.updateInterval > 0) {
publisher = new RollingAveragePublisher<LaCrosseTemperatureReading>(cfg.bufferSize, cfg.updateInterval,
publisher, scheduler) {
@Override
public RollingReadingAverage<LaCrosseTemperatureReading> createRollingReadingAverage(int bufferSize) {
return new LaCrosseRollingReadingAverage(bufferSize);
}
};
}
if (cfg.maxDiff > 0) {
publisher = new DifferenceCheckingPublisher(cfg.maxDiff, publisher);
}
publisher = new BoundsCheckingPublisher(cfg.minTemp, cfg.maxTemp, publisher);
return publisher;
}
}

View File

@@ -0,0 +1,69 @@
/**
* 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.jeelink.internal.lacrosse;
import org.openhab.binding.jeelink.internal.Reading;
/**
* Reading of sensors directly connected to a LGW.
*
* @author Volker Bier - Initial contribution
*/
public class LgwReading implements Reading {
private String sensorId;
private Float temp;
private Integer humidity;
private Integer pressure;
public LgwReading(int sensorId, Float temp, Integer humidity, Integer pressure) {
this.sensorId = String.valueOf(sensorId);
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
}
@Override
public String getSensorId() {
return sensorId;
}
public Float getTemperature() {
return temp;
}
public Integer getHumidity() {
return humidity;
}
public Integer getPressure() {
return pressure;
}
public boolean hasPressure() {
return pressure != null;
}
public boolean hasHumidity() {
return humidity != null;
}
public boolean hasTemperature() {
return temp != null;
}
@Override
public String toString() {
return "sensorId=" + sensorId + ": temp=" + temp + (hasHumidity() ? ", hum=" + humidity : "")
+ (hasPressure() ? ", pressure=" + pressure : "");
}
}

View File

@@ -0,0 +1,78 @@
/**
* 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.jeelink.internal.lacrosse;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openhab.binding.jeelink.internal.JeeLinkReadingConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Converter for converting a line read from a LGW to a LgwReading.
*
* @author Volker Bier - Initial contribution
*/
public class LgwReadingConverter implements JeeLinkReadingConverter<LgwReading> {
private static final Pattern LINE_P = Pattern.compile(
"OK\\s+WS\\s+([0-9]+)\\s+4\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)(?:\\s+255){8}?\\s+0\\s+([0-9]+)\\s+([0-9]+)(?:\\s+255){9}?");
private final Logger logger = LoggerFactory.getLogger(LgwReadingConverter.class);
@Override
public LgwReading createReading(String inputLine) {
// parse lines only if we have registered listeners
if (inputLine != null) {
Matcher matcher = LINE_P.matcher(inputLine);
if (matcher.matches()) {
/*
* Format
* OK WS 71 4 4 203 53 255 255 255 255 255 255 255 255 0 3 219 255 255 255 255 255 255 255 255 255
* OK WS 75 4 4 195 61 255 255 255 255 255 255 255 255 0 255 255 255 255 255 255 255 255 255 255 255
* OK WS 213 4 5 126 40 255 255 255 255 255 255 255 255 0 40 53 0 48 57
* OK WS ID XXX TTT TTT HHH RRR RRR DDD DDD SSS SSS GGG GGG FFF PPP PPP GAS GAS GAS DEB DEB DEB LUX LUX
* LUX
* | | | | | | | | | | | | | | | | | |-------------------------------------- Pressure LSB
* | | | | | | | | | | | | | | | | |------------------------------------------ Pressure MSB
* | | | | | | | | | | | | | | | |-- Fix 0
* | | | | | | |-------------------------------------- Humidity (1 ... 99 %rH) FF = none
* | | | | | |------------------------------------------ Temp * 10 + 1000 LSB (-40 ... +60 °C) FF/FF =
* none
* | | | | |---------------------------------------------- Temp * 10 + 1000 MSB
* | | | |-------------------------------------------------- fix "4"
* | | |------------------------------------------------------ Sensor ID (1 ... 63)
* | |--------------------------------------------------------- fix "WS"
* |------------------------------------------------------------ fix "OK"
*/
logger.trace("Creating reading from: {}", inputLine);
int sensorId = Integer.parseInt(matcher.group(1));
Float temperature = "255".equals(matcher.group(2)) ? null
: (float) (Integer.parseInt(matcher.group(2)) * 256 + Integer.parseInt(matcher.group(3)) - 1000)
/ 10;
Integer humidity = "255".equals(matcher.group(4)) ? null : Integer.parseInt(matcher.group(4));
Integer pressure = null;
if (!"255".equals(matcher.group(5))) {
pressure = Integer.parseInt(matcher.group(5)) * 256 + Integer.parseInt(matcher.group(6));
}
return new LgwReading(sensorId, temperature, humidity, pressure);
}
}
return null;
}
}

View File

@@ -0,0 +1,46 @@
/**
* 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.jeelink.internal.lacrosse;
import org.openhab.binding.jeelink.internal.JeeLinkBindingConstants;
import org.openhab.binding.jeelink.internal.JeeLinkReadingConverter;
import org.openhab.binding.jeelink.internal.JeeLinkSensorHandler;
import org.openhab.binding.jeelink.internal.SensorDefinition;
import org.openhab.core.thing.Thing;
/**
* Sensor definition of a sensor directly connected to a LGW.
*
* @author Volker Bier - Initial contribution
*/
public class LgwSensorDefinition extends SensorDefinition<LgwReading> {
public LgwSensorDefinition() {
super(JeeLinkBindingConstants.LGW_SENSOR_THING_TYPE, "LGW Sensor", "LGW");
}
@Override
public JeeLinkReadingConverter<LgwReading> createConverter() {
return new LgwReadingConverter();
}
@Override
public Class<LgwReading> getReadingClass() {
return LgwReading.class;
}
@Override
public JeeLinkSensorHandler<LgwReading> createHandler(Thing thing) {
return new LgwSensorHandler(thing, type);
}
}

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.jeelink.internal.lacrosse;
import static org.openhab.binding.jeelink.internal.JeeLinkBindingConstants.*;
import static org.openhab.core.library.unit.MetricPrefix.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.jeelink.internal.JeeLinkSensorHandler;
import org.openhab.binding.jeelink.internal.ReadingPublisher;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handler for a LGW Sensor thing.
*
* @author Volker Bier - Initial contribution
*/
@NonNullByDefault
public class LgwSensorHandler extends JeeLinkSensorHandler<LgwReading> {
private final Logger logger = LoggerFactory.getLogger(LgwSensorHandler.class);
private boolean hasHumidityChannel;
private boolean hasPressureChannel;
public LgwSensorHandler(Thing thing, String sensorType) {
super(thing, sensorType);
hasHumidityChannel = getThing().getChannel(HUMIDITY_CHANNEL) != null;
hasPressureChannel = getThing().getChannel(PRESSURE_CHANNEL) != null;
}
@Override
public Class<LgwReading> getReadingClass() {
return LgwReading.class;
}
@Override
public ReadingPublisher<LgwReading> createPublisher() {
ReadingPublisher<LgwReading> publisher = new ReadingPublisher<LgwReading>() {
@Override
public void publish(LgwReading reading) {
if (reading != null && getThing().getStatus() == ThingStatus.ONLINE) {
logger.debug("updating states for thing {} ({}): {}", getThing().getLabel(),
getThing().getUID().getId(), reading);
if (reading.hasTemperature()) {
BigDecimal temp = new BigDecimal(reading.getTemperature()).setScale(1, RoundingMode.HALF_UP);
updateState(TEMPERATURE_CHANNEL, new QuantityType<>(temp, SIUnits.CELSIUS));
}
if (reading.hasHumidity()) {
if (!hasHumidityChannel) {
ThingBuilder thingBuilder = editThing();
thingBuilder.withChannel(ChannelBuilder
.create(new ChannelUID(getThing().getUID(), HUMIDITY_CHANNEL), "Number:Humidity")
.withType(new ChannelTypeUID(getThing().getThingTypeUID().getBindingId(),
HUMIDITY_CHANNEL))
.withLabel(StringUtils.capitalize(HUMIDITY_CHANNEL)).build());
updateThing(thingBuilder.build());
hasHumidityChannel = true;
}
updateState(HUMIDITY_CHANNEL,
new QuantityType<>(reading.getHumidity(), SmartHomeUnits.PERCENT));
}
if (reading.hasPressure()) {
if (!hasPressureChannel) {
ThingBuilder thingBuilder = editThing();
thingBuilder.withChannel(ChannelBuilder
.create(new ChannelUID(getThing().getUID(), PRESSURE_CHANNEL), "Number:Pressure")
.withType(new ChannelTypeUID(getThing().getThingTypeUID().getBindingId(),
PRESSURE_CHANNEL))
.withLabel(StringUtils.capitalize(PRESSURE_CHANNEL)).build());
updateThing(thingBuilder.build());
hasPressureChannel = true;
}
updateState(PRESSURE_CHANNEL, new QuantityType<>(reading.getPressure(), HECTO(SIUnits.PASCAL)));
}
}
}
@Override
public void dispose() {
}
};
return publisher;
}
}

View File

@@ -0,0 +1,91 @@
/**
* 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.jeelink.internal.lacrosse;
/**
* Reading of a TX22 Temperature/Humidity Sensor.
*
* @author Volker Bier - Initial contribution
*/
public class Tx22Reading extends LaCrosseTemperatureReading {
private Integer rain;
private Float windDirection;
private Float windSpeed;
private Float windGust;
private Integer pressure;
public Tx22Reading(int sensorId, int sensorType, int channel, Float temp, Integer humidity, boolean batteryNew,
boolean batteryLow, Integer rain, Float windDirection, Float windSpeed, Float windGust, Integer pressure) {
super(String.valueOf(sensorId), sensorType, channel, temp, humidity, batteryNew, batteryLow);
this.rain = rain;
this.windDirection = windDirection;
this.windSpeed = windSpeed;
this.windGust = windGust;
this.pressure = pressure;
}
public Integer getRain() {
return rain;
}
public Float getWindDirection() {
return windDirection;
}
public Float getWindSpeed() {
return windSpeed;
}
public Float getWindGust() {
return windGust;
}
public Integer getPressure() {
return pressure;
}
public boolean hasWindGust() {
return windGust != null;
}
public boolean hasWindSpeed() {
return windSpeed != null;
}
public boolean hasWindDirection() {
return windDirection != null;
}
public boolean hasPressure() {
return pressure != null;
}
public boolean hasRain() {
return rain != null;
}
public boolean hasHumidity() {
return getHumidity() != null;
}
public boolean hasTemperature() {
return getTemperature() != null;
}
@Override
public String toString() {
return super.toString() + " rain=" + rain + " windDirection=" + windDirection + " windSpeed=" + windSpeed
+ " windGust=" + windGust + " pressure=" + pressure;
}
}

View File

@@ -0,0 +1,111 @@
/**
* 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.jeelink.internal.lacrosse;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openhab.binding.jeelink.internal.JeeLinkReadingConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Converter for converting a line read from a LaCrosseITPlusReader sketch to a Tx22Reading.
*
* @author Volker Bier - Initial contribution
*/
public class Tx22ReadingConverter implements JeeLinkReadingConverter<Tx22Reading> {
private static final Pattern LINE_P = Pattern.compile(
"OK\\s+WS\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)(?:\\s+([0-9]+)\\s+([0-9]+))?");
private final Logger logger = LoggerFactory.getLogger(Tx22ReadingConverter.class);
@Override
public Tx22Reading createReading(String inputLine) {
// parse lines only if we have registered listeners
if (inputLine != null) {
Matcher matcher = LINE_P.matcher(inputLine);
if (matcher.matches()) {
/**
* Format
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* -------------------------------------------------------------------
* OK WS 14 1 4 208 53 0 0 7 8 0 29 0 31 1 4 1 I D=0E 23.2°C 52%rH 0mm Dir.: 180.0° Wind:2.9m/s
* Gust:3.1m/s new Batt. 1025 hPa
* OK WS ID XXX TTT TTT HHH RRR RRR DDD DDD SSS SSS GGG GGG FFF PPP PPP
* | | | | | | | | | | | | | | | | | |-- Pressure LSB (optional, FF/FF = none)
* | | | | | | | | | | | | | | | | |------ Pressure MSB (optional)
* | | | | | | | | | | | | | | | |---------- Flags *
* | | | | | | | | | | | | | | |-------------- WindGust * 10 LSB (0.0 ... 50.0 m/s) FF/FF = none
* | | | | | | | | | | | | | |------------------ WindGust * 10 MSB
* | | | | | | | | | | | | |---------------------- WindSpeed * 10 LSB(0.0 ... 50.0 m/s) FF/FF = none
* | | | | | | | | | | | |-------------------------- WindSpeed * 10 MSB
* | | | | | | | | | | |------------------------------ WindDirection * 10 LSB (0.0 ... 365.0 Degrees)
* FF/FF = none
* | | | | | | | | | |---------------------------------- WindDirection * 10 MSB
* | | | | | | | | |-------------------------------------- Rain * 0.5mm LSB (0 ... 9999 mm) FF/FF = none
* | | | | | | | |------------------------------------------ Rain * 0.5mm MSB
* | | | | | | |---------------------------------------------- Humidity (1 ... 99 %rH) FF = none
* | | | | | |-------------------------------------------------- Temp * 10 + 1000 LSB (-40 ... +60 °C)
* FF/FF = none
* | | | | |------------------------------------------------------ Temp * 10 + 1000 MSB
* | | | |---------------------------------------------------------- Sensor type (1=TX22, 2=NodeSensor)
* | | |------------------------------------------------------------- Sensor ID (0 ... 63)
* | |---------------------------------------------------------------- fix "WS"
* |------------------------------------------------------------------- fix "OK"
*
* Flags: 128 64 32 16 8 4 2 1
* | | |
* | | |-- New battery
* | |------ ERROR
* |---------- Low battery
*/
logger.trace("Creating reading from: {}", inputLine);
int sensorId = Integer.parseInt(matcher.group(1));
int type = Integer.parseInt(matcher.group(2));
Float temperature = "255".equals(matcher.group(3)) ? null
: (float) (Integer.parseInt(matcher.group(3)) * 256 + Integer.parseInt(matcher.group(4)) - 1000)
/ 10;
Integer humidity = "255".equals(matcher.group(5)) ? null : Integer.parseInt(matcher.group(5));
Integer rain = "255".equals(matcher.group(6)) ? null
: (Integer.parseInt(matcher.group(6)) * 256 + Integer.parseInt(matcher.group(7))) * 2;
Float windDirection = "255".equals(matcher.group(8)) ? null
: (Integer.parseInt(matcher.group(8)) * 256 + Integer.parseInt(matcher.group(9))) / 10f;
Float windSpeed = "255".equals(matcher.group(10)) ? null
: (Integer.parseInt(matcher.group(10)) * 256 + Integer.parseInt(matcher.group(11))) / 10f;
Float windGust = "255".equals(matcher.group(12)) ? null
: (Integer.parseInt(matcher.group(12)) * 256 + Integer.parseInt(matcher.group(13))) / 10f;
int flags = Integer.parseInt(matcher.group(14));
boolean batteryNew = (flags & (byte) 1) > 0;
boolean batteryLow = (flags & (byte) 4) > 0;
Integer pressure = null;
if (matcher.groupCount() > 14 && matcher.group(15) != null && !"255".equals(matcher.group(15))) {
pressure = Integer.parseInt(matcher.group(15)) * 256 + Integer.parseInt(matcher.group(16));
}
return new Tx22Reading(sensorId, type, 0, temperature, humidity, batteryNew, batteryLow, rain,
windDirection, windSpeed, windGust, pressure);
}
}
return null;
}
}

View File

@@ -0,0 +1,46 @@
/**
* 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.jeelink.internal.lacrosse;
import org.openhab.binding.jeelink.internal.JeeLinkBindingConstants;
import org.openhab.binding.jeelink.internal.JeeLinkReadingConverter;
import org.openhab.binding.jeelink.internal.JeeLinkSensorHandler;
import org.openhab.binding.jeelink.internal.SensorDefinition;
import org.openhab.core.thing.Thing;
/**
* Sensor Defintion of a TX22 Temperature/Humidity Sensor.
*
* @author Volker Bier - Initial contribution
*/
public class Tx22SensorDefinition extends SensorDefinition<Tx22Reading> {
public Tx22SensorDefinition() {
super(JeeLinkBindingConstants.TX22_SENSOR_THING_TYPE, "TX22 Temperature/Humidity Sensor", "WS");
}
@Override
public JeeLinkReadingConverter<Tx22Reading> createConverter() {
return new Tx22ReadingConverter();
}
@Override
public Class<Tx22Reading> getReadingClass() {
return Tx22Reading.class;
}
@Override
public JeeLinkSensorHandler<Tx22Reading> createHandler(Thing thing) {
return new Tx22SensorHandler(thing, type);
}
}

View File

@@ -0,0 +1,97 @@
/**
* 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.jeelink.internal.lacrosse;
import static org.openhab.binding.jeelink.internal.JeeLinkBindingConstants.*;
import static org.openhab.core.library.unit.MetricPrefix.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import org.openhab.binding.jeelink.internal.JeeLinkSensorHandler;
import org.openhab.binding.jeelink.internal.ReadingPublisher;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handler for a TX22 Temperature/Humidity Sensor thing.
*
* @author Volker Bier - Initial contribution
*/
public class Tx22SensorHandler extends JeeLinkSensorHandler<Tx22Reading> {
private final Logger logger = LoggerFactory.getLogger(Tx22SensorHandler.class);
public Tx22SensorHandler(Thing thing, String sensorType) {
super(thing, sensorType);
}
@Override
public Class<Tx22Reading> getReadingClass() {
return Tx22Reading.class;
}
@Override
public ReadingPublisher<Tx22Reading> createPublisher() {
ReadingPublisher<Tx22Reading> publisher = new ReadingPublisher<Tx22Reading>() {
@Override
public void publish(Tx22Reading reading) {
if (reading != null && getThing().getStatus() == ThingStatus.ONLINE) {
logger.debug("updating states for thing {} ({}): {}", getThing().getLabel(),
getThing().getUID().getId(), reading);
updateState(BATTERY_NEW_CHANNEL, reading.isBatteryNew() ? OnOffType.ON : OnOffType.OFF);
updateState(BATTERY_LOW_CHANNEL, reading.isBatteryLow() ? OnOffType.ON : OnOffType.OFF);
if (reading.hasTemperature()) {
BigDecimal temp = new BigDecimal(reading.getTemperature()).setScale(1, RoundingMode.HALF_UP);
updateState(TEMPERATURE_CHANNEL, new QuantityType<>(temp, SIUnits.CELSIUS));
}
if (reading.hasHumidity()) {
updateState(HUMIDITY_CHANNEL,
new QuantityType<>(reading.getHumidity(), SmartHomeUnits.PERCENT));
}
if (reading.hasRain()) {
updateState(RAIN_CHANNEL, new QuantityType<>(reading.getRain(), MILLI(SIUnits.METRE)));
}
if (reading.hasPressure()) {
updateState(PRESSURE_CHANNEL, new QuantityType<>(reading.getPressure(), HECTO(SIUnits.PASCAL)));
}
if (reading.hasWindDirection()) {
updateState(WIND_ANGLE_CHANNEL,
new QuantityType<>(reading.getWindDirection(), SmartHomeUnits.DEGREE_ANGLE));
}
if (reading.hasWindSpeed()) {
updateState(WIND_STENGTH_CHANNEL,
new QuantityType<>(reading.getWindSpeed(), SmartHomeUnits.METRE_PER_SECOND));
}
if (reading.hasWindGust()) {
updateState(GUST_STRENGTH_CHANNEL,
new QuantityType<>(reading.getWindGust(), SmartHomeUnits.METRE_PER_SECOND));
}
}
}
@Override
public void dispose() {
}
};
return publisher;
}
}

View File

@@ -0,0 +1,58 @@
/**
* 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.jeelink.internal.pca301;
import org.openhab.binding.jeelink.internal.Reading;
/**
* Reading of a PCA301 sensor.
*
* @author Volker Bier - Initial contribution
*/
public class Pca301Reading implements Reading {
private final String sensorId;
private final int channel;
private final boolean on;
private final float current;
private final long total;
public Pca301Reading(String sensorId, int channel, boolean deviceOn, float consumptionCurrent,
long consumptionTotal) {
this.sensorId = sensorId;
this.channel = channel;
on = deviceOn;
current = consumptionCurrent;
total = consumptionTotal;
}
@Override
public String getSensorId() {
return sensorId;
}
public int getChannel() {
return channel;
}
public boolean isOn() {
return on;
}
public float getCurrent() {
return current;
}
public long getTotal() {
return total;
}
}

View File

@@ -0,0 +1,73 @@
/**
* 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.jeelink.internal.pca301;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openhab.binding.jeelink.internal.JeeLinkReadingConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Converter for converting a line read from a pcaSerial sketch to a Pca301Reading.
*
* @author Volker Bier - Initial contribution
*/
public class Pca301ReadingConverter implements JeeLinkReadingConverter<Pca301Reading> {
private static final Pattern READING_P = Pattern.compile(
"OK\\s+24\\s+([0-9]+)\\s+4\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)\\s+([0-9]+)");
private final Logger logger = LoggerFactory.getLogger(Pca301ReadingConverter.class);
@Override
public Pca301Reading createReading(String inputLine) {
// parse lines only if we have registered listeners
if (inputLine != null) {
Matcher matcher = READING_P.matcher(inputLine);
if (matcher.matches()) {
// Format
//
// OK 24 1 4 1 160 236 0 0 0 0 0
// Interpretation:
// OK 24: fixed
// 1 Byte: channel
// 1 Byte: command (04=retrieve measure data, 05=switch device, 06=identify device by toggling device
// LED
// 3 Byte: device address (UID)
// 1 Byte: data -> 1 with command=4 resets device statistics
// -> 0/1 with command=5 switches device off/on
// 2 Byte: current consumption in watt (scale 1/10)
// 2 Byte: total consumption in kWh (scale 1/100)
logger.trace("Creating reading from: {}", inputLine);
int channelId = Integer.parseInt(matcher.group(1));
String sensorId = matcher.group(2) + "-" + matcher.group(3) + "-" + matcher.group(4);
int data = Integer.parseInt(matcher.group(5));
long con1 = Long.parseLong(matcher.group(6));
long con2 = Long.parseLong(matcher.group(7));
long consumptionCurrent = ((con1 << 8) + con2);
con1 = Long.parseLong(matcher.group(8));
con2 = Long.parseLong(matcher.group(9));
long consumptionTotal = ((con1 << 8) + con2);
return new Pca301Reading(sensorId, channelId, data == 1, consumptionCurrent / 10f,
consumptionTotal * 10);
}
}
return null;
}
}

View File

@@ -0,0 +1,46 @@
/**
* 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.jeelink.internal.pca301;
import org.openhab.binding.jeelink.internal.JeeLinkBindingConstants;
import org.openhab.binding.jeelink.internal.JeeLinkReadingConverter;
import org.openhab.binding.jeelink.internal.JeeLinkSensorHandler;
import org.openhab.binding.jeelink.internal.SensorDefinition;
import org.openhab.core.thing.Thing;
/**
* Sensor Defintion of a PCA301 power switchable outlet.
*
* @author Volker Bier - Initial contribution
*/
public class Pca301SensorDefinition extends SensorDefinition<Pca301Reading> {
public Pca301SensorDefinition() {
super(JeeLinkBindingConstants.PCA301_SENSOR_THING_TYPE, "PCA301 power monitoring wireless socket", "24");
}
@Override
public JeeLinkReadingConverter<Pca301Reading> createConverter() {
return new Pca301ReadingConverter();
}
@Override
public Class<Pca301Reading> getReadingClass() {
return Pca301Reading.class;
}
@Override
public JeeLinkSensorHandler<Pca301Reading> createHandler(Thing thing) {
return new Pca301SensorHandler(thing, type);
}
}

View File

@@ -0,0 +1,177 @@
/**
* 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.jeelink.internal.pca301;
import static org.openhab.binding.jeelink.internal.JeeLinkBindingConstants.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.openhab.binding.jeelink.internal.JeeLinkHandler;
import org.openhab.binding.jeelink.internal.JeeLinkSensorHandler;
import org.openhab.binding.jeelink.internal.ReadingPublisher;
import org.openhab.binding.jeelink.internal.config.Pca301SensorConfig;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handler for a EC3000 sensor thing.
*
* @author Volker Bier - Initial contribution
*/
public class Pca301SensorHandler extends JeeLinkSensorHandler<Pca301Reading> {
private final Logger logger = LoggerFactory.getLogger(Pca301SensorHandler.class);
private JeeLinkHandler bridge;
private OnOffType state;
private final AtomicInteger channel = new AtomicInteger(-1);
private ScheduledFuture<?> retry;
private int sendCount;
public Pca301SensorHandler(Thing thing, String sensorType) {
super(thing, sensorType);
}
@Override
public Class<Pca301Reading> getReadingClass() {
return Pca301Reading.class;
}
@Override
public void initialize() {
super.initialize();
bridge = (JeeLinkHandler) getBridge().getHandler();
Pca301SensorConfig cfg = getConfigAs(Pca301SensorConfig.class);
sendCount = cfg.sendCount;
logger.debug("initilized handler for thing {} ({}): sendCount = {}", getThing().getLabel(),
getThing().getUID().getId(), sendCount);
}
@Override
public void dispose() {
super.dispose();
cancelRetry();
}
@Override
public synchronized void handleCommand(ChannelUID channelUid, Command command) {
logger.debug("received command for thing {} ({}): {}", getThing().getLabel(), getThing().getUID().getId(),
command);
if (channelUid.getIdWithoutGroup().equals(SWITCHING_STATE_CHANNEL)) {
if (command instanceof OnOffType) {
sendCommandRetry((OnOffType) command);
} else {
sendCommand(command);
}
} else if (command != RefreshType.REFRESH) {
logger.warn("Unsupported command {} for channel {} of sensor with id {}.", command,
channelUid.getIdWithoutGroup(), this.id);
}
}
@Override
public ReadingPublisher<Pca301Reading> createPublisher() {
return new ReadingPublisher<Pca301Reading>() {
@Override
public void publish(Pca301Reading reading) {
if (reading != null) {
channel.set(reading.getChannel());
BigDecimal current = new BigDecimal(reading.getCurrent()).setScale(1, RoundingMode.HALF_UP);
state = reading.isOn() ? OnOffType.ON : OnOffType.OFF;
updateState(CURRENT_POWER_CHANNEL, new QuantityType<>(current, SmartHomeUnits.WATT));
updateState(CONSUMPTION_CHANNEL, new QuantityType<>(reading.getTotal(), SmartHomeUnits.WATT_HOUR));
updateState(SWITCHING_STATE_CHANNEL, state);
logger.debug("updated states for thing {} ({}): state={}, current={}, total={}",
getThing().getLabel(), getThing().getUID().getId(), state, current, reading.getTotal());
}
}
@Override
public void dispose() {
}
};
}
private void sendCommand(Command command) {
int chan = channel.get();
if (chan != -1) {
if (command == RefreshType.REFRESH) {
bridge.getConnection().sendCommands(chan + ",4," + id.replaceAll("-", ",") + ",0,255,255,255,255s");
} else if (command instanceof OnOffType) {
bridge.getConnection().sendCommands(chan + ",5," + id.replaceAll("-", ",") + ","
+ (command == OnOffType.ON ? "1" : "2") + ",255,255,255,255s");
} else {
logger.warn("Unsupported command {} for sensor with id {}.", command, this.id);
}
} else if (command != RefreshType.REFRESH && !(command instanceof OnOffType)) {
logger.warn("Could not send command {} for sensor with id {}. Ignoring command.", command, this.id);
}
}
private synchronized void sendCommandRetry(OnOffType command) {
cancelRetry();
retry = scheduler.scheduleWithFixedDelay(new Runnable() {
int remainingRetries = sendCount;
@Override
public void run() {
if (state == null) {
logger.debug("skip sending of command (current state not yet known) for thing {} ({}): {}",
getThing().getLabel(), getThing().getUID().getId(), command);
} else if ((state != command && remainingRetries > 0)) {
logger.debug("sending command for thing {} ({}) attempt {}/{}: {}", getThing().getLabel(),
getThing().getUID().getId(), (sendCount - remainingRetries + 1), sendCount, command);
sendCommand(command);
remainingRetries--;
} else {
// we get here when the state is as expected or when the state is still not as expected after
// the configured number of retries. we should cancel the retry for both cases
if (state != command) {
logger.debug("giving up command for thing {} ({}): {}", getThing().getLabel(),
getThing().getUID().getId(), command);
}
cancelRetry();
}
}
}, 0, 2, TimeUnit.SECONDS);
}
private synchronized void cancelRetry() {
if (retry != null) {
retry.cancel(true);
retry = null;
}
}
}

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.jeelink.internal.revolt;
import org.openhab.binding.jeelink.internal.Reading;
/**
* Reading of a Revolt Energy Meter sensor.
*
* @author Volker Bier - Initial contribution
*/
public class RevoltReading implements Reading {
private int voltage;
private float current;
private int frequency;
private float power;
private float powerFact;
private float consumption;
private String sensorId;
public RevoltReading(String sensorId, int voltage, float current, int frequency, float power, float powerFactor,
float consumption) {
this.sensorId = sensorId;
this.voltage = voltage;
this.current = current;
this.frequency = frequency;
this.power = power;
this.powerFact = powerFactor;
this.consumption = consumption;
}
@Override
public String toString() {
return "sensorId=" + sensorId + ": voltage=" + voltage + ", current=" + current + ", frequency=" + frequency
+ ", power=" + power + ", powerFact=" + powerFact + ", consumption=" + consumption;
}
@Override
public String getSensorId() {
return sensorId;
}
public int getVoltage() {
return voltage;
}
public float getCurrent() {
return current;
}
public int getFrequency() {
return frequency;
}
public float getPower() {
return power;
}
public float getPowerFactor() {
return powerFact;
}
public float getConsumption() {
return consumption;
}
}

View File

@@ -0,0 +1,58 @@
/**
* 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.jeelink.internal.revolt;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openhab.binding.jeelink.internal.JeeLinkReadingConverter;
/**
* Converter for converting a line read from a SlowRF CUL to a RevoltReading.
*
* @author Volker Bier - Initial contribution
*/
public class RevoltReadingConverter implements JeeLinkReadingConverter<RevoltReading> {
private static final Pattern LINE_P = Pattern
.compile("r([0-9A-Za-z]{4})([0-9A-Za-z]{2})([0-9A-Za-z]{4})([0-9A-Za-z]{2})([0-9A-Za-z]{4})"
+ "([0-9A-Za-z]{2})([0-9A-Za-z]{4})[0-9A-Za-z][0-9A-Za-z]");
@Override
public RevoltReading createReading(String inputLine) {
if (inputLine != null) {
Matcher matcher = LINE_P.matcher(inputLine);
if (matcher.matches()) {
/*
* r4F1BE400513206875B312F25
*/
String id = matcher.group(1); // 4F1B
int voltage = toInt(matcher.group(2)); // 0xE4 = 228 => 228 V
float current = toInt(matcher.group(3)) / 100f; // 0x0051 = 81 => 0,81 A
int frequency = toInt(matcher.group(4)); // 0x32 = 50 => 50 Hz
float power = toInt(matcher.group(5)) / 10f; // 0x0687 = 1671 => 167,1 W
float powerFact = toInt(matcher.group(6)) / 100f; // 0x5B = 91 => 0,91 VA
float consumption = toInt(matcher.group(7)) / 100f; // 0x312F = 12591 => 125,91 Wh
return new RevoltReading(id, voltage, current, frequency, power, powerFact, consumption);
}
}
return null;
}
private int toInt(String hex) {
Integer i = Integer.parseInt(hex, 16);
return i.intValue();
}
}

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.jeelink.internal.revolt;
import org.openhab.binding.jeelink.internal.JeeLinkBindingConstants;
import org.openhab.binding.jeelink.internal.JeeLinkReadingConverter;
import org.openhab.binding.jeelink.internal.JeeLinkSensorHandler;
import org.openhab.binding.jeelink.internal.SensorDefinition;
import org.openhab.core.thing.Thing;
/**
* Sensor Definition of a Revolt Energy Meter.
*
* @author Volker Bier - Initial contribution
*/
public class RevoltSensorDefinition extends SensorDefinition<RevoltReading> {
public RevoltSensorDefinition() {
super(JeeLinkBindingConstants.REVOLT_SENSOR_THING_TYPE, "Revolt Power Monitor", "r");
}
@Override
public JeeLinkReadingConverter<RevoltReading> createConverter() {
return new RevoltReadingConverter();
}
@Override
public Class<RevoltReading> getReadingClass() {
return RevoltReading.class;
}
@Override
public JeeLinkSensorHandler<RevoltReading> createHandler(Thing thing) {
return new RevoltSensorHandler(thing, type);
}
}

View File

@@ -0,0 +1,80 @@
/**
* 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.jeelink.internal.revolt;
import static org.openhab.binding.jeelink.internal.JeeLinkBindingConstants.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import org.openhab.binding.jeelink.internal.JeeLinkSensorHandler;
import org.openhab.binding.jeelink.internal.ReadingPublisher;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Handler for a Revolt Energy Meter sensor thing.
*
* @author Volker Bier - Initial contribution
*/
public class RevoltSensorHandler extends JeeLinkSensorHandler<RevoltReading> {
private final Logger logger = LoggerFactory.getLogger(RevoltSensorHandler.class);
public RevoltSensorHandler(Thing thing, String sensorType) {
super(thing, sensorType);
}
@Override
public Class<RevoltReading> getReadingClass() {
return RevoltReading.class;
}
@Override
public ReadingPublisher<RevoltReading> createPublisher() {
ReadingPublisher<RevoltReading> publisher = new ReadingPublisher<RevoltReading>() {
@Override
public void publish(RevoltReading reading) {
if (reading != null && getThing().getStatus() == ThingStatus.ONLINE) {
BigDecimal power = new BigDecimal(reading.getPower()).setScale(1, RoundingMode.HALF_UP);
BigDecimal powerFactor = new BigDecimal(reading.getPowerFactor()).setScale(2, RoundingMode.HALF_UP);
BigDecimal consumption = new BigDecimal(reading.getConsumption()).setScale(2, RoundingMode.HALF_UP);
BigDecimal current = new BigDecimal(reading.getCurrent()).setScale(2, RoundingMode.HALF_UP);
logger.debug(
"updating states for thing {}: power={}, powerFactor={}, consumption={}, current={}, voltage={}, frequency={} ",
getThing().getUID().getId(), power, powerFactor, consumption, current, reading.getVoltage(),
reading.getFrequency());
updateState(CURRENT_POWER_CHANNEL, new QuantityType<>(power, SmartHomeUnits.WATT));
updateState(POWER_FACTOR_CHANNEL, new DecimalType(powerFactor));
updateState(CONSUMPTION_CHANNEL, new QuantityType<>(consumption, SmartHomeUnits.WATT_HOUR));
updateState(ELECTRIC_CURRENT_CHANNEL, new QuantityType<>(current, SmartHomeUnits.AMPERE));
updateState(ELECTRIC_POTENTIAL_CHANNEL,
new QuantityType<>(reading.getVoltage(), SmartHomeUnits.VOLT));
updateState(FREQUENCY_CHANNEL, new QuantityType<>(reading.getFrequency(), SmartHomeUnits.HERTZ));
}
}
@Override
public void dispose() {
}
};
return publisher;
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="jeelink" 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>@text/binding.jeelink.name</name>
<description>@text/binding.jeelink.description</description>
<author>Volker Bier</author>
</binding:binding>

View File

@@ -0,0 +1,102 @@
# binding
binding.jeelink.name = JeeLink Binding
binding.jeelink.description = This is the binding for JeeLink USB Receivers, LaCrosseGateways and connected sensors.
# bridge types
bridge-type.jeelinkUsb.label = JeeLink (connected to USB)
bridge-type.jeelinkUsb.description = Thing for a JeeLink USB Receiver. Currently supports LaCrosseITPlusReader and ec3kSerial sketches.
bridge-type.jeelinkTcp.label = JeeLink (connected over TCP)
bridge-type.jeelinkTcp.description = Thing for a JeeLink USB Receiver that is connected to a different machine on the network and made available to the openHAB server via TCP. Currently supports LaCrosseITPlusReader and ec3kSerial sketches.
bridge-type.lgwUsb.label = LaCrosseGateway (connected to USB)
bridge-type.lgwUsb.description = Thing for a LaCrosseGateway connected directly to the USB port.
bridge-type.lgwTcp.label = LaCrosseGateway (connected over TCP)
bridge-type.lgwTcp.description = Thing for a LaCrosseGateway that is connected via network.
# thing types
thing-type.lacrosse.label = Lacrosse Temperature Sensor
thing-type.lacrosse.description = Thing for a Lacrosse Temperature Sensor connected to a JeeLink USB Receiver.
thing-type.ec3k.label = ec3k
thing-type.ec3k.description = Thing for a EnergyCount 3000 Power Monitor connected to a JeeLink USB Receiver.
thing-type.pca301.label = PCA301
thing-type.pca301.description = Thing for a PCA301 power monitoring wireless socket connected to a JeeLink USB Receiver.
thing-type.tx22.label = TX22 Sensor
thing-type.tx22.description = Thing for a TX22 Sensor connected to a JeeLink USB Receiver.
thing-type.revolt.label = Revolt Power Monitor
thing-type.revolt.description = Thing for a Revolt Power Monitor connected to a JeeLink USB Receiver.
thing-type.lgw.label = LGW Sensor
thing-type.lgw.description = Thing for a Sensor directly connected to a LGW.
# parameters
parameter.serialport.label = Serial Port
parameter.serialport.description = The serial port name for the USB receiver. Valid values are e.g. COM1 for Windows and /dev/ttyS0 or /dev/ttyUSB0 for Linux.
parameter.serialportlgw.description = The serial port name for the LaCrosseGateway. Valid values are e.g. COM1 for Windows and /dev/ttyS0 or /dev/ttyUSB0 for Linux.
parameter.baudrate.label = Baud Rate
parameter.baudrate.description = The baud rate of the USB Receiver / LGW. Valid values are 9600, 19200, 38400, 57600 (default), and 115200.
parameter.ipaddress.label = IP Address
parameter.ipaddress.description = The IP address of the Server to which the USB Receiver is connected.
parameter.ipaddresslgw.description = The IP address of the LGW.
parameter.port.label = TCP Port
parameter.port.description = The TCP port over which the serial port is made available.
parameter.portlgw.description = The TCP port of the LGW (usually this is port 81).
parameter.initcommands.label = Init Commands
parameter.initcommands.description = Optional initialization commands (semicolon separated) that will be send after the first reading has been received, e.g. "0a" to turn of the LED.
parameter.initdelay.label = Init Delay
parameter.initdelay.description = Time after which the init command is send after the connection has been established if no readings have been received.
parameter.reconnectinterval.label = Reconnect Interval
parameter.reconnectinterval.description = The number of seconds after which a reconnect is triggered when no values could be read fron any of the sensors.
parameter.sensorid.label = Sensor ID
parameter.sensorid.description = The sensor ID used by this sensor.
parameter.updateinterval.label = Update Interval
parameter.updateinterval.description = The update interval in seconds (0 puts the sensor in live mode). Setting this to a value greater than zero only makes sense if you specify a buffer size greater one.
parameter.sensortimeout.label = Sensor Timeout
parameter.sensortimeout.description = The amount of time that should result in OFFLINE status when no readings have been received from the sensor (in seconds).
parameter.buffersize.label = Buffer Size
parameter.buffersize.description = The amount of readings that should be used to compute a rolling average (1 disables the rolling average).
parameter.mintemp.label = Lower Temperature Limit
parameter.mintemp.description = The lowest temperature allowed as valid reading from the sensor. All lower readings will be ignored.
parameter.maxtemp.label = Upper Temperature Limit
parameter.maxtemp.description = The highest temperature allowed as valid reading from the sensor. All higher readings will be ignored.
parameter.maxdiff.label = Maximum Allowed Temperature Difference
parameter.maxdiff.description = The maximum allowed absolute difference from a value to the previous value (0 disables this check). If the difference is higher, the reading will be ignored.
parameter.sendCount.label = Switching Command Count
parameter.sendCount.description = The number of times a switching command will be sent (every 2 seconds) to the socket until giving up.
# channel types
channel-type.current-power.label = Current Power
channel-type.current-power.description = The current power draw of the appliance.
channel-type.max-power.label = Max Power
channel-type.max-power.description = The maximum power draw of the appliance.
channel-type.consumption-total.label = Total Consumption
channel-type.consumption-total.description = The total consumption of the connected appliance.
channel-type.appliance-time.label = Appliance On Time
channel-type.appliance-time.description = The time the appliance was turned on.
channel-type.sensor-time.label = Sensor On Time
channel-type.sensor-time.description = The time the EC3000 was connected to an outlet.
channel-type.resets.label = Resets
channel-type.resets.description = Number of resets performed by the sensor.
channel-type.temperature.label = Temperature
channel-type.temperature.description = The temperature read from the sensor.
channel-type.humidity.label = Humidity
channel-type.humidity.description = The humidity read from the sensor.
channel-type.battery-new.label = Battery New
channel-type.battery-new.description = Indicator for new battery.
channel-type.switching-state.label = Switching State
channel-type.switching-state.description = Whether the socket is currently switched on or not.
channel-type.wind-angle.label = Wind Angle
channel-type.wind-angle.description = Current wind direction
channel-type.wind-strength.label = Wind Strength
channel-type.wind-strength.description = Current wind speed
channel-type.rain.label = Rain
channel-type.rain.description = Quantity of water
channel-type.pressure.label = Pressure
channel-type.pressure.description = Current pressure
gust-strength.label = Gust Strength
gust-strength.description = Current gust speed
channel-type.electric-current.label = Electric Current
channel-type.electric-current.description = The measured electric current.
channel-type.power-factor.label = Power Factor
channel-type.power-factor.description = The ratio of the real power absorbed by the load to the apparent power flowing in the circuit.
channel-type.electric-potential.label = Voltage
channel-type.electric-potential.description = The measured electric potential.
channel-type.power-frequency.label = Power Frequency
channel-type.power-frequency.description = The measured AC power frequency.

View File

@@ -0,0 +1,102 @@
# binding
binding.jeelink.name = JeeLink Binding
binding.jeelink.description = Binding für JeeLink USB Empfänger, LaCrosseGateways und damit verbundene Sensoren.
# bridge types
bridge-type.jeelinkUsb.label = JeeLink USB Empfänger
bridge-type.jeelinkUsb.description = JeeLink USB Empfänger. Momentan werden LaCrosseITPlusReader und ec3kSerial Sketche unterstützt.
bridge-type.jeelinkTcp.label = JeeLink über TCP
bridge-type.jeelinkTcp.description = Jeelink USB Empfänger, der an einem anderen Rechner angeschlossen ist. Die Schnittstelle muss über TCP zur Verfügung gestellt werden (z.B. auf Linux mit ser2net).
bridge-type.lgwUsb.label = LaCrosseGateway am USB Port
bridge-type.lgwUsb.description = LaCrosseGateway, das direkt am USB Port des openHAB Rechners angeschlossen ist.
bridge-type.lgwTcp.label = LaCrosseGateway über TCP
bridge-type.lgwTcp.description = LaCrosseGateway der über das Netzwerk verbunden ist.
# thing types
thing-type.lacrosse.label = Lacrosse Temperatur-Sensor
thing-type.lacrosse.description = Ein mit dem JeeLink USB Empfänger verbundener Lacrosse Temperatur-Sensor.
thing-type.ec3k.label = ec3k
thing-type.ec3k.description = Ein mit dem JeeLink USB Empfänger verbundenes EC3000 Energiekosten-Messgerät.
thing-type.pca301.label = PCA301
thing-type.pca301.description = Eine mit dem JeeLink USB Empfänger verbundene PCA301 Steckdose.
thing-type.tx22.label = TX22 Sensor
thing-type.tx22.description = Ein mit dem JeeLink USB Empfänger verbundener TX22 Sensor.
thing-type.revolt.label = Revolt Energie-Messgerät
thing-type.revolt.description = Ein mit dem JeeLink USB Empfänger verbundenes Revolt Energie-Messgerät.
thing-type.lgw.label = LGW Sensor
thing-type.lgw.description = Ein direkt mit dem LGW verbundener Sensor.
# parameters
parameter.serialport.label = Serielle Schnittstelle
parameter.serialport.description = Der Name der seriellen Schnittstelle, an die der Jeelink angeschlossen ist. Gültige Werte sind z.B. COM1 für Windows und /dev/ttyS0 oder /dev/ttyUSB0 für Linux.
parameter.serialportlgw.description = Der Name der seriellen Schnittstelle, an die das LGW angeschlossen ist. Gültige Werte sind z.B. COM1 für Windows und /dev/ttyS0 oder /dev/ttyUSB0 für Linux.
parameter.baudrate.label = Baud Rate
parameter.baudrate.description = Die Baud Rate der seriellen Schnittstelle. Gültige Werte sind 9600, 19200, 38400, 57600 (default), and 115200.
parameter.ipaddress.label = IP Adresse
parameter.ipaddress.description = Die IP Adresse des Rechners, an den der Jeelink angeschlossen ist.
parameter.ipaddresslgw.description = Die IP Adresse LGWs.
parameter.port.label = TCP Port
parameter.port.description = Der TCP Port über den die serielle Schnittstelle bereit gestellt wird.
parameter.portlgw.description = Der TCP Port des LGWs (dies ist normalerweise Port 81).
parameter.initcommands.label = Init Kommandos
parameter.initcommands.description = Optionale Initialisierungs-Kommandos (durch Semikolon getrennt) die nach Empfang des ersten Wertes an den Jeelink geschickt werden, z.B. "0a" um die LED auszuschalten.
parameter.initdelay.label = Init Verzögerung
parameter.initdelay.description = Zeit nach der die Init Kommandos gesendet werden nachdem die Verbindung hergestellt wurde wenn keine Werte empfangen wurden.
parameter.reconnectinterval.label = Reconnect Intervall
parameter.reconnectinterval.description = Die Anzahl an Sekunden, nach denen die Verbindung neu aufgebaut wird, falls in der Zwischenzeit kein Wert von einem Sensor gelesen werden konnte.
parameter.sensorid.label = Sensor ID
parameter.sensorid.description = Die Sensor ID dieses Sensors.
parameter.updateinterval.label = Update Intervall
parameter.updateinterval.description = Das Update Intervall in Sekunden (0 setzt den Sensor in den 'live' Modus, d.h. jeder Wert wird an openHAB weitergereicht). Werte größer 0 machen nur Sinn, wenn eine Buffer Größe größer als 1 konfiguriert ist.
parameter.sensortimeout.label = Sensor Timeout
parameter.sensortimeout.description = Die Anzahl an Sekunden, nach deren Ausbleiben von Messwerten der Sensor Status auf OFFLINE gesetzt wird.
parameter.buffersize.label = Buffer Größe
parameter.buffersize.description = Die Anzahl von Messwertden, die zur Berechnung des Durchschnitts benutzt werden (1 schaltet die Durchschnittsberechnung ab).
parameter.mintemp.label = Minimal gültige Temperatur
parameter.mintemp.description = Die niedrigste vom Sensor gelesene, als gültig erachtete Temperatur. Alle niedrigeren Werte werden ignoriert.
parameter.maxtemp.label = Maximal gültige Temperatur
parameter.maxtemp.description = Die höchste vom Sensor gelesene, als gültig erachtete Temperatur. Alle höheren Werte werden ignoriert.
parameter.maxdiff.label = Maximal erlaubte Temperaturdifferenz
parameter.maxdiff.description = Die maximal erlaubte absolute Differenz eines Wertes zum Vorherigen (0 schaltet diese Prüfung ab). Alle Werte mit größerer Differenz werden ignoriert.
parameter.sendCount.label = Anzahl der Schaltversuche
parameter.sendCount.description = Die Anzahl der Versuche eine Steckdose zu schalten (alle 2 Sekunden) bevor aufgegeben wird.
# channel types
channel-type.current-power.label = Momentaner Verbrauch
channel-type.current-power.description = Der momentane Verbrauch des angeschlossenen Gerätes.
channel-type.max-power.label = Höchster Verbrauch
channel-type.max-power.description = Der höchste Verbrauch des angeschlossenen Gerätes.
channel-type.consumption-total.label = Absoluter Verbrauch
channel-type.consumption-total.description = Der absolute Verbrauch des angeschlossenen Gerätes.
channel-type.appliance-time.label = Einschaltzeit des Gerätes
channel-type.appliance-time.description = Die Zeit während derer das angeschlossene Gerät eingeschaltet war.
channel-type.sensor-time.label = Einschaltzeit des Sensoren
channel-type.sensor-time.description = Die Zeit während derer der Sensor eingeschaltet war.
channel-type.resets.label = Resets
channel-type.resets.description = Anzahl der Resets dieses Sensors.
channel-type.temperature.label = Temperatur
channel-type.temperature.description = Die vom Sensor gelesene Temperatur.
channel-type.humidity.label = Luftfeuchtigkeit
channel-type.humidity.description = Die vom Sensor gelesene Luftfeuchtigkeit.
channel-type.battery-new.label = Battery neu
channel-type.battery-new.description = Indikator für eine neu eingesetzte Batterie.
channel-type.switching-state.label = Steckdose Ein
channel-type.switching-state.description = Schaltzustand der Steckdose.
channel-type.wind-angle.label = Wind Richtung
channel-type.wind-angle.description = Momentane Wind Richtung
channel-type.wind-strength.label = Windstärke
channel-type.wind-strength.description = Momentane Windstärke
channel-type.rain.label = Regen
channel-type.rain.description = Regenmenge
channel-type.pressure.label = Luftdruck
channel-type.pressure.description = Momentaner Luftdruck
gust-strength.label = Böenstärke
gust-strength.description = Momentane Böenstärke
channel-type.electric-current.label = Stromstärke
channel-type.electric-current.description = Die gemessene elektrische Stromstärke .
channel-type.power-factor.label = Leistungsfaktor
channel-type.power-factor.description = Verhältnis des Betrages der Wirkleistung zur Scheinleistung.
channel-type.electric-potential.label = Spannung
channel-type.electric-potential.description = Die gemessene elektrische Spannung.
channel-type.power-frequency.label = Stromnetzfrequenz
channel-type.power-frequency.description = Die gemessene Netzfrequenz des Stromnetzes.

View File

@@ -0,0 +1,537 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="jeelink"
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">
<!-- JeeLink USB Receiver Bridge Type -->
<bridge-type id="jeelinkUsb">
<label>@text/bridge-type.jeelinkUsb.label</label>
<description>@text/bridge-type.jeelinkUsb.description</description>
<config-description>
<parameter name="serialPort" type="text" required="true">
<label>@text/parameter.serialport.label</label>
<context>serial-port</context>
<limitToOptions>false</limitToOptions>
<description>@text/parameter.serialport.description</description>
</parameter>
<parameter name="initCommands" type="text" required="false">
<label>@text/parameter.initcommands.label</label>
<description>@text/parameter.initcommands.description</description>
</parameter>
<parameter name="baudRate" type="integer" required="false">
<label>@text/parameter.baudrate.label</label>
<description>@text/parameter.baudrate.description</description>
<default>57600</default>
<advanced>true</advanced>
</parameter>
<parameter name="initDelay" type="integer" required="false">
<label>@text/parameter.initdelay.label</label>
<description>@text/parameter.initdelay.description</description>
<default>10</default>
<advanced>true</advanced>
</parameter>
<parameter name="reconnectInterval" type="integer" required="false">
<label>@text/parameter.reconnectinterval.label</label>
<description>@text/parameter.reconnectinterval.description</description>
<default>300</default>
<advanced>true</advanced>
</parameter>
</config-description>
</bridge-type>
<!-- LGW connected to USB port -->
<bridge-type id="lgwUsb">
<label>@text/bridge-type.lgwUsb.label</label>
<description>@text/bridge-type.lgwUsb.description</description>
<config-description>
<parameter name="serialPort" type="text" required="true">
<label>@text/parameter.serialport.label</label>
<context>serial-port</context>
<limitToOptions>false</limitToOptions>
<description>@text/parameter.serialportlgw.description</description>
</parameter>
<parameter name="initCommands" type="text" required="false">
<label>@text/parameter.initcommands.label</label>
<description>@text/parameter.initcommands.description</description>
</parameter>
<parameter name="baudRate" type="integer" required="false">
<label>@text/parameter.baudrate.label</label>
<description>@text/parameter.baudrate.description</description>
<default>57600</default>
<advanced>true</advanced>
</parameter>
<parameter name="initDelay" type="integer" required="false">
<label>@text/parameter.initdelay.label</label>
<description>@text/parameter.initdelay.description</description>
<default>20</default>
<advanced>true</advanced>
</parameter>
<parameter name="reconnectInterval" type="integer" required="false">
<label>@text/parameter.reconnectinterval.label</label>
<description>@text/parameter.reconnectinterval.description</description>
<default>300</default>
<advanced>true</advanced>
</parameter>
</config-description>
</bridge-type>
<!-- JeeLink USB Receiver Bridge connected over TCP Type -->
<bridge-type id="jeelinkTcp">
<label>@text/bridge-type.jeelinkTcp.label</label>
<description>@text/bridge-type.jeelinkTcp.description</description>
<config-description>
<parameter name="ipAddress" type="text" required="true">
<label>@text/parameter.ipaddress.label</label>
<description>@text/parameter.ipaddress.description</description>
<context>network-address</context>
</parameter>
<parameter name="port" type="integer" required="true">
<label>@text/parameter.port.label</label>
<description>@text/parameter.port.description</description>
</parameter>
<parameter name="initCommands" type="text" required="false">
<label>@text/parameter.initcommands.label</label>
<description>@text/parameter.initcommands.description</description>
</parameter>
<parameter name="initDelay" type="integer" required="false">
<label>@text/parameter.initdelay.label</label>
<description>@text/parameter.initdelay.description</description>
<default>10</default>
<advanced>true</advanced>
</parameter>
<parameter name="reconnectInterval" type="integer" required="false">
<label>@text/parameter.reconnectinterval.label</label>
<description>@text/parameter.reconnectinterval.description</description>
<default>300</default>
<advanced>true</advanced>
</parameter>
</config-description>
</bridge-type>
<!-- LaCrosseGateway connected over TCP Type -->
<bridge-type id="lgwTcp">
<label>@text/bridge-type.lgwTcp.label</label>
<description>@text/bridge-type.lgwTcp.description</description>
<config-description>
<parameter name="ipAddress" type="text" required="true">
<label>@text/parameter.ipaddress.label</label>
<description>@text/parameter.ipaddresslgw.description</description>
<context>network-address</context>
</parameter>
<parameter name="initCommands" type="text" required="false">
<label>@text/parameter.initcommands.label</label>
<description>@text/parameter.initcommands.description</description>
<advanced>true</advanced>
</parameter>
<parameter name="port" type="integer" required="false">
<label>@text/parameter.port.label</label>
<description>@text/parameter.portlgw.description</description>
<default>81</default>
<advanced>true</advanced>
</parameter>
<parameter name="initDelay" type="integer" required="false">
<label>@text/parameter.initdelay.label</label>
<description>@text/parameter.initdelay.description</description>
<default>20</default>
<advanced>true</advanced>
</parameter>
<parameter name="reconnectInterval" type="integer" required="false">
<label>@text/parameter.reconnectinterval.label</label>
<description>@text/parameter.reconnectinterval.description</description>
<default>300</default>
<advanced>true</advanced>
</parameter>
</config-description>
</bridge-type>
<!-- Lacrosse Temperature Sensor Thing Type -->
<thing-type id="lacrosse">
<supported-bridge-type-refs>
<bridge-type-ref id="jeelinkTcp"/>
<bridge-type-ref id="jeelinkUsb"/>
<bridge-type-ref id="lgwTcp"/>
<bridge-type-ref id="lgwUsb"/>
</supported-bridge-type-refs>
<label>@text/thing-type.lacrosse.label</label>
<description>@text/thing-type.lacrosse.description</description>
<channels>
<channel id="temperature" typeId="temperature"/>
<channel id="humidity" typeId="humidity"/>
<channel id="batteryNew" typeId="battery-new"/>
<channel id="batteryLow" typeId="system.low-battery"/>
</channels>
<config-description>
<parameter name="sensorId" type="text" required="true">
<label>@text/parameter.sensorid.label</label>
<description>@text/parameter.sensorid.description</description>
</parameter>
<parameter name="updateInterval" type="integer" required="false" min="0" max="3600" unit="s" step="5">
<label>@text/parameter.updateinterval.label</label>
<description>@text/parameter.updateinterval.description</description>
<default>60</default>
</parameter>
<parameter name="sensorTimeout" type="integer" required="false" min="5" max="3600" unit="s" step="5">
<label>@text/parameter.sensortimeout.label</label>
<description>@text/parameter.sensortimeout.description</description>
<default>60</default>
<advanced>true</advanced>
</parameter>
<parameter name="bufferSize" type="integer" required="false" min="1" max="100">
<label>@text/parameter.buffersize.label</label>
<description>@text/parameter.buffersize.description</description>
<default>20</default>
<advanced>true</advanced>
</parameter>
<parameter name="minTemp" type="decimal" required="false">
<label>@text/parameter.mintemp.label</label>
<description>@text/parameter.mintemp.description</description>
<default>-100</default>
</parameter>
<parameter name="maxTemp" type="decimal" required="false">
<label>@text/parameter.maxtemp.label</label>
<description>@text/parameter.maxtemp.description</description>
<default>100</default>
</parameter>
<parameter name="maxDiff" type="decimal" required="true">
<label>@text/parameter.maxdiff.label</label>
<description>@text/parameter.maxdiff.description</description>
<default>2</default>
</parameter>
</config-description>
</thing-type>
<!-- EC3000 Power Monitor Thing Type -->
<thing-type id="ec3k">
<supported-bridge-type-refs>
<bridge-type-ref id="jeelinkTcp"/>
<bridge-type-ref id="jeelinkUsb"/>
<bridge-type-ref id="lgwTcp"/>
<bridge-type-ref id="lgwUsb"/>
</supported-bridge-type-refs>
<label>@text/thing-type.ec3k.label</label>
<description>@text/thing-type.ec3k.description</description>
<channels>
<channel id="currentPower" typeId="current-power"/>
<channel id="maxPower" typeId="max-power"/>
<channel id="consumptionTotal" typeId="consumption-total"/>
<channel id="applianceTime" typeId="appliance-time"/>
<channel id="sensorTime" typeId="sensor-time"/>
<channel id="resets" typeId="resets"/>
</channels>
<config-description>
<parameter name="sensorId" type="text" required="true">
<label>@text/parameter.sensorid.label</label>
<description>@text/parameter.sensorid.description</description>
</parameter>
<parameter name="updateInterval" type="integer" required="false" min="0" max="3600" unit="s" step="5">
<label>@text/parameter.updateinterval.label</label>
<description>@text/parameter.updateinterval.description</description>
<default>60</default>
</parameter>
<parameter name="sensorTimeout" type="integer" required="false" min="5" max="3600" unit="s" step="5">
<label>@text/parameter.sensortimeout.label</label>
<description>@text/parameter.sensortimeout.description</description>
<default>60</default>
</parameter>
<parameter name="bufferSize" type="integer" required="false" min="1" max="100">
<label>@text/parameter.buffersize.label</label>
<description>@text/parameter.buffersize.description</description>
<default>20</default>
<advanced>true</advanced>
</parameter>
</config-description>
</thing-type>
<!-- PCA301 power monitoring wireless socket Thing Type -->
<thing-type id="pca301">
<supported-bridge-type-refs>
<bridge-type-ref id="jeelinkTcp"/>
<bridge-type-ref id="jeelinkUsb"/>
<bridge-type-ref id="lgwTcp"/>
<bridge-type-ref id="lgwUsb"/>
</supported-bridge-type-refs>
<label>@text/thing-type.pca301.label</label>
<description>@text/thing-type.pca301.description</description>
<channels>
<channel id="switchingState" typeId="switching-state"/>
<channel id="currentPower" typeId="current-power"/>
<channel id="consumptionTotal" typeId="consumption-total"/>
</channels>
<config-description>
<parameter name="sensorId" type="text" required="true">
<label>@text/parameter.sensorid.label</label>
<description>@text/parameter.sensorid.description</description>
</parameter>
<parameter name="sensorTimeout" type="integer" required="false" min="5" max="3600" unit="s" step="5">
<label>@text/parameter.sensortimeout.label</label>
<description>@text/parameter.sensortimeout.description</description>
<default>600</default>
</parameter>
<parameter name="sendCount" type="integer" required="false" min="1" max="300">
<label>@text/parameter.sendCount.label</label>
<description>@text/parameter.sendCount.description</description>
<default>10</default>
</parameter>
</config-description>
</thing-type>
<!-- Revolt Energy Meter Thing Type -->
<thing-type id="revolt">
<supported-bridge-type-refs>
<bridge-type-ref id="jeelinkTcp"/>
<bridge-type-ref id="jeelinkUsb"/>
<bridge-type-ref id="lgwTcp"/>
<bridge-type-ref id="lgwUsb"/>
</supported-bridge-type-refs>
<label>@text/thing-type.revolt.label</label>
<description>@text/thing-type.revolt.description</description>
<channels>
<channel id="currentPower" typeId="current-power"/>
<channel id="consumptionTotal" typeId="consumption-total"/>
<channel id="powerFactor" typeId="power-factor"/>
<channel id="electricCurrent" typeId="electric-current"/>
<channel id="electricPotential" typeId="electric-potential"/>
<channel id="powerFrequency" typeId="power-frequency"/>
</channels>
<config-description>
<parameter name="sensorId" type="text" required="true">
<label>@text/parameter.sensorid.label</label>
<description>@text/parameter.sensorid.description</description>
</parameter>
<parameter name="sensorTimeout" type="integer" required="false" min="5" max="3600" unit="s" step="5">
<label>@text/parameter.sensortimeout.label</label>
<description>@text/parameter.sensortimeout.description</description>
<default>60</default>
</parameter>
</config-description>
</thing-type>
<!-- TX22 Temperature/Humidity SensorThing Type -->
<thing-type id="tx22">
<supported-bridge-type-refs>
<bridge-type-ref id="jeelinkTcp"/>
<bridge-type-ref id="jeelinkUsb"/>
<bridge-type-ref id="lgwTcp"/>
<bridge-type-ref id="lgwUsb"/>
</supported-bridge-type-refs>
<label>@text/thing-type.tx22.label</label>
<description>@text/thing-type.tx22.description</description>
<channels>
<channel id="temperature" typeId="temperature"/>
<channel id="humidity" typeId="humidity"/>
<channel id="batteryNew" typeId="battery-new"/>
<channel id="batteryLow" typeId="system.low-battery"/>
<channel id="pressure" typeId="pressure"/>
<channel id="rain" typeId="rain"/>
<channel id="windStrength" typeId="wind-strength"/>
<channel id="windAngle" typeId="wind-angle"/>
<channel id="gustStrength" typeId="wind-strength">
<label>@text/gust-strength.label</label>
<description>@text/gust-strength.description</description>
</channel>
</channels>
<config-description>
<parameter name="sensorId" type="text" required="true">
<label>@text/parameter.sensorid.label</label>
<description>@text/parameter.sensorid.description</description>
</parameter>
<parameter name="sensorTimeout" type="integer" required="false" min="5" max="3600" unit="s" step="5">
<label>@text/parameter.sensortimeout.label</label>
<description>@text/parameter.sensortimeout.description</description>
<default>600</default>
</parameter>
</config-description>
</thing-type>
<!-- LGW Sensor SensorThing Type -->
<thing-type id="lgw">
<supported-bridge-type-refs>
<bridge-type-ref id="jeelinkTcp"/>
<bridge-type-ref id="jeelinkUsb"/>
<bridge-type-ref id="lgwTcp"/>
<bridge-type-ref id="lgwUsb"/>
</supported-bridge-type-refs>
<label>@text/thing-type.lgw.label</label>
<description>@text/thing-type.lgw.description</description>
<channels>
<channel id="temperature" typeId="temperature"/>
</channels>
<config-description>
<parameter name="sensorId" type="text" required="true">
<label>@text/parameter.sensorid.label</label>
<description>@text/parameter.sensorid.description</description>
</parameter>
<parameter name="sensorTimeout" type="integer" required="false" min="5" max="3600" unit="s" step="5">
<label>@text/parameter.sensortimeout.label</label>
<description>@text/parameter.sensortimeout.description</description>
<default>600</default>
</parameter>
</config-description>
</thing-type>
<channel-type id="wind-angle">
<item-type>Number:Angle</item-type>
<label>@text/channel-type.wind-angle.label</label>
<description>@text/channel-type.wind-angle.description</description>
<category>Wind</category>
<state min="0" max="360" step="1" readOnly="true" pattern="%d %unit%"/>
</channel-type>
<channel-type id="wind-strength">
<item-type>Number:Speed</item-type>
<label>@text/channel-type.wind-strength.label</label>
<description>@text/channel-type.wind-strength.description</description>
<category>Wind</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="rain">
<item-type>Number:Length</item-type>
<label>@text/channel-type.rain.label</label>
<description>@text/channel-type.rain.description</description>
<category>Rain</category>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<channel-type id="pressure">
<item-type>Number:Pressure</item-type>
<label>@text/channel-type.pressure.label</label>
<description>@text/channel-type.pressure.description</description>
<category>Pressure</category>
<state readOnly="true" pattern="%.3f %unit%"/>
</channel-type>
<!-- Current Power Channel Type -->
<channel-type id="current-power">
<item-type>Number:Power</item-type>
<label>@text/channel-type.current-power.label</label>
<description>@text/channel-type.current-power.description</description>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<!-- Max Power Channel Type -->
<channel-type id="max-power">
<item-type>Number:Power</item-type>
<label>@text/channel-type.max-power.label</label>
<description>@text/channel-type.max-power.description</description>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<!-- Total Energy Consumption Channel Type -->
<channel-type id="consumption-total">
<item-type>Number:Energy</item-type>
<label>@text/channel-type.consumption-total.label</label>
<description>@text/channel-type.consumption-total.description</description>
<state readOnly="true" pattern="%d %unit%"/>
</channel-type>
<!-- Appliance On Time Channel Type -->
<channel-type id="appliance-time" advanced="true">
<item-type>Number:Time</item-type>
<label>@text/channel-type.appliance-time.label</label>
<description>@text/channel-type.appliance-time.description</description>
<state readOnly="true" pattern="%d %unit%"/>
</channel-type>
<!-- Sensor On Time Channel Type -->
<channel-type id="sensor-time" advanced="true">
<item-type>Number:Time</item-type>
<label>@text/channel-type.sensor-time.label</label>
<description>@text/channel-type.sensor-time.description</description>
<state readOnly="true" pattern="%d %unit%"/>
</channel-type>
<!-- Resets Channel Type -->
<channel-type id="resets" advanced="true">
<item-type>Number</item-type>
<label>@text/channel-type.resets.label</label>
<description>@text/channel-type.resets.description</description>
<state readOnly="true" pattern="%d"/>
</channel-type>
<!-- Temperature Channel Type -->
<channel-type id="temperature">
<item-type>Number:Temperature</item-type>
<label>@text/channel-type.temperature.label</label>
<description>@text/channel-type.temperature.description</description>
<category>Temperature</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<!-- Humidity Channel Type -->
<channel-type id="humidity">
<item-type>Number:Dimensionless</item-type>
<label>@text/channel-type.humidity.label</label>
<description>@text/channel-type.humidity.description</description>
<category>Humidity</category>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<!-- Battery New Channel Type -->
<channel-type id="battery-new">
<item-type>Switch</item-type>
<label>@text/channel-type.battery-new.label</label>
<description>@text/channel-type.battery-new.description</description>
<state readOnly="true"/>
</channel-type>
<!-- PCA301 Socket On Type -->
<channel-type id="switching-state">
<item-type>Switch</item-type>
<label>@text/channel-type.switching-state.label</label>
<description>@text/channel-type.switching-state.description</description>
</channel-type>
<!-- Revolt Current Type -->
<channel-type id="electric-current">
<item-type>Number:ElectricCurrent</item-type>
<label>@text/channel-type.electric-current.label</label>
<description>@text/channel-type.electric-current.description</description>
</channel-type>
<!-- Revolt Apparent Power Type -->
<channel-type id="power-factor">
<item-type>Number:Dimensionless</item-type>
<label>@text/channel-type.power-factor.label</label>
<description>@text/channel-type.power-factor.description</description>
</channel-type>
<!-- Revolt Electric Potential Type -->
<channel-type id="electric-potential">
<item-type>Number:ElectricPotential</item-type>
<label>@text/channel-type.electric-potential.label</label>
<description>@text/channel-type.electric-potential.description</description>
</channel-type>
<!-- Revolt Frequency Type -->
<channel-type id="power-frequency">
<item-type>Number:Frequency</item-type>
<label>@text/channel-type.power-frequency.label</label>
<description>@text/channel-type.power-frequency.description</description>
</channel-type>
</thing:thing-descriptions>