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,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.onewire</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

View File

@@ -0,0 +1,320 @@
# OneWire Binding
The OneWire binding integrates OneWire (also spelled 1-Wire) devices.
OneWire is a serial bus developed by Dallas Semiconductor.
It provides cheap sensors for temperature, humidity, digital I/O and more.
## Getting Started
The OneWire File System (OWFS, https://owfs.org) provides an abstraction layer between the OneWire bus and this binding.
It is assumed that you already have a working OWFS installation.
Besides your sensors, you need a busmaster device (e.g. DS9490R).
## Supported Things
### Bridges
Currently only one bridge is supported.
The `owserver` is the bridge that connects to an existing OWFS installation.
### Things
There are different types of things: the simple one (`basic`), multisensors built around the DS1923/DS2438 chip (`ms-tx`) and more advanced sensors from Elaborated Networks (www.wiregate.de) (`ams`, `bms`), Embedded Data System (www.embeddeddatasystems.com)(`edsenv`) and Brain4Home (www.brain4home.eu) (`bae091x`).
** Important: Breaking Change **
The thing types `ms-th`, `ms-tv`, `counter2`, `digitalio`, `digitalio2`, `digitalio8`, `ibutton`, `temperature` have been removed.
## Discovery
Discovery is supported for things. You have to add the bridges manually.
## Thing Configuration
It is strongly recommended to use discovery and Paper UI for thing configuration.
Please note that:
* All things need a bridge.
* The sensor id parameter supports only the dotted format, including the family id (e.g. `28.7AA256050000`).
DS2409 MicroLAN couplers (hubs) are supported by adding their id and the branch (`main` or `aux`) in a directory-like format in front of the sensor id (e.g. `1F.EDC601000000/main/28.945042000000`).
* Refresh time is the minimum time in seconds between two checks of that thing.
It defaults to 300s for analog channels and 10s for digital channels.
* Some thing channels need additional configuration, please see below in the channels section.
### OWFS Bridge (`owserver`)
There are no configuration options for the owserver besides the network address.
It consists of two parts: `address` and `port`.
The `address` parameter is used to denote the location of the owserver instance.
It supports both, a hostname or an IP address.
The `port` parameter is used to adjust non-standard OWFS installations.
It defaults to `4304`, which is the default of each OWFS installation.
Bridges of type `owserver` are extensible with channels of type `owfs-number` and `owfs-string`.
### Generic (`basic`)
The `basic` thing supports iButton-like chips (DS1420, DS2401/DS1990A), temperature sensors (DS18B20, DS18S20, DS1822), digital i/o chips (DS2405, DS2406, DS2408, DS2413) and counter chips (DS2423).
It has two parameters: sensor id `id` and refresh time `refresh`.
Depending on the chip, either `present`, `temperature`, `digitalX` or `counterX` channel(s) are added.
`X` is the number of the channel, starting from `0`.
### Multisensor (`ms-tx`)
The multisensor is build around the DS2438 or DS1923 chipset.
It always provides a `temperature` channel.
Depnding on the actual sensor, additional channels (`current`, `humidity`, `light`, `voltage`, `supplyvoltage`) are added.
If the voltage input of the DS2438 is connected to a humidity sensor, several common types are supported (see below).
It has three parameters: sensor id `id`, refresh time `refresh` and `manualsensor` (advanced option).
Known DS2438-base sensors are iButtonLink (https://www.ibuttonlink.com/) MS-T (recognized as generic DS2438), MS-TH, MS-TC, MS-TL, MS-TV.
Unknown multisensors are added as generic DS2438 and have `temperature`, `current`, `voltage` and `supplyvoltage` channels.
In case the sensor is not properly detected (e.g. because it is a self-made sensor), check if it is compatible with one of the sensors listed above.
You can use `manualsensor` to override the auto-detected sensortype by setting `DS2438`, `MS_TH`, `MS_TV`, `MS_TL` or `MS_TC`.
### Elaborated Networks Multisensors (`ams`, `bms`)
These things are complex devices from Elaborated networks.
They consist of a DS2438 and a DS18B20 with additional circuitry on one PCB.
The AMS additionally has a second DS2438 and a DS2413 for digital I/O on-board.
Analog light sensors can optionally be attached to both sensors.
These sensors provide `temperature`, `humidity` and `supplyvoltage` channels.
If the light sensor is attached and configured, a `light` channel is provided, otherwise a `current` channel.
The AMS has an additional `voltage`and two `digitalX` channels.
It has two (`bms`) or four (`ams`) sensors.
The id parameter (`id`) has to be configured with the sensor id of the humidity sensor.
Additionally the refresh time `refresh` can be configured.
The AMS supports a `digitalrefresh` parameter for the refresh time of the digital channels.
Since both multisensors have two temperature sensors on-board, the `temperaturesensor` parameter allows to select `DS18B20` or `DS2438` to be used for temperature measurement.
This parameter has a default of `DS18B20` as this is considered more accurate.
The `temperature` channel is of type `temperature` if the internal sensor is used and of type `temperature-por-res` for the external DS18B20.
The last parameter is the `lightsensor` option to configure if an ambient light sensor is attached.
It defaults to `false`.
In that mode, a `current` channel is provided.
If set to `true`, a `light` channel is added to the thing.
The correct formula for the ambient light is automatically determined from the sensor version.
### Embedded Data System Environmental sensors (`edsenv`)
This thing supports EDS0064, EDS0065, EDS0066 or EDS0067 sensors.
It has two parameters: sensor id `id` and refresh time `refresh`.
All things have a `temperature` channel.
Additional channels (`light`, `pressure`, `humidity`, `dewpoint`, `abshumidity`) will be added if available from the sensor automatically.
### Brain4Home BAE091x (`bae091x`)
Currently this thing only supports BAE0910 sensors.
All functional pins of this sensor have multiple functions which can be configured individually.
For detailed information of each mode, please see the official documentation.
Each pin has the can be configured as `disabled`.
The necessary channels are automatically added.
Pin 1 (`pin1`) has only one function `counter` (channel `counter`).
Pin 2 (`pin2`) can be configured as digital output (`output`, channel `digital2`) or pulse width modulated output (`pwm`, software PWM 4, channels `freq2`, `duty4`).
Pin 6 (`pin6`) can be configured as digital in-/output (`pio`, channel `digital6`) or pulse width modulated output (`pwm`, software PWM 3, channels `freq1`, `duty3`).
Pin 7 (`pin7`) can be configured as analog input (`analog`), digital output (`output`, channel `digital7`) or pulse width modulated output (`pwm`, hardware PWM 2, channels `freq2`, `duty2`).
Pin 8 (`pin8`) can be configured as digital input (`input`, channel `digital8`), digital output (`output`, channel `digital8`) or pulse width modulated output (`pwm`, hardware PWM 1, channels `freq1`, `duty1`).
Please note: support for this sensor is considered experimental.
## Channels
| Type-ID | Thing | Item | readonly | Description |
|---------------------|----------------------------|--------------------------|------------|----------------------------------------------------|
| absolutehumidity | ms-tx, ams, bms, edsenv | Number:Density | yes | absolute humidity |
| current | ms-tx, ams | Number:ElectricCurrent | yes | current |
| counter | counter2 | Number | yes | countervalue |
| dewpoint | ms-tx, ams, bms, edsenv | Number:Temperature | yes | dewpoint |
| dio | digitalX, ams | Switch | no | digital I/O, can be configured as input or output |
| humidity | ms-tx, ams, bms, edsenv | Number:Dimensionless | yes | relative humidity |
| humidityconf | ms-tx | Number:Dimensionless | yes | relative humidity |
| light | ams, bms, edsenv | Number:Illuminance | yes | lightness |
| owfs-number | owserver | Number | yes | direct access to OWFS nodes |
| owfs-string | owserver | String | yes | direct access to OWFS nodes |
| present | all | Switch | yes | sensor found on bus (yes = ON) |
| pressure | edsenv | Number:Pressure | yes | environmental pressure |
| supplyvoltage | ms-tx | Number:ElectricPotential | yes | sensor supplyvoltage |
| temperature | temperature, ms-tx, edsenv | Number:Temperature | yes | environmental temperature |
| temperature-por | temperature | Number:Temperature | yes | environmental temperature |
| temperature-por-res | temperature, ams, bms | Number:Temperature | yes | environmental temperature |
| voltage | ms-tx, ams | Number:ElectricPotential | yes | voltage input |
| bae-pwm-frequency | bae091x | Number:Frequency | no | frequency for PWM output |
| bae-pwm-duty | bae091x | Number:Dimensionless | no | duty cycle (0-100%) for PWM output |
| bae-di | bae091x | Switch | yes | digital input |
| bae-do | bae091x | Switch | no | digital output |
| bae-pio | bae091x | Switch | yes | digital in-/output |
| bae-analog | bae091x | Number:ElectricPotential | yes | analog input |
| bae-counter | bae091x | Number | yes | countervalue |
### Digital I/O (`dio`)
Channels of type `dio` channels each have two parameters: `mode` and `logic`.
The `mode` parameter is used to configure this channels as `input` or `output`.
The `logic` parameter can be used to invert the channel.
In `normal` mode the channel is considered `ON` for logic high, and `OFF` for logic low.
In `inverted` mode `ON` is logic low and `OFF` is logic high.
### Humidity (`humidity`, `humidityconf`, `abshumidity`, `dewpoint`)
Depending on the sensor, a `humidity` or `humidityconf` channel may be added.
This is only relevant for DS2438-based sensors of thing-type `ms-tx`.
`humidityconf`-type channels have the `humiditytype` parameter.
Possible options are `/humidity` for HIH-3610 sensors, `/HIH4000/humidity` for HIH-4000 sensors, `/HTM1735/humidity` for HTM-1735 sensors and `/DATANAB/humidity` for sensors from Datanab.
All humidity sensors also support `absolutehumidity` and `dewpoint`.
### OWFS Direct Access (`owfs-number`, `owfs-string`)
These channels allow direct access to OWFS nodes.
They have two configuration parameters: `path` and `refresh`.
The `path` parameter is mandatory and contains a full path inside the OWFS (e.g. `statistics/errors/CRC8_errors`).
The `refresh` parameter is the number of seconds between two consecutive (successful) reads of the node.
It defaults to 300s.
### Temperature (`temperature`, `temperature-por`, `temperature-por-res`)
There are three temperature channel types: `temperature`, `temperature-por`and `temperature-por-res`.
The correct channel-type is selected automatically by the thing handler depending on the sensor type.
If the channel-type is `temperature`, there is nothing else to configure.
Some sensors (e.g. DS18x20) report 85 °C as Power-On-Reset value.
In some installations this leads to errorneous temperature readings.
If the `ignorepor` parameter is set to `true` 85 °C values will be filtered.
The default is `false` as correct reading of 85 °C will otherwise be filtered, too.
Please note that the parameter value must not be set in quotation marks (see example below).
A channel of type `temperature-por-res` has one parameter: `resolution`.
OneWire temperature sensors are capable of different resolutions: `9`, `10`, `11` and `12` bits.
This corresponds to 0.5 °C, 0.25 °C, 0.125 °C, 0.0625 °C respectively.
The conversion time is inverse to that and ranges from 95 ms to 750 ms.
For best performance it is recommended to set the resolution only as high as needed.
### BAE PWM (`bae-pwm-frequency`, `bae-pwm-duty`)
PWM output 1 and 3 (2 and 4) share a frequency channel `pwmfreq1` (`pwmfreq2`).
Each PWM output has its own duty cycle (`pwmduty1` to `pwmduty4`).
The frequency channel has two configuration options (`prescaler`, `reversePolarity`).
The `prescaler` sets the frequency range which can be used.
Valid values are `0`to `7` (`0` => 245 Hz - 8 MHz, `1`=> 123 Hz - 4 MHz, `2` => 62 Hz - 2 MHz, `3` => 31 Hz - 1 MHz, `4` => 16 Hz - 500 kHz, `5` => 8 Hz - 250 kHz, `6` => 4 Hz - 125 kHz, `7` => 2 Hz - 62.5 kHz).
The default value is `0`.
The `reversePolarity` option is used to invert the output.
It can be `true` or `false`.
The default value is `false`.
The duty cycle can be set from 0-100%.
### BAE PIO (`bae-pio`)
The PIO channel (programmable I/O channel) has two configuration options: `mode` and `pulldevice`.
The `mode`can be set to `input`or `output`.
The default is `input`.
The `pulldevice` is only relevant for `input` mode.
It can be configured as `disabled`, `pullup`, `pulldown`.
The default is disabled.
## Full Example
** Attention: Adding channels with UIDs different from the ones mentioned in the thing description will not work and may cause problems.
Please use the pre-defined channel names only. **
This is the configuration for a OneWire network consisting of an owserver as bridge (`onewire:owserver:mybridge`) as well as a temperature sensor, a BMS and a 2-port Digital I/O as things (`onewire:basic:mybridge:mysensor`, `onewire:bms:mybridge:mybms`, `onewire:basic:mybridge:mydio`).
### demo.things:
```
Bridge onewire:owserver:mybridge [
network-address="192.168.0.51"
] {
Thing basic mysensor [
id="28.505AF0020000",
refresh=60
] {
Channels:
Type temperature-por-res : temperature [
ignorepor=true,
resolution="11"
]
}
Thing bms mybms [
id="26.CD497C010000",
refresh=60,
lightsensor=true,
temperaturesensor="DS18B20"
] {
Channels:
Type temperature-por-res : temperature [
ignorepor=false,
resolution="9"
]
}
Thing basic mydio [
id="3A.67F113000000"
] {
Channels:
Type dio : digital0 [
mode="input"
]
Type dio : digital1 [
mode="output"
]
}
Channels:
Type owfs-number : crc8errors [
path="statistics/errors/CRC8_errors"
]
}
```
### demo.items:
```
Number:Temperature MySensor "MySensor [%.1f °C]" { channel="onewire:basic:mybridge:mysensor:temperature" }
Number:Temperature MyBMS_T "MyBMS Temperature [%.1f °F]" { channel="onewire:bms:mybridge:mybms:temperature" }
Number:Dimensionless MyBMS_H "MyBMS Humidity [%.1f %unit%]" { channel="onewire:bms:mybridge:mybms:humidity" }
Switch Digital0 "Digital 0" { channel="onewire:basic:mybridge:mydio:digital0" }
Switch Digital1 "Digital 1" { channel="onewire:basic:mybridge:mydio:digital1" }
Number CRC8Errors "Bus-Errors [%d]" { channel="onewire:owserver:mybridge:crc8errors" }
```
### demo.sitemap:
```
sitemap demo label="Main Menu"
{
Frame {
Text item=MySensor
Text item=MyBMS_T
Text item=MyBMS_H
Text item=CRC8Errors
Text item=Digital0
Switch item=Digital1
}
}
```

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.onewire</artifactId>
<name>openHAB Add-ons :: Bundles :: OneWire Binding</name>
</project>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.onewire-${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-onewire" description="OneWire Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.onewire/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,230 @@
/**
* 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.onewire.internal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.device.OwSensorType;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
/**
* The {@link DS2438Configuration} is a helper class for the multisensor thing configuration
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DS2438Configuration {
private OwSensorType sensorSubType = OwSensorType.DS2438;
private String vendor = "Dallas/Maxim";
private String hwRevision = "0";
private String prodDate = "unknown";
private final Map<SensorId, OwSensorType> associatedSensors = new HashMap<>();
public DS2438Configuration(OwserverBridgeHandler bridgeHandler, SensorId sensorId) throws OwException {
OwSensorType sensorType = bridgeHandler.getType(sensorId);
if (sensorType != OwSensorType.DS2438) {
throw new OwException("sensor " + sensorId.getId() + " is not a DS2438!");
}
OwPageBuffer pageBuffer = bridgeHandler.readPages(sensorId);
String sensorTypeId = pageBuffer.getPageString(3).substring(0, 2);
switch (sensorTypeId) {
case "19":
vendor = "iButtonLink";
sensorSubType = OwSensorType.MS_TH;
break;
case "1A":
vendor = "iButtonLink";
sensorSubType = OwSensorType.MS_TV;
break;
case "1B":
vendor = "iButtonLink";
sensorSubType = OwSensorType.MS_TL;
break;
case "1C":
vendor = "iButtonLink";
sensorSubType = OwSensorType.MS_TC;
break;
case "F1":
case "F3":
vendor = "Elaborated Networks";
sensorSubType = OwSensorType.MS_TH;
break;
case "F2":
vendor = "Elaborated Networks";
sensorSubType = OwSensorType.MS_TH_S;
break;
case "F4":
vendor = "Elaborated Networks";
sensorSubType = OwSensorType.MS_TV;
break;
default:
}
if (sensorSubType == OwSensorType.MS_TH || sensorSubType == OwSensorType.MS_TH_S
|| sensorSubType == OwSensorType.MS_TV) {
for (int i = 4; i < 7; i++) {
String str = new StringBuilder(pageBuffer.getPageString(i)).insert(2, ".").delete(15, 17).toString();
Matcher matcher = SensorId.SENSOR_ID_PATTERN.matcher(str);
if (matcher.matches()) {
SensorId associatedSensorId = new SensorId(sensorId.getPath() + matcher.group(2));
switch (matcher.group(2).substring(0, 2)) {
case "26":
DS2438Configuration associatedDs2438Config = new DS2438Configuration(bridgeHandler,
associatedSensorId);
associatedSensors.put(associatedSensorId, associatedDs2438Config.getSensorSubType());
associatedSensors.putAll(associatedDs2438Config.getAssociatedSensors());
break;
case "28":
associatedSensors.put(associatedSensorId, OwSensorType.DS18B20);
break;
case "3A":
associatedSensors.put(associatedSensorId, OwSensorType.DS2413);
break;
default:
}
}
}
prodDate = String.format("%d/%d", pageBuffer.getByte(5, 0),
256 * pageBuffer.getByte(5, 1) + pageBuffer.getByte(5, 2));
hwRevision = String.valueOf(pageBuffer.getByte(5, 3));
}
}
public Map<SensorId, OwSensorType> getAssociatedSensors() {
return associatedSensors;
}
/**
* get a list of sensor ids associated with this sensor
*
* @return a list of the sensor ids (if found), empty list otherwise
*/
public List<SensorId> getAssociatedSensorIds() {
return new ArrayList<>(associatedSensors.keySet());
}
/**
* get all secondary sensor ids of a given type
*
* @param sensorType filter for sensors
* @return a list of OwDiscoveryItems
*/
public List<SensorId> getAssociatedSensorIds(OwSensorType sensorType) {
return associatedSensors.entrySet().stream().filter(s -> s.getValue() == sensorType).map(s -> s.getKey())
.collect(Collectors.toList());
}
/**
* get a list of sensor types associated with this sensor
*
* @return a list of the sensor typess (if found), empty list otherwise
*/
public List<OwSensorType> getAssociatedSensorTypes() {
return new ArrayList<>(associatedSensors.values());
}
/**
* get the number of associated sensors
*
* @return the number
*/
public int getAssociatedSensorCount() {
return associatedSensors.size();
}
/**
* get hardware revision (available on some multisensors)
*
* @return hardware revision
*/
public String getHardwareRevision() {
return hwRevision;
}
/**
* get production date (available on some multisensors)
*
* @return production date in ww/yy
*/
public String getProductionDate() {
return prodDate;
}
/**
* get sensor type (without associated sensors)
*
* @return basic sensor type
*/
public OwSensorType getSensorSubType() {
return sensorSubType;
}
/**
* get vendor name (if available)
*
* @return the vendor name
*/
public String getVendor() {
return vendor;
}
/**
* determine multisensor type
*
* @param mainsensorType the type of the main sensor
* @param associatedSensorTypes a list of OwSensorTypes of all associated sensors
* @return the multisensor type (if known)
*/
public static OwSensorType getMultisensorType(OwSensorType mainsensorType,
List<OwSensorType> associatedSensorTypes) {
OwSensorType multisensorType = OwSensorType.UNKNOWN;
switch (associatedSensorTypes.size()) {
case 0:
multisensorType = mainsensorType;
break;
case 1:
if (mainsensorType == OwSensorType.MS_TH_S && associatedSensorTypes.contains(OwSensorType.DS18B20)) {
multisensorType = OwSensorType.BMS_S;
} else if (mainsensorType == OwSensorType.MS_TH
&& associatedSensorTypes.contains(OwSensorType.DS18B20)) {
multisensorType = OwSensorType.BMS;
}
break;
case 3:
if (mainsensorType == OwSensorType.MS_TH_S && associatedSensorTypes.contains(OwSensorType.MS_TV)
&& associatedSensorTypes.contains(OwSensorType.DS18B20)
&& associatedSensorTypes.contains(OwSensorType.DS2413)) {
// two DS2438 (first THS, second TV), DS18B20, DS2413
multisensorType = OwSensorType.AMS_S;
} else if (mainsensorType == OwSensorType.MS_TH && associatedSensorTypes.contains(OwSensorType.MS_TV)
&& associatedSensorTypes.contains(OwSensorType.DS18B20)
&& associatedSensorTypes.contains(OwSensorType.DS2413)) {
// two DS2438 (first TH, second TV), DS18B20, DS2413
multisensorType = OwSensorType.AMS;
}
break;
default:
}
return multisensorType;
}
}

View File

@@ -0,0 +1,105 @@
/**
* 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.onewire.internal;
import static org.openhab.binding.onewire.internal.OwBindingConstants.CHANNEL_DIGITAL;
import java.util.Arrays;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.State;
/**
* The {@link DigitalIoConfig} class provides the configuration of a digital IO channel
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DigitalIoConfig {
private final String channelID;
private final ChannelUID channelUID;
private final OwserverDeviceParameter inParam;
private final OwserverDeviceParameter outParam;
private DigitalIoMode ioMode = DigitalIoMode.INPUT;
private DigitalIoLogic ioLogic = DigitalIoLogic.NORMAL;
public DigitalIoConfig(Thing thing, Integer channelIndex, OwserverDeviceParameter inParam,
OwserverDeviceParameter outParam) {
this.channelUID = new ChannelUID(thing.getUID(), String.format("%s%d", CHANNEL_DIGITAL, channelIndex));
this.channelID = String.format("%s%d", CHANNEL_DIGITAL, channelIndex);
this.inParam = inParam;
this.outParam = outParam;
}
public void setIoMode(String ioMode) {
this.ioMode = DigitalIoMode.valueOf(ioMode.toUpperCase());
}
public void setIoLogic(String ioLogic) {
this.ioLogic = DigitalIoLogic.valueOf(ioLogic.toUpperCase());
}
public Boolean isInverted() {
return (ioLogic == DigitalIoLogic.INVERTED);
}
public ChannelUID getChannelUID() {
return channelUID;
}
public String getChannelId() {
return channelID;
}
public OwserverDeviceParameter getParameter() {
return (ioMode == DigitalIoMode.INPUT) ? inParam : outParam;
}
public Boolean isInput() {
return (ioMode == DigitalIoMode.INPUT);
}
public Boolean isOutput() {
return (ioMode == DigitalIoMode.OUTPUT);
}
public DigitalIoMode getIoDirection() {
return ioMode;
}
public State convertState(Boolean rawValue) {
if (ioLogic == DigitalIoLogic.NORMAL) {
return rawValue ? OnOffType.ON : OnOffType.OFF;
} else {
return rawValue ? OnOffType.OFF : OnOffType.ON;
}
}
public DecimalType convertState(OnOffType command) {
if (ioLogic == DigitalIoLogic.NORMAL) {
return command.equals(OnOffType.ON) ? new DecimalType(1) : DecimalType.ZERO;
} else {
return command.equals(OnOffType.ON) ? DecimalType.ZERO : new DecimalType(1);
}
}
@Override
public String toString() {
return String.format("path=%s, mode=%s, logic=%s", Arrays.asList(getParameter()), ioMode, ioLogic);
}
}

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.onewire.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link DigitalIoLogic} provides the logic level of a digital IO channel
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public enum DigitalIoLogic {
NORMAL,
INVERTED
}

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.onewire.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link DigitalIoMode} provides the direction of a digital IO channel
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public enum DigitalIoMode {
INPUT,
OUTPUT
}

View File

@@ -0,0 +1,162 @@
/**
* 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.onewire.internal;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.device.OwChannelConfig;
import org.openhab.binding.onewire.internal.device.OwSensorType;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.type.ChannelTypeUID;
/**
* The {@link OneWireBinding} class defines common constants, which are
* used across the whole binding.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class OwBindingConstants {
public static final String BINDING_ID = "onewire";
// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_OWSERVER = new ThingTypeUID(BINDING_ID, "owserver");
public static final ThingTypeUID THING_TYPE_MS_TX = new ThingTypeUID(BINDING_ID, "ms-tx");
public static final ThingTypeUID THING_TYPE_BMS = new ThingTypeUID(BINDING_ID, "bms");
public static final ThingTypeUID THING_TYPE_AMS = new ThingTypeUID(BINDING_ID, "ams");
public static final ThingTypeUID THING_TYPE_BASIC = new ThingTypeUID(BINDING_ID, "basic");
public static final ThingTypeUID THING_TYPE_EDS_ENV = new ThingTypeUID(BINDING_ID, "edsenv");
public static final ThingTypeUID THING_TYPE_BAE091X = new ThingTypeUID(BINDING_ID, "bae091x");
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections
.unmodifiableSet(Stream.of(THING_TYPE_OWSERVER, THING_TYPE_AMS, THING_TYPE_BMS, THING_TYPE_MS_TX,
THING_TYPE_EDS_ENV, THING_TYPE_BASIC, THING_TYPE_BAE091X).collect(Collectors.toSet()));
// List of all config options
public static final String CONFIG_ADDRESS = "network-address";
public static final String CONFIG_PORT = "port";
public static final String CONFIG_ID = "id";
public static final String CONFIG_RESOLUTION = "resolution";
public static final String CONFIG_IGNORE_POR = "ignorepor";
public static final String CONFIG_REFRESH = "refresh";
public static final String CONFIG_DIGITALREFRESH = "digitalrefresh";
public static final String CONFIG_OFFSET = "offset";
public static final String CONFIG_HUMIDITY = "humiditytype";
public static final String CONFIG_DIGITAL_MODE = "mode";
public static final String CONFIG_DIGITAL_LOGIC = "logic";
public static final String CONFIG_TEMPERATURESENSOR = "temperaturesensor";
public static final String CONFIG_LIGHTSENSOR = "lightsensor";
public static final String CONFIG_BAE_PIN_DISABLED = "disabled";
public static final String CONFIG_BAE_PIN_PIO = "pio";
public static final String CONFIG_BAE_PIN_COUNTER = "counter";
public static final String CONFIG_BAE_PIN_PWM = "pwm";
public static final String CONFIG_BAE_PIN_ANALOG = "analog";
public static final String CONFIG_BAE_PIN_IN = "input";
public static final String CONFIG_BAE_PIN_OUT = "output";
// list of all properties
public static final String PROPERTY_MODELID = "modelId";
public static final String PROPERTY_VENDOR = "vendor";
public static final String PROPERTY_SENSORCOUNT = "sensorCount";
public static final String PROPERTY_PROD_DATE = "prodDate";
public static final String PROPERTY_HW_REVISION = "hwRevision";
// List of all channel ids
public static final String CHANNEL_HUMIDITY = "humidity";
public static final String CHANNEL_ABSOLUTE_HUMIDITY = "absolutehumidity";
public static final String CHANNEL_DEWPOINT = "dewpoint";
public static final String CHANNEL_PRESENT = "present";
public static final String CHANNEL_TEMPERATURE = "temperature";
public static final String CHANNEL_LIGHT = "light";
public static final String CHANNEL_SUPPLYVOLTAGE = "supplyvoltage";
public static final String CHANNEL_VOLTAGE = "voltage";
public static final String CHANNEL_CURRENT = "current";
public static final String CHANNEL_PRESSURE = "pressure";
public static final String CHANNEL_DIGITAL = "digital";
public static final String CHANNEL_DIGITAL0 = "digital0";
public static final String CHANNEL_DIGITAL1 = "digital1";
public static final String CHANNEL_DIGITAL2 = "digital2";
public static final String CHANNEL_DIGITAL3 = "digital3";
public static final String CHANNEL_DIGITAL4 = "digital4";
public static final String CHANNEL_DIGITAL5 = "digital5";
public static final String CHANNEL_DIGITAL6 = "digital6";
public static final String CHANNEL_DIGITAL7 = "digital7";
public static final String CHANNEL_DIGITAL8 = "digital8";
public static final String CHANNEL_COUNTER = "counter";
public static final String CHANNEL_COUNTER0 = "counter0";
public static final String CHANNEL_COUNTER1 = "counter1";
public static final String CHANNEL_PWM_DUTY1 = "pwmduty1";
public static final String CHANNEL_PWM_DUTY2 = "pwmduty2";
public static final String CHANNEL_PWM_DUTY3 = "pwmduty3";
public static final String CHANNEL_PWM_DUTY4 = "pwmduty4";
public static final String CHANNEL_PWM_FREQ1 = "pwmfreq1";
public static final String CHANNEL_PWM_FREQ2 = "pwmfreq2";
public static final ChannelTypeUID CHANNEL_TYPE_UID_ABSHUMIDITY = new ChannelTypeUID(BINDING_ID, "abshumidity");
public static final ChannelTypeUID CHANNEL_TYPE_UID_COUNTER = new ChannelTypeUID(BINDING_ID, "counter");
public static final ChannelTypeUID CHANNEL_TYPE_UID_CURRENT = new ChannelTypeUID(BINDING_ID, "current");
public static final ChannelTypeUID CHANNEL_TYPE_UID_DEWPOINT = new ChannelTypeUID(BINDING_ID, "dewpoint");
public static final ChannelTypeUID CHANNEL_TYPE_UID_DIO = new ChannelTypeUID(BINDING_ID, "dio");
public static final ChannelTypeUID CHANNEL_TYPE_UID_HUMIDITY = new ChannelTypeUID(BINDING_ID, "humidity");
public static final ChannelTypeUID CHANNEL_TYPE_UID_HUMIDITYCONF = new ChannelTypeUID(BINDING_ID, "humidityconf");
public static final ChannelTypeUID CHANNEL_TYPE_UID_LIGHT = new ChannelTypeUID(BINDING_ID, "light");
public static final ChannelTypeUID CHANNEL_TYPE_UID_PRESENT = new ChannelTypeUID(BINDING_ID, "present");
public static final ChannelTypeUID CHANNEL_TYPE_UID_PRESSURE = new ChannelTypeUID(BINDING_ID, "pressure");
public static final ChannelTypeUID CHANNEL_TYPE_UID_TEMPERATURE = new ChannelTypeUID(BINDING_ID, "temperature");
public static final ChannelTypeUID CHANNEL_TYPE_UID_TEMPERATURE_POR = new ChannelTypeUID(BINDING_ID,
"temperature-por");
public static final ChannelTypeUID CHANNEL_TYPE_UID_TEMPERATURE_POR_RES = new ChannelTypeUID(BINDING_ID,
"temperature-por-res");
public static final ChannelTypeUID CHANNEL_TYPE_UID_VOLTAGE = new ChannelTypeUID(BINDING_ID, "voltage");
public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_ANALOG = new ChannelTypeUID(BINDING_ID, "bae-analog");
public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_COUNTER = new ChannelTypeUID(BINDING_ID, "bae-counter");
public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_DIGITAL_OUT = new ChannelTypeUID(BINDING_ID, "bae-do");
public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_DIN = new ChannelTypeUID(BINDING_ID, "bae-in");
public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_DOUT = new ChannelTypeUID(BINDING_ID, "bae-out");
public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_PIO = new ChannelTypeUID(BINDING_ID, "bae-pio");
public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_PWM_DUTY = new ChannelTypeUID(BINDING_ID, "bae-pwm-duty");
public static final ChannelTypeUID CHANNEL_TYPE_UID_BAE_PWM_FREQUENCY = new ChannelTypeUID(BINDING_ID,
"bae-pwm-frequency");
public static final ChannelTypeUID CHANNEL_TYPE_UID_OWFS_NUMBER = new ChannelTypeUID(BINDING_ID, "owfs-number");
public static final ChannelTypeUID CHANNEL_TYPE_UID_OWFS_STRING = new ChannelTypeUID(BINDING_ID, "owfs-string");
// Maps for Discovery
public static final Map<OwSensorType, ThingTypeUID> THING_TYPE_MAP;
public static final Map<OwSensorType, String> THING_LABEL_MAP;
public static final Map<OwSensorType, Set<OwChannelConfig>> SENSOR_TYPE_CHANNEL_MAP;
public static final Map<String, String> ACCEPTED_ITEM_TYPES_MAP = Util
.readPropertiesFile("accepted_itemtypes.properties");
static {
Map<String, String> properties = Util.readPropertiesFile("sensor.properties");
THING_TYPE_MAP = properties.entrySet().stream().filter(e -> e.getKey().endsWith(".thingtype"))
.collect(Collectors.toConcurrentMap(e -> OwSensorType.valueOf(e.getKey().split("\\.")[0]),
e -> new ThingTypeUID(BINDING_ID, e.getValue())));
SENSOR_TYPE_CHANNEL_MAP = properties.entrySet().stream().filter(e -> e.getKey().endsWith(".channels"))
.collect(Collectors.toConcurrentMap(e -> OwSensorType.valueOf(e.getKey().split("\\.")[0]),
e -> !e.getValue().isEmpty() ? Stream.of(e.getValue().split(","))
.map(c -> OwChannelConfig.fromString(c)).collect(Collectors.toSet())
: new HashSet<>()));
THING_LABEL_MAP = properties.entrySet().stream().filter(e -> e.getKey().endsWith(".label")).collect(
Collectors.toConcurrentMap(e -> OwSensorType.valueOf(e.getKey().split("\\.")[0]), e -> e.getValue()));
}
}

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.onewire.internal;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.type.DynamicStateDescriptionProvider;
import org.openhab.core.types.StateDescription;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Dynamic channel state description provider.
* Overrides the state description for the controls, which receive its configuration in the runtime.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
@Component(service = { DynamicStateDescriptionProvider.class,
OwDynamicStateDescriptionProvider.class }, immediate = true)
public class OwDynamicStateDescriptionProvider implements DynamicStateDescriptionProvider {
private final Map<ChannelUID, StateDescription> descriptions = new ConcurrentHashMap<>();
private final Logger logger = LoggerFactory.getLogger(OwDynamicStateDescriptionProvider.class);
/**
* Set a state description for a channel. This description will be used when preparing the channel state by
* the framework for presentation. A previous description, if existed, will be replaced.
*
* @param channelUID
* channel UID
* @param description
* state description for the channel
*/
public void setDescription(ChannelUID channelUID, StateDescription description) {
logger.trace("adding state description for channel {}", channelUID);
descriptions.put(channelUID, description);
}
/**
* remove all descriptions for a given thing
*
* @param thingUID the thing's UID
*/
public void removeDescriptionsForThing(ThingUID thingUID) {
logger.trace("removing state description for thing {}", thingUID);
descriptions.entrySet().removeIf(entry -> entry.getKey().getThingUID().equals(thingUID));
}
@Override
public @Nullable StateDescription getStateDescription(Channel channel,
@Nullable StateDescription originalStateDescription, @Nullable Locale locale) {
if (descriptions.containsKey(channel.getUID())) {
logger.trace("returning new stateDescription for {}", channel.getUID());
return descriptions.get(channel.getUID());
} else {
return null;
}
}
}

View File

@@ -0,0 +1,29 @@
/**
* 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.onewire.internal;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link OwException} class defines an exception for handling OneWireExceptions
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class OwException extends Exception {
private static final long serialVersionUID = 71120587360960199L;
public OwException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,123 @@
/**
* 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.onewire.internal;
import static org.openhab.binding.onewire.internal.OwBindingConstants.SUPPORTED_THING_TYPES;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.onewire.internal.discovery.OwDiscoveryService;
import org.openhab.binding.onewire.internal.handler.AdvancedMultisensorThingHandler;
import org.openhab.binding.onewire.internal.handler.BAE091xSensorThingHandler;
import org.openhab.binding.onewire.internal.handler.BasicMultisensorThingHandler;
import org.openhab.binding.onewire.internal.handler.BasicThingHandler;
import org.openhab.binding.onewire.internal.handler.EDSSensorThingHandler;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
import org.openhab.core.config.discovery.DiscoveryService;
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.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link OwHandlerFactory} is responsible for creating things and thing
* handlers.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.onewire")
public class OwHandlerFactory extends BaseThingHandlerFactory {
Logger logger = LoggerFactory.getLogger(OwHandlerFactory.class);
private final Map<ThingUID, @Nullable ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
@NonNullByDefault({})
private OwDynamicStateDescriptionProvider dynamicStateDescriptionProvider;
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (OwserverBridgeHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
OwserverBridgeHandler owserverBridgeHandler = new OwserverBridgeHandler((Bridge) thing);
registerDiscoveryService(owserverBridgeHandler);
return owserverBridgeHandler;
} else if (BasicMultisensorThingHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
return new BasicMultisensorThingHandler(thing, dynamicStateDescriptionProvider);
} else if (AdvancedMultisensorThingHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
return new AdvancedMultisensorThingHandler(thing, dynamicStateDescriptionProvider);
} else if (BasicThingHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
return new BasicThingHandler(thing, dynamicStateDescriptionProvider);
} else if (EDSSensorThingHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
return new EDSSensorThingHandler(thing, dynamicStateDescriptionProvider);
} else if (BAE091xSensorThingHandler.SUPPORTED_THING_TYPES.contains(thingTypeUID)) {
return new BAE091xSensorThingHandler(thing, dynamicStateDescriptionProvider);
}
return null;
}
@Override
public void unregisterHandler(Thing thing) {
super.unregisterHandler(thing);
logger.error("factory {} deleting thing {}", this, thing);
}
private synchronized void registerDiscoveryService(OwserverBridgeHandler owserverBridgeHandler) {
OwDiscoveryService owDiscoveryService = new OwDiscoveryService(owserverBridgeHandler);
this.discoveryServiceRegs.put(owserverBridgeHandler.getThing().getUID(),
bundleContext.registerService(DiscoveryService.class.getName(), owDiscoveryService, new Hashtable<>()));
}
@Override
protected synchronized void removeHandler(ThingHandler thingHandler) {
if (thingHandler instanceof OwserverBridgeHandler) {
// remove discovery service, if bridge handler is removed
ServiceRegistration<?> serviceReg = this.discoveryServiceRegs.remove(thingHandler.getThing().getUID());
if (serviceReg != null) {
OwDiscoveryService service = (OwDiscoveryService) bundleContext.getService(serviceReg.getReference());
serviceReg.unregister();
if (service != null) {
service.deactivate();
}
}
}
}
@Reference
protected void setDynamicStateDescriptionProvider(OwDynamicStateDescriptionProvider provider) {
this.dynamicStateDescriptionProvider = provider;
}
protected void unsetDynamicStateDescriptionProvider(OwDynamicStateDescriptionProvider provider) {
this.dynamicStateDescriptionProvider = null;
}
}

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.onewire.internal;
import java.nio.ByteBuffer;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.util.HexUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link OwPageBuffer} provides encapsulates a buffer for OwPacket payloads
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class OwPageBuffer {
private final Logger logger = LoggerFactory.getLogger(OwPageBuffer.class);
public static final int PAGE_SIZE = 8;
private ByteBuffer byteBuffer;
/**
* constructor for empty buffer
*
*/
public OwPageBuffer() {
byteBuffer = ByteBuffer.allocate(0);
}
/**
* constructor for new buffer of given length
*
* @param pageNum number of pages
*/
public OwPageBuffer(int pageNum) {
byteBuffer = ByteBuffer.allocate(pageNum * PAGE_SIZE);
}
/**
* constructor for given byte array
*
* @param bytes byte array containing the data
*/
public OwPageBuffer(byte[] bytes) {
if (bytes.length % PAGE_SIZE != 0) {
byteBuffer = ByteBuffer.allocate((bytes.length / PAGE_SIZE + 1) * PAGE_SIZE);
logger.warn("initializing buffer which is not aligned to pages (requested size is {})!", bytes.length);
} else {
byteBuffer = ByteBuffer.allocate(bytes.length);
}
byteBuffer.put(bytes);
}
/**
* get number of pages in this buffer
*
* @return number of pages
*/
public int length() {
return byteBuffer.limit() / PAGE_SIZE;
}
/**
* get a single page as byte array
*
* @param pageNum page number, starting with 0
* @return byte array containing the page's data
*/
public byte[] getPage(int pageNum) {
byte[] page = new byte[PAGE_SIZE];
byteBuffer.position(pageNum * PAGE_SIZE);
byteBuffer.get(page);
return page;
}
/**
* get a single page
*
* @param pageNum page number, starting with 0
* @return string representation of the page's data
*/
public String getPageString(int pageNum) {
return HexUtils.bytesToHex(getPage(pageNum));
}
/**
* get a single byte in a page
*
* @param pageNum page number, starting with 0
* @param byteNum byte number, starting from 0 (beginning of page)
* @return integer of the requested byte
*/
public int getByte(int pageNum, int byteNum) {
int index = pageNum * PAGE_SIZE + byteNum;
if (index < byteBuffer.limit()) {
return byteBuffer.get(index) & 0xFF;
} else {
return 0;
}
}
public void setByte(int pageNum, int byteNum, byte value) {
int index = pageNum * PAGE_SIZE + byteNum;
if (index < byteBuffer.limit()) {
byteBuffer.put(index, value);
} else {
throw new IllegalArgumentException("index out of range");
}
}
/**
* get this page buffer as byte array
*
* @return this page buffer as byte array
*/
public byte[] getBytes() {
return byteBuffer.array();
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append(new String("["));
for (int i = 0; i < length(); i++) {
if (i > 0) {
s.append(new String(", "));
}
s.append(getPageString(i));
}
s.append(new String("]"));
return s.toString();
}
}

View File

@@ -0,0 +1,115 @@
/**
* 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.onewire.internal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link SensorId} provides a sensorID for the Onewire bus.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class SensorId {
public static final Pattern SENSOR_ID_PATTERN = Pattern
.compile("^\\/?((?:(?:1F\\.[0-9A-Fa-f]{12})\\/(?:main|aux)\\/)+)?([0-9A-Fa-f]{2}\\.[0-9A-Fa-f]{12})$");
private final String sensorId;
private final String path;
private final String fullPath;
/**
* construct a new SensorId object
*
* allowed formats:
* - "28.0123456789ab"
* - "1F.1234566890ab/main/28.0123456789ab"
* - "1F.1234566890ab/aux/28.0123456789ab"
* - leading "/" characters are allowed but not required
* - characters are case-insensitive
* - hubs ("1F.xxxxxxxxxxxx/aux/") may be repeated
*/
public SensorId(String fullPath) {
Matcher matcher = SENSOR_ID_PATTERN.matcher(fullPath);
if (matcher.matches() && matcher.groupCount() == 2) {
path = matcher.group(1) == null ? "" : matcher.group(1);
sensorId = matcher.group(2);
this.fullPath = "/" + path + sensorId;
} else {
throw new IllegalArgumentException();
}
}
/**
* get the full path to the sensor
*
* @return full path (including hub parts, separated by "/" characters)
*/
public String getFullPath() {
return fullPath;
}
/**
* get the sensor id
*
* @return sensor id without leading "/" character
*/
public String getId() {
return sensorId;
}
/**
* get the path of this sensorId
*
* @return path without sensor id (including hub parts, separated by "/" characters)
*/
public String getPath() {
return path;
}
/**
* get family id (first to characters of sensor id)
*
* @return the family id
*/
public String getFamilyId() {
return sensorId.substring(0, 2);
}
@Override
public String toString() {
return fullPath;
}
@Override
public int hashCode() {
return this.fullPath.hashCode();
}
@Override
public boolean equals(@Nullable Object o) {
if (o == this) {
return true;
}
if (!(o instanceof SensorId)) {
return false;
}
return ((SensorId) o).fullPath.equals(fullPath);
}
}

View File

@@ -0,0 +1,102 @@
/**
* 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.onewire.internal;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
import javax.measure.quantity.Dimensionless;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
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.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link Util} is a set of helper functions
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class Util {
private static final Logger LOGGER = LoggerFactory.getLogger(Util.class);
/**
* calculate absolute humidity in g/m³ from measured values
*
* @param temperature the measured temperature
* @param relativeHumidity the measured relative humidity
* @return the corresponding absolute humidity
*/
public static State calculateAbsoluteHumidity(QuantityType<Temperature> temperature,
QuantityType<Dimensionless> relativeHumidity) {
QuantityType<Temperature> temperatureDegC = temperature.toUnit(SIUnits.CELSIUS);
if (temperatureDegC == null) {
throw new IllegalArgumentException("could not change unit");
}
Double theta = temperatureDegC.doubleValue();
// saturation vapor pressure in kg/(m s^2)
Double saturationVaporPressure = 611.2 * Math.exp(17.62 * theta / (243.12 + theta));
// absolute humidity in kg/m^3
Double aH = relativeHumidity.doubleValue() / 100 * saturationVaporPressure / (461.52 * (273.15 + theta));
State absoluteHumidity = new QuantityType<>(aH, SmartHomeUnits.KILOGRAM_PER_CUBICMETRE).toUnit("g/m³");
if (absoluteHumidity != null) {
return absoluteHumidity;
} else {
throw new IllegalArgumentException("could not change unit");
}
}
/**
* calculates the dewpoint in °C from measured values
*
* @param temperature the measured temperature
* @param relativeHumidity the measured relative humidity
* @return the corresponding dewpoint
*/
public static State calculateDewpoint(QuantityType<Temperature> temperature,
QuantityType<Dimensionless> relativeHumidity) {
QuantityType<Temperature> temperatureDegC = temperature.toUnit(SIUnits.CELSIUS);
if (temperatureDegC == null) {
throw new IllegalArgumentException("could not change unit");
}
Double theta = temperatureDegC.doubleValue();
Double rH = relativeHumidity.doubleValue() / 100;
// dewpoint in °C
Double dP = 243.12 * (((17.62 * theta) / (243.12 + theta) + Math.log(rH))
/ (((17.62 * 243.12) / (243.12 + theta) - Math.log(rH))));
State dewPoint = new QuantityType<>(dP, SIUnits.CELSIUS);
return dewPoint;
}
public static Map<String, String> readPropertiesFile(String filename) {
URL resource = Thread.currentThread().getContextClassLoader().getResource(filename);
Properties properties = new Properties();
try {
properties.load(resource.openStream());
return properties.entrySet().stream()
.collect(Collectors.toMap(e -> (String) e.getKey(), e -> (String) e.getValue()));
} catch (IOException e) {
LOGGER.warn("Could not read resource file {}, binding will probably fail: {}", filename, e.getMessage());
return new HashMap<>();
}
}
}

View File

@@ -0,0 +1,25 @@
/**
* 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.onewire.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link AMSHandlerConfiguration} is a helper class for the mstx thing handler configuration
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class AMSHandlerConfiguration extends BaseHandlerConfiguration {
public int digitalRefresh = 10;
}

View File

@@ -0,0 +1,25 @@
/**
* 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.onewire.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link BAE091xAnalogConfiguration} is a helper class for the BAE091x ADC Pin configuration
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class BAE091xAnalogConfiguration {
public Boolean hires = false;
}

View File

@@ -0,0 +1,30 @@
/**
* 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.onewire.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.OwBindingConstants;
/**
* The {@link BAE091xHandlerConfiguration} is a helper class for the BAE091x thing handler configuration
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class BAE091xHandlerConfiguration extends BaseHandlerConfiguration {
public String pin1 = OwBindingConstants.CONFIG_BAE_PIN_DISABLED;
public String pin2 = OwBindingConstants.CONFIG_BAE_PIN_DISABLED;
public String pin6 = OwBindingConstants.CONFIG_BAE_PIN_DISABLED;
public String pin7 = OwBindingConstants.CONFIG_BAE_PIN_DISABLED;
public String pin8 = OwBindingConstants.CONFIG_BAE_PIN_DISABLED;
}

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.onewire.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link BAE091xPIOConfiguration} is a helper class for the BAE091x PIO Pin configuration
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class BAE091xPIOConfiguration {
public String mode = "input";
public String pulldevice = "disabled";
}

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.onewire.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link BAE091xPWMConfiguration} is a helper class for the BAE091x PWM Frequency configuration
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class BAE091xPWMConfiguration {
public int prescaler = 0;
public boolean reversePolarity = false;
}

View File

@@ -0,0 +1,27 @@
/**
* 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.onewire.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link BaseHandlerConfiguration} is a helper class for the base thing handler configuration
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class BaseHandlerConfiguration {
public @Nullable String id;
public int refresh = 300;
}

View File

@@ -0,0 +1,27 @@
/**
* 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.onewire.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.onewire.internal.device.OwSensorType;
/**
* The {@link MstxHandlerConfiguration} is a helper class for the mstx thing handler configuration
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class MstxHandlerConfiguration extends BaseHandlerConfiguration {
public @Nullable OwSensorType manualsensor;
}

View File

@@ -0,0 +1,158 @@
/**
* 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.onewire.internal.device;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.DigitalIoConfig;
import org.openhab.binding.onewire.internal.OwDynamicStateDescriptionProvider;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.Thing;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.openhab.core.types.StateDescription;
import org.openhab.core.types.StateDescriptionFragmentBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link AbstractDigitalOwDevice} class defines an abstract digital I/O device
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public abstract class AbstractDigitalOwDevice extends AbstractOwDevice {
private final Logger logger = LoggerFactory.getLogger(AbstractDigitalOwDevice.class);
protected @NonNullByDefault({}) OwserverDeviceParameter fullInParam;
protected @NonNullByDefault({}) OwserverDeviceParameter fullOutParam;
protected final List<DigitalIoConfig> ioConfig = new ArrayList<>();
public AbstractDigitalOwDevice(SensorId sensorId, OwBaseThingHandler callback) {
super(sensorId, callback);
}
@Override
public void configureChannels() throws OwException {
Thing thing = callback.getThing();
OwDynamicStateDescriptionProvider dynamicStateDescriptionProvider = callback
.getDynamicStateDescriptionProvider();
for (Integer i = 0; i < ioConfig.size(); i++) {
String channelId = ioConfig.get(i).getChannelId();
Channel channel = thing.getChannel(channelId);
if (channel != null) {
Configuration channelConfig = channel.getConfiguration();
try {
if (channelConfig.get(CONFIG_DIGITAL_MODE) != null) {
ioConfig.get(i).setIoMode((String) channelConfig.get(CONFIG_DIGITAL_MODE));
}
if (channelConfig.get(CONFIG_DIGITAL_LOGIC) != null) {
ioConfig.get(i).setIoLogic((String) channelConfig.get(CONFIG_DIGITAL_LOGIC));
}
} catch (IllegalArgumentException e) {
throw new OwException(channelId + " has invalid configuration");
}
if (dynamicStateDescriptionProvider != null) {
StateDescription stateDescription = StateDescriptionFragmentBuilder.create()
.withReadOnly(ioConfig.get(i).isInput()).build().toStateDescription();
if (stateDescription != null) {
dynamicStateDescriptionProvider.setDescription(ioConfig.get(i).getChannelUID(),
stateDescription);
} else {
logger.warn("Failed to create state description in thing {}", thing.getUID());
}
} else {
logger.debug(
"state description may be inaccurate, state description provider not available in thing {}",
thing.getUID());
}
logger.debug("configured {} channel {}: {}", thing.getUID(), i, ioConfig.get(i));
} else {
throw new OwException(channelId + " not found");
}
}
isConfigured = true;
}
/**
* refreshes this sensor - note that the update interval check is not performed as its and i/o device
*/
@Override
public void refresh(OwserverBridgeHandler bridgeHandler, Boolean forcedRefresh) throws OwException {
logger.trace("refresh of sensor {} started", sensorId);
if (isConfigured) {
State state;
BitSet statesSensed = bridgeHandler.readBitSet(sensorId, fullInParam);
BitSet statesPIO = bridgeHandler.readBitSet(sensorId, fullOutParam);
for (int i = 0; i < ioConfig.size(); i++) {
if (ioConfig.get(i).isInput()) {
state = ioConfig.get(i).convertState(statesSensed.get(i));
logger.trace("{} IN{}: raw {}, final {}", sensorId, i, statesSensed, state);
} else {
state = ioConfig.get(i).convertState(statesPIO.get(i));
logger.trace("{} OUT{}: raw {}, final {}", sensorId, i, statesPIO, state);
}
callback.postUpdate(ioConfig.get(i).getChannelId(), state);
}
}
}
/**
* get the number of channels
*
* @return number of channels
*/
public int getChannelCount() {
return ioConfig.size();
}
public boolean writeChannel(OwserverBridgeHandler bridgeHandler, Integer ioChannel, Command command) {
if (ioChannel < getChannelCount()) {
try {
if (ioConfig.get(ioChannel).isOutput()) {
bridgeHandler.writeDecimalType(sensorId, ioConfig.get(ioChannel).getParameter(),
ioConfig.get(ioChannel).convertState((OnOffType) command));
return true;
} else {
return false;
}
} catch (OwException e) {
logger.info("could not write {} to {}: {}", command, ioChannel, e.getMessage());
return false;
}
} else {
throw new IllegalArgumentException("channel number out of range");
}
}
}

View File

@@ -0,0 +1,140 @@
/**
* 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.onewire.internal.device;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link AbstractOwClass} class defines an abstract onewire device
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public abstract class AbstractOwDevice {
private final Logger logger = LoggerFactory.getLogger(AbstractOwDevice.class);
protected SensorId sensorId;
protected OwSensorType sensorType;
protected OwBaseThingHandler callback;
protected Boolean isConfigured = false;
protected Set<String> enabledChannels = new HashSet<>();
/**
* constructor for the onewire device
*
* @param sensorId onewire ID of the sensor
* @param callback ThingHandler callback for posting updates
*/
public AbstractOwDevice(SensorId sensorId, OwBaseThingHandler callback) {
this.sensorId = sensorId;
this.callback = callback;
this.sensorType = OwSensorType.UNKNOWN;
}
public AbstractOwDevice(SensorId sensorId, OwSensorType sensorType, OwBaseThingHandler callback) {
this.sensorId = sensorId;
this.callback = callback;
this.sensorType = sensorType;
}
/**
* configures the onewire devices channels
*
*/
public abstract void configureChannels() throws OwException;
/**
* refresh this sensor
*
* @param bridgeHandler for sending requests
* @param forcedRefresh post update even if state did not change
* @throws OwException in case of communication error
*/
public abstract void refresh(OwserverBridgeHandler owBridgeHandler, Boolean forcedRefresh) throws OwException;
/**
* enables a channel on this device
*
* @param channelID the channels channelID
*/
public void enableChannel(String channelID) {
if (!enabledChannels.contains(channelID)) {
enabledChannels.add(channelID);
}
}
/**
* disables a channel on this device
*
* @param channelID the channels channelID
*/
public void disableChannel(String channelID) {
if (enabledChannels.contains(channelID)) {
enabledChannels.remove(channelID);
}
}
/**
* get onewire ID of this sensor
*
* @return sensor ID
*/
public SensorId getSensorId() {
return sensorId;
}
/**
* check sensor presence and update thing state
*
* @param owServerConnection
* @return sensors presence state
*/
public Boolean checkPresence(OwserverBridgeHandler bridgeHandler) {
try {
State present = bridgeHandler.checkPresence(sensorId);
callback.updatePresenceStatus(present);
return OnOffType.ON.equals(present);
} catch (OwException e) {
logger.debug("error refreshing presence {} on bridge {}: {}", this.sensorId,
bridgeHandler.getThing().getUID(), e.getMessage());
return false;
}
}
/**
* get this sensors type
*
* @param bridgeHandler bridge handler to request from if type formerly unknown
* @return this sensors type
* @throws OwException
*/
public OwSensorType getSensorType(OwserverBridgeHandler bridgeHandler) throws OwException {
if (sensorType == OwSensorType.UNKNOWN) {
sensorType = bridgeHandler.getType(sensorId);
}
return sensorType;
}
}

View File

@@ -0,0 +1,408 @@
/**
* 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.onewire.internal.device;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.util.BitSet;
import javax.measure.quantity.Dimensionless;
import javax.measure.quantity.Frequency;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.config.BAE091xAnalogConfiguration;
import org.openhab.binding.onewire.internal.config.BAE091xPIOConfiguration;
import org.openhab.binding.onewire.internal.config.BAE091xPWMConfiguration;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter;
import org.openhab.core.library.types.DecimalType;
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.Channel;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link BAE0910} class defines an BAE0910 device
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class BAE0910 extends AbstractOwDevice {
private static final int OUTC_OUTEN = 4;
private static final int OUTC_DS = 3;
private static final int PIOC_PIOEN = 4;
private static final int PIOC_DS = 3;
private static final int PIOC_PD = 2;
private static final int PIOC_PE = 1;
private static final int PIOC_DD = 0;
private static final int ADCC_ADCEN = 4;
private static final int ADCC_10BIT = 3;
@SuppressWarnings("unused") // for future use
private static final int ADCC_OFS = 2;
@SuppressWarnings("unused")
private static final int ADCC_GRP = 1;
@SuppressWarnings("unused")
private static final int ADCC_STP = 0;
private static final int TPMC_POL = 7;
private static final int TPMC_INENA = 5;
private static final int TPMC_PWMDIS = 4;
private static final int TPMC_PS2 = 2;
private static final int TPMC_PS1 = 1;
private static final int TPMC_PS0 = 0;
private final Logger logger = LoggerFactory.getLogger(BAE0910.class);
private final OwserverDeviceParameter pin1CounterParameter = new OwserverDeviceParameter("/counter");
private final OwserverDeviceParameter pin2OutParameter = new OwserverDeviceParameter("/out");
private final OwserverDeviceParameter pin6PIOParameter = new OwserverDeviceParameter("/pio");
private final OwserverDeviceParameter pin7AnalogParameter = new OwserverDeviceParameter("/adc");
private final OwserverDeviceParameter outcParameter = new OwserverDeviceParameter("/outc");
private final OwserverDeviceParameter piocParameter = new OwserverDeviceParameter("/pioc");
private final OwserverDeviceParameter adccParameter = new OwserverDeviceParameter("/adcc");
private final OwserverDeviceParameter tpm1cParameter = new OwserverDeviceParameter("/tpm1c");
private final OwserverDeviceParameter tpm2cParameter = new OwserverDeviceParameter("/tpm2c");
private final OwserverDeviceParameter period1Parameter = new OwserverDeviceParameter("/period1");
private final OwserverDeviceParameter period2Parameter = new OwserverDeviceParameter("/period2");
private final OwserverDeviceParameter duty1Parameter = new OwserverDeviceParameter("/duty1");
private final OwserverDeviceParameter duty2Parameter = new OwserverDeviceParameter("/duty2");
private final OwserverDeviceParameter duty3Parameter = new OwserverDeviceParameter("/duty3");
private final OwserverDeviceParameter duty4Parameter = new OwserverDeviceParameter("/duty4");
private BitSet outcRegister = new BitSet(8);
private BitSet piocRegister = new BitSet(8);
private BitSet adccRegister = new BitSet(8);
private BitSet tpm1cRegister = new BitSet(8);
private BitSet tpm2cRegister = new BitSet(8);
private double resolution1 = 8; // in µs
private double resolution2 = 8; // in µs
public BAE0910(SensorId sensorId, OwBaseThingHandler callback) {
super(sensorId, callback);
}
@Override
public void configureChannels() {
}
public void configureChannels(OwserverBridgeHandler bridgeHandler) throws OwException {
outcRegister.clear();
piocRegister.clear();
adccRegister.clear();
tpm1cRegister.clear();
tpm2cRegister.clear();
if (enabledChannels.contains(CHANNEL_PWM_FREQ1)) {
Channel channel = callback.getThing().getChannel(CHANNEL_PWM_FREQ1);
if (channel != null) {
BAE091xPWMConfiguration channelConfig = channel.getConfiguration().as(BAE091xPWMConfiguration.class);
tpm1cRegister.set(TPMC_POL, channelConfig.reversePolarity);
tpm1cRegister.set(TPMC_PS2, (channelConfig.prescaler & 4) == 4);
tpm1cRegister.set(TPMC_PS1, (channelConfig.prescaler & 2) == 2);
tpm1cRegister.set(TPMC_PS0, (channelConfig.prescaler & 1) == 1);
resolution1 = 0.0625 * (1 << channelConfig.prescaler);
} else {
throw new OwException("trying to configure pwm but frequency channel is missing");
}
}
if (enabledChannels.contains(CHANNEL_PWM_FREQ2)) {
Channel channel = callback.getThing().getChannel(CHANNEL_PWM_FREQ2);
if (channel != null) {
BAE091xPWMConfiguration channelConfig = channel.getConfiguration().as(BAE091xPWMConfiguration.class);
tpm2cRegister.set(TPMC_POL, channelConfig.reversePolarity);
tpm2cRegister.set(TPMC_PS2, (channelConfig.prescaler & 4) == 4);
tpm2cRegister.set(TPMC_PS1, (channelConfig.prescaler & 2) == 2);
tpm2cRegister.set(TPMC_PS0, (channelConfig.prescaler & 1) == 1);
resolution2 = 0.0625 * (1 << channelConfig.prescaler);
} else {
throw new OwException("trying to configure pwm but frequency channel is missing");
}
}
// Pin 2
if (enabledChannels.contains(CHANNEL_DIGITAL2)) {
outcRegister.set(OUTC_DS);
outcRegister.set(OUTC_OUTEN);
}
// Pin 6
if (enabledChannels.contains(CHANNEL_DIGITAL6)) {
piocRegister.set(PIOC_PIOEN);
piocRegister.set(PIOC_DS);
Channel channel = callback.getThing().getChannel(CHANNEL_DIGITAL6);
if (channel != null) {
BAE091xPIOConfiguration channelConfig = channel.getConfiguration().as(BAE091xPIOConfiguration.class);
piocRegister.set(PIOC_DD, channelConfig.mode.equals("output"));
switch (channelConfig.pulldevice) {
case "pullup":
piocRegister.set(PIOC_PE);
piocRegister.clear(PIOC_PD);
break;
case "pulldown":
piocRegister.set(PIOC_PE);
piocRegister.set(PIOC_PD);
break;
default:
}
} else {
throw new OwException("trying to configure pin 6 but channel is missing");
}
}
// Pin 7
if (enabledChannels.contains(CHANNEL_VOLTAGE)) {
adccRegister.set(ADCC_ADCEN);
Channel channel = callback.getThing().getChannel(CHANNEL_VOLTAGE);
if (channel != null) {
BAE091xAnalogConfiguration channelConfig = channel.getConfiguration()
.as(BAE091xAnalogConfiguration.class);
adccRegister.set(ADCC_10BIT, channelConfig.hires);
} else {
throw new OwException("trying to configure pin 7 but channel is missing");
}
}
if (enabledChannels.contains(CHANNEL_DIGITAL7)) {
tpm2cRegister.set(TPMC_PWMDIS);
}
// Pin 8
if (enabledChannels.contains(CHANNEL_DIGITAL8)) {
tpm1cRegister.set(TPMC_PWMDIS);
Channel channel = callback.getThing().getChannel(CHANNEL_DIGITAL8);
if (channel != null) {
if ((new ChannelTypeUID(BINDING_ID, "bae-in")).equals(channel.getChannelTypeUID())) {
tpm1cRegister.set(TPMC_INENA);
}
} else {
throw new OwException("trying to configure pin 8 but channel is missing");
}
}
// write configuration
bridgeHandler.writeBitSet(sensorId, outcParameter, outcRegister);
bridgeHandler.writeBitSet(sensorId, piocParameter, piocRegister);
bridgeHandler.writeBitSet(sensorId, adccParameter, adccRegister);
bridgeHandler.writeBitSet(sensorId, tpm1cParameter, tpm1cRegister);
bridgeHandler.writeBitSet(sensorId, tpm2cParameter, tpm2cRegister);
isConfigured = true;
}
@Override
public void refresh(OwserverBridgeHandler bridgeHandler, Boolean forcedRefresh) throws OwException {
if (isConfigured) {
logger.trace("refresh of sensor {} started", sensorId);
// Counter
if (enabledChannels.contains(CHANNEL_COUNTER)) {
State counterValue = bridgeHandler.readDecimalType(sensorId, pin1CounterParameter);
callback.postUpdate(CHANNEL_COUNTER, counterValue);
}
// Digital Pins
if (enabledChannels.contains(CHANNEL_DIGITAL2)) {
BitSet value = bridgeHandler.readBitSet(sensorId, pin2OutParameter);
callback.postUpdate(CHANNEL_DIGITAL2, OnOffType.from(value.get(0)));
}
if (enabledChannels.contains(CHANNEL_DIGITAL6)) {
BitSet value = bridgeHandler.readBitSet(sensorId, pin6PIOParameter);
callback.postUpdate(CHANNEL_DIGITAL6, OnOffType.from(value.get(0)));
}
if (enabledChannels.contains(CHANNEL_DIGITAL7)) {
BitSet value = bridgeHandler.readBitSet(sensorId, tpm2cParameter);
callback.postUpdate(CHANNEL_DIGITAL7, OnOffType.from(value.get(TPMC_POL)));
}
if (enabledChannels.contains(CHANNEL_DIGITAL8)) {
BitSet value = bridgeHandler.readBitSet(sensorId, tpm1cParameter);
callback.postUpdate(CHANNEL_DIGITAL8, OnOffType.from(value.get(TPMC_POL)));
}
// Analog
if (enabledChannels.contains(CHANNEL_VOLTAGE)) {
State analogValue = bridgeHandler.readDecimalType(sensorId, pin7AnalogParameter);
callback.postUpdate(CHANNEL_VOLTAGE,
new QuantityType<>((DecimalType) analogValue, SmartHomeUnits.VOLT));
}
// PWM
int period1 = 0;
int period2 = 0;
if (enabledChannels.contains(CHANNEL_PWM_FREQ1)) {
period1 = ((DecimalType) bridgeHandler.readDecimalType(sensorId, period1Parameter)).intValue();
double frequency = (period1 > 0) ? 1 / (period1 * resolution1 * 1e-6) : 0;
callback.postUpdate(CHANNEL_PWM_FREQ1, new QuantityType<>(frequency, SmartHomeUnits.HERTZ));
}
if (enabledChannels.contains(CHANNEL_PWM_FREQ2)) {
period2 = ((DecimalType) bridgeHandler.readDecimalType(sensorId, period2Parameter)).intValue();
double frequency = (period2 > 0) ? 1 / (period2 * resolution2 * 1e-6) : 0;
callback.postUpdate(CHANNEL_PWM_FREQ2, new QuantityType<>(frequency, SmartHomeUnits.HERTZ));
}
if (enabledChannels.contains(CHANNEL_PWM_DUTY1)) {
int dutyValue = ((DecimalType) bridgeHandler.readDecimalType(sensorId, duty1Parameter)).intValue();
double duty = (period1 > 0 && dutyValue <= period1) ? 100 * dutyValue / period1 : 100;
callback.postUpdate(CHANNEL_PWM_DUTY1, new QuantityType<>(duty, SmartHomeUnits.PERCENT));
}
if (enabledChannels.contains(CHANNEL_PWM_DUTY2)) {
int dutyValue = ((DecimalType) bridgeHandler.readDecimalType(sensorId, duty2Parameter)).intValue();
double duty = (period2 > 0 && dutyValue <= period2) ? 100 * dutyValue / period2 : 100;
callback.postUpdate(CHANNEL_PWM_DUTY2, new QuantityType<>(duty, SmartHomeUnits.PERCENT));
}
if (enabledChannels.contains(CHANNEL_PWM_DUTY3)) {
int dutyValue = ((DecimalType) bridgeHandler.readDecimalType(sensorId, duty3Parameter)).intValue();
double duty = (period1 > 0 && dutyValue <= period1) ? 100 * dutyValue / period1 : 100;
callback.postUpdate(CHANNEL_PWM_DUTY3, new QuantityType<>(duty, SmartHomeUnits.PERCENT));
}
if (enabledChannels.contains(CHANNEL_PWM_DUTY4)) {
int dutyValue = ((DecimalType) bridgeHandler.readDecimalType(sensorId, duty4Parameter)).intValue();
double duty = (period2 > 0 && dutyValue <= period2) ? 100 * dutyValue / period2 : 100;
callback.postUpdate(CHANNEL_PWM_DUTY4, new QuantityType<>(duty, SmartHomeUnits.PERCENT));
}
}
}
public boolean writeChannel(OwserverBridgeHandler bridgeHandler, String channelId, Command command) {
try {
BitSet value = new BitSet(8);
switch (channelId) {
case CHANNEL_DIGITAL2:
// output
if (!outcRegister.get(OUTC_OUTEN)) {
return false;
}
value.set(0, ((OnOffType) command).equals(OnOffType.ON));
bridgeHandler.writeBitSet(sensorId, pin2OutParameter, value);
break;
case CHANNEL_DIGITAL6:
// not input, pio
if (!piocRegister.get(PIOC_DD) || !piocRegister.get(PIOC_PIOEN)) {
return false;
}
value.set(0, ((OnOffType) command).equals(OnOffType.ON));
bridgeHandler.writeBitSet(sensorId, pin6PIOParameter, value);
break;
case CHANNEL_DIGITAL7:
// not pwm, not analog
if (!tpm2cRegister.get(TPMC_PWMDIS) || adccRegister.get(ADCC_ADCEN)) {
return false;
}
tpm2cRegister.set(TPMC_POL, ((OnOffType) command).equals(OnOffType.ON));
bridgeHandler.writeBitSet(sensorId, tpm2cParameter, tpm2cRegister);
break;
case CHANNEL_DIGITAL8:
// not input, not pwm
if (tpm1cRegister.get(TPMC_INENA) || !tpm1cRegister.get(TPMC_PWMDIS)) {
return false;
}
tpm1cRegister.set(TPMC_POL, ((OnOffType) command).equals(OnOffType.ON));
bridgeHandler.writeBitSet(sensorId, tpm1cParameter, tpm1cRegister);
break;
case CHANNEL_PWM_FREQ1:
if (command instanceof QuantityType<?>) {
bridgeHandler.writeDecimalType(sensorId, period1Parameter,
convertFrequencyToPeriod(command, resolution1));
}
break;
case CHANNEL_PWM_FREQ2:
if (command instanceof QuantityType<?>) {
bridgeHandler.writeDecimalType(sensorId, period2Parameter,
convertFrequencyToPeriod(command, resolution2));
}
break;
case CHANNEL_PWM_DUTY1:
if (command instanceof QuantityType<?>) {
bridgeHandler.writeDecimalType(sensorId, duty1Parameter, calculateDutyCycle(command,
(DecimalType) bridgeHandler.readDecimalType(sensorId, period1Parameter)));
}
break;
case CHANNEL_PWM_DUTY2:
if (command instanceof QuantityType<?>) {
bridgeHandler.writeDecimalType(sensorId, duty2Parameter, calculateDutyCycle(command,
(DecimalType) bridgeHandler.readDecimalType(sensorId, period2Parameter)));
}
break;
case CHANNEL_PWM_DUTY3:
if (command instanceof QuantityType<?>) {
bridgeHandler.writeDecimalType(sensorId, duty3Parameter, calculateDutyCycle(command,
(DecimalType) bridgeHandler.readDecimalType(sensorId, period1Parameter)));
}
break;
case CHANNEL_PWM_DUTY4:
if (command instanceof QuantityType<?>) {
bridgeHandler.writeDecimalType(sensorId, duty4Parameter, calculateDutyCycle(command,
(DecimalType) bridgeHandler.readDecimalType(sensorId, period2Parameter)));
}
break;
default:
throw new OwException("unknown or invalid channel");
}
return true;
} catch (
OwException e) {
logger.info("could not write {} to {}: {}", command, channelId, e.getMessage());
return false;
}
}
public static OwSensorType getDeviceSubType(OwserverBridgeHandler bridgeHandler, SensorId sensorId)
throws OwException {
OwserverDeviceParameter deviceTypeParameter = new OwserverDeviceParameter("/device_type");
String subDeviceType = bridgeHandler.readString(sensorId, deviceTypeParameter);
switch (subDeviceType) {
case "2":
return OwSensorType.BAE0910;
case "3":
return OwSensorType.BAE0911;
default:
return OwSensorType.UNKNOWN;
}
}
private DecimalType convertFrequencyToPeriod(Command command, double resolution) throws OwException {
@SuppressWarnings("unchecked")
QuantityType<Frequency> fHz = ((QuantityType<Frequency>) command).toUnit(SmartHomeUnits.HERTZ);
if (fHz == null) {
throw new OwException("could not convert command to frequency");
}
double f = fHz.doubleValue();
int period = 0;
if (f > 0) {
period = (int) Math.min(Math.round(1 / (f * resolution * 1e-6)), 65535);
}
return new DecimalType(period);
}
private DecimalType calculateDutyCycle(Command command, DecimalType period) throws OwException {
@SuppressWarnings("unchecked")
double dutyCycle = ((QuantityType<Dimensionless>) command).doubleValue();
int dutyValue = 0;
if (dutyCycle > 0 && dutyCycle <= 100) {
dutyValue = (int) Math.round(dutyCycle / 100.0 * period.intValue());
} else if (dutyCycle > 100) {
dutyValue = 65535;
}
return new DecimalType(dutyValue);
}
}

View File

@@ -0,0 +1,90 @@
/**
* 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.onewire.internal.device;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.Thing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link DS18x20} class defines an DS18x20 or DS1822 device
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DS18x20 extends AbstractOwDevice {
private final Logger logger = LoggerFactory.getLogger(DS18x20.class);
private OwserverDeviceParameter temperatureParameter = new OwserverDeviceParameter("/temperature");
private boolean ignorePOR = false;
public DS18x20(SensorId sensorId, OwBaseThingHandler callback) {
super(sensorId, callback);
}
@Override
public void configureChannels() throws OwException {
Thing thing = callback.getThing();
Channel temperatureChannel = thing.getChannel(CHANNEL_TEMPERATURE);
if (temperatureChannel != null) {
Configuration channelConfiguration = temperatureChannel.getConfiguration();
if (channelConfiguration.containsKey(CONFIG_RESOLUTION)) {
temperatureParameter = new OwserverDeviceParameter(
"/temperature" + (String) channelConfiguration.get(CONFIG_RESOLUTION));
} else {
temperatureParameter = new OwserverDeviceParameter("/temperature");
}
if (channelConfiguration.containsKey(CONFIG_IGNORE_POR)) {
ignorePOR = (Boolean) channelConfiguration.get(CONFIG_IGNORE_POR);
} else {
ignorePOR = false;
}
} else {
throw new OwException(CHANNEL_TEMPERATURE + " not found");
}
isConfigured = true;
}
@Override
public void refresh(OwserverBridgeHandler bridgeHandler, Boolean forcedRefresh) throws OwException {
if (isConfigured && enabledChannels.contains(CHANNEL_TEMPERATURE)) {
logger.trace("refresh of sensor {} started", sensorId);
QuantityType<Temperature> temperature = new QuantityType<>(
(DecimalType) bridgeHandler.readDecimalType(sensorId, temperatureParameter), SIUnits.CELSIUS);
logger.trace("read temperature {} from {}", temperature, sensorId);
if (ignorePOR && (Double.compare(temperature.doubleValue(), 85.0) == 0)) {
logger.trace("ignored POR value from sensor {}", sensorId);
} else {
callback.postUpdate(CHANNEL_TEMPERATURE, temperature);
}
}
}
}

View File

@@ -0,0 +1,88 @@
/**
* 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.onewire.internal.device;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import javax.measure.quantity.Dimensionless;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.Util;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link DS1923} class defines an DS1923 device
*
* @author Jan N. Klug - Initial contribution
* @author Michał Wójcik - Adapted to DS1923
*/
@NonNullByDefault
public class DS1923 extends AbstractOwDevice {
private final Logger logger = LoggerFactory.getLogger(DS1923.class);
private final OwserverDeviceParameter temperatureParameter = new OwserverDeviceParameter("/temperature");
private final OwserverDeviceParameter humidityParameter = new OwserverDeviceParameter("/humidity");
public DS1923(SensorId sensorId, OwBaseThingHandler callback) {
super(sensorId, callback);
}
@Override
public void configureChannels() throws OwException {
isConfigured = true;
}
@Override
public void refresh(OwserverBridgeHandler bridgeHandler, Boolean forcedRefresh) throws OwException {
if (isConfigured) {
logger.trace("refresh of sensor {} started", sensorId);
if (enabledChannels.contains(CHANNEL_TEMPERATURE) || enabledChannels.contains(CHANNEL_HUMIDITY)
|| enabledChannels.contains(CHANNEL_ABSOLUTE_HUMIDITY)
|| enabledChannels.contains(CHANNEL_DEWPOINT)) {
QuantityType<Temperature> temperature = new QuantityType<>(
(DecimalType) bridgeHandler.readDecimalType(sensorId, temperatureParameter), SIUnits.CELSIUS);
callback.postUpdate(CHANNEL_TEMPERATURE, temperature);
if (enabledChannels.contains(CHANNEL_HUMIDITY) || enabledChannels.contains(CHANNEL_ABSOLUTE_HUMIDITY)
|| enabledChannels.contains(CHANNEL_DEWPOINT)) {
QuantityType<Dimensionless> humidity = new QuantityType<>(
(DecimalType) bridgeHandler.readDecimalType(sensorId, humidityParameter),
SmartHomeUnits.PERCENT);
if (enabledChannels.contains(CHANNEL_HUMIDITY)) {
callback.postUpdate(CHANNEL_HUMIDITY, humidity);
}
if (enabledChannels.contains(CHANNEL_ABSOLUTE_HUMIDITY)) {
callback.postUpdate(CHANNEL_ABSOLUTE_HUMIDITY,
Util.calculateAbsoluteHumidity(temperature, humidity));
}
if (enabledChannels.contains(CHANNEL_DEWPOINT)) {
callback.postUpdate(CHANNEL_DEWPOINT, Util.calculateDewpoint(temperature, humidity));
}
}
}
}
}
}

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.onewire.internal.device;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
/**
* The {@link DS2401} class defines an DS2401 (iButton) device
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DS2401 extends AbstractOwDevice {
public DS2401(SensorId sensorId, OwBaseThingHandler callback) {
super(sensorId, callback);
isConfigured = true;
}
@Override
public void configureChannels() throws OwException {
}
@Override
public void refresh(OwserverBridgeHandler bridgeHandler, Boolean forcedRefresh) throws OwException {
}
}

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.onewire.internal.device;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.DigitalIoConfig;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter;
/**
* The {@link DS2405} class defines an DS2405 device
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DS2405 extends AbstractDigitalOwDevice {
public DS2405(SensorId sensorId, OwBaseThingHandler callback) {
super(sensorId, callback);
}
@Override
public void configureChannels() throws OwException {
OwserverDeviceParameter inParam = new OwserverDeviceParameter("uncached/", "/sensed");
OwserverDeviceParameter outParam = new OwserverDeviceParameter("/PIO");
ioConfig.add(new DigitalIoConfig(callback.getThing(), 0, inParam, outParam));
fullInParam = inParam;
fullOutParam = outParam;
super.configureChannels();
}
}

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.onewire.internal.device;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.DigitalIoConfig;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter;
/**
* The {@link DS2406_DS2413} class defines an DS2406 or DS2413 device
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DS2406_DS2413 extends AbstractDigitalOwDevice {
public DS2406_DS2413(SensorId sensorId, OwBaseThingHandler callback) {
super(sensorId, callback);
}
@Override
public void configureChannels() throws OwException {
ioConfig.clear();
ioConfig.add(new DigitalIoConfig(callback.getThing(), 0, new OwserverDeviceParameter("uncached/", "/sensed.A"),
new OwserverDeviceParameter("/PIO.A")));
ioConfig.add(new DigitalIoConfig(callback.getThing(), 1, new OwserverDeviceParameter("uncached/", "/sensed.B"),
new OwserverDeviceParameter("/PIO.B")));
fullInParam = new OwserverDeviceParameter("uncached/", "/sensed.BYTE");
fullOutParam = new OwserverDeviceParameter("/PIO.BYTE");
super.configureChannels();
}
}

View File

@@ -0,0 +1,48 @@
/**
* 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.onewire.internal.device;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.DigitalIoConfig;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter;
/**
* The {@link DS2408} class defines an DS2408 device
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DS2408 extends AbstractDigitalOwDevice {
public DS2408(SensorId sensorId, OwBaseThingHandler callback) {
super(sensorId, callback);
}
@Override
public void configureChannels() throws OwException {
ioConfig.clear();
for (int i = 0; i < 8; i++) {
ioConfig.add(new DigitalIoConfig(callback.getThing(), i,
new OwserverDeviceParameter("uncached/", String.format("/sensed.%d", i)),
new OwserverDeviceParameter(String.format("/PIO.%d", i))));
}
fullInParam = new OwserverDeviceParameter("uncached/", "/sensed.BYTE");
fullOutParam = new OwserverDeviceParameter("/PIO.BYTE");
super.configureChannels();
}
}

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.onewire.internal.device;
import static org.openhab.binding.onewire.internal.OwBindingConstants.CHANNEL_COUNTER;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link DS2423} class defines an DS2423 device
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DS2423 extends AbstractOwDevice {
private final Logger logger = LoggerFactory.getLogger(DS2423.class);
private final OwserverDeviceParameter counterParameter = new OwserverDeviceParameter("/counters.ALL");
public DS2423(SensorId sensorId, OwBaseThingHandler callback) {
super(sensorId, callback);
}
@Override
public void configureChannels() throws OwException {
isConfigured = true;
}
@Override
public void refresh(OwserverBridgeHandler bridgeHandler, Boolean forcedRefresh) throws OwException {
if (isConfigured) {
logger.trace("refresh of sensor {} started", sensorId);
List<State> states = bridgeHandler.readDecimalTypeArray(sensorId, counterParameter);
if (states.size() != 2) {
throw new OwException("Expected exactly two values, got " + String.valueOf(states.size()));
} else {
callback.postUpdate(CHANNEL_COUNTER + "0", states.get(0));
callback.postUpdate(CHANNEL_COUNTER + "1", states.get(1));
}
}
}
}

View File

@@ -0,0 +1,222 @@
/**
* 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.onewire.internal.device;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import static org.openhab.core.library.unit.MetricPrefix.MILLI;
import javax.measure.quantity.Dimensionless;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.Util;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.DecimalType;
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.Thing;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link DS2438} class defines an DS2438 device
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DS2438 extends AbstractOwDevice {
private final Logger logger = LoggerFactory.getLogger(DS2438.class);
public enum LightSensorType {
ELABNET_V1,
ELABNET_V2,
IBUTTONLINK
}
public enum CurrentSensorType {
INTERNAL,
IBUTTONLINK
}
private LightSensorType lightSensorType = LightSensorType.ELABNET_V1;
private CurrentSensorType currentSensorType = CurrentSensorType.INTERNAL;
private final OwserverDeviceParameter temperatureParameter = new OwserverDeviceParameter("/temperature");
private OwserverDeviceParameter humidityParameter = new OwserverDeviceParameter("/humidity");
private final OwserverDeviceParameter voltageParameter = new OwserverDeviceParameter("/VAD");
private final OwserverDeviceParameter currentParamater = new OwserverDeviceParameter("/vis");
private final OwserverDeviceParameter supplyVoltageParameter = new OwserverDeviceParameter("/VDD");
public DS2438(SensorId sensorId, OwBaseThingHandler callback) {
super(sensorId, callback);
}
@Override
public void configureChannels() {
Thing thing = callback.getThing();
Channel humidityChannel = thing.getChannel(CHANNEL_HUMIDITY);
if (humidityChannel != null) {
Configuration channelConfiguration = humidityChannel.getConfiguration();
if (channelConfiguration.get(CONFIG_HUMIDITY) != null) {
humidityParameter = new OwserverDeviceParameter((String) channelConfiguration.get(CONFIG_HUMIDITY));
} else {
humidityParameter = new OwserverDeviceParameter("/humidity");
}
}
isConfigured = true;
}
@Override
public void refresh(OwserverBridgeHandler bridgeHandler, Boolean forcedRefresh) throws OwException {
if (isConfigured) {
logger.trace("refresh of sensor {} started", sensorId);
double Vcc = 5.0;
if (enabledChannels.contains(CHANNEL_TEMPERATURE) || enabledChannels.contains(CHANNEL_HUMIDITY)
|| enabledChannels.contains(CHANNEL_ABSOLUTE_HUMIDITY)
|| enabledChannels.contains(CHANNEL_DEWPOINT)) {
QuantityType<Temperature> temperature = new QuantityType<>(
(DecimalType) bridgeHandler.readDecimalType(sensorId, temperatureParameter), SIUnits.CELSIUS);
logger.trace("read temperature {} from {}", temperature, sensorId);
if (enabledChannels.contains(CHANNEL_TEMPERATURE)) {
callback.postUpdate(CHANNEL_TEMPERATURE, temperature);
}
if (enabledChannels.contains(CHANNEL_HUMIDITY) || enabledChannels.contains(CHANNEL_ABSOLUTE_HUMIDITY)
|| enabledChannels.contains(CHANNEL_DEWPOINT)) {
QuantityType<Dimensionless> humidity = new QuantityType<>(
(DecimalType) bridgeHandler.readDecimalType(sensorId, humidityParameter),
SmartHomeUnits.PERCENT);
logger.trace("read humidity {} from {}", humidity, sensorId);
if (enabledChannels.contains(CHANNEL_HUMIDITY)) {
callback.postUpdate(CHANNEL_HUMIDITY, humidity);
}
if (enabledChannels.contains(CHANNEL_ABSOLUTE_HUMIDITY)) {
callback.postUpdate(CHANNEL_ABSOLUTE_HUMIDITY,
Util.calculateAbsoluteHumidity(temperature, humidity));
}
if (enabledChannels.contains(CHANNEL_DEWPOINT)) {
callback.postUpdate(CHANNEL_DEWPOINT, Util.calculateDewpoint(temperature, humidity));
}
}
}
if (enabledChannels.contains(CHANNEL_VOLTAGE)) {
double measured = ((DecimalType) bridgeHandler.readDecimalType(sensorId, voltageParameter))
.doubleValue();
if (measured < 0 || measured > 10.0) {
// workaround bug in DS2438
measured = 0.0;
}
State voltage = new QuantityType<>(measured, SmartHomeUnits.VOLT);
logger.trace("read voltage {} from {}", voltage, sensorId);
callback.postUpdate(CHANNEL_VOLTAGE, voltage);
}
if (enabledChannels.contains(CHANNEL_CURRENT)) {
if (currentSensorType == CurrentSensorType.IBUTTONLINK) {
State current = bridgeHandler.readDecimalType(sensorId, voltageParameter);
if (current instanceof DecimalType) {
double currentDouble = ((DecimalType) current).doubleValue();
if (currentDouble >= 0.1 || currentDouble <= 3.78) {
current = new QuantityType<>(currentDouble * 5.163 + 0.483, SmartHomeUnits.AMPERE);
}
callback.postUpdate(CHANNEL_CURRENT, current);
} else {
callback.postUpdate(CHANNEL_CURRENT, UnDefType.UNDEF);
}
} else {
State current = new QuantityType<>(
(DecimalType) bridgeHandler.readDecimalType(sensorId, currentParamater),
MILLI(SmartHomeUnits.AMPERE));
callback.postUpdate(CHANNEL_CURRENT, current);
}
}
if (enabledChannels.contains(CHANNEL_SUPPLYVOLTAGE)) {
Vcc = ((DecimalType) bridgeHandler.readDecimalType(sensorId, supplyVoltageParameter)).doubleValue();
State supplyVoltage = new QuantityType<>(Vcc, SmartHomeUnits.VOLT);
callback.postUpdate(CHANNEL_SUPPLYVOLTAGE, supplyVoltage);
}
if (enabledChannels.contains(CHANNEL_LIGHT)) {
switch (lightSensorType) {
case ELABNET_V2:
State light = bridgeHandler.readDecimalType(sensorId, currentParamater);
if (light instanceof DecimalType) {
light = new QuantityType<>(
Math.round(Math.pow(10, ((DecimalType) light).doubleValue() / 47 * 1000)),
SmartHomeUnits.LUX);
callback.postUpdate(CHANNEL_LIGHT, light);
}
break;
case ELABNET_V1:
light = bridgeHandler.readDecimalType(sensorId, currentParamater);
if (light instanceof DecimalType) {
light = new QuantityType<>(Math.round(Math
.exp(1.059 * Math.log(1000000 * ((DecimalType) light).doubleValue() / (4096 * 390))
+ 4.518)
* 20000), SmartHomeUnits.LUX);
callback.postUpdate(CHANNEL_LIGHT, light);
}
break;
case IBUTTONLINK:
double measured = ((DecimalType) bridgeHandler.readDecimalType(sensorId, voltageParameter))
.doubleValue();
if (measured <= 0 || measured > 10.0) {
// workaround bug in DS2438
light = new QuantityType<>(0, SmartHomeUnits.LUX);
} else {
light = new QuantityType<>(Math.pow(10, (65 / 7.5) - (47 / 7.5) * (Vcc / measured)),
SmartHomeUnits.LUX);
}
callback.postUpdate(CHANNEL_LIGHT, light);
}
}
}
}
/**
* set the type of the attached light sensor
*
* @param lightSensorType
*/
public void setLightSensorType(LightSensorType lightSensorType) {
this.lightSensorType = lightSensorType;
}
/**
* set the type of the attached current sensor
*
* @param currentSensorType
*/
public void setCurrentSensorType(CurrentSensorType currentSensorType) {
this.currentSensorType = currentSensorType;
}
}

View File

@@ -0,0 +1,114 @@
/**
* 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.onewire.internal.device;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import javax.measure.quantity.Dimensionless;
import javax.measure.quantity.Illuminance;
import javax.measure.quantity.Pressure;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.Util;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.MetricPrefix;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link EDS006x} class defines an EDS006x device
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class EDS006x extends AbstractOwDevice {
private final Logger logger = LoggerFactory.getLogger(EDS006x.class);
private OwserverDeviceParameter temperatureParameter = new OwserverDeviceParameter("/temperature");
private OwserverDeviceParameter humidityParameter = new OwserverDeviceParameter("/humidity");
private OwserverDeviceParameter pressureParameter = new OwserverDeviceParameter("/pressure");
private OwserverDeviceParameter lightParameter = new OwserverDeviceParameter("/light");
public EDS006x(SensorId sensorId, OwSensorType sensorType, OwBaseThingHandler callback) {
super(sensorId, callback);
String sensorTypeName = sensorType.name();
temperatureParameter = new OwserverDeviceParameter("/" + sensorTypeName + "/temperature");
humidityParameter = new OwserverDeviceParameter("/" + sensorTypeName + "/humidity");
pressureParameter = new OwserverDeviceParameter("/" + sensorTypeName + "/pressure");
lightParameter = new OwserverDeviceParameter("/" + sensorTypeName + "/light");
}
@Override
public void configureChannels() {
isConfigured = true;
}
@Override
public void refresh(OwserverBridgeHandler bridgeHandler, Boolean forcedRefresh) throws OwException {
if (isConfigured) {
logger.trace("refresh of sensor {} started", sensorId);
if (enabledChannels.contains(CHANNEL_TEMPERATURE) || enabledChannels.contains(CHANNEL_HUMIDITY)
|| enabledChannels.contains(CHANNEL_ABSOLUTE_HUMIDITY)
|| enabledChannels.contains(CHANNEL_DEWPOINT)) {
QuantityType<Temperature> temperature = new QuantityType<>(
(DecimalType) bridgeHandler.readDecimalType(sensorId, temperatureParameter), SIUnits.CELSIUS);
if (enabledChannels.contains(CHANNEL_TEMPERATURE)) {
callback.postUpdate(CHANNEL_TEMPERATURE, temperature);
}
if (enabledChannels.contains(CHANNEL_HUMIDITY) || enabledChannels.contains(CHANNEL_ABSOLUTE_HUMIDITY)
|| enabledChannels.contains(CHANNEL_DEWPOINT)) {
QuantityType<Dimensionless> humidity = new QuantityType<>(
(DecimalType) bridgeHandler.readDecimalType(sensorId, humidityParameter),
SmartHomeUnits.PERCENT);
if (enabledChannels.contains(CHANNEL_HUMIDITY)) {
callback.postUpdate(CHANNEL_HUMIDITY, humidity);
}
if (enabledChannels.contains(CHANNEL_ABSOLUTE_HUMIDITY)) {
callback.postUpdate(CHANNEL_ABSOLUTE_HUMIDITY,
Util.calculateAbsoluteHumidity(temperature, humidity));
}
if (enabledChannels.contains(CHANNEL_DEWPOINT)) {
callback.postUpdate(CHANNEL_DEWPOINT, Util.calculateDewpoint(temperature, humidity));
}
}
}
if (enabledChannels.contains(CHANNEL_LIGHT)) {
QuantityType<Illuminance> light = new QuantityType<>(
(DecimalType) bridgeHandler.readDecimalType(sensorId, lightParameter), SmartHomeUnits.LUX);
callback.postUpdate(CHANNEL_LIGHT, light);
}
if (enabledChannels.contains(CHANNEL_PRESSURE)) {
QuantityType<Pressure> pressure = new QuantityType<>(
(DecimalType) bridgeHandler.readDecimalType(sensorId, pressureParameter),
MetricPrefix.HECTO(SIUnits.PASCAL));
callback.postUpdate(CHANNEL_PRESSURE, pressure);
}
}
}
}

View File

@@ -0,0 +1,66 @@
/**
* 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.onewire.internal.device;
import static org.openhab.binding.onewire.internal.OwBindingConstants.BINDING_ID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.type.ChannelTypeUID;
/**
* The {@link OwChannelConfig} class defines a map entry
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class OwChannelConfig {
private static final Pattern CONFIG_PATTERN = Pattern.compile("^(.+):(.+):(.*)$");
public String channelId;
public ChannelTypeUID channelTypeUID;
public @Nullable String label;
public OwChannelConfig(String channelId, ChannelTypeUID channelTypeUID, @Nullable String label) {
this.channelId = channelId;
this.channelTypeUID = channelTypeUID;
this.label = label;
}
public OwChannelConfig(String channelId, ChannelTypeUID channelTypeUID) {
this(channelId, channelTypeUID, null);
}
public static OwChannelConfig fromString(String configString) {
Matcher matcher = CONFIG_PATTERN.matcher(configString);
if (matcher.matches()) {
if (matcher.group(3).trim().isEmpty()) {
return new OwChannelConfig(matcher.group(1).trim(),
new ChannelTypeUID(BINDING_ID, matcher.group(2).trim()));
} else {
return new OwChannelConfig(matcher.group(1).trim(),
new ChannelTypeUID(BINDING_ID, matcher.group(2).trim()), matcher.group(3).trim());
}
} else {
throw new IllegalArgumentException();
}
}
@Override
public String toString() {
return channelId + "/" + channelTypeUID.getAsString() + "/" + label;
}
}

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.onewire.internal.device;
/**
* The {@link OwSensorType} defines all known sensor types
*
* @author Jan N. Klug - Initial contribution
*/
public enum OwSensorType {
DS1420,
DS18S20,
DS18B20,
DS1822,
DS1923,
DS2401,
DS2405,
DS2406,
DS2408,
DS2409,
DS2413,
DS2423,
DS2431,
DS2438,
MS_TC,
MS_TH,
MS_TL,
MS_TH_S,
MS_TV,
AMS,
AMS_S,
BAE,
BAE0910,
BAE0911,
BMS,
BMS_S,
EDS,
EDS0064,
EDS0065,
EDS0066,
EDS0067,
EDS0068,
UNKNOWN
}

View File

@@ -0,0 +1,167 @@
/**
* 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.onewire.internal.discovery;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.DS2438Configuration;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.OwPageBuffer;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.device.OwSensorType;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
import org.openhab.core.thing.ThingTypeUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link OwDiscoveryItem} class defines a discovery item for OneWire devices
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class OwDiscoveryItem {
private final Logger logger = LoggerFactory.getLogger(OwDiscoveryItem.class);
private final SensorId sensorId;
private OwSensorType sensorType = OwSensorType.UNKNOWN;
private String vendor = "Dallas/Maxim";
private OwPageBuffer pages = new OwPageBuffer();
private ThingTypeUID thingTypeUID = new ThingTypeUID(BINDING_ID, "");
private final Map<SensorId, OwSensorType> associatedSensors = new HashMap<>();
public OwDiscoveryItem(OwserverBridgeHandler bridgeHandler, SensorId sensorId) throws OwException {
this.sensorId = sensorId;
sensorType = bridgeHandler.getType(sensorId);
switch (sensorType) {
case DS2438:
pages = bridgeHandler.readPages(sensorId);
DS2438Configuration config = new DS2438Configuration(bridgeHandler, sensorId);
associatedSensors.putAll(config.getAssociatedSensors());
logger.trace("found associated sensors: {}", associatedSensors);
vendor = config.getVendor();
sensorType = config.getSensorSubType();
break;
case EDS:
vendor = "Embedded Data Systems";
pages = bridgeHandler.readPages(sensorId);
try { // determine subsensorType
sensorType = OwSensorType.valueOf(new String(pages.getPage(0), 0, 7, StandardCharsets.US_ASCII));
} catch (IllegalArgumentException e) {
sensorType = OwSensorType.UNKNOWN;
}
break;
default:
}
}
/**
* get sensor type
*
* @return sensor type
*/
public OwSensorType getSensorType() {
return sensorType;
}
/**
* get this sensor id
*
* @return sensor id
*/
public SensorId getSensorId() {
return sensorId;
}
/**
* normalized sensor id (for naming the discovery result)
*
* @return sensor id in format familyId_xxxxxxxxxx
*/
public String getNormalizedSensorId() {
return sensorId.getId().replace(".", "_");
}
/**
* get vendor name (if available)
*
* @return vendor name
*/
public String getVendor() {
return vendor;
}
/**
* get this sensors ThingTypeUID
*
* @return ThingTypeUID if mapping successful
*/
public ThingTypeUID getThingTypeUID() throws OwException {
if (THING_TYPE_MAP.containsKey(sensorType)) {
thingTypeUID = THING_TYPE_MAP.get(sensorType);
return thingTypeUID;
} else {
throw new OwException(sensorType + " cannot be mapped to thing type");
}
}
/**
* get a list of all sensors associated to this sensor
*
* @return list of strings
*/
public List<SensorId> getAssociatedSensorIds() {
return new ArrayList<>(associatedSensors.keySet());
}
/**
* determine this sensors type
*/
public void checkSensorType() {
logger.debug("checkSensorType: {} with {}", this, associatedSensors);
switch (sensorType) {
case MS_TH:
case MS_TH_S:
sensorType = DS2438Configuration.getMultisensorType(sensorType,
new ArrayList<>(associatedSensors.values()));
break;
default:
}
}
/**
* get Label "<thingtype> (<id>)"
*
* @return the thing label
*/
public String getLabel() {
return THING_LABEL_MAP.get(sensorType) + " (" + this.sensorId.getId() + ")";
}
@Override
public String toString() {
return String.format("%s/%s (associated: %d)", sensorId, sensorType, associatedSensors.size());
}
}

View File

@@ -0,0 +1,137 @@
/**
* 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.onewire.internal.discovery;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.device.OwSensorType;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
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.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link OwDiscoveryService} implements the discovery service for the OneWire binding.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class OwDiscoveryService extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(OwDiscoveryService.class);
private final OwserverBridgeHandler owBridgeHandler;
private final ThingUID bridgeUID;
Map<SensorId, OwDiscoveryItem> owDiscoveryItems = new HashMap<>();
Set<SensorId> associatedSensors = new HashSet<>();
public OwDiscoveryService(OwserverBridgeHandler owBridgeHandler) {
super(SUPPORTED_THING_TYPES, 60, false);
this.owBridgeHandler = owBridgeHandler;
this.bridgeUID = owBridgeHandler.getThing().getUID();
logger.debug("registering discovery service for {}", owBridgeHandler);
}
private void scanDirectory(String baseDirectory) {
List<SensorId> directoryList;
logger.trace("scanning {} on bridge {}", baseDirectory, bridgeUID);
try {
directoryList = owBridgeHandler.getDirectory(baseDirectory);
} catch (OwException e) {
logger.info("empty directory '{}' for {}", baseDirectory, bridgeUID);
return;
}
// find all valid sensors
for (SensorId directoryEntry : directoryList) {
try {
OwDiscoveryItem owDiscoveryItem = new OwDiscoveryItem(owBridgeHandler, directoryEntry);
if (owDiscoveryItem.getSensorType() == OwSensorType.DS2409) {
// scan hub sub-directories
logger.trace("found hub {}, scanning sub-directories", directoryEntry);
scanDirectory(owDiscoveryItem.getSensorId().getFullPath() + "/main/");
scanDirectory(owDiscoveryItem.getSensorId().getFullPath() + "/aux/");
} else {
// add found sensor to list
logger.trace("found sensor {} (type: {})", directoryEntry, owDiscoveryItem.getSensorType());
owDiscoveryItems.put(owDiscoveryItem.getSensorId(), owDiscoveryItem);
associatedSensors.addAll(owDiscoveryItem.getAssociatedSensorIds());
}
} catch (OwException e) {
logger.debug("error while scanning for sensors in directory {} on bridge {}: {}", baseDirectory,
bridgeUID, e.getMessage());
}
}
}
@Override
public void startScan() {
scanDirectory("/");
// remove duplicates
owDiscoveryItems.entrySet().removeIf(s -> associatedSensors.contains(s.getKey()));
// make discovery results
for (OwDiscoveryItem owDiscoveryItem : owDiscoveryItems.values()) {
owDiscoveryItem.checkSensorType();
try {
ThingTypeUID thingTypeUID = owDiscoveryItem.getThingTypeUID();
String normalizedId = owDiscoveryItem.getNormalizedSensorId();
ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, normalizedId);
logger.debug("created thing UID {} for sensor {}, type {}", thingUID, owDiscoveryItem.getSensorId(),
owDiscoveryItem.getSensorType());
Map<String, Object> properties = new HashMap<>();
properties.put(PROPERTY_MODELID, owDiscoveryItem.getSensorType().toString());
properties.put(PROPERTY_VENDOR, owDiscoveryItem.getVendor());
properties.put(CONFIG_ID, owDiscoveryItem.getSensorId().getFullPath());
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID)
.withProperties(properties).withBridge(bridgeUID).withLabel(owDiscoveryItem.getLabel()).build();
thingDiscovered(discoveryResult);
} catch (OwException e) {
logger.info("sensor-id {}: {}", owDiscoveryItem.getSensorId(), e.getMessage());
}
}
}
@Override
protected synchronized void stopScan() {
removeOlderResults(getTimestampOfLastScan());
super.stopScan();
}
@Override
public void deactivate() {
removeOlderResults(new Date().getTime());
}
}

View File

@@ -0,0 +1,265 @@
/**
* 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.onewire.internal.handler;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.DS2438Configuration;
import org.openhab.binding.onewire.internal.OwDynamicStateDescriptionProvider;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.config.AMSHandlerConfiguration;
import org.openhab.binding.onewire.internal.device.AbstractOwDevice;
import org.openhab.binding.onewire.internal.device.DS18x20;
import org.openhab.binding.onewire.internal.device.DS2406_DS2413;
import org.openhab.binding.onewire.internal.device.DS2438;
import org.openhab.binding.onewire.internal.device.DS2438.LightSensorType;
import org.openhab.binding.onewire.internal.device.OwChannelConfig;
import org.openhab.binding.onewire.internal.device.OwSensorType;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link AdvancedMultisensorThingHandler} is responsible for handling DS2438 based multisensors (modules)
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class AdvancedMultisensorThingHandler extends OwBaseThingHandler {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = new HashSet<>(
Arrays.asList(THING_TYPE_AMS, THING_TYPE_BMS));
public static final Set<OwSensorType> SUPPORTED_SENSOR_TYPES = Collections
.unmodifiableSet(Stream.of(OwSensorType.AMS, OwSensorType.AMS_S, OwSensorType.BMS, OwSensorType.BMS_S)
.collect(Collectors.toSet()));
private static final String PROPERTY_DS18B20 = "ds18b20";
private static final String PROPERTY_DS2413 = "ds2413";
private static final String PROPERTY_DS2438 = "ds2438";
private static final Set<String> REQUIRED_PROPERTIES_AMS = Collections.unmodifiableSet(
Stream.of(PROPERTY_HW_REVISION, PROPERTY_PROD_DATE, PROPERTY_DS18B20, PROPERTY_DS2438, PROPERTY_DS2413)
.collect(Collectors.toSet()));
private static final Set<String> REQUIRED_PROPERTIES_BMS = Collections.unmodifiableSet(
Stream.of(PROPERTY_HW_REVISION, PROPERTY_PROD_DATE, PROPERTY_DS18B20).collect(Collectors.toSet()));
private final Logger logger = LoggerFactory.getLogger(AdvancedMultisensorThingHandler.class);
private final ThingTypeUID thingType = this.thing.getThingTypeUID();
private int hwRevision = 0;
private int digitalRefreshInterval = 10 * 1000;
private long digitalLastRefresh = 0;
public AdvancedMultisensorThingHandler(Thing thing,
OwDynamicStateDescriptionProvider dynamicStateDescriptionProvider) {
super(thing, dynamicStateDescriptionProvider, SUPPORTED_SENSOR_TYPES,
getRequiredProperties(thing.getThingTypeUID()));
}
@Override
public void initialize() {
AMSHandlerConfiguration configuration = getConfig().as(AMSHandlerConfiguration.class);
Map<String, String> properties = editProperties();
if (!super.configureThingHandler()) {
return;
}
hwRevision = Integer.valueOf(properties.get(PROPERTY_HW_REVISION));
sensors.add(new DS2438(sensorId, this));
sensors.add(new DS18x20(new SensorId(properties.get(PROPERTY_DS18B20)), this));
if (THING_TYPE_AMS.equals(thingType)) {
sensors.add(new DS2438(new SensorId(properties.get(PROPERTY_DS2438)), this));
sensors.add(new DS2406_DS2413(new SensorId(properties.get(PROPERTY_DS2413)), this));
digitalRefreshInterval = configuration.digitalRefresh * 1000;
digitalLastRefresh = 0;
}
scheduler.execute(() -> {
configureThingChannels();
});
}
@Override
public void refresh(OwserverBridgeHandler bridgeHandler, long now) {
try {
if ((now >= (digitalLastRefresh + digitalRefreshInterval)) && (thingType.equals(THING_TYPE_AMS))) {
logger.trace("refreshing digital {}", this.thing.getUID());
Boolean forcedRefresh = digitalLastRefresh == 0;
digitalLastRefresh = now;
if (!sensors.get(3).checkPresence(bridgeHandler)) {
return;
}
sensors.get(3).refresh(bridgeHandler, forcedRefresh);
}
if (now >= (lastRefresh + refreshInterval)) {
if (!sensors.get(0).checkPresence(bridgeHandler)) {
return;
}
logger.trace("refreshing analog {}", this.thing.getUID());
Boolean forcedRefresh = lastRefresh == 0;
lastRefresh = now;
if (thingType.equals(THING_TYPE_AMS)) {
for (int i = 0; i < sensors.size() - 1; i++) {
sensors.get(i).refresh(bridgeHandler, forcedRefresh);
}
} else {
for (int i = 0; i < sensors.size(); i++) {
sensors.get(i).refresh(bridgeHandler, forcedRefresh);
}
}
}
} catch (OwException e) {
logger.debug("{}: refresh exception '{}'", this.thing.getUID(), e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "refresh exception");
}
}
@Override
protected void configureThingChannels() {
Configuration configuration = getConfig();
ThingBuilder thingBuilder = editThing();
// delete unwanted channels
Set<String> existingChannelIds = thing.getChannels().stream().map(channel -> channel.getUID().getId())
.collect(Collectors.toSet());
Set<String> wantedChannelIds = SENSOR_TYPE_CHANNEL_MAP.get(sensorType).stream()
.map(channelConfig -> channelConfig.channelId).collect(Collectors.toSet());
wantedChannelIds.add(CHANNEL_TEMPERATURE);
wantedChannelIds.add(CHANNEL_HUMIDITY);
existingChannelIds.stream().filter(channelId -> !wantedChannelIds.contains(channelId))
.forEach(channelId -> removeChannelIfExisting(thingBuilder, channelId));
// add or update wanted channels
SENSOR_TYPE_CHANNEL_MAP.get(sensorType).stream().forEach(channelConfig -> {
addChannelIfMissingAndEnable(thingBuilder, channelConfig);
});
// temperature channel
if (configuration.containsKey(CONFIG_TEMPERATURESENSOR)
&& configuration.get(CONFIG_TEMPERATURESENSOR).equals("DS18B20")) {
addChannelIfMissingAndEnable(thingBuilder,
new OwChannelConfig(CHANNEL_TEMPERATURE, CHANNEL_TYPE_UID_TEMPERATURE_POR_RES), 1);
} else {
addChannelIfMissingAndEnable(thingBuilder,
new OwChannelConfig(CHANNEL_TEMPERATURE, CHANNEL_TYPE_UID_TEMPERATURE));
}
// humidity channel
addChannelIfMissingAndEnable(thingBuilder, new OwChannelConfig(CHANNEL_HUMIDITY, CHANNEL_TYPE_UID_HUMIDITY),
new Configuration(new HashMap<String, Object>() {
private static final long serialVersionUID = 1L;
{
put(CONFIG_HUMIDITY, "/HIH4000/humidity");
}
}));
// configure light channel
if (sensorType == OwSensorType.AMS_S || sensorType == OwSensorType.BMS_S) {
if (hwRevision <= 13) {
((DS2438) sensors.get(0)).setLightSensorType(LightSensorType.ELABNET_V1);
} else {
((DS2438) sensors.get(0)).setLightSensorType(LightSensorType.ELABNET_V2);
}
}
updateThing(thingBuilder.build());
try {
for (AbstractOwDevice sensor : sensors) {
sensor.configureChannels();
}
} catch (OwException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
return;
}
validConfig = true;
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE);
}
@Override
public void updateSensorProperties(OwserverBridgeHandler bridgeHandler) throws OwException {
Map<String, String> properties = editProperties();
DS2438Configuration ds2438configuration = new DS2438Configuration(bridgeHandler, sensorId);
sensorType = DS2438Configuration.getMultisensorType(ds2438configuration.getSensorSubType(),
ds2438configuration.getAssociatedSensorTypes());
properties.put(PROPERTY_MODELID, sensorType.toString());
properties.put(PROPERTY_VENDOR, ds2438configuration.getVendor());
properties.put(PROPERTY_PROD_DATE, ds2438configuration.getProductionDate());
properties.put(PROPERTY_HW_REVISION, ds2438configuration.getHardwareRevision());
switch (sensorType) {
case BMS:
case BMS_S:
properties.put(PROPERTY_DS18B20,
ds2438configuration.getAssociatedSensorIds(OwSensorType.DS18B20).get(0).getFullPath());
break;
case AMS:
case AMS_S:
properties.put(PROPERTY_DS18B20,
ds2438configuration.getAssociatedSensorIds(OwSensorType.DS18B20).get(0).getFullPath());
properties.put(PROPERTY_DS2413,
ds2438configuration.getAssociatedSensorIds(OwSensorType.DS2413).get(0).getFullPath());
properties.put(PROPERTY_DS2438,
ds2438configuration.getAssociatedSensorIds(OwSensorType.MS_TV).get(0).getFullPath());
break;
default:
throw new OwException("sensorType " + sensorType.toString() + " not supported by this thing handler");
}
updateProperties(properties);
}
/**
* used to determine the correct set of required properties
*
* @param thingType
* @return
*/
private static Set<String> getRequiredProperties(ThingTypeUID thingType) {
if (THING_TYPE_AMS.equals(thingType)) {
return REQUIRED_PROPERTIES_AMS;
} else {
return REQUIRED_PROPERTIES_BMS;
}
}
}

View File

@@ -0,0 +1,247 @@
/**
* 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.onewire.internal.handler;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.OwDynamicStateDescriptionProvider;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.config.BAE091xHandlerConfiguration;
import org.openhab.binding.onewire.internal.device.BAE0910;
import org.openhab.binding.onewire.internal.device.OwChannelConfig;
import org.openhab.binding.onewire.internal.device.OwSensorType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link BAE091xSensorThingHandler} is responsible for handling BAE0910 based multisensors
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class BAE091xSensorThingHandler extends OwBaseThingHandler {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_BAE091X);
private final Logger logger = LoggerFactory.getLogger(BAE091xSensorThingHandler.class);
public static final Set<OwSensorType> SUPPORTED_SENSOR_TYPES = Collections.singleton(OwSensorType.BAE0910);
public BAE091xSensorThingHandler(Thing thing, OwDynamicStateDescriptionProvider dynamicStateDescriptionProvider) {
super(thing, dynamicStateDescriptionProvider, SUPPORTED_SENSOR_TYPES);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof OnOffType) {
if (channelUID.getId().startsWith(CHANNEL_DIGITAL)) {
Bridge bridge = getBridge();
if (bridge != null) {
OwserverBridgeHandler bridgeHandler = (OwserverBridgeHandler) bridge.getHandler();
if (bridgeHandler != null) {
if (!((BAE0910) sensors.get(0)).writeChannel(bridgeHandler, channelUID.getId(), command)) {
logger.debug("writing to channel {} in thing {} not permitted (input channel)", channelUID,
this.thing.getUID());
}
} else {
logger.warn("bridge handler not found");
}
} else {
logger.warn("bridge not found");
}
}
}
// TODO: PWM channels
super.handleCommand(channelUID, command);
}
@Override
public void initialize() {
if (!super.configureThingHandler()) {
return;
}
sensors.add(new BAE0910(sensorId, this));
scheduler.execute(() -> {
configureThingChannels();
});
}
@Override
protected void configureThingChannels() {
ThingUID thingUID = getThing().getUID();
logger.debug("configuring sensors for {}", thingUID);
BAE091xHandlerConfiguration configuration = getConfig().as(BAE091xHandlerConfiguration.class);
Set<OwChannelConfig> wantedChannel = new HashSet<>();
wantedChannel.addAll(SENSOR_TYPE_CHANNEL_MAP.get(sensorType));
// Pin1:
switch (configuration.pin1) {
case CONFIG_BAE_PIN_DISABLED:
break;
case CONFIG_BAE_PIN_COUNTER:
wantedChannel.add(new OwChannelConfig(CHANNEL_COUNTER, CHANNEL_TYPE_UID_BAE_COUNTER));
break;
default:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"unknown configuration option for pin 1");
return;
}
// Pin2:
switch (configuration.pin2) {
case CONFIG_BAE_PIN_DISABLED:
break;
case CONFIG_BAE_PIN_OUT:
wantedChannel.add(
new OwChannelConfig(CHANNEL_DIGITAL2, CHANNEL_TYPE_UID_BAE_DIGITAL_OUT, "Digital Out Pin 2"));
break;
case CONFIG_BAE_PIN_PWM:
wantedChannel
.add(new OwChannelConfig(CHANNEL_PWM_DUTY3, CHANNEL_TYPE_UID_BAE_PWM_DUTY, "Duty Cycle PWM 3"));
wantedChannel.add(new OwChannelConfig(CHANNEL_PWM_FREQ1, CHANNEL_TYPE_UID_BAE_PWM_FREQUENCY,
"Frequency PWM 1/3"));
break;
default:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"unknown configuration option for pin 2");
return;
}
// Pin6:
switch (configuration.pin6) {
case CONFIG_BAE_PIN_DISABLED:
break;
case CONFIG_BAE_PIN_PIO:
wantedChannel.add(new OwChannelConfig(CHANNEL_DIGITAL6, CHANNEL_TYPE_UID_BAE_PIO, "PIO Pin 6"));
break;
case CONFIG_BAE_PIN_PWM:
wantedChannel
.add(new OwChannelConfig(CHANNEL_PWM_DUTY4, CHANNEL_TYPE_UID_BAE_PWM_DUTY, "Duty Cycle PWM 4"));
wantedChannel.add(new OwChannelConfig(CHANNEL_PWM_FREQ2, CHANNEL_TYPE_UID_BAE_PWM_FREQUENCY,
"Frequency PWM 2/4"));
break;
default:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"unknown configuration option for pin 6");
return;
}
// Pin7:
switch (configuration.pin7) {
case CONFIG_BAE_PIN_DISABLED:
break;
case CONFIG_BAE_PIN_ANALOG:
wantedChannel.add(new OwChannelConfig(CHANNEL_VOLTAGE, CHANNEL_TYPE_UID_BAE_ANALOG, "Analog Input"));
break;
case CONFIG_BAE_PIN_OUT:
wantedChannel.add(
new OwChannelConfig(CHANNEL_DIGITAL7, CHANNEL_TYPE_UID_BAE_DIGITAL_OUT, "Digital Out Pin 7"));
break;
case CONFIG_BAE_PIN_PWM:
wantedChannel
.add(new OwChannelConfig(CHANNEL_PWM_DUTY2, CHANNEL_TYPE_UID_BAE_PWM_DUTY, "Duty Cycle PWM 2"));
wantedChannel.add(new OwChannelConfig(CHANNEL_PWM_FREQ2, CHANNEL_TYPE_UID_BAE_PWM_FREQUENCY,
"Frequency PWM 2/4"));
break;
default:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"unknown configuration option for pin 7");
return;
}
// Pin8:
switch (configuration.pin8) {
case CONFIG_BAE_PIN_DISABLED:
break;
case CONFIG_BAE_PIN_IN:
wantedChannel.add(new OwChannelConfig(CHANNEL_DIGITAL8, CHANNEL_TYPE_UID_BAE_DIN, "Digital In Pin 8"));
break;
case CONFIG_BAE_PIN_OUT:
wantedChannel
.add(new OwChannelConfig(CHANNEL_DIGITAL8, CHANNEL_TYPE_UID_BAE_DOUT, "Digital Out Pin 8"));
break;
case CONFIG_BAE_PIN_PWM:
wantedChannel
.add(new OwChannelConfig(CHANNEL_PWM_DUTY1, CHANNEL_TYPE_UID_BAE_PWM_DUTY, "Duty Cycle PWM 1"));
wantedChannel.add(new OwChannelConfig(CHANNEL_PWM_FREQ1, CHANNEL_TYPE_UID_BAE_PWM_FREQUENCY,
"Frequency PWM 1/3"));
break;
default:
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"unknown configuration option for pin 8");
return;
}
ThingBuilder thingBuilder = editThing();
// remove unwanted channels
Set<String> existingChannelIds = thing.getChannels().stream().map(channel -> channel.getUID().getId())
.collect(Collectors.toSet());
Set<String> wantedChannelIds = wantedChannel.stream().map(channelConfig -> channelConfig.channelId)
.collect(Collectors.toSet());
existingChannelIds.stream().filter(channelId -> !wantedChannelIds.contains(channelId))
.forEach(channelId -> removeChannelIfExisting(thingBuilder, channelId));
// add or update wanted channels
wantedChannel.stream().forEach(channelConfig -> {
addChannelIfMissingAndEnable(thingBuilder, channelConfig);
});
updateThing(thingBuilder.build());
try {
sensors.get(0).configureChannels();
} catch (OwException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
return;
}
validConfig = true;
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE);
}
@Override
public void updateSensorProperties(OwserverBridgeHandler bridgeHandler) throws OwException {
Map<String, String> properties = editProperties();
sensorType = BAE0910.getDeviceSubType(bridgeHandler, sensorId);
properties.put(PROPERTY_MODELID, sensorType.toString());
properties.put(PROPERTY_VENDOR, "Brain4home");
updateProperties(properties);
logger.trace("updated modelid/vendor to {} / {}", sensorType.name(), "Brain4home");
}
}

View File

@@ -0,0 +1,120 @@
/**
* 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.onewire.internal.handler;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.DS2438Configuration;
import org.openhab.binding.onewire.internal.OwDynamicStateDescriptionProvider;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.config.MstxHandlerConfiguration;
import org.openhab.binding.onewire.internal.device.DS1923;
import org.openhab.binding.onewire.internal.device.DS2438;
import org.openhab.binding.onewire.internal.device.DS2438.CurrentSensorType;
import org.openhab.binding.onewire.internal.device.DS2438.LightSensorType;
import org.openhab.binding.onewire.internal.device.OwSensorType;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link BasicMultisensorThingHandler} is responsible for handling DS2438/DS1923 based multisensors (single
* sensors)
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class BasicMultisensorThingHandler extends OwBaseThingHandler {
public Logger logger = LoggerFactory.getLogger(BasicMultisensorThingHandler.class);
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_MS_TX);
public static final Set<OwSensorType> SUPPORTED_SENSOR_TYPES = Collections
.unmodifiableSet(Stream.of(OwSensorType.MS_TH, OwSensorType.MS_TC, OwSensorType.MS_TL, OwSensorType.MS_TV,
OwSensorType.DS1923, OwSensorType.DS2438).collect(Collectors.toSet()));
public BasicMultisensorThingHandler(Thing thing,
OwDynamicStateDescriptionProvider dynamicStateDescriptionProvider) {
super(thing, dynamicStateDescriptionProvider, SUPPORTED_SENSOR_TYPES);
}
@Override
public void initialize() {
if (!super.configureThingHandler()) {
return;
}
MstxHandlerConfiguration configuration = getConfig().as(MstxHandlerConfiguration.class);
if (configuration.manualsensor != null && sensorType != configuration.manualsensor) {
logger.debug("sensorType override for thing {}: old={}, new={}", thing.getUID(), sensorType,
configuration.manualsensor);
sensorType = configuration.manualsensor;
}
// add sensors
if (sensorType == OwSensorType.DS1923) {
sensors.add(new DS1923(sensorId, this));
} else {
sensors.add(new DS2438(sensorId, this));
}
scheduler.execute(() -> {
configureThingChannels();
});
}
@Override
protected void configureThingChannels() {
switch (sensorType) {
case DS2438:
((DS2438) sensors.get(0)).setCurrentSensorType(CurrentSensorType.INTERNAL);
break;
case MS_TC:
((DS2438) sensors.get(0)).setCurrentSensorType(CurrentSensorType.IBUTTONLINK);
break;
case MS_TL:
((DS2438) sensors.get(0)).setLightSensorType(LightSensorType.IBUTTONLINK);
break;
default:
}
super.configureThingChannels();
}
@Override
public void updateSensorProperties(OwserverBridgeHandler bridgeHandler) throws OwException {
Map<String, String> properties = editProperties();
sensorType = bridgeHandler.getType(sensorId);
if (sensorType == OwSensorType.DS1923) {
properties.put(PROPERTY_MODELID, sensorType.toString());
properties.put(PROPERTY_VENDOR, "Dallas/Maxim");
} else {
DS2438Configuration ds2438configuration = new DS2438Configuration(bridgeHandler, sensorId);
sensorType = ds2438configuration.getSensorSubType();
properties.put(PROPERTY_MODELID, sensorType.toString());
String vendor = ds2438configuration.getVendor();
properties.put(PROPERTY_VENDOR, vendor);
}
updateProperties(properties);
}
}

View File

@@ -0,0 +1,118 @@
/**
* 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.onewire.internal.handler;
import static org.openhab.binding.onewire.internal.OwBindingConstants.CHANNEL_DIGITAL;
import static org.openhab.binding.onewire.internal.OwBindingConstants.THING_TYPE_BASIC;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.OwDynamicStateDescriptionProvider;
import org.openhab.binding.onewire.internal.device.*;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link BasicThingHandler} is responsible for handling simple sensors
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class BasicThingHandler extends OwBaseThingHandler {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_BASIC);
public static final Set<OwSensorType> SUPPORTED_SENSOR_TYPES = Collections
.unmodifiableSet(Stream.of(OwSensorType.DS1420, OwSensorType.DS18B20, OwSensorType.DS18S20,
OwSensorType.DS1822, OwSensorType.DS2401, OwSensorType.DS2405, OwSensorType.DS2406,
OwSensorType.DS2408, OwSensorType.DS2413, OwSensorType.DS2423).collect(Collectors.toSet()));
private final Logger logger = LoggerFactory.getLogger(BasicThingHandler.class);
public BasicThingHandler(Thing thing, OwDynamicStateDescriptionProvider dynamicStateDescriptionProvider) {
super(thing, dynamicStateDescriptionProvider, SUPPORTED_SENSOR_TYPES);
}
@Override
public void initialize() {
if (!super.configureThingHandler()) {
return;
}
// add sensor
switch (sensorType) {
case DS18B20:
case DS18S20:
case DS1822:
sensors.add(new DS18x20(sensorId, this));
break;
case DS1420:
case DS2401:
sensors.add(new DS2401(sensorId, this));
break;
case DS2405:
sensors.add(new DS2405(sensorId, this));
break;
case DS2406:
case DS2413:
sensors.add(new DS2406_DS2413(sensorId, this));
break;
case DS2408:
sensors.add(new DS2408(sensorId, this));
break;
case DS2423:
sensors.add(new DS2423(sensorId, this));
break;
default:
throw new IllegalArgumentException(
"unsupported sensorType " + sensorType.name() + ", this should have been checked before!");
}
scheduler.execute(() -> {
configureThingChannels();
});
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof OnOffType) {
if (channelUID.getId().startsWith(CHANNEL_DIGITAL) && thing.getChannel(channelUID.getId()) != null) {
Integer ioChannel = Integer.valueOf(channelUID.getId().substring(channelUID.getId().length() - 1));
Bridge bridge = getBridge();
if (bridge != null) {
OwserverBridgeHandler bridgeHandler = (OwserverBridgeHandler) bridge.getHandler();
if (bridgeHandler != null) {
if (!((AbstractDigitalOwDevice) sensors.get(0)).writeChannel(bridgeHandler, ioChannel,
command)) {
logger.debug("writing to channel {} in thing {} not permitted (input channel)", channelUID,
this.thing.getUID());
}
} else {
logger.warn("bridge handler not found");
}
} else {
logger.warn("bridge not found");
}
}
}
super.handleCommand(channelUID, command);
}
}

View File

@@ -0,0 +1,90 @@
/**
* 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.onewire.internal.handler;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.OwDynamicStateDescriptionProvider;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.OwPageBuffer;
import org.openhab.binding.onewire.internal.device.EDS006x;
import org.openhab.binding.onewire.internal.device.OwSensorType;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
/**
* The {@link EDSSensorThingHandler} is responsible for handling EDS multisensors
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class EDSSensorThingHandler extends OwBaseThingHandler {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_EDS_ENV);
public static final Set<OwSensorType> SUPPORTED_SENSOR_TYPES = Collections
.unmodifiableSet(Stream.of(OwSensorType.EDS0064, OwSensorType.EDS0065, OwSensorType.EDS0066,
OwSensorType.EDS0067, OwSensorType.EDS0068).collect(Collectors.toSet()));
private static final Set<String> REQUIRED_PROPERTIES = Collections.singleton(PROPERTY_HW_REVISION);
public EDSSensorThingHandler(Thing thing, OwDynamicStateDescriptionProvider dynamicStateDescriptionProvider) {
super(thing, dynamicStateDescriptionProvider, SUPPORTED_SENSOR_TYPES, REQUIRED_PROPERTIES);
}
@Override
public void initialize() {
if (!super.configureThingHandler()) {
return;
}
// add sensors
sensors.add(new EDS006x(sensorId, sensorType, this));
scheduler.execute(() -> {
configureThingChannels();
});
}
@Override
public void updateSensorProperties(OwserverBridgeHandler bridgeHandler) throws OwException {
Map<String, String> properties = editProperties();
OwPageBuffer pages = bridgeHandler.readPages(sensorId);
OwSensorType sensorType = OwSensorType.UNKNOWN;
try {
sensorType = OwSensorType.valueOf(new String(pages.getPage(0), 0, 7, StandardCharsets.US_ASCII));
} catch (IllegalArgumentException e) {
}
if (!SUPPORTED_SENSOR_TYPES.contains(sensorType)) {
throw new OwException("sensorType not supported for EDSSensorThing");
}
int fwRevisionLow = pages.getByte(3, 3);
int fwRevisionHigh = pages.getByte(3, 4);
String fwRevision = String.format("%d.%d", fwRevisionHigh, fwRevisionLow);
properties.put(PROPERTY_MODELID, sensorType.name());
properties.put(PROPERTY_VENDOR, "Embedded Data Systems");
properties.put(PROPERTY_HW_REVISION, String.valueOf(fwRevision));
updateProperties(properties);
}
}

View File

@@ -0,0 +1,445 @@
/**
* 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.onewire.internal.handler;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.util.ArrayList;
import java.util.Collections;
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.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.onewire.internal.OwDynamicStateDescriptionProvider;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.config.BaseHandlerConfiguration;
import org.openhab.binding.onewire.internal.device.AbstractOwDevice;
import org.openhab.binding.onewire.internal.device.OwChannelConfig;
import org.openhab.binding.onewire.internal.device.OwSensorType;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link OwBaseThingHandler} class defines a handler for simple OneWire devices
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public abstract class OwBaseThingHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(OwBaseThingHandler.class);
protected static final int PROPERTY_UPDATE_INTERVAL = 5000; // in ms
protected static final int PROPERTY_UPDATE_MAX_RETRY = 5;
private static final Set<String> REQUIRED_PROPERTIES = Collections
.unmodifiableSet(Stream.of(PROPERTY_MODELID, PROPERTY_VENDOR).collect(Collectors.toSet()));
protected List<String> requiredProperties = new ArrayList<>(REQUIRED_PROPERTIES);
protected Set<OwSensorType> supportedSensorTypes;
protected final List<AbstractOwDevice> sensors = new ArrayList<>();
protected @NonNullByDefault({}) SensorId sensorId;
protected @NonNullByDefault({}) OwSensorType sensorType;
protected long lastRefresh = 0;
protected long refreshInterval = 300 * 1000;
protected boolean validConfig = false;
protected boolean showPresence = false;
protected OwDynamicStateDescriptionProvider dynamicStateDescriptionProvider;
protected @Nullable ScheduledFuture<?> updateTask;
public OwBaseThingHandler(Thing thing, OwDynamicStateDescriptionProvider dynamicStateDescriptionProvider,
Set<OwSensorType> supportedSensorTypes) {
super(thing);
this.dynamicStateDescriptionProvider = dynamicStateDescriptionProvider;
this.supportedSensorTypes = supportedSensorTypes;
}
public OwBaseThingHandler(Thing thing, OwDynamicStateDescriptionProvider dynamicStateDescriptionProvider,
Set<OwSensorType> supportedSensorTypes, Set<String> requiredProperties) {
super(thing);
this.dynamicStateDescriptionProvider = dynamicStateDescriptionProvider;
this.supportedSensorTypes = supportedSensorTypes;
this.requiredProperties.addAll(requiredProperties);
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
lastRefresh = 0;
logger.trace("scheduled {} for refresh", this.thing.getUID());
}
}
@Override
public void initialize() {
configureThingHandler();
}
protected boolean configureThingHandler() {
BaseHandlerConfiguration configuration = getConfig().as(BaseHandlerConfiguration.class);
Map<String, String> properties = thing.getProperties();
if (getBridge() == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "bridge missing");
return false;
}
sensors.clear();
final String id = configuration.id;
if (id != null) {
try {
this.sensorId = new SensorId(id);
} catch (IllegalArgumentException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "sensor id format mismatch");
return false;
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "sensor id missing");
return false;
}
refreshInterval = configuration.refresh * 1000;
// check if all required properties are present. update if not
for (String property : requiredProperties) {
if (!properties.containsKey(property)) {
updateSensorProperties();
return false;
}
}
sensorType = OwSensorType.valueOf(properties.get(PROPERTY_MODELID));
if (!supportedSensorTypes.contains(sensorType)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"sensor type not supported by this thing type");
return false;
}
lastRefresh = 0;
return true;
}
protected void configureThingChannels() {
ThingBuilder thingBuilder = editThing();
logger.debug("configuring sensors for {}", thing.getUID());
// remove unwanted channels
Set<String> existingChannelIds = thing.getChannels().stream().map(channel -> channel.getUID().getId())
.collect(Collectors.toSet());
Set<String> wantedChannelIds = SENSOR_TYPE_CHANNEL_MAP.get(sensorType).stream()
.map(channelConfig -> channelConfig.channelId).collect(Collectors.toSet());
existingChannelIds.stream().filter(channelId -> !wantedChannelIds.contains(channelId))
.forEach(channelId -> removeChannelIfExisting(thingBuilder, channelId));
// add or update wanted channels
SENSOR_TYPE_CHANNEL_MAP.get(sensorType).stream().forEach(channelConfig -> {
addChannelIfMissingAndEnable(thingBuilder, channelConfig);
});
updateThing(thingBuilder.build());
try {
sensors.get(0).configureChannels();
} catch (OwException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
return;
}
if (thing.getChannel(CHANNEL_PRESENT) != null) {
showPresence = true;
}
validConfig = true;
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE);
}
/**
* check if thing can be refreshed from the bridge handler
*
* @return true if thing can be refreshed
*/
public boolean isRefreshable() {
return super.isInitialized()
&& this.thing.getStatusInfo().getStatusDetail() != ThingStatusDetail.CONFIGURATION_ERROR
&& this.thing.getStatusInfo().getStatusDetail() != ThingStatusDetail.BRIDGE_OFFLINE;
}
/**
* refresh this thing
*
* needs proper exception handling for refresh errors if overridden
*
* @param bridgeHandler bridge handler to use for communication with ow bus
* @param now current time
*/
public void refresh(OwserverBridgeHandler bridgeHandler, long now) {
try {
Boolean forcedRefresh = lastRefresh == 0;
if (now >= (lastRefresh + refreshInterval)) {
logger.trace("refreshing {}", this.thing.getUID());
lastRefresh = now;
if (!sensors.get(0).checkPresence(bridgeHandler)) {
logger.trace("sensor not present");
return;
}
for (int i = 0; i < sensors.size(); i++) {
logger.trace("refreshing sensor {} ({})", i, sensors.get(i).getSensorId());
sensors.get(i).refresh(bridgeHandler, forcedRefresh);
}
}
} catch (OwException e) {
logger.debug("{}: refresh exception {}", this.thing.getUID(), e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "refresh exception");
}
}
/**
* update presence status to present state of slave
*
* @param presentState current present state
*/
public void updatePresenceStatus(State presentState) {
if (OnOffType.ON.equals(presentState)) {
updateStatus(ThingStatus.ONLINE);
if (showPresence) {
updateState(CHANNEL_PRESENT, OnOffType.ON);
}
} else if (OnOffType.OFF.equals(presentState)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "slave missing");
if (showPresence) {
updateState(CHANNEL_PRESENT, OnOffType.OFF);
}
} else {
updateStatus(ThingStatus.UNKNOWN);
if (showPresence) {
updateState(CHANNEL_PRESENT, UnDefType.UNDEF);
}
}
}
/**
* post update to channel
*
* @param channelId channel id
* @param state new channel state
*/
public void postUpdate(String channelId, State state) {
if (this.thing.getChannel(channelId) != null) {
updateState(channelId, state);
} else {
logger.warn("{} missing channel {} when posting update {}", this.thing.getUID(), channelId, state);
}
}
@Override
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE
&& getThing().getStatusInfo().getStatusDetail() == ThingStatusDetail.BRIDGE_OFFLINE) {
if (validConfig) {
updatePresenceStatus(UnDefType.UNDEF);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
}
} else if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
}
@Override
public void dispose() {
dynamicStateDescriptionProvider.removeDescriptionsForThing(thing.getUID());
super.dispose();
}
/**
* add this sensor to the property update list of the bridge handler
*
*/
protected void updateSensorProperties() {
Bridge bridge = getBridge();
if (bridge == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "bridge not found");
return;
}
OwserverBridgeHandler bridgeHandler = (OwserverBridgeHandler) bridge.getHandler();
if (bridgeHandler == null) {
logger.debug("bridgehandler for {} not available for scheduling property update, retrying in 5s",
thing.getUID());
scheduler.schedule(() -> {
updateSensorProperties();
}, 5000, TimeUnit.MILLISECONDS);
return;
}
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "required properties missing");
bridgeHandler.scheduleForPropertiesUpdate(thing);
}
/**
* thing specific update method for sensor properties
*
* called by the bridge handler
*
* @param bridgeHandler the bridge handler to be used
* @return properties to be added to the properties map
* @throws OwException
*/
public void updateSensorProperties(OwserverBridgeHandler bridgeHandler) throws OwException {
Map<String, String> properties = editProperties();
OwSensorType sensorType = bridgeHandler.getType(sensorId);
properties.put(PROPERTY_MODELID, sensorType.toString());
properties.put(PROPERTY_VENDOR, "Dallas/Maxim");
updateProperties(properties);
logger.trace("updated modelid/vendor to {} / {}", sensorType.name(), "Dallas/Maxim");
}
/**
* get the dynamic state description provider for this thing
*
* @return
*/
public @Nullable OwDynamicStateDescriptionProvider getDynamicStateDescriptionProvider() {
return dynamicStateDescriptionProvider;
}
/**
* remove a channel during initialization if it exists
*
* @param thingBuilder ThingBuilder of the edited thing
* @param channelId id of the channel
*/
protected void removeChannelIfExisting(ThingBuilder thingBuilder, String channelId) {
if (thing.getChannel(channelId) != null) {
thingBuilder.withoutChannel(new ChannelUID(thing.getUID(), channelId));
}
}
/**
* adds (or replaces) a channel and enables it within the sensor (configuration preserved, default sensor)
*
* @param thingBuilder ThingBuilder of the edited thing
* @param channelConfig a OwChannelConfig for the new channel
* @return the newly created channel
*/
protected Channel addChannelIfMissingAndEnable(ThingBuilder thingBuilder, OwChannelConfig channelConfig) {
return addChannelIfMissingAndEnable(thingBuilder, channelConfig, null, 0);
}
/**
* adds (or replaces) a channel and enables it within the sensor (configuration overridden, default sensor)
*
* @param thingBuilder ThingBuilder of the edited thing
* @param channelConfig a OwChannelConfig for the new channel
* @param configuration the new Configuration for this channel
* @return the newly created channel
*/
protected Channel addChannelIfMissingAndEnable(ThingBuilder thingBuilder, OwChannelConfig channelConfig,
Configuration configuration) {
return addChannelIfMissingAndEnable(thingBuilder, channelConfig, configuration, 0);
}
/**
* adds (or replaces) a channel and enables it within the sensor (configuration preserved)
*
* @param thingBuilder ThingBuilder of the edited thing
* @param channelConfig a OwChannelConfig for the new channel
* @param sensorNo number of sensor that provides this channel
* @return the newly created channel
*/
protected Channel addChannelIfMissingAndEnable(ThingBuilder thingBuilder, OwChannelConfig channelConfig,
int sensorNo) {
return addChannelIfMissingAndEnable(thingBuilder, channelConfig, null, sensorNo);
}
/**
* adds (or replaces) a channel and enables it within the sensor (configuration overridden)
*
* @param thingBuilder ThingBuilder of the edited thing
* @param channelConfig a OwChannelConfig for the new channel
* @param configuration the new Configuration for this channel
* @param sensorNo number of sensor that provides this channel
* @return the newly created channel
*/
protected Channel addChannelIfMissingAndEnable(ThingBuilder thingBuilder, OwChannelConfig channelConfig,
@Nullable Configuration configuration, int sensorNo) {
Channel channel = thing.getChannel(channelConfig.channelId);
Configuration config = configuration;
String label = channelConfig.label;
// remove channel if wrong type uid and preserve config if not overridden
if (channel != null && !channelConfig.channelTypeUID.equals(channel.getChannelTypeUID())) {
removeChannelIfExisting(thingBuilder, channelConfig.channelId);
if (config == null) {
config = channel.getConfiguration();
}
channel = null;
}
// create channel if missing
if (channel == null) {
ChannelBuilder channelBuilder = ChannelBuilder
.create(new ChannelUID(thing.getUID(), channelConfig.channelId),
ACCEPTED_ITEM_TYPES_MAP.get(channelConfig.channelId))
.withType(channelConfig.channelTypeUID);
if (label != null) {
channelBuilder.withLabel(label);
}
if (config != null) {
channelBuilder.withConfiguration(config);
}
channel = channelBuilder.build();
thingBuilder.withChannel(channel);
}
// enable channel in sensor
sensors.get(sensorNo).enableChannel(channelConfig.channelId);
return channel;
}
}

View File

@@ -0,0 +1,430 @@
/**
* 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.onewire.internal.handler;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.OwPageBuffer;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.device.OwSensorType;
import org.openhab.binding.onewire.internal.owserver.OwfsDirectChannelConfig;
import org.openhab.binding.onewire.internal.owserver.OwserverConnection;
import org.openhab.binding.onewire.internal.owserver.OwserverConnectionState;
import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link OwserverBridgeHandler} class implements the refresher and the interface for reading from the bridge
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class OwserverBridgeHandler extends BaseBridgeHandler {
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = Collections.singleton(THING_TYPE_OWSERVER);
private final Logger logger = LoggerFactory.getLogger(OwserverBridgeHandler.class);
protected boolean refreshable = false;
protected ScheduledFuture<?> refreshTask = scheduler.scheduleWithFixedDelay(this::refresh, 1, 1000,
TimeUnit.MILLISECONDS);
// thing update
private final Queue<@Nullable Thing> thingPropertiesUpdateQueue = new ConcurrentLinkedQueue<>();
private static final int RECONNECT_AFTER_FAIL_TIME = 5000; // in ms
private final OwserverConnection owserverConnection;
private final List<OwfsDirectChannelConfig> channelConfigs = new ArrayList<>();
public OwserverBridgeHandler(Bridge bridge) {
super(bridge);
this.owserverConnection = new OwserverConnection(this);
}
public OwserverBridgeHandler(Bridge bridge, OwserverConnection owserverConnection) {
super(bridge);
this.owserverConnection = owserverConnection;
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
@Override
public void initialize() {
Configuration configuration = getConfig();
if (configuration.get(CONFIG_ADDRESS) != null) {
owserverConnection.setHost((String) configuration.get(CONFIG_ADDRESS));
}
if (configuration.get(CONFIG_PORT) != null) {
owserverConnection.setPort(((BigDecimal) configuration.get(CONFIG_PORT)).intValue());
}
for (Channel channel : thing.getChannels()) {
if (CHANNEL_TYPE_UID_OWFS_NUMBER.equals(channel.getChannelTypeUID())
|| CHANNEL_TYPE_UID_OWFS_STRING.equals(channel.getChannelTypeUID())) {
final OwfsDirectChannelConfig channelConfig = channel.getConfiguration()
.as(OwfsDirectChannelConfig.class);
if (channelConfig.initialize(channel.getUID(), channel.getAcceptedItemType())) {
channelConfigs.add(channelConfig);
} else {
logger.info("configuration mismatch: {}", channelConfig);
}
}
}
// makes it possible for unit tests to differentiate direct update and
// postponed update through the owserverConnection:
updateStatus(ThingStatus.UNKNOWN);
scheduler.execute(() -> {
synchronized (owserverConnection) {
owserverConnection.start();
}
});
if (refreshTask.isCancelled()) {
refreshTask = scheduler.scheduleWithFixedDelay(this::refresh, 1, 1000, TimeUnit.MILLISECONDS);
}
}
/**
* refresh all sensors on this bridge
*/
private void refresh() {
try {
long now = System.currentTimeMillis();
if (!refreshable) {
logger.trace("refresh requested by thread ID {} denied, as not refresheable",
Thread.currentThread().getId());
return;
}
// refresh thing channels
List<Thing> thingList = getThing().getThings();
int thingCount = thingList.size();
Iterator<Thing> childListIterator = thingList.iterator();
logger.trace("refreshTask with thread ID {} starts at {}, {} childs", Thread.currentThread().getId(), now,
thingCount);
while (childListIterator.hasNext() && refreshable) {
Thing owThing = childListIterator.next();
logger.trace("refresh: getting handler for {} ({} to go)", owThing.getUID(), thingCount);
OwBaseThingHandler owHandler = (OwBaseThingHandler) owThing.getHandler();
if (owHandler != null) {
if (owHandler.isRefreshable()) {
logger.trace("{} initialized, refreshing", owThing.getUID());
owHandler.refresh(OwserverBridgeHandler.this, now);
} else {
logger.trace("{} not initialized, skipping refresh", owThing.getUID());
}
} else {
logger.debug("{} handler missing", owThing.getUID());
}
thingCount--;
}
if (!refreshable) {
logger.trace("refresh aborted, as brige became non-refresheable.");
return;
}
refreshBridgeChannels(now);
// update thing properties (only one per refresh cycle)
if (!refreshable) {
logger.trace("refresh aborted, as brige became non-refresheable.");
return;
}
Thing updateThing = thingPropertiesUpdateQueue.poll();
if (updateThing != null) {
logger.trace("update: getting handler for {} ({} total in list)", updateThing.getUID(),
thingPropertiesUpdateQueue.size());
OwBaseThingHandler owHandler = (OwBaseThingHandler) updateThing.getHandler();
if (owHandler != null) {
try {
owHandler.updateSensorProperties(this);
owHandler.initialize();
logger.debug("{} sucessfully updated properties, removing from property update list",
updateThing.getUID());
} catch (OwException e) {
thingPropertiesUpdateQueue.add(updateThing);
logger.debug("updating thing properties for {} failed: {}, adding to end of list",
updateThing.getUID(), e.getMessage());
}
} else {
logger.debug("{} is missing handler, removing from property update list", updateThing.getUID());
}
}
} catch (RuntimeException e) {
// catching RuntimeException because scheduled tasks finish once an exception occurs
logger.error("refresh encountered exception of {}: {}, please report bug", e.getClass(), e.getMessage());
}
}
@Override
public void dispose() {
refreshable = false;
if (!refreshTask.isCancelled()) {
refreshTask.cancel(false);
}
owserverConnection.stop();
}
/**
* schedules a thing for updating the thing properties
*
* @param thing the thing to be updated
*/
public void scheduleForPropertiesUpdate(Thing thing) {
thingPropertiesUpdateQueue.add(thing);
}
/**
* get all sensors attached to this bridge
*
* @return a list of all sensor-IDs
*/
public List<SensorId> getDirectory(String basePath) throws OwException {
synchronized (owserverConnection) {
return owserverConnection.getDirectory(basePath);
}
}
/**
* check the presence of a sensor on the bus
*
* @param sensorId the sensor's full ID
* @return ON if present, OFF if missing
* @throws OwException
*/
public State checkPresence(SensorId sensorId) throws OwException {
synchronized (owserverConnection) {
return owserverConnection.checkPresence(sensorId.getFullPath());
}
}
/**
* get a sensors type string
*
* @param sensorId the sensor's full ID
* @return a String containing the sensor type
* @throws OwException
*/
public OwSensorType getType(SensorId sensorId) throws OwException {
OwSensorType sensorType = OwSensorType.UNKNOWN;
synchronized (owserverConnection) {
try {
sensorType = OwSensorType.valueOf(owserverConnection.readString(sensorId + "/type"));
} catch (IllegalArgumentException e) {
}
}
return sensorType;
}
/**
* get full sensor information stored in pages (not available on all sensors)
*
* @param sensorId the sensor's full ID
* @return a OwPageBuffer object containing the requested information
* @throws OwException
*/
public OwPageBuffer readPages(SensorId sensorId) throws OwException {
synchronized (owserverConnection) {
return owserverConnection.readPages(sensorId.getFullPath());
}
}
/**
* read a single decimal value from a sensor
*
* @param sensorId the sensor's full ID
* @param parameter device parameters needed for this request
* @return a DecimalType
* @throws OwException
*/
public State readDecimalType(SensorId sensorId, OwserverDeviceParameter parameter) throws OwException {
synchronized (owserverConnection) {
return owserverConnection.readDecimalType(parameter.getPath(sensorId));
}
}
/**
* read a BitSet value from a sensor
*
* @param sensorId the sensor's full ID
* @param parameter device parameters needed for this request
* @return a BitSet
* @throws OwException
*/
public BitSet readBitSet(SensorId sensorId, OwserverDeviceParameter parameter) throws OwException {
return BitSet.valueOf(new long[] { ((DecimalType) readDecimalType(sensorId, parameter)).longValue() });
}
/**
* read an array of decimal values from a sensor
*
* @param sensorId the sensor's full ID
* @param parameter device parameters needed for this request
* @return a list of DecimalType values
* @throws OwException
*/
public List<State> readDecimalTypeArray(SensorId sensorId, OwserverDeviceParameter parameter) throws OwException {
synchronized (owserverConnection) {
return owserverConnection.readDecimalTypeArray(parameter.getPath(sensorId));
}
}
/**
* read a string from a sensor
*
* @param sensorId the sensor's full ID
* @param parameter device parameters needed for this request
* @return a String
* @throws OwException
*/
public String readString(SensorId sensorId, OwserverDeviceParameter parameter) throws OwException {
synchronized (owserverConnection) {
return owserverConnection.readString(parameter.getPath(sensorId));
}
}
/**
* writes a DecimalType to the sensor
*
* @param sensorId the sensor's full ID
* @param parameter device parameters needed for this request
* @throws OwException
*/
public void writeDecimalType(SensorId sensorId, OwserverDeviceParameter parameter, DecimalType value)
throws OwException {
synchronized (owserverConnection) {
owserverConnection.writeDecimalType(parameter.getPath(sensorId), value);
}
}
/**
* writes a BitSet to the sensor
*
* @param sensorId the sensor's full ID
* @param parameter device parameters needed for this request
* @throws OwException
*/
public void writeBitSet(SensorId sensorId, OwserverDeviceParameter parameter, BitSet value) throws OwException {
writeDecimalType(sensorId, parameter, new DecimalType(value.toLongArray()[0]));
}
/**
* returns if this bridge is refreshable
*
* @return true if implementation reports communication ready
*/
public boolean isRefreshable() {
return refreshable;
}
/**
* updates the thing status with the current connection state
*
* @param connectionState current connection state
*/
public void reportConnectionState(OwserverConnectionState connectionState) {
logger.debug("Updating owserverconnectionstate to {}", connectionState);
switch (connectionState) {
case FAILED:
refreshable = false;
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
scheduler.schedule(() -> {
synchronized (owserverConnection) {
owserverConnection.start();
}
}, RECONNECT_AFTER_FAIL_TIME, TimeUnit.MILLISECONDS);
break;
case STOPPED:
refreshable = false;
break;
case OPENED:
case CLOSED:
refreshable = true;
updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
break;
}
}
/**
* refreshes channels attached to the bridge
*
* @param now current time
*/
public void refreshBridgeChannels(long now) {
for (OwfsDirectChannelConfig channelConfig : channelConfigs) {
if (now > channelConfig.lastRefresh + channelConfig.refreshCycle) {
State value;
try {
synchronized (owserverConnection) {
if (channelConfig.acceptedItemType.equals("String")) {
value = new StringType(owserverConnection.readString(channelConfig.path));
} else if (channelConfig.acceptedItemType.equals("Number")) {
value = owserverConnection.readDecimalType(channelConfig.path);
} else {
logger.debug("mismatched configuration, itemType unknown for channel {}",
channelConfig.channelUID);
continue;
}
}
final ChannelUID channelUID = channelConfig.channelUID;
if (channelUID == null) {
throw new OwException("channelUID is null");
}
updateState(channelUID, value);
logger.trace("updated {} to {}", channelConfig.channelUID, value);
channelConfig.lastRefresh = now;
} catch (OwException e) {
logger.debug("could not read direct channel {}: {}", channelConfig.channelUID, e.getMessage());
}
}
}
}
}

View File

@@ -0,0 +1,44 @@
/**
* 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.onewire.internal.owserver;
import java.math.BigDecimal;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.thing.ChannelUID;
/**
* The {@link OwfsDirectChannelConfig} defines config for owfsdirect channels
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class OwfsDirectChannelConfig {
public String path = "";
public BigDecimal refresh = new BigDecimal(300);
public long lastRefresh = 0;
public int refreshCycle = 300;
public @Nullable ChannelUID channelUID;
public String acceptedItemType = "";
public boolean initialize(ChannelUID channelUID, @Nullable String acceptedItemType) {
this.channelUID = channelUID;
this.acceptedItemType = acceptedItemType != null ? acceptedItemType : "";
refreshCycle = refresh.intValue() * 1000;
return !path.isEmpty();
}
}

View File

@@ -0,0 +1,518 @@
/**
* 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.onewire.internal.owserver;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.OwPageBuffer;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link OwserverConnection} defines the protocol for connections to owservers.
*
* Data is requested by using one of the read / write methods. In case of errors, an {@link OwException}
* is thrown. All other exceptions are caught and handled.
*
* The data request methods follow a general pattern:
* * build the appropriate {@link OwserverPacket} for the request
* * call {@link #request(OwserverPacket)} to ask for the data, which then
* * uses {@link #write(OwserverPacket)} to get the request to the server and
* * uses {@link #read(boolean)} to get the result
*
* Hereby, the resulting packet is examined on an appropriate return code (!= -1) and whether the
* expected payload is attached. If not, an {@link OwException} is thrown.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class OwserverConnection {
public static final int DEFAULT_PORT = 4304;
public static final int KEEPALIVE_INTERVAL = 1000;
private static final int CONNECTION_MAX_RETRY = 5;
private final Logger logger = LoggerFactory.getLogger(OwserverConnection.class);
private final OwserverBridgeHandler thingHandlerCallback;
private String owserverAddress = "";
private int owserverPort = DEFAULT_PORT;
private @Nullable Socket owserverSocket = null;
private @Nullable DataInputStream owserverInputStream = null;
private @Nullable DataOutputStream owserverOutputStream = null;
private OwserverConnectionState owserverConnectionState = OwserverConnectionState.STOPPED;
private boolean tryingConnectionRecovery = false;
// reset to 0 after successful request
private int connectionErrorCounter = 0;
public OwserverConnection(OwserverBridgeHandler owBaseBridgeHandler) {
this.thingHandlerCallback = owBaseBridgeHandler;
}
/**
* set the owserver host address
*
* @param address as String (IP or FQDN), defaults to localhost
*/
public void setHost(String address) {
this.owserverAddress = address;
if (owserverConnectionState != OwserverConnectionState.STOPPED) {
close();
}
}
/**
* set the owserver port
*
* @param port defaults to 4304
*/
public void setPort(int port) {
this.owserverPort = port;
if (owserverConnectionState != OwserverConnectionState.STOPPED) {
close();
}
}
/**
* start the owserver connection
*/
public void start() {
logger.debug("Trying to (re)start OW server connection - previous state: {}",
owserverConnectionState.toString());
connectionErrorCounter = 0;
tryingConnectionRecovery = true;
boolean success = false;
do {
success = open();
if (success && owserverConnectionState != OwserverConnectionState.FAILED) {
tryingConnectionRecovery = false;
}
} while (!success && (owserverConnectionState != OwserverConnectionState.FAILED || tryingConnectionRecovery));
}
/**
* stop the owserver connection and report new {@link OwserverConnectionState} to {@link #thingHandlerCallback}.
*/
public void stop() {
close();
owserverConnectionState = OwserverConnectionState.STOPPED;
thingHandlerCallback.reportConnectionState(owserverConnectionState);
}
/**
* list all devices on this owserver
*
* @return a list of device ids
*/
public @NonNullByDefault({}) List<SensorId> getDirectory(String basePath) throws OwException {
OwserverPacket requestPacket = new OwserverPacket(OwserverMessageType.DIRALL, basePath);
OwserverPacket returnPacket = request(requestPacket);
if ((returnPacket.getReturnCode() != -1) && returnPacket.hasPayload()) {
return Arrays.stream(returnPacket.getPayloadString().split(",")).map(this::stringToSensorId)
.filter(Objects::nonNull).collect(Collectors.toList());
} else {
throw new OwException("invalid of empty packet when requesting directory");
}
}
private @Nullable SensorId stringToSensorId(String s) {
try {
return new SensorId(s);
} catch (IllegalArgumentException e) {
return null;
}
}
/**
* check sensor presence
*
* Errors are caught and interpreted as sensor not present.
*
* @param path full owfs path to sensor
* @return OnOffType, ON=present, OFF=not present
*/
public State checkPresence(String path) {
State returnValue = OnOffType.OFF;
try {
OwserverPacket requestPacket;
requestPacket = new OwserverPacket(OwserverMessageType.PRESENT, path, OwserverControlFlag.UNCACHED);
OwserverPacket returnPacket = request(requestPacket);
if (returnPacket.getReturnCode() == 0) {
returnValue = OnOffType.ON;
}
} catch (OwException e) {
returnValue = OnOffType.OFF;
}
logger.trace("presence {} : {}", path, returnValue);
return returnValue;
}
/**
* read a decimal type
*
* @param path full owfs path to sensor
* @return DecimalType if successful
* @throws OwException
*/
public State readDecimalType(String path) throws OwException {
State returnState = UnDefType.UNDEF;
OwserverPacket requestPacket = new OwserverPacket(OwserverMessageType.READ, path);
OwserverPacket returnPacket = request(requestPacket);
if ((returnPacket.getReturnCode() != -1) && returnPacket.hasPayload()) {
try {
returnState = DecimalType.valueOf(returnPacket.getPayloadString().trim());
} catch (NumberFormatException e) {
throw new OwException("could not parse '" + returnPacket.getPayloadString().trim() + "' to a number");
}
} else {
throw new OwException("invalid or empty packet when requesting decimal type");
}
return returnState;
}
/**
* read a decimal type array
*
* @param path full owfs path to sensor
* @return a List of DecimalType values if successful
* @throws OwException
*/
public List<State> readDecimalTypeArray(String path) throws OwException {
List<State> returnList = new ArrayList<>();
OwserverPacket requestPacket = new OwserverPacket(OwserverMessageType.READ, path);
OwserverPacket returnPacket = request(requestPacket);
if ((returnPacket.getReturnCode() != -1) && returnPacket.hasPayload()) {
Arrays.stream(returnPacket.getPayloadString().split(","))
.forEach(v -> returnList.add(DecimalType.valueOf(v.trim())));
} else {
throw new OwException("invalid or empty packet when requesting decimal type array");
}
return returnList;
}
/**
* read a string
*
* @param path full owfs path to sensor
* @return requested String
* @throws OwException
*/
public String readString(String path) throws OwException {
OwserverPacket requestPacket = new OwserverPacket(OwserverMessageType.READ, path);
OwserverPacket returnPacket = request(requestPacket);
if ((returnPacket.getReturnCode() != -1) && returnPacket.hasPayload()) {
return returnPacket.getPayloadString().trim();
} else {
throw new OwException("invalid or empty packet when requesting string type");
}
}
/**
* read all sensor pages
*
* @param path full owfs path to sensor
* @return page buffer
* @throws OwException
*/
public OwPageBuffer readPages(String path) throws OwException {
OwserverPacket requestPacket = new OwserverPacket(OwserverMessageType.READ, path + "/pages/page.ALL");
OwserverPacket returnPacket = request(requestPacket);
if ((returnPacket.getReturnCode() != -1) && returnPacket.hasPayload()) {
return returnPacket.getPayload();
} else {
throw new OwException("invalid or empty packet when requesting pages");
}
}
/**
* write a DecimalType
*
* @param path full owfs path to the sensor
* @param value the value to write
* @throws OwException
*/
public void writeDecimalType(String path, DecimalType value) throws OwException {
OwserverPacket requestPacket = new OwserverPacket(OwserverMessageType.WRITE, path);
requestPacket.appendPayload(String.valueOf(value));
// request method throws an OwException in case of issues...
OwserverPacket returnPacket = request(requestPacket);
logger.trace("wrote: {}, got: {} ", requestPacket, returnPacket);
}
/**
* process a request to the owserver
*
* @param requestPacket the request to be send
* @return the raw owserver answer
* @throws OwException
*/
private OwserverPacket request(OwserverPacket requestPacket) throws OwException {
OwserverPacket returnPacket = new OwserverPacket(OwserverPacketType.RETURN);
// answer to value write is always empty
boolean payloadExpected = requestPacket.getMessageType() != OwserverMessageType.WRITE;
try {
// write request - error may be thrown
write(requestPacket);
// try to read data as long as we don't get any feedback and no error is thrown...
do {
if (requestPacket.getMessageType() == OwserverMessageType.PRESENT
|| requestPacket.getMessageType() == OwserverMessageType.NOP) {
returnPacket = read(true);
} else {
returnPacket = read(false);
}
} while (returnPacket.isPingPacket() || !(returnPacket.hasPayload() == payloadExpected));
} catch (OwException e) {
logger.debug("failed requesting {}->{} [{}]", requestPacket, returnPacket, e.getMessage());
throw e;
}
if (!returnPacket.hasControlFlag(OwserverControlFlag.PERSISTENCE)) {
logger.trace("closing connection because persistence was denied");
close();
}
// Success! Reset error counter.
connectionErrorCounter = 0;
return returnPacket;
}
/**
* open/reopen the connection to the owserver
*
* In case of issues, the connection is closed using {@link #closeOnError()} and false is returned.
* If the {@link #owserverConnectionState} is in STOPPED or FAILED, the method directly returns false.
*
* @return true if open
*/
private boolean open() {
try {
if (owserverConnectionState == OwserverConnectionState.CLOSED || tryingConnectionRecovery) {
// open socket & set timeout to 3000ms
final Socket owserverSocket = new Socket(owserverAddress, owserverPort);
owserverSocket.setSoTimeout(3000);
this.owserverSocket = owserverSocket;
owserverInputStream = new DataInputStream(owserverSocket.getInputStream());
owserverOutputStream = new DataOutputStream(owserverSocket.getOutputStream());
owserverConnectionState = OwserverConnectionState.OPENED;
thingHandlerCallback.reportConnectionState(owserverConnectionState);
logger.debug("OW connection state: opened to {}:{}", owserverAddress, owserverPort);
return true;
} else if (owserverConnectionState == OwserverConnectionState.OPENED) {
// socket already open, clear input buffer
logger.trace("owServerConnection already open, skipping input buffer");
final DataInputStream owserverInputStream = this.owserverInputStream;
while (owserverInputStream != null) {
if (owserverInputStream.skip(owserverInputStream.available()) == 0) {
return true;
}
}
logger.debug("input stream not available on skipping");
closeOnError();
return false;
} else {
return false;
}
} catch (IOException e) {
logger.debug("could not open owServerConnection to {}:{}: {}", owserverAddress, owserverPort,
e.getMessage());
closeOnError();
return false;
}
}
/**
* close connection and report connection state to callback
*/
private void close() {
this.close(true);
}
/**
* close the connection to the owserver instance.
*
* @param reportConnectionState true, if connection state shall be reported to callback
*/
private void close(boolean reportConnectionState) {
final Socket owserverSocket = this.owserverSocket;
if (owserverSocket != null) {
try {
owserverSocket.close();
owserverConnectionState = OwserverConnectionState.CLOSED;
logger.debug("closed connection");
} catch (IOException e) {
owserverConnectionState = OwserverConnectionState.FAILED;
logger.warn("could not close connection: {}", e.getMessage());
}
}
this.owserverSocket = null;
this.owserverInputStream = null;
this.owserverOutputStream = null;
if (reportConnectionState) {
thingHandlerCallback.reportConnectionState(owserverConnectionState);
}
}
/**
* check if the connection is dead and close it
*/
private void checkConnection() {
try {
int pid = ((DecimalType) readDecimalType("/system/process/pid")).intValue();
logger.debug("read pid {} -> connection still alive", pid);
return;
} catch (OwException e) {
closeOnError();
}
}
/**
* close the connection to the owserver instance after an error occured.
* if {@link #CONNECTION_MAX_RETRY} is exceeded, {@link #owserverConnectionState} is set to FAILED
* and state is reported to callback.
*/
private void closeOnError() {
connectionErrorCounter++;
close(false);
if (connectionErrorCounter > CONNECTION_MAX_RETRY) {
logger.debug("OW connection state: set to failed as max retries exceeded.");
owserverConnectionState = OwserverConnectionState.FAILED;
tryingConnectionRecovery = false;
thingHandlerCallback.reportConnectionState(owserverConnectionState);
} else if (!tryingConnectionRecovery) {
// as close did not report connections state and we are not trying to recover ...
thingHandlerCallback.reportConnectionState(owserverConnectionState);
}
}
/**
* write to the owserver
*
* In case of issues, the connection is closed using {@link #closeOnError()} and an
* {@link OwException} is thrown.
*
* @param requestPacket data to write
* @throws OwException
*/
private void write(OwserverPacket requestPacket) throws OwException {
try {
if (open()) {
requestPacket.setControlFlags(OwserverControlFlag.PERSISTENCE);
final DataOutputStream owserverOutputStream = this.owserverOutputStream;
if (owserverOutputStream != null) {
owserverOutputStream.write(requestPacket.toBytes());
logger.trace("wrote: {}", requestPacket);
} else {
logger.debug("output stream not available on write");
closeOnError();
throw new OwException("I/O Error: output stream not available on write");
}
} else {
// was not opened
throw new OwException("I/O error: could not open connection to send request packet");
}
} catch (IOException e) {
closeOnError();
logger.debug("couldn't send {}, {}", requestPacket, e.getMessage());
throw new OwException("I/O Error: exception while sending request packet - " + e.getMessage());
}
}
/**
* read from owserver
*
* In case of errors (which may also be due to an erroneous path), the connection is checked and potentially closed
* using {@link #checkConnection()}.
*
* @param noTimeoutException retry in case of read time outs instead of exiting with an {@link OwException}.
* @return the read packet
* @throws OwException
*/
private OwserverPacket read(boolean noTimeoutException) throws OwException {
OwserverPacket returnPacket = new OwserverPacket(OwserverPacketType.RETURN);
final DataInputStream owserverInputStream = this.owserverInputStream;
if (owserverInputStream != null) {
DataInputStream inputStream = owserverInputStream;
try {
returnPacket = new OwserverPacket(inputStream, OwserverPacketType.RETURN);
} catch (EOFException e) {
// Read suddenly ended ....
logger.warn("EOFException: exception while reading packet - {}", e.getMessage());
checkConnection();
throw new OwException("EOFException: exception while reading packet - " + e.getMessage());
} catch (OwException e) {
// Some other issue
checkConnection();
throw e;
} catch (IOException e) {
// Read time out
if (e.getMessage().equals("Read timed out") && noTimeoutException) {
logger.trace("timeout - setting error code to -1");
// will lead to re-try reading in request method!!!
returnPacket.setPayload("timeout");
returnPacket.setReturnCode(-1);
} else {
// Other I/O issue
checkConnection();
throw new OwException("I/O error: exception while reading packet - " + e.getMessage());
}
}
logger.trace("read: {}", returnPacket);
} else {
logger.debug("input stream not available on read");
closeOnError();
throw new OwException("I/O Error: input stream not available on read");
}
return returnPacket;
}
}

View File

@@ -0,0 +1,41 @@
/**
* 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.onewire.internal.owserver;
/**
* The {@link OwserverConnectionState} defines the state for connections to owservers
*
* @author Jan N. Klug - Initial contribution
*/
public enum OwserverConnectionState {
/**
* The {@link OwserverConnection} is being torn down (mostly due to dispose of handler).
* No refresh, etc. are possible.
*/
STOPPED,
/**
* The connection is open.
*/
OPENED,
/**
* The connection is closed. On next read / write it will be opened.
*/
CLOSED,
/**
* The connection is erroneous and was closed by the {@link OwserverConnection}. After due wait time, it
* is tried to reopen it by a scheduled task of
* {@link OwserverBridgeHandler#reportConnectionState(OwserverConnectionState)}.
*/
FAILED
}

View File

@@ -0,0 +1,57 @@
/**
* 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.onewire.internal.owserver;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link OwserverControlFlag} provides the owserver protocol control flag
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public enum OwserverControlFlag {
UNCACHED(0x00000020),
SAFEMODE(0x00000010),
ALIAS(0x00000008),
PERSISTENCE(0x00000004),
BUS_RET(0x00000002),
DEVICE_DISPLAY(0x00000000),
OWNET(0x00000100);
private final int controlFlag;
OwserverControlFlag(int controlFlag) {
this.controlFlag = controlFlag;
}
/**
* get the this flag's numeric representation
*
* @return integer value of this flag
*/
public int getValue() {
return controlFlag;
}
/**
* check if a this flag is set in the parameter
*
* @param controlFlags full control flag
* @return true if this flag is set in the parameter
*/
public boolean isSet(int controlFlags) {
return (this.getValue() & controlFlags) == this.getValue();
}
}

View File

@@ -0,0 +1,89 @@
/**
* 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.onewire.internal.owserver;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.onewire.internal.SensorId;
/**
* The {@link OwserverDeviceParameter} device parameter definition for owserver bridge handler
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class OwserverDeviceParameter {
private String prefix = "";
private String path = "";
/**
* device parameter for owserver bridge handler
*
* @param prefix path prefix (e.g. "uncached/")
* @param path path without sensor id (e.g. "/humidity")
*/
public OwserverDeviceParameter(String prefix, String path) {
if (prefix.endsWith("/")) {
this.prefix = prefix.substring(0, prefix.length() - 1);
} else {
this.prefix = prefix;
}
if (path.startsWith("/")) {
this.path = path;
} else {
this.path = "/" + path;
}
}
/**
* device parameter for owserver bridge handler
*
* @param path path without sensor id (e.g. "/humidity")
*/
public OwserverDeviceParameter(String path) {
this("", path);
}
/**
* get the full owfs path for a given sensor id
*
* @param sensorId
*/
public String getPath(SensorId sensorId) {
return prefix + sensorId.getFullPath() + path;
}
@Override
public String toString() {
return prefix + "/sensorId" + path;
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public boolean equals(@Nullable Object o) {
if (o == this) {
return true;
}
if (!(o instanceof OwserverDeviceParameter)) {
return false;
}
return ((OwserverDeviceParameter) o).toString().equals(toString());
}
}

View File

@@ -0,0 +1,66 @@
/**
* 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.onewire.internal.owserver;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link OwserverMessageType} provides the owserver protocol message type
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public enum OwserverMessageType {
ERROR(0x00000000),
NOP(0x00000001),
READ(0x00000002),
WRITE(0x00000003),
DIR(0x00000004),
SIZE(0x00000005),
PRESENT(0x00000006),
DIRALL(0x00000007),
GET(0x00000008),
DIRALLSLASH(0x00000009),
GETSLASH(0x0000000a);
private final int messageType;
OwserverMessageType(int messageType) {
this.messageType = messageType;
}
/**
* get the this message type's numeric representation
*
* @return integer value of this message type
*/
public int getValue() {
return messageType;
}
/**
* return a new OwMessageType from an integer
*
* @param messageType the message type as integer
* @return OwMessageType
*/
public static OwserverMessageType fromInt(int messageType) throws IllegalArgumentException {
for (OwserverMessageType value : values()) {
if (value.getValue() == messageType) {
return value;
}
}
throw new IllegalArgumentException();
}
}

View File

@@ -0,0 +1,347 @@
/**
* 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.onewire.internal.owserver;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.OwPageBuffer;
/**
* The {@link OwserverPacket} class provides a single packet for communication with the owserver
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class OwserverPacket {
public static final int PROTOCOL_VERSION = 0;
// 6x4 bytes
public static final int HEADER_SIZE = 24;
protected int payloadLength = 0;
protected final OwserverPacketType packetType;
protected int protocolVersion = PROTOCOL_VERSION;
protected int controlFlags;
protected int packetCode = 0;
protected int packetSize = 0;
protected int payloadOffset = 0;
protected byte payload[] = new byte[0];
/**
* constructor for new packet
*
* @param packetType packetType;
*/
public OwserverPacket(OwserverPacketType packetType) {
this.packetType = packetType;
setControlFlags(OwserverControlFlag.OWNET, OwserverControlFlag.DEVICE_DISPLAY);
}
/**
* constructor for reading packet from stream
*
* @param owInputStream input stream to read from
* @throws IOException
* @throws OwExeption
*/
public OwserverPacket(DataInputStream owInputStream, OwserverPacketType packetType)
throws IOException, OwException, EOFException {
this.packetType = packetType;
// header
protocolVersion = owInputStream.readInt();
payloadLength = owInputStream.readInt();
packetCode = owInputStream.readInt();
controlFlags = owInputStream.readInt();
packetSize = owInputStream.readInt();
payloadOffset = owInputStream.readInt();
// payload
if (payloadLength != -1) {
if ((protocolVersion != PROTOCOL_VERSION) || !OwserverControlFlag.OWNET.isSet(controlFlags)) {
throw new OwException("invalid data read");
}
if (payloadLength > 0) {
payload = new byte[payloadLength];
owInputStream.readFully(payload, 0, payloadLength);
}
}
}
/**
* constructor for a new request message
*
* @param owMessageType
* @param path
* @param owControlFlags
*/
public OwserverPacket(OwserverMessageType owMessageType, String path, OwserverControlFlag... owControlFlags) {
this(OwserverPacketType.REQUEST);
packetCode = owMessageType.getValue();
setPayload(path);
setTemperatureScale(OwserverTemperatureScale.CENTIGRADE);
setControlFlags(owControlFlags);
if (owMessageType == OwserverMessageType.WRITE) {
packetSize = 0x00000000;
} else {
packetSize = 0x00010000;
}
}
/**
* set one or more control flags for this packet
*
* @param flags one or more flags as OwControlFlag
*/
public void setControlFlags(OwserverControlFlag... flags) {
for (int i = 0; i < flags.length; i++) {
controlFlags |= flags[i].getValue();
}
}
/**
* check if a certain flag is set in this packet
*
* @param flag flag to be tested
* @return true if flag is set
*/
public boolean hasControlFlag(OwserverControlFlag flag) {
return flag.isSet(controlFlags);
}
/**
* set this packet's pressure scale
*
* @param pressureScale
*/
public void setPressureScale(OwserverPressureScale pressureScale) {
controlFlags = pressureScale.setFlag(controlFlags);
}
/**
* get this packets pressure scale
*
* @return
*/
public OwserverPressureScale getPressureScale() {
return OwserverPressureScale.getFlag(controlFlags);
}
/**
* set this packet's temperature scale
*
* @param pressureScale
*/
public void setTemperatureScale(OwserverTemperatureScale temperatureScale) {
controlFlags = temperatureScale.setFlag(controlFlags);
}
/**
* get this packets temperature scale
*
* @return
*/
public OwserverTemperatureScale getTemperatureScale() {
return OwserverTemperatureScale.getFlag(controlFlags);
}
/**
* set (or replace) this packet's payload from a string
*
* @param payload string representation of the payload
*/
public void setPayload(String payload) {
byte[] bytes = payload.getBytes();
payloadLength = bytes.length + 1;
this.payload = new byte[payloadLength];
System.arraycopy(bytes, 0, this.payload, 0, bytes.length);
}
/**
* append to this packet's payload from a string
*
* @param payload string representation of the payload to append
*/
public void appendPayload(String payload) {
byte appendBytes[] = payload.getBytes();
byte[] fullPayload = new byte[this.payload.length + appendBytes.length];
System.arraycopy(this.payload, 0, fullPayload, 0, this.payload.length);
System.arraycopy(appendBytes, 0, fullPayload, this.payload.length, appendBytes.length);
this.packetSize += appendBytes.length;
this.payloadLength = fullPayload.length;
this.payload = fullPayload;
}
/**
* set this packet payload from a OwPageBuffer
*
* @param payload string representation of the payload
*/
public void setPayload(OwPageBuffer payload) {
byte[] bytes = payload.getBytes();
payloadLength = bytes.length + 1;
this.payload = new byte[payloadLength];
System.arraycopy(bytes, 0, this.payload, 0, bytes.length);
}
/**
* get the payload of this packet
*
* @return string representation of this packet's payload
*/
public String getPayloadString() {
if (payloadLength > 0) {
// already null terminated strings skip the termination character
if (payload[payloadLength - 1] == 0) {
return new String(payload, 0, payloadLength - 1);
} else {
return new String(payload, 0, payloadLength);
}
} else {
return "";
}
}
/**
* set this packet's return code (0 is ok)
*
* @param returnCode an integer
*/
public void setReturnCode(int returnCode) {
if (packetType == OwserverPacketType.RETURN) {
this.packetCode = returnCode;
} else {
throw new IllegalStateException("setting return code not allowed in REQUEST packets");
}
}
/**
* get this packet's return code (0 is ok)
*
* @return
*/
public int getReturnCode() {
if (packetType == OwserverPacketType.RETURN) {
return packetCode;
} else {
throw new IllegalStateException("getting return code not allowed in REQUEST packets");
}
}
/**
* set this packet's message type
*
* @param messageType
*/
public void setMessageType(OwserverMessageType messageType) {
if (packetType == OwserverPacketType.REQUEST) {
packetCode = messageType.getValue();
} else {
throw new IllegalStateException("setting message type not allowed in RETURN packets");
}
}
/**
* get this packets message type
*
* @return
*/
public OwserverMessageType getMessageType() {
if (packetType == OwserverPacketType.REQUEST) {
return OwserverMessageType.fromInt(packetCode);
} else {
throw new IllegalStateException("getting message type not allowed in RETURN packets");
}
}
/**
* check if packed is valid return packet
*
* @return true if valid
*/
public boolean isValidReturnPacket() {
return (packetCode == 0 && packetType == OwserverPacketType.RETURN);
}
/**
* check if packed is valid return packet
*
* @return true if valid
*/
public boolean isPingPacket() {
return (payloadLength == -1 && packetType == OwserverPacketType.RETURN);
}
/**
* get the payload of this packet
*
* @return OwPageBuffer with this packet's payload
*/
public OwPageBuffer getPayload() {
OwPageBuffer byteBuffer = new OwPageBuffer(payload);
return byteBuffer;
}
/**
* check if this packet has a payload
*
* @return true if payload present
*/
public boolean hasPayload() {
return (payloadLength > 0);
}
/**
* convert this packet to an array of bytes
*
* @return array of bytes
*/
public byte[] toBytes() {
ByteBuffer byteBuffer = ByteBuffer.allocate(HEADER_SIZE + payloadLength);
byteBuffer.putInt(protocolVersion);
byteBuffer.putInt(payloadLength);
byteBuffer.putInt(packetCode);
byteBuffer.putInt(controlFlags);
byteBuffer.putInt(packetSize);
byteBuffer.putInt(payloadOffset);
if (payloadLength > 0) {
byteBuffer.put(payload);
}
return byteBuffer.array();
}
@Override
public String toString() {
String prefix;
if (packetType == OwserverPacketType.RETURN) {
prefix = String.format("return code %d", packetCode);
} else {
prefix = String.format("messageType %s", OwserverMessageType.fromInt(packetCode));
}
return String.format("%s, size %d, controlFlags 0x%08x, payload '%s'", prefix, HEADER_SIZE + payloadLength,
controlFlags, getPayloadString().replaceAll("\\p{C}", "?"));
}
}

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.onewire.internal.owserver;
/**
* The {@link OwserverPacketType} defines owserver packet types
*
* @author Jan N. Klug - Initial contribution
*/
public enum OwserverPacketType {
REQUEST,
RETURN
}

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.onewire.internal.owserver;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link OwserverPressureScale} provides the owserver protocol pressure scale flags
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public enum OwserverPressureScale {
MILLIBAR(0x00000000),
ATM(0x00040000),
MMHG(0x00080000),
INHG(0x000C0000),
PSI(0x00100000),
PASCAL(0x00140000);
private static final int CLEAR_MASK = 0x001C0000;
private final int flag;
OwserverPressureScale(int flag) {
this.flag = flag;
}
/**
* get numeric value of this flag
*
* @return
*/
public int getValue() {
return flag;
}
/**
* set this flag in a set of given flags
*
* @param flags aggregated flags
* @return parameter with this flag set
*/
public int setFlag(int flags) {
return (flags & ~CLEAR_MASK) | this.getValue();
}
/**
* get the pressure scale flag from a given set of flags
*
* @param flags set of flags
* @return pressure scale flag
* @throws IllegalArgumentException
*/
public static OwserverPressureScale getFlag(int flags) throws IllegalArgumentException {
for (OwserverPressureScale value : values()) {
if (value.getValue() == (flags & CLEAR_MASK)) {
return value;
}
}
throw new IllegalArgumentException("Pressure scale flag not found");
}
}

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.onewire.internal.owserver;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link OwserverTemperatureScale} provides the owserver protocol temperature scale flags
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public enum OwserverTemperatureScale {
CENTIGRADE(0x00000000),
FAHRENHEIT(0x00010000),
KELVIN(0x00020000),
RANKINE(0x00030000);
private static final int CLEAR_MASK = 0x00030000;
private final int flag;
OwserverTemperatureScale(int flag) {
this.flag = flag;
}
/**
* get numeric value of this flag
*
* @return
*/
public int getValue() {
return flag;
}
/**
* set this flag in a set of given flags
*
* @param flags aggregated flags
* @return parameter with this flag set
*/
public int setFlag(int flags) {
int tempFlags = flags;
tempFlags &= ~CLEAR_MASK;
tempFlags |= this.getValue();
return tempFlags;
}
/**
* get the temperature scale flag from a given set of flags
*
* @param flags set of flags
* @return temperature scale flag
* @throws IllegalArgumentException
*/
public static OwserverTemperatureScale getFlag(int flags) {
int tempFlags = flags;
tempFlags &= CLEAR_MASK;
for (OwserverTemperatureScale value : values()) {
if (value.getValue() == tempFlags) {
return value;
}
}
return CENTIGRADE;
}
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="onewire" 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>OneWire Binding</name>
<description>This is the binding for OneWire.</description>
<author>Jan N. Klug</author>
</binding:binding>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="thing-type:onewire:basethingconfig">
<parameter name="id" type="text">
<label>Sensor ID</label>
<description>Sensor ID in format: xx.xxxxxxxxxxxx or a full path including hubs/branches</description>
<required>true</required>
</parameter>
<parameter name="refresh" type="integer" min="1">
<label>Refresh Time</label>
<description>Time in seconds after which the thing is refreshed</description>
<default>300</default>
<unitLabel>s</unitLabel>
<required>false</required>
</parameter>
</config-description>
<config-description uri="thing-type:onewire:mstxconfig">
<parameter name="id" type="text">
<label>Sensor ID</label>
<description>Sensor ID in format: xx.xxxxxxxxxxxx or a full path including hubs/branches</description>
<required>true</required>
</parameter>
<parameter name="refresh" type="integer" min="1">
<label>Refresh Time</label>
<description>Time in seconds after which the thing is refreshed</description>
<default>300</default>
<unitLabel>s</unitLabel>
<required>false</required>
</parameter>
<parameter name="manualsensor" type="text">
<label>Manual Sensor Type</label>
<description>Overrides detected sensor type</description>
<options>
<option value="DS2438">Generic</option>
<option value="MS_TH">MS-TH</option>
<option value="MS_TV">MS-TV</option>
<option value="MS_TL">MS-TL</option>
<option value="MS_TC">MS-TC</option>
</options>
<limitToOptions>true</limitToOptions>
<required>false</required>
<advanced>true</advanced>
</parameter>
</config-description>
</config-description:config-descriptions>

View File

@@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="onewire"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="bae091x">
<supported-bridge-type-refs>
<bridge-type-ref id="owserver"/>
</supported-bridge-type-refs>
<label>Multisensor BAE0910</label>
<description>1-wire multisensor (BAE0910-based)</description>
<properties>
<property name="sensorCount">1</property>
</properties>
<config-description>
<parameter name="id" type="text">
<label>Sensor ID</label>
<description>Sensor ID in format: xx.xxxxxxxxxxxx)</description>
<required>true</required>
</parameter>
<parameter name="refresh" type="integer" min="1">
<label>Refresh Time</label>
<description>Time in seconds after which the thing is refreshed</description>
<default>300</default>
<unitLabel>s</unitLabel>
</parameter>
<parameter name="pin1" type="text">
<label>Pin 1 Mode Configuration</label>
<options>
<option value="disabled">disabled</option>
<option value="counter">Counter</option>
</options>
<default>counter</default>
<limitToOptions>true</limitToOptions>
</parameter>
<parameter name="pin2" type="text">
<label>Pin 2 Mode Configuration</label>
<options>
<option value="disabled">disabled</option>
<option value="output">Digital Output</option>
<option value="pwm">Software PWM 4</option>
</options>
<default>output</default>
<limitToOptions>true</limitToOptions>
</parameter>
<parameter name="pin6" type="text">
<label>Pin 6 Mode Configuration</label>
<options>
<option value="disabled">disabled</option>
<option value="pio">PIO</option>
<option value="pwm">Software PWM 3</option>
</options>
<default>pio</default>
<limitToOptions>true</limitToOptions>
</parameter>
<parameter name="pin7" type="text">
<label>Pin 7 Mode Configuration</label>
<options>
<option value="disabled">disabled</option>
<option value="analog">Analog Input</option>
<option value="output">Digital Output</option>
<option value="pwm">Hardware PWM 2</option>
</options>
<default>analog</default>
<limitToOptions>true</limitToOptions>
</parameter>
<parameter name="pin8" type="text">
<label>Pin 8 Mode Configuration</label>
<options>
<option value="disabled">disabled</option>
<option value="input">Digital Input</option>
<option value="output">Digital Output</option>
<option value="pwm">Hardware PWM 1</option>
</options>
<default>output</default>
<limitToOptions>true</limitToOptions>
</parameter>
</config-description>
</thing-type>
<!-- PWM Channels -->
<channel-type id="bae-pwm-frequency">
<item-type>Number:Frequency</item-type>
<label>Frequency</label>
<description>Frequency of PWM output in Hz</description>
<config-description>
<parameter name="prescaler" type="integer">
<label>Range</label>
<description>defines the frequency range of PWM output</description>
<options>
<option value="0">245 Hz - 8 MHz</option>
<option value="1">123 Hz - 4 MHz</option>
<option value="2">62 Hz - 2 MHz</option>
<option value="3">31 Hz - 1 MHz</option>
<option value="4">16 Hz - 500 kHz</option>
<option value="5">8 Hz - 250 kHz</option>
<option value="6">4 Hz - 125 kHz</option>
<option value="7">2 Hz - 62.5 kHz</option>
</options>
<default>0</default>
<limitToOptions>true</limitToOptions>
</parameter>
<parameter name="reversePolarity" type="boolean">
<label>Reverse Polarity</label>
<default>false</default>
</parameter>
</config-description>
</channel-type>
<channel-type id="bae-pwm-duty">
<item-type>Number:Dimensionless</item-type>
<label>Duty Cycle</label>
<description>Duty cycle of PWM output in %</description>
</channel-type>
<!-- Digital Channels -->
<channel-type id="bae-pio">
<item-type>Switch</item-type>
<label>PIO</label>
<description>Programmable I/O channel</description>
<config-description>
<parameter name="mode" type="text">
<label>Mode</label>
<options>
<option value="input">Input</option>
<option value="output">Output</option>
</options>
<default>input</default>
<limitToOptions>true</limitToOptions>
</parameter>
<parameter name="pulldevice" type="text">
<label>Pull-Up/Pull-Down Resistor</label>
<options>
<option value="disabled">disabled</option>
<option value="pullup">Pull-Up</option>
<option value="pulldown">Pull-Down</option>
</options>
<default>disabled</default>
<limitToOptions>true</limitToOptions>
</parameter>
</config-description>
</channel-type>
<channel-type id="bae-do">
<item-type>Switch</item-type>
<label>Digital Out</label>
</channel-type>
<channel-type id="bae-di">
<item-type>Switch</item-type>
<label>Digital In</label>
</channel-type>
<!-- Analog In (ADC) -->
<channel-type id="bae-analog">
<item-type>Number:ElectricPotential</item-type>
<label>Analog Input</label>
<description>Analog input (ADC)</description>
<state readOnly="true" pattern="%.1f %unit%"/>
<config-description>
<parameter name="hires" type="boolean">
<label>Hires</label>
<description>High resolution Mode (10bit)</description>
<default>false</default>
</parameter>
</config-description>
</channel-type>
<!-- Counter Channel -->
<channel-type id="bae-counter">
<item-type>Number</item-type>
<label>Counter</label>
<state readOnly="true" pattern="%d"/>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="onewire"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="basic">
<supported-bridge-type-refs>
<bridge-type-ref id="owserver"/>
</supported-bridge-type-refs>
<label>Basic 1 Wire Sensor</label>
<config-description-ref uri="thing-type:onewire:basethingconfig"/>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="onewire"
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">
<bridge-type id="owserver" extensible="owfs-string,owfs-number">
<label>OW Server</label>
<description>An owserver instance</description>
<config-description>
<parameter name="network-address" type="text">
<context>network_address</context>
<label>Network Address</label>
<description>Network address of the host running the owserver</description>
<required>true</required>
</parameter>
<parameter name="port" type="integer">
<label>Port</label>
<description>Listening port of the owserver</description>
<default>4304</default>
<required>false</required>
</parameter>
</config-description>
</bridge-type>
<channel-type id="owfs-string">
<item-type>String</item-type>
<label>Direct Access to OWFS-Path (String)</label>
<description>Allows direct access to the OWFS</description>
<state readOnly="true"/>
<config-description>
<parameter name="path" type="text">
<label>OWFS Path</label>
<description>full path to the OWFS-node (e.g. statistics/errors/CRC8_errors)</description>
<required>true</required>
</parameter>
<parameter name="refresh" type="integer" min="1">
<label>Refresh Time</label>
<description>Time in seconds after which the channel is refreshed</description>
<default>300</default>
<unitLabel>s</unitLabel>
<required>false</required>
</parameter>
</config-description>
</channel-type>
<channel-type id="owfs-number">
<item-type>Number</item-type>
<label>Direct Access to OWFS-Path (Number)</label>
<description>Allows direct access to the OWFS</description>
<state readOnly="true"/>
<config-description>
<parameter name="path" type="text">
<label>OWFS Path</label>
<description>full path to the OWFS-node (e.g. statistics/errors/CRC8_errors)</description>
<required>true</required>
</parameter>
<parameter name="refresh" type="integer" min="1">
<label>Refresh Time</label>
<description>Time in seconds after which the channel is refreshed</description>
<default>300</default>
<unitLabel>s</unitLabel>
<required>false</required>
</parameter>
</config-description>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="onewire"
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">
<!-- Device Present Channel -->
<channel-type id="present">
<item-type>Switch</item-type>
<label>Present</label>
<description>ON if device present on OneWire bus</description>
<state readOnly="true"/>
</channel-type>
<!-- Temperature Channel -->
<channel-type id="temperature">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<description>temperature value of this sensor</description>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<channel-type id="temperature-por">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<description>temperature value of this sensor</description>
<state readOnly="true" pattern="%.1f %unit%"/>
<config-description>
<parameter name="ignorepor" type="boolean">
<label>Ignore POR-value</label>
<description>filters all 85°C readings (POR-value), may suppress valid readings if enabled</description>
<default>false</default>
<required>false</required>
</parameter>
</config-description>
</channel-type>
<channel-type id="temperature-por-res">
<item-type>Number:Temperature</item-type>
<label>Temperature</label>
<description>temperature value of this sensor</description>
<state readOnly="true" pattern="%.1f %unit%"/>
<config-description>
<parameter name="ignorepor" type="boolean">
<label>Ignore POR-value</label>
<description>filters all 85°C readings (POR-value), may suppress valid readings if enabled</description>
<default>false</default>
<required>false</required>
</parameter>
<parameter name="resolution" type="text">
<label>Sensor Resolution</label>
<options>
<option value="9">9 bit</option>
<option value="10">10 bit</option>
<option value="11">11 bit</option>
<option value="12">12 bit</option>
</options>
<default>10</default>
<limitToOptions>true</limitToOptions>
<required>false</required>
</parameter>
</config-description>
</channel-type>
<!-- Dewpoint Channel -->
<channel-type id="dewpoint">
<item-type>Number:Temperature</item-type>
<label>Dewpoint</label>
<description>dewpoint (calculated from temperature and relative humidity)</description>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<!-- Relative Humidity Channel -->
<channel-type id="humidity">
<item-type>Number:Dimensionless</item-type>
<label>Humidity</label>
<description>relative humidity (0-100%)</description>
<state readOnly="true" pattern="%d %%"/>
</channel-type>
<channel-type id="humidityconf">
<item-type>Number:Dimensionless</item-type>
<label>Humidity</label>
<description>relative humidity (0-100%)</description>
<state readOnly="true" pattern="%d %%"/>
<config-description>
<parameter name="humiditytype" type="text">
<label>Humidity Sensor-Type</label>
<limitToOptions>true</limitToOptions>
<options>
<option value="/humidity">HIH-3610</option>
<option value="/HIH4000/humidity">HIH-4000</option>
<option value="/HTM1735/humidity">HTM-1735</option>
<option value="/DATANAB/humidity">Datanab</option>
</options>
<default>/humidity</default>
<required>false</required>
</parameter>
</config-description>
</channel-type>
<!-- Absolute Humidity Channel -->
<channel-type id="absolutehumidity">
<item-type>Number:Density</item-type>
<label>Abs. Humidity</label>
<description>absolute humidity (calculated from temperature and relative humidity)</description>
<state readOnly="true" pattern="%.2f %unit%"/>
</channel-type>
<!-- Voltage Channel -->
<channel-type id="voltage">
<item-type>Number:ElectricPotential</item-type>
<label>Voltage</label>
<description>The voltage measured by the sensor</description>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<!-- Supply-Voltage Channel -->
<channel-type id="supplyvoltage">
<item-type>Number:ElectricPotential</item-type>
<label>Supply Voltage</label>
<description>The sensor's supply voltage</description>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<!-- Light Channel -->
<channel-type id="light">
<item-type>Number:Illuminance</item-type>
<label>Illuminance</label>
<description>Ambient light</description>
<state readOnly="true" pattern="%.0f %unit%"/>
</channel-type>
<!-- Current Channel -->
<channel-type id="current">
<item-type>Number:ElectricCurrent</item-type>
<label>Current</label>
<description>The current measured by the sensor</description>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<!-- Digital I/O Channel -->
<channel-type id="dio">
<item-type>Switch</item-type>
<label>Digital I/O</label>
<config-description>
<parameter name="mode" type="text">
<label>Mode</label>
<options>
<option value="input">Input</option>
<option value="output">Output</option>
</options>
<default>input</default>
<limitToOptions>true</limitToOptions>
</parameter>
<parameter name="logic" type="text">
<label>Channel Logic</label>
<options>
<option value="normal">normal</option>
<option value="inverted">inverted</option>
</options>
<default>normal</default>
<limitToOptions>true</limitToOptions>
</parameter>
</config-description>
</channel-type>
<!-- Pressure Channel -->
<channel-type id="pressure">
<item-type>Number:Pressure</item-type>
<label>Pressure</label>
<description>The pressure measured by the sensor</description>
<state readOnly="true" pattern="%.1f %unit%"/>
</channel-type>
<!-- Counter Channel -->
<channel-type id="counter">
<item-type>Number</item-type>
<label>Counter</label>
<description>A single counter (reset on power loss)</description>
<state readOnly="true" pattern="%d"/>
</channel-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<thing:thing-descriptions bindingId="onewire"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
<thing-type id="ms-tx">
<supported-bridge-type-refs>
<bridge-type-ref id="owserver"/>
</supported-bridge-type-refs>
<label>Multisensor (T, TC, TH, TL, TV, Generic)</label>
<description>A 1-wire multisensor (DS1923/DS2438-based)</description>
<config-description-ref uri="thing-type:onewire:mstxconfig"/>
</thing-type>
<thing-type id="bms">
<supported-bridge-type-refs>
<bridge-type-ref id="owserver"/>
</supported-bridge-type-refs>
<label>Multisensor BMS</label>
<description>1-wire multisensor (DS2438-based)</description>
<config-description>
<parameter name="id" type="text">
<label>TH(S) Sensor ID</label>
<description>Sensor ID of the DS2438 sensor in format: xx.xxxxxxxxxxxx or a full path including hubs/branches</description>
<required>true</required>
</parameter>
<parameter name="refresh" type="integer" min="1">
<label>Refresh Time</label>
<description>Time in seconds after which the thing is refreshed</description>
<default>300</default>
<unitLabel>s</unitLabel>
<required>false</required>
</parameter>
<parameter name="temperaturesensor" type="text">
<label>Temperature Sensor</label>
<options>
<option value="DS2438">internal (DS2438)</option>
<option value="DS18B20">external (DS18B20)</option>
</options>
<default>DS2438</default>
<limitToOptions>true</limitToOptions>
<required>false</required>
</parameter>
</config-description>
</thing-type>
<thing-type id="ams">
<supported-bridge-type-refs>
<bridge-type-ref id="owserver"/>
</supported-bridge-type-refs>
<label>Multisensor AMS</label>
<description>1-wire multisensor (DS2438-based)</description>
<config-description>
<parameter name="id" type="text">
<label>TH(S) Sensor ID</label>
<description>Sensor ID of the DS2438 sensor in format: xx.xxxxxxxxxxxx or a full path including hubs/branches</description>
<required>true</required>
</parameter>
<parameter name="refresh" type="integer" min="1">
<label>Refresh Time for Analog Channels</label>
<description>Time in seconds after which the thing is refreshed</description>
<default>300</default>
<unitLabel>s</unitLabel>
<required>false</required>
</parameter>
<parameter name="refreshdigital" type="integer" min="1">
<label>Refresh Time for Digital Channels</label>
<description>Time in seconds after which the digital I/Os are refreshed</description>
<default>10</default>
<unitLabel>s</unitLabel>
<required>false</required>
</parameter>
<parameter name="temperaturesensor" type="text">
<label>Temperature Sensor</label>
<options>
<option value="DS2438">internal (DS2438)</option>
<option value="DS18B20">external (DS18B20)</option>
</options>
<default>DS2438</default>
<limitToOptions>true</limitToOptions>
<required>false</required>
</parameter>
</config-description>
</thing-type>
<thing-type id="edsenv">
<supported-bridge-type-refs>
<bridge-type-ref id="owserver"/>
</supported-bridge-type-refs>
<label>Multisensor EDS</label>
<description>A 1-wire multisensor (EDS00xx-based)</description>
<properties>
<property name="sensorCount">1</property>
</properties>
<config-description>
<parameter name="id" type="text">
<label>Sensor ID</label>
<description>Sensor ID in format: xx.xxxxxxxxxxxx)</description>
<required>true</required>
</parameter>
<parameter name="refresh" type="integer" min="1">
<label>Refresh Time</label>
<description>Time in seconds after which the thing is refreshed</description>
<default>300</default>
<unitLabel>s</unitLabel>
<required>false</required>
</parameter>
</config-description>
</thing-type>
</thing:thing-descriptions>

View File

@@ -0,0 +1,29 @@
humidity = Number:Dimensionless
absolutehumidity = Number:Density
dewpoint = Number:Temperature
temperature = Number:Temperature
light = Number:Illuminance
pressure = Number:Pressure
voltage = Number:ElectricPotential
supplyvoltage = Number:ElectricPotential
current = Number:ElectricCurrent
counter = Number
counter0 = Number
counter1 = Number
digital = Switch
digital0 = Switch
digital1 = Switch
digital2 = Switch
digital3 = Switch
digital4 = Switch
digital5 = Switch
digital6 = Switch
digital7 = Switch
digital8 = Switch
present = Switch
pwmduty1 = Number:Dimensionsless
pwmduty2 = Number:Dimensionsless
pwmduty3 = Number:Dimensionsless
pwmduty4 = Number:Dimensionsless
pwmfreq1 = Number:Frequency
pwmfreq2 = Number:Frequency

View File

@@ -0,0 +1,89 @@
#
# sensor.properties - This file defines the sensor properties
#
# Format: sensorType.channels = name:type:[label][,name:type:[label]]...
# sensorType.label = label
# sensorType.thingtype = thingtype
#
DS1420.channels = present:present:
DS1420.label = 1kb EEPROM
DS1420.thingtype = basic
DS1822.channels = temperature:temperature-por-res:
DS1822.label = Temperature Sensor
DS1822.thingtype = basic
DS18B20.channels = temperature:temperature-por-res:
DS18B20.label = Temperature Sensor
DS18B20.thingtype = basic
DS18S20.channels = temperature:temperature-por:
DS18S20.label = Temperature Sensor
DS18S20.thingtype = basic
DS1923.channels = temperature:temperature:,humidity:humidity:,absolutehumidity:abshumidity:,dewpoint:temperature:Dewpoint
DS1923.label = Multisensor
DS1923.thingtype = ms-tx
DS2401.channels = present:present:
DS2401.label = iButton
DS2401.thingtype = basic
DS2405.channels = digital0:dio:Digital I/O 0
DS2405.label = Single Digital I/O
DS2405.thingtype = basic
DS2406.channels = digital0:dio:Digital I/O 0,digital1:dio:Digital I/O 1
DS2406.label = Dual Digital I/O
DS2406.thingtype = basic
DS2408.channels = digital0:dio:Digital I/O 0,digital1:dio:Digital I/O 1,digital2:dio:Digital I/O 2,digital3:dio:Digital I/O 3,digital4:dio:Digital I/O 4,digital5:dio:Digital I/O 5,digital6:dio:Digital I/O 6,digital7:dio:Digital I/O 7
DS2408.label = Octal Digital I/O
DS2408.thingtype = basic
DS2413.channels = digital0:dio:Digital I/O 0,digital1:dio:Digital I/O 1
DS2413.label = Dual Digital I/O
DS2413.thingtype = basic
DS2423.channels = counter0:counter:Counter 0,counter1:counter:Counter 1
DS2423.label = Dual Counter
DS2423.thingtype = basic
# AMS/BMS: humidity and temperature added by handler
AMS.channels = supplyvoltage:voltage:Supplyvoltage,absolutehumidity:abshumidity:,dewpoint:temperature:Dewpoint,voltage:voltage:,digital0:dio:Digital I/O 0,digital1:dio:Digital I/O 1
AMS.label = Multisensor AMS
AMS.thingtype = ams
AMS_S.channels = supplyvoltage:voltage:Supplyvoltage,absolutehumidity:abshumidity:,dewpoint:temperature:Dewpoint,light:light:,voltage:voltage:,digital0:dio:Digital I/O 0,digital1:dio:Digital I/O 1
AMS_S.label = Multisensor AMS
AMS_S.thingtype = ams
BMS.channels = supplyvoltage:voltage:Supplyvoltage,absolutehumidity:abshumidity:,dewpoint:temperature:Dewpoint
BMS.label = Multisensor BMS
BMS.thingtype = bms
BMS_S.channels = supplyvoltage:voltage:Supplyvoltage,absolutehumidity:abshumidity:,dewpoint:temperature:Dewpoint,light:light:
BMS_S.label = Multisensor BMS
BMS_S.thingtype = bms
# DS2438
DS2438.channels = supplyvoltage:voltage:Supplyvoltage,temperature:temperature:,voltage:voltage:,current:current:
DS2438.label = Multisensor (generic)
DS2438.thingtype = ms-tx
MS_TC.channels = supplyvoltage:voltage:Supplyvoltage,temperature:temperature:,voltage:voltage:
MS_TC.label = Multisensor TC
MS_TC.thingtype = ms-tx
MS_TH.channels = supplyvoltage:voltage:Supplyvoltage,temperature:temperature:,humidity:humidityconf:,absolutehumidity:abshumidity:,dewpoint:temperature:Dewpoint
MS_TH.label = Multisensor TH
MS_TH.thingtype = ms-tx
MS_TL.channels = supplyvoltage:voltage:Supplyvoltage,temperature:temperature:,light:light:
MS_TL.label = Multisensor TL
MS_TL.thingtype = ms-tx
MS_TV.channels = supplyvoltage:voltage:Supplyvoltage,temperature:temperature:,voltage:voltage:
MS_TV.label = Multisensor TV
MS_TV.thingtype = ms-tx
# EDS
EDS0064.channels = temperature:temperature:
EDS0064.label = Multisensor EDS
EDS0064.thingtype = edsenv
EDS0065.channels = temperature:temperature:,humidity:humidity:,absolutehumidity:abshumidity:,dewpoint:temperature:Dewpoint
EDS0065.label = Multisensor EDS
EDS0065.thingtype = edsenv
EDS0066.channels = temperature:temperature:,pressure:pressure:
EDS0066.label = Multisensor EDS
EDS0066.thingtype = edsenv
EDS0067.channels = temperature:temperature:,light:light:
EDS0067.label = Multisensor EDS
EDS0067.thingtype = edsenv
EDS0068.channels = temperature:temperature:,humidity:humidity:,absolutehumidity:abshumidity:,dewpoint:temperature:Dewpoint,pressure:pressure:,light:light:
EDS0068.label = Multisensor EDS
EDS0068.thingtype = edsenv
# BAE091x
BAE0910.channels =
BAE0910.label = Multisensor BAE0910
BAE0910.thingtype = bae091x

View File

@@ -0,0 +1,104 @@
/**
* 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.onewire;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Assert;
import org.junit.Test;
import org.openhab.binding.onewire.internal.OwBindingConstants;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.device.OwSensorType;
import org.openhab.binding.onewire.internal.handler.*;
/**
* Tests cases for binding completeness
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class CompletenessTest {
// internal/temporary types, DS2409 (MicroLAN Coupler), DS2431 (EEPROM)
private static final Set<OwSensorType> IGNORED_SENSOR_TYPES = Collections
.unmodifiableSet(Stream.of(OwSensorType.DS2409, OwSensorType.DS2431, OwSensorType.EDS, OwSensorType.MS_TH_S,
OwSensorType.BAE, OwSensorType.BAE0911, OwSensorType.UNKNOWN).collect(Collectors.toSet()));
private static final Set<OwSensorType> THINGHANDLER_SENSOR_TYPES = Collections.unmodifiableSet(Stream
.of(AdvancedMultisensorThingHandler.SUPPORTED_SENSOR_TYPES,
BasicMultisensorThingHandler.SUPPORTED_SENSOR_TYPES, BasicThingHandler.SUPPORTED_SENSOR_TYPES,
EDSSensorThingHandler.SUPPORTED_SENSOR_TYPES, BAE091xSensorThingHandler.SUPPORTED_SENSOR_TYPES)
.flatMap(Set::stream).collect(Collectors.toSet()));
@Test
public void allSupportedTypesInThingHandlerMap() {
for (OwSensorType sensorType : EnumSet.allOf(OwSensorType.class)) {
if (!OwBindingConstants.THING_TYPE_MAP.containsKey(sensorType)
&& !IGNORED_SENSOR_TYPES.contains(sensorType)) {
Assert.fail("missing thing type map for sensor type " + sensorType.name());
}
}
}
@Test
public void allSupportedTypesInThingChannelsMap() {
for (OwSensorType sensorType : EnumSet.allOf(OwSensorType.class)) {
if (!OwBindingConstants.SENSOR_TYPE_CHANNEL_MAP.containsKey(sensorType)
&& !IGNORED_SENSOR_TYPES.contains(sensorType)) {
Assert.fail("missing channel configuration map for sensor type " + sensorType.name());
}
}
}
@Test
public void allSensorsSupportedByThingHandlers() {
for (OwSensorType sensorType : EnumSet.allOf(OwSensorType.class)) {
if (!THINGHANDLER_SENSOR_TYPES.contains(sensorType) && !IGNORED_SENSOR_TYPES.contains(sensorType)) {
Assert.fail("missing thing handler for sensor type " + sensorType.name());
}
}
}
@Test
public void allSensorTypesInLabelMap() {
for (OwSensorType sensorType : EnumSet.allOf(OwSensorType.class)) {
if (!OwBindingConstants.THING_LABEL_MAP.containsKey(sensorType)
&& !IGNORED_SENSOR_TYPES.contains(sensorType)) {
Assert.fail("missing label for sensor type " + sensorType.name());
}
}
}
@Test
public void acceptedItemTypeMapCompleteness() throws OwException {
List<String> channels = Arrays.stream(OwBindingConstants.class.getDeclaredFields())
.filter(f -> Modifier.isStatic(f.getModifiers()))
.filter(f -> f.getName().startsWith("CHANNEL") && !f.getName().startsWith("CHANNEL_TYPE")).map(f -> {
try {
return (String) f.get(null);
} catch (IllegalAccessException e) {
Assert.fail("unexpected");
return null;
}
}).collect(Collectors.toList());
for (String channel : channels) {
if (!OwBindingConstants.ACCEPTED_ITEM_TYPES_MAP.containsKey(channel)) {
Assert.fail("missing accepted item type for channel " + channel);
}
}
}
}

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.onewire;
import static org.junit.Assert.assertEquals;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter;
/**
* Tests cases for {@link SensorId}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class OwserverDeviceParameterTest {
private final SensorId sensorId = new SensorId("/1F.0123456789ab/main/00.1234567890ab");
@Test
public void withoutPrefixTest() {
OwserverDeviceParameter owserverDeviceParameter = new OwserverDeviceParameter("/humidity");
assertEquals("/1F.0123456789ab/main/00.1234567890ab/humidity", owserverDeviceParameter.getPath(sensorId));
owserverDeviceParameter = new OwserverDeviceParameter("humidity");
assertEquals("/1F.0123456789ab/main/00.1234567890ab/humidity", owserverDeviceParameter.getPath(sensorId));
}
public void withPrefixTest() {
OwserverDeviceParameter owserverDeviceParameter = new OwserverDeviceParameter("uncached", "/humidity");
assertEquals("/uncached/1F.0123456789ab/main/00.1234567890ab/humidity",
owserverDeviceParameter.getPath(sensorId));
owserverDeviceParameter = new OwserverDeviceParameter("uncached", "/humidity");
assertEquals("/uncached/1F.0123456789ab/main/00.1234567890ab/humidity",
owserverDeviceParameter.getPath(sensorId));
owserverDeviceParameter = new OwserverDeviceParameter("/uncached", "/humidity");
assertEquals("/uncached/1F.0123456789ab/main/00.1234567890ab/humidity",
owserverDeviceParameter.getPath(sensorId));
owserverDeviceParameter = new OwserverDeviceParameter("/uncached/", "/humidity");
assertEquals("/uncached/1F.0123456789ab/main/00.1234567890ab/humidity",
owserverDeviceParameter.getPath(sensorId));
owserverDeviceParameter = new OwserverDeviceParameter("uncached/", "/humidity");
assertEquals("/uncached/1F.0123456789ab/main/00.1234567890ab/humidity",
owserverDeviceParameter.getPath(sensorId));
}
}

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.onewire;
import static org.junit.Assert.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.binding.onewire.internal.SensorId;
/**
* Tests cases for {@link SensorId}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class SensorIdTest {
@Test
public void bareSensorIdConstructionTest() {
SensorId sensorId = new SensorId("28.0123456789ab");
assertEquals("/28.0123456789ab", sensorId.getFullPath());
assertEquals("28.0123456789ab", sensorId.getId());
assertEquals("28", sensorId.getFamilyId());
sensorId = new SensorId("/28.0123456789ab");
assertEquals("/28.0123456789ab", sensorId.getFullPath());
assertEquals("28.0123456789ab", sensorId.getId());
assertEquals("28", sensorId.getFamilyId());
}
@Test
public void hubMainSensorIdConstructionTest() {
SensorId sensorId = new SensorId("1F.0123456789ab/main/28.0123456789ab");
assertEquals("/1F.0123456789ab/main/28.0123456789ab", sensorId.getFullPath());
assertEquals("28.0123456789ab", sensorId.getId());
assertEquals("28", sensorId.getFamilyId());
sensorId = new SensorId("/1F.0123456789ab/main/28.0123456789ab");
assertEquals("/1F.0123456789ab/main/28.0123456789ab", sensorId.getFullPath());
assertEquals("28.0123456789ab", sensorId.getId());
assertEquals("28", sensorId.getFamilyId());
}
@Test
public void hubAuxSensorIdConstructionTest() {
SensorId sensorId = new SensorId("1F.0123456789ab/aux/28.0123456789ab");
assertEquals("/1F.0123456789ab/aux/28.0123456789ab", sensorId.getFullPath());
assertEquals("28.0123456789ab", sensorId.getId());
assertEquals("28", sensorId.getFamilyId());
sensorId = new SensorId("/1F.0123456789ab/aux/28.0123456789ab");
assertEquals("/1F.0123456789ab/aux/28.0123456789ab", sensorId.getFullPath());
assertEquals("28.0123456789ab", sensorId.getId());
assertEquals("28", sensorId.getFamilyId());
}
@Test
public void equalsTest() {
SensorId sensorId1 = new SensorId("1F.0123456789ab/aux/28.0123456789ab");
SensorId sensorId2 = new SensorId("1F.0123456789ab/aux/28.0123456789ab");
SensorId sensorId3 = new SensorId("1F.0123456789ab/aux/28.0123456789ac");
assertTrue(sensorId1.equals(sensorId2));
assertFalse(sensorId1.equals(sensorId3));
}
}

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.onewire;
import static org.junit.Assert.assertEquals;
import javax.measure.quantity.Dimensionless;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Test;
import org.openhab.binding.onewire.internal.Util;
import org.openhab.core.library.dimension.Density;
import org.openhab.core.library.types.QuantityType;
/**
* Tests cases for {@link Util}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class UtilTest {
@Test
public void convertAbsoluteHumidityTest() {
QuantityType<Temperature> temperature = new QuantityType<>("20 °C");
QuantityType<Dimensionless> relativeHumidity = new QuantityType<>("75%");
@SuppressWarnings("unchecked")
QuantityType<Density> absoluteHumidity = (QuantityType<Density>) Util.calculateAbsoluteHumidity(temperature,
relativeHumidity);
assertEquals(12.93, absoluteHumidity.doubleValue(), 0.01);
}
@Test
public void dewPointTest() {
QuantityType<Temperature> temperature = new QuantityType<>("20 °C");
QuantityType<Dimensionless> relativeHumidity = new QuantityType<>("75%");
@SuppressWarnings("unchecked")
QuantityType<Temperature> dewPoint = (QuantityType<Temperature>) Util.calculateDewpoint(temperature,
relativeHumidity);
assertEquals(15.43, dewPoint.doubleValue(), 0.01);
}
}

View File

@@ -0,0 +1,342 @@
/**
* 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.onewire.device;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.util.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.device.BAE0910;
import org.openhab.binding.onewire.internal.owserver.OwserverDeviceParameter;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.State;
/**
* Tests cases for {@link BAE0910}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class BAE0910Test extends DeviceTestParent<BAE0910> {
@Before
public void setupMocks() {
setupMocks(THING_TYPE_BAE091X, BAE0910.class);
}
// pin 1: counter
@Test
public void counter() {
addChannel(CHANNEL_COUNTER, "Number");
final BAE0910 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(
mockBridgeHandler.readDecimalType(eq(testSensorId), eq(new OwserverDeviceParameter("/counter"))))
.thenReturn(new DecimalType(34567));
testDevice.enableChannel(CHANNEL_COUNTER);
testDevice.configureChannels(mockBridgeHandler);
// refresh
ArgumentCaptor<State> stateArgumentCaptor = ArgumentCaptor.forClass(State.class);
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId),
eq(new OwserverDeviceParameter("/counter")));
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_COUNTER), stateArgumentCaptor.capture());
assertEquals(new DecimalType(34567), stateArgumentCaptor.getValue());
// write
assertFalse(testDevice.writeChannel(mockBridgeHandler, CHANNEL_COUNTER, new DecimalType(12345)));
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
// pin 2: digital2 or pwm1
@Test
public void digitalOut2() {
addChannel(CHANNEL_DIGITAL2, "Switch");
digitalBaseChannel(CHANNEL_DIGITAL2, bitSet(3, 4), 0, "/out", bitSet(0), true);
}
@Test
public void pwm4() {
pwmBaseChannel(CHANNEL_PWM_FREQ2, CHANNEL_PWM_DUTY4, "/period2", "/duty4", 2);
}
// pin 6: pio or pwm 3
@Test
public void digital6PioIn() {
Map<String, Object> channelConfig = new HashMap<>();
channelConfig.put("pulldevice", "pulldown");
channelConfig.put("mode", "input");
addChannel(CHANNEL_DIGITAL6, "Switch", new Configuration(channelConfig));
digitalBaseChannel(CHANNEL_DIGITAL6, bitSet(1, 2, 3, 4), 1, "/pio", bitSet(0), false);
}
@Test
public void digital6PioOut() {
Map<String, Object> channelConfig = new HashMap<>();
channelConfig.put("mode", "output");
addChannel(CHANNEL_DIGITAL6, "Switch", new Configuration(channelConfig));
digitalBaseChannel(CHANNEL_DIGITAL6, bitSet(0, 3, 4), 1, "/pio", bitSet(0), true);
}
@Test
public void pwm3() {
pwmBaseChannel(CHANNEL_PWM_FREQ1, CHANNEL_PWM_DUTY3, "/period1", "/duty3", 1);
}
// pin 7: analog, output, pwm2
@Test
public void analog() {
Map<String, Object> channelConfig = new HashMap<>();
channelConfig.put("hires", "true");
addChannel(CHANNEL_VOLTAGE, "Number:ElectricPotential", new Configuration(channelConfig));
final BAE0910 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), eq(new OwserverDeviceParameter("/adc"))))
.thenReturn(new DecimalType(5.2));
testDevice.enableChannel(CHANNEL_VOLTAGE);
testDevice.configureChannels(mockBridgeHandler);
// test configuration
assertEquals(bitSet(3, 4), checkConfiguration(2));
// refresh
ArgumentCaptor<State> stateArgumentCaptor = ArgumentCaptor.forClass(State.class);
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId),
eq(new OwserverDeviceParameter("/adc")));
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_VOLTAGE), stateArgumentCaptor.capture());
assertEquals(new QuantityType<>("5.2 V"), stateArgumentCaptor.getValue());
// write (should fail)
assertFalse(testDevice.writeChannel(mockBridgeHandler, CHANNEL_VOLTAGE, new QuantityType<>("3 V")));
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void digitalOut7() {
addChannel(CHANNEL_DIGITAL7, "Switch");
digitalBaseChannel(CHANNEL_DIGITAL7, bitSet(4), 4, "/tpm2c", bitSet(4, 7), true);
}
@Test
public void pwm2() {
pwmBaseChannel(CHANNEL_PWM_FREQ2, CHANNEL_PWM_DUTY2, "/period2", "/duty2", 2);
}
// pin 8: digital in, digital out or pwm
@Test
public void digitalIn8() {
addChannel(CHANNEL_DIGITAL8, "Switch", new ChannelTypeUID(BINDING_ID, "bae-in"));
digitalBaseChannel(CHANNEL_DIGITAL8, bitSet(4, 5), 3, "/tpm1c", bitSet(4, 5, 7), false);
}
@Test
public void digitalOut8() {
addChannel(CHANNEL_DIGITAL8, "Switch");
digitalBaseChannel(CHANNEL_DIGITAL8, bitSet(4), 3, "/tpm1c", bitSet(4, 7), true);
}
@Test
public void pwm1() {
pwmBaseChannel(CHANNEL_PWM_FREQ1, CHANNEL_PWM_DUTY1, "/period1", "/duty1", 1);
}
/**
* base test for digital channels
*
* @param channel channel name
* @param configBitSet expected config register
* @param configRegister config register number
* @param channelParam channel parameter
* @param returnBitSet which bitset should be returned on read
* @param isOutput if this channel is an output
*/
private void digitalBaseChannel(String channel, BitSet configBitSet, int configRegister, String channelParam,
BitSet returnBitSet, boolean isOutput) {
final BAE0910 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readBitSet(eq(testSensorId), eq(new OwserverDeviceParameter(channelParam))))
.thenReturn(returnBitSet);
testDevice.enableChannel(channel);
testDevice.configureChannels(mockBridgeHandler);
// test configuration
assertEquals(configBitSet, checkConfiguration(configRegister));
// refresh
ArgumentCaptor<State> stateArgumentCaptor = ArgumentCaptor.forClass(State.class);
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler).readBitSet(eq(testSensorId),
eq(new OwserverDeviceParameter(channelParam)));
inOrder.verify(mockThingHandler).postUpdate(eq(channel), stateArgumentCaptor.capture());
assertEquals(OnOffType.ON, stateArgumentCaptor.getValue());
// write
if (isOutput) {
ArgumentCaptor<BitSet> bitSetArgumentCaptor = ArgumentCaptor.forClass(BitSet.class);
assertTrue(testDevice.writeChannel(mockBridgeHandler, channel, OnOffType.ON));
inOrder.verify(mockBridgeHandler).writeBitSet(eq(testSensorId),
eq(new OwserverDeviceParameter(channelParam)), bitSetArgumentCaptor.capture());
assertEquals(returnBitSet, bitSetArgumentCaptor.getValue());
} else {
assertFalse(testDevice.writeChannel(mockBridgeHandler, channel, OnOffType.ON));
}
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
/**
* base test case for PWM channels
*
* @param freqChannel channel name for frequency
* @param dutyChannel channel name for duty cycle
* @param freqParam owfs parameter for frequency
* @param dutyParam owfs parameter for duty cycle
* @param registerIndex index for TPM configuration register
*/
private void pwmBaseChannel(String freqChannel, String dutyChannel, String freqParam, String dutyParam,
int registerIndex) {
Map<String, Object> channelConfig = new HashMap<>();
channelConfig.put("prescaler", 5);
addChannel(freqChannel, "Number:Frequency", new Configuration(channelConfig));
addChannel(dutyChannel, "Number:Dimensionless");
final BAE0910 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(
mockBridgeHandler.readDecimalType(eq(testSensorId), eq(new OwserverDeviceParameter(freqParam))))
.thenReturn(new DecimalType(32768));
Mockito.when(
mockBridgeHandler.readDecimalType(eq(testSensorId), eq(new OwserverDeviceParameter(dutyParam))))
.thenReturn(new DecimalType(16384));
testDevice.enableChannel(freqChannel);
testDevice.enableChannel(dutyChannel);
testDevice.configureChannels(mockBridgeHandler);
// test configuration
assertEquals(bitSet(0, 2), checkConfiguration(registerIndex + 2));
// refresh
ArgumentCaptor<State> stateArgumentCaptor = ArgumentCaptor.forClass(State.class);
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId),
eq(new OwserverDeviceParameter(freqParam)));
inOrder.verify(mockThingHandler).postUpdate(eq(freqChannel), stateArgumentCaptor.capture());
assertEquals(new QuantityType<>("15.2587890625 Hz"), stateArgumentCaptor.getValue());
inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId),
eq(new OwserverDeviceParameter(dutyParam)));
inOrder.verify(mockThingHandler).postUpdate(eq(dutyChannel), stateArgumentCaptor.capture());
assertEquals(new QuantityType<>("50 %"), stateArgumentCaptor.getValue());
// write
ArgumentCaptor<DecimalType> decimalTypeArgumentCaptor = ArgumentCaptor.forClass(DecimalType.class);
assertTrue(testDevice.writeChannel(mockBridgeHandler, freqChannel, new QuantityType<>("50000 Hz")));
inOrder.verify(mockBridgeHandler).writeDecimalType(eq(testSensorId),
eq(new OwserverDeviceParameter(freqParam)), decimalTypeArgumentCaptor.capture());
assertEquals(new DecimalType(10), decimalTypeArgumentCaptor.getValue());
testDevice.writeChannel(mockBridgeHandler, dutyChannel, new QuantityType<>("25 %"));
inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId),
eq(new OwserverDeviceParameter(freqParam)));
inOrder.verify(mockBridgeHandler).writeDecimalType(eq(testSensorId),
eq(new OwserverDeviceParameter(dutyParam)), decimalTypeArgumentCaptor.capture());
assertEquals(new DecimalType(8192), decimalTypeArgumentCaptor.getValue());
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
/**
* check if all registers are written and return one
*
* @param registerIndex number of register to return
* @return this register's BitSet
* @throws OwException
*/
private BitSet checkConfiguration(int registerIndex) throws OwException {
ArgumentCaptor<BitSet> configArgumentCaptor = ArgumentCaptor.forClass(BitSet.class);
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
inOrder.verify(mockBridgeHandler).writeBitSet(eq(testSensorId), eq(new OwserverDeviceParameter("/outc")),
configArgumentCaptor.capture());
inOrder.verify(mockBridgeHandler).writeBitSet(eq(testSensorId), eq(new OwserverDeviceParameter("/pioc")),
configArgumentCaptor.capture());
inOrder.verify(mockBridgeHandler).writeBitSet(eq(testSensorId), eq(new OwserverDeviceParameter("/adcc")),
configArgumentCaptor.capture());
inOrder.verify(mockBridgeHandler).writeBitSet(eq(testSensorId), eq(new OwserverDeviceParameter("/tpm1c")),
configArgumentCaptor.capture());
inOrder.verify(mockBridgeHandler).writeBitSet(eq(testSensorId), eq(new OwserverDeviceParameter("/tpm2c")),
configArgumentCaptor.capture());
return configArgumentCaptor.getAllValues().get(registerIndex);
}
/**
* BitSet with pre-set bits
*
* @param bits which bits to set
* @return the BitSet
*/
private BitSet bitSet(int... bits) {
BitSet bitSet = new BitSet(8);
Arrays.stream(bits).forEach(b -> bitSet.set(b));
return bitSet;
}
}

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.onewire.device;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.device.DS18x20;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
/**
* Tests cases for {@link DS18x20}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DS18x20Test extends DeviceTestParent<DS18x20> {
@Before
public void setupMocks() {
setupMocks(THING_TYPE_BASIC, DS18x20.class);
Map<String, Object> channelConfig = new HashMap<>();
channelConfig.put(CONFIG_IGNORE_POR, true);
addChannel(CHANNEL_TEMPERATURE, "Number:Temperature", new Configuration(channelConfig));
}
@Test
public void temperatureTest() {
final DS18x20 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(15.0));
testDevice.enableChannel(CHANNEL_TEMPERATURE);
testDevice.configureChannels();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler, times(1)).readDecimalType(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_TEMPERATURE), eq(new QuantityType<>("15.0 °C")));
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void temperatureIgnorePORTest() {
final DS18x20 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(85.0));
testDevice.enableChannel(CHANNEL_TEMPERATURE);
testDevice.configureChannels();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler, times(1)).readDecimalType(eq(testSensorId), any());
inOrder.verify(mockThingHandler, times(0)).postUpdate(eq(CHANNEL_TEMPERATURE), any());
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
}

View File

@@ -0,0 +1,98 @@
/**
* 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.onewire.device;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.times;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.device.DS1923;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
/**
* Tests cases for {@link DS1923}.
*
* @author Jan N. Klug - Initial contribution
* @author Michał Wójcik - Adapted to DS1923
*/
@NonNullByDefault
public class DS1923Test extends DeviceTestParent<DS1923> {
@Before
public void setupMocks() {
setupMocks(THING_TYPE_MS_TX, DS1923.class);
addChannel(CHANNEL_TEMPERATURE, "Number:Temperature");
addChannel(CHANNEL_HUMIDITY, "Number:Dimensionless");
addChannel(CHANNEL_ABSOLUTE_HUMIDITY, "Number:Density");
addChannel(CHANNEL_DEWPOINT, "Number:Temperature");
}
@Test
public void temperatureChannel() {
final DS1923 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(10.0));
testDevice.enableChannel(CHANNEL_TEMPERATURE);
testDevice.configureChannels();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_TEMPERATURE), eq(new QuantityType<>("10.0 °C")));
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void humidityChannel() {
final DS1923 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(10.0));
testDevice.enableChannel(CHANNEL_HUMIDITY);
testDevice.enableChannel(CHANNEL_ABSOLUTE_HUMIDITY);
testDevice.enableChannel(CHANNEL_DEWPOINT);
testDevice.configureChannels();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler, times(2)).readDecimalType(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_HUMIDITY), eq(new QuantityType<>("10.0 %")));
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_ABSOLUTE_HUMIDITY),
eq(new QuantityType<>("0.9381970824113001000 g/m³")));
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_DEWPOINT),
eq(new QuantityType<>("-20.31395053870025 °C")));
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
}

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.onewire.device;
import static org.openhab.binding.onewire.internal.OwBindingConstants.THING_TYPE_BASIC;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Before;
import org.junit.Test;
import org.openhab.binding.onewire.internal.device.DS2401;
import org.openhab.core.library.types.OnOffType;
/**
* Tests cases for {@link DS2401}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DS2401Test extends DeviceTestParent<DS2401> {
@Before
public void setupMocks() {
setupMocks(THING_TYPE_BASIC, DS2401.class);
}
@Test
public void presenceTestOn() {
presenceTest(OnOffType.ON);
}
@Test
public void presenceTestOff() {
presenceTest(OnOffType.OFF);
}
}

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.onewire.device;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.times;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.util.BitSet;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.device.DS2405;
import org.openhab.core.library.types.OnOffType;
/**
* Tests cases for {@link DS2405}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DS2405Test extends DeviceTestParent<DS2405> {
@Before
public void setupMocks() {
setupMocks(THING_TYPE_BASIC, DS2405.class);
addChannel(channelName(0), "Switch");
}
@Test
public void digitalChannel() {
digitalChannelTest(OnOffType.ON, 0);
digitalChannelTest(OnOffType.OFF, 0);
}
private void digitalChannelTest(OnOffType state, int channelNo) {
final DS2405 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
BitSet returnValue = new BitSet(8);
if (state == OnOffType.ON) {
returnValue.flip(0, 7);
}
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readBitSet(eq(testSensorId), any())).thenReturn(returnValue);
testDevice.configureChannels();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler, times(2)).readBitSet(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(channelName(channelNo)), eq(state));
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
private String channelName(int channelNo) {
return CHANNEL_DIGITAL + channelNo;
}
}

View File

@@ -0,0 +1,82 @@
/**
* 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.onewire.device;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.times;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.util.BitSet;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.device.DS2406_DS2413;
import org.openhab.core.library.types.OnOffType;
/**
* Tests cases for {@link DS2406_DS2413}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DS2406_DS2413Test extends DeviceTestParent<DS2406_DS2413> {
@Before
public void setupMocks() {
setupMocks(THING_TYPE_BASIC, DS2406_DS2413.class);
for (int i = 0; i < 2; i++) {
addChannel(channelName(i), "Switch");
}
}
@Test
public void digitalChannel() {
for (int i = 0; i < 2; i++) {
digitalChannelTest(OnOffType.ON, i);
digitalChannelTest(OnOffType.OFF, i);
}
}
private void digitalChannelTest(OnOffType state, int channelNo) {
final DS2406_DS2413 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
BitSet returnValue = new BitSet(8);
if (state == OnOffType.ON) {
returnValue.flip(0, 7);
}
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readBitSet(eq(testSensorId), any())).thenReturn(returnValue);
testDevice.configureChannels();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler, times(2)).readBitSet(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(channelName(channelNo)), eq(state));
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
private String channelName(int channelNo) {
return CHANNEL_DIGITAL + channelNo;
}
}

View File

@@ -0,0 +1,82 @@
/**
* 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.onewire.device;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.times;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.util.BitSet;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.device.DS2408;
import org.openhab.core.library.types.OnOffType;
/**
* Tests cases for {@link DS2408}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DS2408Test extends DeviceTestParent<DS2408> {
@Before
public void setupMocks() {
setupMocks(THING_TYPE_BASIC, DS2408.class);
for (int i = 0; i < 8; i++) {
addChannel(channelName(i), "Switch");
}
}
@Test
public void digitalChannel() {
for (int i = 0; i < 8; i++) {
digitalChannelTest(OnOffType.ON, i);
digitalChannelTest(OnOffType.OFF, i);
}
}
private void digitalChannelTest(OnOffType state, int channelNo) {
final DS2408 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
BitSet returnValue = new BitSet(8);
if (state == OnOffType.ON) {
returnValue.flip(0, 8);
}
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readBitSet(eq(testSensorId), any())).thenReturn(returnValue);
testDevice.configureChannels();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler, times(2)).readBitSet(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(channelName(channelNo)), eq(state));
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
private String channelName(int channelNo) {
return CHANNEL_DIGITAL + channelNo;
}
}

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.onewire.device;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.times;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.device.DS2423;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.types.State;
/**
* Tests cases for {@link DS2423}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DS2423Test extends DeviceTestParent<DS2423> {
@Before
public void setupMocks() {
setupMocks(THING_TYPE_BASIC, DS2423.class);
for (int i = 0; i < 2; i++) {
addChannel(channelName(i), "Number");
}
}
@Test
public void counterChannelTest() {
List<State> returnValue = new ArrayList<>();
returnValue.add(new DecimalType(1408));
returnValue.add(new DecimalType(3105));
final DS2423 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalTypeArray(eq(testSensorId), any())).thenReturn(returnValue);
testDevice.configureChannels();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler, times(1)).readDecimalTypeArray(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(channelName(0)), eq(returnValue.get(0)));
inOrder.verify(mockThingHandler).postUpdate(eq(channelName(1)), eq(returnValue.get(1)));
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
private String channelName(int channelNo) {
return CHANNEL_COUNTER + channelNo;
}
}

View File

@@ -0,0 +1,224 @@
/**
* 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.onewire.device;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.times;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.device.DS2438;
import org.openhab.binding.onewire.internal.device.DS2438.LightSensorType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
/**
* Tests cases for {@link DS2438}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class DS2438Test extends DeviceTestParent<DS2438> {
@Before
public void setupMocks() {
setupMocks(THING_TYPE_MS_TX, DS2438.class);
addChannel(CHANNEL_TEMPERATURE, "Number:Temperature");
addChannel(CHANNEL_HUMIDITY, "Number:Dimensionless");
addChannel(CHANNEL_ABSOLUTE_HUMIDITY, "Number:Density");
addChannel(CHANNEL_DEWPOINT, "Number:Temperature");
addChannel(CHANNEL_VOLTAGE, "Number:Voltage");
addChannel(CHANNEL_CURRENT, "Number:Current");
addChannel(CHANNEL_LIGHT, "Number:Illuminance");
addChannel(CHANNEL_SUPPLYVOLTAGE, "Number:Voltage");
}
@Test
public void temperatureChannel() {
final DS2438 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(10.0));
testDevice.enableChannel(CHANNEL_TEMPERATURE);
testDevice.configureChannels();
inOrder.verify(mockThingHandler).getThing();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_TEMPERATURE), eq(new QuantityType<>("10.0 °C")));
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void humidityChannel() {
final DS2438 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(10.0));
testDevice.enableChannel(CHANNEL_HUMIDITY);
testDevice.enableChannel(CHANNEL_ABSOLUTE_HUMIDITY);
testDevice.enableChannel(CHANNEL_DEWPOINT);
testDevice.configureChannels();
inOrder.verify(mockThingHandler).getThing();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler, times(2)).readDecimalType(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_HUMIDITY), eq(new QuantityType<>("10.0 %")));
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_ABSOLUTE_HUMIDITY),
eq(new QuantityType<>("0.9381970824113001000 g/m³")));
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_DEWPOINT),
eq(new QuantityType<>("-20.31395053870025 °C")));
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void voltageChannel() {
final DS2438 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(2.0));
testDevice.enableChannel(CHANNEL_VOLTAGE);
testDevice.configureChannels();
inOrder.verify(mockThingHandler).getThing();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_VOLTAGE), eq(new QuantityType<>("2.0 V")));
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void currentChannel() {
final DS2438 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(2.0));
testDevice.enableChannel(CHANNEL_CURRENT);
testDevice.configureChannels();
inOrder.verify(mockThingHandler).getThing();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_CURRENT), eq(new QuantityType<>("2.0 mA")));
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void lightChannel() {
final DS2438 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(0.1));
testDevice.enableChannel(CHANNEL_LIGHT);
testDevice.configureChannels();
inOrder.verify(mockThingHandler).getThing();
testDevice.setLightSensorType(LightSensorType.ELABNET_V1);
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_LIGHT), eq(new QuantityType<>("97442 lx")));
testDevice.setLightSensorType(LightSensorType.ELABNET_V2);
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_LIGHT), eq(new QuantityType<>("134 lx")));
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void supplyVoltageChannel() {
final DS2438 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(2.0));
testDevice.enableChannel(CHANNEL_SUPPLYVOLTAGE);
testDevice.configureChannels();
inOrder.verify(mockThingHandler).getThing();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_SUPPLYVOLTAGE), eq(new QuantityType<>("2.0 V")));
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void noChannel() {
final DS2438 testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(2.0));
testDevice.configureChannels();
inOrder.verify(mockThingHandler).getThing();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
}

View File

@@ -0,0 +1,135 @@
/**
* 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.onewire.device;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.openhab.binding.onewire.internal.OwBindingConstants.CHANNEL_PRESENT;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.Assert;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.device.AbstractOwDevice;
import org.openhab.binding.onewire.internal.device.OwSensorType;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.*;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.type.ChannelTypeUID;
/**
* Abtract test class for onewire devices.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public abstract class DeviceTestParent<T extends AbstractOwDevice> {
private @Nullable Class<T> deviceTestClazz;
@Mock
@NonNullByDefault({})
protected OwBaseThingHandler mockThingHandler;
@Mock
@NonNullByDefault({})
protected OwserverBridgeHandler mockBridgeHandler;
@Mock
@NonNullByDefault({})
protected Thing mockThing;
protected SensorId testSensorId = new SensorId("00.000000000000");
public void setupMocks(ThingTypeUID thingTypeUID, Class<T> deviceTestClazz) {
this.deviceTestClazz = deviceTestClazz;
initMocks(this);
Mockito.when(mockThingHandler.getThing()).thenReturn(mockThing);
Mockito.when(mockThing.getUID()).thenReturn(new ThingUID(thingTypeUID, "testsensor"));
addChannel(CHANNEL_PRESENT, "Switch");
}
public void addChannel(String channelId, String itemType) {
Channel channel = ChannelBuilder.create(new ChannelUID(mockThing.getUID(), channelId), itemType).build();
Mockito.when(mockThing.getChannel(channelId)).thenReturn(channel);
}
public void addChannel(String channelId, String itemType, Configuration channelConfiguration) {
Channel channel = ChannelBuilder.create(new ChannelUID(mockThing.getUID(), channelId), itemType)
.withConfiguration(channelConfiguration).build();
Mockito.when(mockThing.getChannel(channelId)).thenReturn(channel);
}
public void addChannel(String channelId, String itemType, ChannelTypeUID channelTypeUID) {
Channel channel = ChannelBuilder.create(new ChannelUID(mockThing.getUID(), channelId), itemType)
.withType(channelTypeUID).build();
Mockito.when(mockThing.getChannel(channelId)).thenReturn(channel);
}
public T instantiateDevice() {
final Class<T> deviceTestClazz = this.deviceTestClazz;
if (deviceTestClazz == null) {
throw new IllegalStateException("deviceTestClazz is null");
}
try {
Constructor<T> constructor = deviceTestClazz.getConstructor(SensorId.class, OwBaseThingHandler.class);
T testDevice = constructor.newInstance(testSensorId, mockThingHandler);
Assert.assertNotNull(testDevice);
return testDevice;
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException
| InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
public T instantiateDevice(OwSensorType sensorType) {
final Class<T> deviceTestClazz = this.deviceTestClazz;
if (deviceTestClazz == null) {
throw new IllegalStateException("deviceTestClazz is null");
}
try {
Constructor<T> constructor = deviceTestClazz.getConstructor(SensorId.class, OwSensorType.class,
OwBaseThingHandler.class);
T testDevice = constructor.newInstance(testSensorId, sensorType, mockThingHandler);
Assert.assertNotNull(testDevice);
return testDevice;
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException
| InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
public void presenceTest(OnOffType state) {
final T testDevice = instantiateDevice();
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(state);
testDevice.checkPresence(mockBridgeHandler);
inOrder.verify(mockThingHandler).updatePresenceStatus(eq(state));
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
}

View File

@@ -0,0 +1,163 @@
/**
* 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.onewire.device;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.times;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.device.EDS006x;
import org.openhab.binding.onewire.internal.device.OwSensorType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.QuantityType;
/**
* Tests cases for {@link EDS006x}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class EDS006xTest extends DeviceTestParent<EDS006x> {
@Before
public void setupMocks() {
setupMocks(THING_TYPE_EDS_ENV, EDS006x.class);
addChannel(CHANNEL_TEMPERATURE, "Number:Temperature");
addChannel(CHANNEL_HUMIDITY, "Number:Dimensionless");
addChannel(CHANNEL_ABSOLUTE_HUMIDITY, "Number:Density");
addChannel(CHANNEL_DEWPOINT, "Number:Temperature");
addChannel(CHANNEL_LIGHT, "Number:Illuminance");
addChannel(CHANNEL_PRESSURE, "Number:Pressure");
}
@Test
public void temperatureChannel() {
final EDS006x testDevice = instantiateDevice(OwSensorType.EDS0068);
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(10.0));
testDevice.enableChannel(CHANNEL_TEMPERATURE);
testDevice.configureChannels();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_TEMPERATURE), eq(new QuantityType<>("10.0 °C")));
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void humidityChannel() {
final EDS006x testDevice = instantiateDevice(OwSensorType.EDS0068);
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(10.0));
testDevice.enableChannel(CHANNEL_HUMIDITY);
testDevice.enableChannel(CHANNEL_ABSOLUTE_HUMIDITY);
testDevice.enableChannel(CHANNEL_DEWPOINT);
testDevice.configureChannels();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler, times(2)).readDecimalType(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_HUMIDITY), eq(new QuantityType<>("10.0 %")));
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_ABSOLUTE_HUMIDITY),
eq(new QuantityType<>("0.9381970824113001000 g/m³")));
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_DEWPOINT),
eq(new QuantityType<>("-20.31395053870025 °C")));
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void pressureChannel() {
final EDS006x testDevice = instantiateDevice(OwSensorType.EDS0068);
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(2.0));
testDevice.enableChannel(CHANNEL_PRESSURE);
testDevice.configureChannels();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_PRESSURE), eq(new QuantityType<>("2.0 mbar")));
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void lightChannel() {
final EDS006x testDevice = instantiateDevice(OwSensorType.EDS0068);
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(100));
testDevice.enableChannel(CHANNEL_LIGHT);
testDevice.configureChannels();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verify(mockBridgeHandler).readDecimalType(eq(testSensorId), any());
inOrder.verify(mockThingHandler).postUpdate(eq(CHANNEL_LIGHT), eq(new QuantityType<>("100 lx")));
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void noChannel() {
final EDS006x testDevice = instantiateDevice(OwSensorType.EDS0068);
final InOrder inOrder = Mockito.inOrder(mockThingHandler, mockBridgeHandler);
try {
Mockito.when(mockBridgeHandler.checkPresence(testSensorId)).thenReturn(OnOffType.ON);
Mockito.when(mockBridgeHandler.readDecimalType(eq(testSensorId), any())).thenReturn(new DecimalType(2.0));
testDevice.configureChannels();
testDevice.refresh(mockBridgeHandler, true);
inOrder.verifyNoMoreInteractions();
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
}

View File

@@ -0,0 +1,148 @@
/**
* 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.onewire.internal;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.openhab.binding.onewire.internal.OwBindingConstants.CONFIG_ID;
import static org.openhab.binding.onewire.internal.OwBindingConstants.THING_TYPE_BASIC;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.openhab.binding.onewire.internal.device.OwSensorType;
import org.openhab.binding.onewire.internal.handler.BasicThingHandler;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.test.AbstractThingHandlerTest;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.builder.ThingBuilder;
/**
* Tests cases for {@link BasicThingHandler}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class BasicThingHandlerTest extends AbstractThingHandlerTest {
private static final String TEST_ID = "00.000000000000";
@Before
public void setup() throws OwException {
MockitoAnnotations.initMocks(this);
initializeBridge();
final Bridge bridge = this.bridge;
if (bridge == null) {
Assert.fail("bridge is null");
return;
}
thingConfiguration.put(CONFIG_ID, TEST_ID);
thing = ThingBuilder.create(THING_TYPE_BASIC, "testthing").withLabel("Test thing")
.withConfiguration(new Configuration(thingConfiguration)).withProperties(thingProperties)
.withBridge(bridge.getUID()).build();
final Thing thing = this.thing;
if (thing == null) {
Assert.fail("thing is null");
return;
}
thingHandler = new BasicThingHandler(thing, stateProvider) {
@Override
protected @Nullable Bridge getBridge() {
return bridge;
}
};
initializeHandlerMocks();
}
@Test
public void testInitializationEndsWithUnknown() throws OwException {
final ThingHandler thingHandler = this.thingHandler;
if (thingHandler == null) {
Assert.fail("thingHandler is null");
return;
}
Mockito.doAnswer(answer -> {
return OwSensorType.DS2401;
}).when(secondBridgeHandler).getType(any());
thingHandler.initialize();
waitForAssert(() -> assertEquals(ThingStatus.UNKNOWN, thingHandler.getThing().getStatusInfo().getStatus()));
}
@Test
public void testRefreshAnalog() throws OwException {
final OwBaseThingHandler thingHandler = this.thingHandler;
final InOrder inOrder = this.inOrder;
if (thingHandler == null || inOrder == null) {
Assert.fail("prerequisite is null");
return;
}
Mockito.doAnswer(answer -> {
return OwSensorType.DS18B20;
}).when(secondBridgeHandler).getType(any());
thingHandler.initialize();
waitForAssert(() -> assertEquals(ThingStatus.UNKNOWN, thingHandler.getThing().getStatusInfo().getStatus()));
thingHandler.refresh(bridgeHandler, System.currentTimeMillis());
inOrder.verify(bridgeHandler, times(1)).checkPresence(new SensorId(TEST_ID));
inOrder.verify(bridgeHandler, times(1)).readDecimalType(eq(new SensorId(TEST_ID)), any());
inOrder.verifyNoMoreInteractions();
}
@Test
public void testRefreshDigital() throws OwException {
final OwBaseThingHandler thingHandler = this.thingHandler;
final InOrder inOrder = this.inOrder;
if (thingHandler == null || inOrder == null) {
Assert.fail("prerequisite is null");
return;
}
Mockito.doAnswer(answer -> {
return OwSensorType.DS2408;
}).when(secondBridgeHandler).getType(any());
thingHandler.initialize();
waitForAssert(() -> assertEquals(ThingStatus.UNKNOWN, thingHandler.getThing().getStatusInfo().getStatus()));
thingHandler.refresh(bridgeHandler, System.currentTimeMillis());
inOrder.verify(bridgeHandler, times(1)).checkPresence(new SensorId(TEST_ID));
inOrder.verify(bridgeHandler, times(2)).readBitSet(eq(new SensorId(TEST_ID)), any());
inOrder.verifyNoMoreInteractions();
}
}

View File

@@ -0,0 +1,130 @@
/**
* 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.onewire.internal;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.openhab.binding.onewire.internal.handler.EDSSensorThingHandler;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.test.AbstractThingHandlerTest;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.*;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
/**
* Tests cases for {@link EDSSensorThingHandler}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class EDSSensorThingHandlerTest extends AbstractThingHandlerTest {
private static final String TEST_ID = "00.000000000000";
private static final ThingUID THING_UID = new ThingUID(THING_TYPE_EDS_ENV, "testthing");
private static final ChannelUID CHANNEL_UID_TEMPERATURE = new ChannelUID(THING_UID, CHANNEL_TEMPERATURE);
private static final ChannelUID CHANNEL_UID_HUMIDITY = new ChannelUID(THING_UID, CHANNEL_HUMIDITY);
private static final ChannelUID CHANNEL_UID_ABSOLUTE_HUMIDITY = new ChannelUID(THING_UID,
CHANNEL_ABSOLUTE_HUMIDITY);
private static final ChannelUID CHANNEL_UID_DEWPOINT = new ChannelUID(THING_UID, CHANNEL_DEWPOINT);
@Before
public void setup() throws OwException {
MockitoAnnotations.initMocks(this);
initializeBridge();
final Bridge bridge = this.bridge;
if (bridge == null) {
Assert.fail("bridge is null");
return;
}
thingConfiguration.put(CONFIG_ID, TEST_ID);
channels.add(ChannelBuilder.create(CHANNEL_UID_TEMPERATURE, "Number:Temperature").build());
channels.add(ChannelBuilder.create(CHANNEL_UID_HUMIDITY, "Number:Dimensionless").build());
channels.add(ChannelBuilder.create(CHANNEL_UID_ABSOLUTE_HUMIDITY, "Number:Density").build());
channels.add(ChannelBuilder.create(CHANNEL_UID_DEWPOINT, "Number:Temperature").build());
thing = ThingBuilder.create(THING_TYPE_EDS_ENV, "testthing").withLabel("Test thing").withChannels(channels)
.withConfiguration(new Configuration(thingConfiguration)).withProperties(thingProperties)
.withBridge(bridge.getUID()).build();
final Thing thing = this.thing;
if (thing == null) {
Assert.fail("thing is null");
return;
}
thingHandler = new EDSSensorThingHandler(thing, stateProvider) {
@Override
protected @Nullable Bridge getBridge() {
return bridge;
}
};
initializeHandlerMocks();
Mockito.doAnswer(answer -> {
return new OwPageBuffer("EDS0065 ".getBytes());
}).when(secondBridgeHandler).readPages(any());
}
@Test
public void testInitializationEndsWithUnknown() {
final ThingHandler thingHandler = this.thingHandler;
if (thingHandler == null) {
Assert.fail("thingHandler is null");
return;
}
thingHandler.initialize();
waitForAssert(() -> assertEquals(ThingStatus.UNKNOWN, thingHandler.getThing().getStatusInfo().getStatus()));
}
@Test
public void testRefresh() throws OwException {
final OwBaseThingHandler thingHandler = this.thingHandler;
final InOrder inOrder = this.inOrder;
if (thingHandler == null || inOrder == null) {
Assert.fail("prerequisite is null");
return;
}
thingHandler.initialize();
// needed to determine initialization is finished
waitForAssert(() -> assertEquals(ThingStatus.UNKNOWN, thingHandler.getThing().getStatusInfo().getStatus()));
thingHandler.refresh(bridgeHandler, System.currentTimeMillis());
inOrder.verify(bridgeHandler, times(1)).checkPresence(new SensorId(TEST_ID));
inOrder.verify(bridgeHandler, times(2)).readDecimalType(eq(new SensorId(TEST_ID)), any());
inOrder.verifyNoMoreInteractions();
}
}

View File

@@ -0,0 +1,126 @@
/**
* 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.onewire.internal;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.openhab.binding.onewire.internal.OwBindingConstants.CONFIG_ID;
import static org.openhab.binding.onewire.internal.OwBindingConstants.THING_TYPE_MS_TX;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.openhab.binding.onewire.internal.device.OwSensorType;
import org.openhab.binding.onewire.internal.handler.BasicMultisensorThingHandler;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.test.AbstractThingHandlerTest;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.builder.ThingBuilder;
/**
* Tests cases for {@link BasicMultisensorThingHandler}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class MultisensorThingHandlerTest extends AbstractThingHandlerTest {
private static final String TEST_ID = "00.000000000000";
@Before
public void setup() throws OwException {
MockitoAnnotations.initMocks(this);
initializeBridge();
final Bridge bridge = this.bridge;
if (bridge == null) {
Assert.fail("bridge is null");
return;
}
thingConfiguration.put(CONFIG_ID, TEST_ID);
thing = ThingBuilder.create(THING_TYPE_MS_TX, "testthing").withLabel("Test thing").withChannels(channels)
.withConfiguration(new Configuration(thingConfiguration)).withProperties(thingProperties)
.withBridge(bridge.getUID()).build();
final Thing thing = this.thing;
if (thing == null) {
Assert.fail("thing is null");
return;
}
thingHandler = new BasicMultisensorThingHandler(thing, stateProvider) {
@Override
protected @Nullable Bridge getBridge() {
return bridge;
}
};
initializeHandlerMocks();
Mockito.doAnswer(answer -> {
return OwSensorType.DS2438;
}).when(secondBridgeHandler).getType(any());
Mockito.doAnswer(answer -> {
OwPageBuffer pageBuffer = new OwPageBuffer(8);
pageBuffer.setByte(3, 0, (byte) 0x19);
return pageBuffer;
}).when(secondBridgeHandler).readPages(any());
}
@Test
public void testInitializationEndsWithUnknown() {
final ThingHandler thingHandler = this.thingHandler;
if (thingHandler == null) {
Assert.fail("thingHandler is null");
return;
}
thingHandler.initialize();
waitForAssert(() -> assertEquals(ThingStatus.UNKNOWN, thingHandler.getThing().getStatusInfo().getStatus()));
}
@Test
public void testRefresh() throws OwException {
final OwBaseThingHandler thingHandler = this.thingHandler;
final InOrder inOrder = this.inOrder;
if (thingHandler == null || inOrder == null) {
Assert.fail("prerequisite is null");
return;
}
thingHandler.initialize();
// needed to determine initialization is finished
waitForAssert(() -> assertEquals(ThingStatus.UNKNOWN, thingHandler.getThing().getStatusInfo().getStatus()));
thingHandler.refresh(bridgeHandler, System.currentTimeMillis());
inOrder.verify(bridgeHandler, times(1)).checkPresence(new SensorId(TEST_ID));
inOrder.verify(bridgeHandler, times(3)).readDecimalType(eq(new SensorId(TEST_ID)), any());
inOrder.verifyNoMoreInteractions();
}
}

View File

@@ -0,0 +1,171 @@
/**
* 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.onewire.internal;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.openhab.binding.onewire.internal.OwBindingConstants.*;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
import org.openhab.binding.onewire.internal.owserver.OwserverConnection;
import org.openhab.binding.onewire.internal.owserver.OwserverConnectionState;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.test.java.JavaTest;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.openhab.core.thing.binding.builder.BridgeBuilder;
/**
* Tests cases for {@link OwserverBridgeHandler}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class OwserverBridgeHandlerTest extends JavaTest {
private static final String TEST_HOST = "foo.bar";
private static final int TEST_PORT = 4711;
Map<String, Object> bridgeProperties = new HashMap<>();
@Mock
@NonNullByDefault({})
private OwserverConnection owserverConnection;
@Mock
@NonNullByDefault({})
private ThingHandlerCallback thingHandlerCallback;
private @Nullable OwserverBridgeHandler bridgeHandler;
private @Nullable Bridge bridge;
@Before
public void setup() {
bridgeProperties.put(CONFIG_ADDRESS, TEST_HOST);
bridgeProperties.put(CONFIG_PORT, TEST_PORT);
initMocks(this);
bridge = BridgeBuilder.create(THING_TYPE_OWSERVER, "owserver").withLabel("owserver")
.withConfiguration(new Configuration(bridgeProperties)).build();
doAnswer(answer -> {
((Thing) answer.getArgument(0)).setStatusInfo(answer.getArgument(1));
return null;
}).when(thingHandlerCallback).statusUpdated(any(), any());
final Bridge bridge = this.bridge;
if (bridge == null) {
Assert.fail("bridge is null");
return;
}
final OwserverBridgeHandler bridgeHandler = new OwserverBridgeHandler(bridge, owserverConnection);
bridgeHandler.getThing().setHandler(bridgeHandler);
bridgeHandler.setCallback(thingHandlerCallback);
this.bridgeHandler = bridgeHandler;
}
@After
public void tearDown() {
final OwserverBridgeHandler bridgeHandler = this.bridgeHandler;
if (bridgeHandler != null) {
bridgeHandler.dispose();
}
}
@Test
public void testInitializationStartsConnectionWithOptions() {
final OwserverBridgeHandler bridgeHandler = this.bridgeHandler;
if (bridgeHandler == null) {
Assert.fail("bridgeHandler is null");
return;
}
bridgeHandler.initialize();
Mockito.verify(owserverConnection).setHost(TEST_HOST);
Mockito.verify(owserverConnection).setPort(TEST_PORT);
Mockito.verify(owserverConnection, timeout(5000)).start();
}
@Test
public void testInitializationReportsRefreshableOnSuccessfullConnection() {
final OwserverBridgeHandler bridgeHandler = this.bridgeHandler;
if (bridgeHandler == null) {
Assert.fail("bridgeHandler is null");
return;
}
Mockito.doAnswer(answer -> {
bridgeHandler.reportConnectionState(OwserverConnectionState.OPENED);
return null;
}).when(owserverConnection).start();
bridgeHandler.initialize();
ArgumentCaptor<ThingStatusInfo> statusCaptor = ArgumentCaptor.forClass(ThingStatusInfo.class);
waitForAssert(() -> {
verify(thingHandlerCallback, times(2)).statusUpdated(eq(bridge), statusCaptor.capture());
});
assertThat(statusCaptor.getAllValues().get(0).getStatus(), is(ThingStatus.UNKNOWN));
assertThat(statusCaptor.getAllValues().get(1).getStatus(), is(ThingStatus.ONLINE));
waitForAssert(() -> assertTrue(bridgeHandler.isRefreshable()));
}
@Test
public void testInitializationReportsNotRefreshableOnFailedConnection() {
final OwserverBridgeHandler bridgeHandler = this.bridgeHandler;
if (bridgeHandler == null) {
Assert.fail("bridgeHandler is null");
return;
}
Mockito.doAnswer(answer -> {
bridgeHandler.reportConnectionState(OwserverConnectionState.FAILED);
return null;
}).when(owserverConnection).start();
bridgeHandler.initialize();
ArgumentCaptor<ThingStatusInfo> statusCaptor = ArgumentCaptor.forClass(ThingStatusInfo.class);
waitForAssert(() -> {
verify(thingHandlerCallback, times(2)).statusUpdated(eq(bridge), statusCaptor.capture());
});
assertThat(statusCaptor.getAllValues().get(0).getStatus(), is(ThingStatus.UNKNOWN));
assertThat(statusCaptor.getAllValues().get(1).getStatus(), is(ThingStatus.OFFLINE));
waitForAssert(() -> assertFalse(bridgeHandler.isRefreshable()));
}
}

View File

@@ -0,0 +1,224 @@
/**
* 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.onewire.owserver;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.OwPageBuffer;
import org.openhab.binding.onewire.internal.SensorId;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
import org.openhab.binding.onewire.internal.owserver.OwserverConnection;
import org.openhab.binding.onewire.internal.owserver.OwserverConnectionState;
import org.openhab.binding.onewire.test.OwserverTestServer;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.test.TestPortUtil;
import org.openhab.core.test.java.JavaTest;
import org.openhab.core.types.State;
/**
* Tests cases for {@link OwserverConnection}.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class OwserverConnectionTest extends JavaTest {
private static final String TEST_HOST = "127.0.0.1";
private @Nullable OwserverTestServer testServer;
private @Nullable OwserverConnection owserverConnection;
@Mock
private @NonNullByDefault({}) OwserverBridgeHandler bridgeHandler;
private int testPort;
@Before
public void setup() throws Exception {
initMocks(this);
CompletableFuture<Boolean> serverStarted = new CompletableFuture<>();
testPort = TestPortUtil.findFreePort();
try {
final OwserverTestServer testServer = new OwserverTestServer(testPort);
testServer.startServer(serverStarted);
this.testServer = testServer;
} catch (IOException e) {
fail("could not start test server");
}
final OwserverConnection owserverConnection = new OwserverConnection(bridgeHandler);
owserverConnection.setHost(TEST_HOST);
owserverConnection.setPort(testPort);
this.owserverConnection = owserverConnection;
serverStarted.get(); // wait for the server thread to start
}
@After
public void tearDown() {
try {
final OwserverTestServer testServer = this.testServer;
if (testServer != null) {
testServer.stopServer();
}
} catch (IOException e) {
fail("could not stop test server");
}
}
@Test
public void successfullConnectionReportedToBridgeHandler() {
final OwserverConnection owserverConnection = this.owserverConnection;
if (owserverConnection == null) {
Assert.fail("connection is null");
return;
}
owserverConnection.start();
Mockito.verify(bridgeHandler).reportConnectionState(OwserverConnectionState.OPENED);
}
@Test
public void failedConnectionReportedToBridgeHandler() {
final OwserverConnection owserverConnection = this.owserverConnection;
if (owserverConnection == null) {
Assert.fail("connection is null");
return;
}
owserverConnection.setPort(1);
owserverConnection.start();
Mockito.verify(bridgeHandler, timeout(100)).reportConnectionState(OwserverConnectionState.FAILED);
}
@Test
public void testGetDirectory() {
final OwserverConnection owserverConnection = this.owserverConnection;
if (owserverConnection == null) {
Assert.fail("connection is null");
return;
}
owserverConnection.start();
try {
List<SensorId> directory = owserverConnection.getDirectory("/");
assertEquals(3, directory.size());
assertEquals(new SensorId("/00.0123456789ab"), directory.get(0));
assertEquals(new SensorId("/00.0123456789ac"), directory.get(1));
assertEquals(new SensorId("/00.0123456789ad"), directory.get(2));
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void testCheckPresence() {
final OwserverConnection owserverConnection = this.owserverConnection;
if (owserverConnection == null) {
Assert.fail("connection is null");
return;
}
owserverConnection.start();
State presence = owserverConnection.checkPresence("present");
assertEquals(OnOffType.ON, presence);
presence = owserverConnection.checkPresence("notpresent");
assertEquals(OnOffType.OFF, presence);
}
@Test
public void testReadDecimalType() {
final OwserverConnection owserverConnection = this.owserverConnection;
if (owserverConnection == null) {
Assert.fail("connection is null");
return;
}
owserverConnection.start();
try {
DecimalType number = (DecimalType) owserverConnection.readDecimalType("testsensor/decimal");
assertEquals(17.4, number.doubleValue(), 0.01);
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void testReadDecimalTypeArray() {
final OwserverConnection owserverConnection = this.owserverConnection;
if (owserverConnection == null) {
Assert.fail("connection is null");
return;
}
owserverConnection.start();
try {
List<State> numbers = owserverConnection.readDecimalTypeArray("testsensor/decimalarray");
assertEquals(3834, ((DecimalType) numbers.get(0)).intValue());
assertEquals(0, ((DecimalType) numbers.get(1)).intValue());
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void testGetPages() {
final OwserverConnection owserverConnection = this.owserverConnection;
if (owserverConnection == null) {
Assert.fail("connection is null");
return;
}
owserverConnection.start();
try {
OwPageBuffer pageBuffer = owserverConnection.readPages("testsensor");
assertEquals(31, pageBuffer.getByte(5, 7));
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
@Test
public void testWriteDecimalType() {
final OwserverConnection owserverConnection = this.owserverConnection;
if (owserverConnection == null) {
Assert.fail("connection is null");
return;
}
owserverConnection.start();
try {
owserverConnection.writeDecimalType("testsensor/decimal", new DecimalType(2009));
Mockito.verify(bridgeHandler, never()).reportConnectionState(OwserverConnectionState.FAILED);
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
}

View File

@@ -0,0 +1,143 @@
/**
* 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.onewire.test;
import static org.mockito.ArgumentMatchers.any;
import static org.openhab.binding.onewire.internal.OwBindingConstants.THING_TYPE_OWSERVER;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.After;
import org.junit.Assert;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.openhab.binding.onewire.internal.OwDynamicStateDescriptionProvider;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.handler.OwBaseThingHandler;
import org.openhab.binding.onewire.internal.handler.OwserverBridgeHandler;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.test.java.JavaTest;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerCallback;
import org.openhab.core.thing.binding.builder.BridgeBuilder;
/**
* Base class for thing handler tests.
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public abstract class AbstractThingHandlerTest extends JavaTest {
protected Map<String, Object> bridgeProperties = new HashMap<>();
protected Map<String, String> thingProperties = new HashMap<>();
protected Map<String, Object> thingConfiguration = new HashMap<>();
protected Map<String, Object> channelProperties = new HashMap<>();
@Mock
@NonNullByDefault({})
protected ThingHandlerCallback thingHandlerCallback;
@Mock
@NonNullByDefault({})
protected OwDynamicStateDescriptionProvider stateProvider;
@Mock
@NonNullByDefault({})
protected ThingHandlerCallback bridgeHandlerCallback;
@Mock
@NonNullByDefault({})
protected OwserverBridgeHandler bridgeHandler;
@Mock
@NonNullByDefault({})
protected OwserverBridgeHandler secondBridgeHandler;
protected List<Channel> channels = new ArrayList<>();
protected @Nullable Bridge bridge;
protected @Nullable Thing thing;
protected @Nullable OwBaseThingHandler thingHandler;
protected @Nullable InOrder inOrder;
@After
public void tearDown() {
final ThingHandler thingHandler = this.thingHandler;
if (thingHandler != null) {
thingHandler.dispose();
}
}
protected void initializeHandlerMocks() {
final ThingHandler thingHandler = this.thingHandler;
if (thingHandler == null) {
Assert.fail("thingHandler is null");
return;
}
thingHandler.getThing().setHandler(thingHandler);
thingHandler.setCallback(thingHandlerCallback);
Mockito.doAnswer(answer -> {
((Thing) answer.getArgument(0)).setStatusInfo(answer.getArgument(1));
return null;
}).when(thingHandlerCallback).statusUpdated(any(), any());
inOrder = Mockito.inOrder(bridgeHandler);
}
public void initializeBridge() throws OwException {
bridgeProperties = new HashMap<>();
final Bridge bridge = BridgeBuilder.create(THING_TYPE_OWSERVER, "testbridge").withLabel("Test Bridge")
.withConfiguration(new Configuration(bridgeProperties)).build();
bridge.setHandler(bridgeHandler);
this.bridge = bridge;
Mockito.doAnswer(answer -> {
((Thing) answer.getArgument(0)).setStatusInfo(answer.getArgument(1));
return null;
}).when(bridgeHandlerCallback).statusUpdated(any(), any());
Mockito.doAnswer(answer -> OnOffType.ON).when(bridgeHandler).checkPresence(any());
Mockito.doAnswer(answer -> new DecimalType(10)).when(bridgeHandler).readDecimalType(any(), any());
Mockito.doAnswer(answer -> new BitSet(8)).when(bridgeHandler).readBitSet(any(), any());
Mockito.doAnswer(answer -> {
final OwBaseThingHandler thingHandler = this.thingHandler;
if (thingHandler == null) {
Assert.fail("thingHandler is null");
return null;
}
thingHandler.updateSensorProperties(secondBridgeHandler);
thingHandler.initialize();
return null;
}).when(bridgeHandler).scheduleForPropertiesUpdate(any());
}
}

View File

@@ -0,0 +1,142 @@
/**
* 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.onewire.test;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.Assert;
import org.openhab.binding.onewire.internal.OwException;
import org.openhab.binding.onewire.internal.OwPageBuffer;
import org.openhab.binding.onewire.internal.owserver.OwserverPacket;
import org.openhab.binding.onewire.internal.owserver.OwserverPacketType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link OwserverTestServer} defines a server for testing the OwserverConnection class
*
* @author Jan N. Klug - Initial contribution
*/
@NonNullByDefault
public class OwserverTestServer {
private final Logger logger = LoggerFactory.getLogger(OwserverTestServer.class);
private final ServerSocket serverSocket;
private boolean isRunning = false;
public OwserverTestServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void startServer(CompletableFuture<Boolean> serverStarted) throws IOException {
isRunning = true;
new Thread() {
@Override
public void run() {
OwserverPacket receivedPacket;
List<OwserverPacket> answerPackets;
serverStarted.complete(true);
try {
while (isRunning) {
final Socket connectionSocket = serverSocket.accept();
final DataInputStream inputStream = new DataInputStream(connectionSocket.getInputStream());
final DataOutputStream outputStream = new DataOutputStream(connectionSocket.getOutputStream());
receivedPacket = new OwserverPacket(inputStream, OwserverPacketType.REQUEST);
logger.debug("received {}", receivedPacket);
answerPackets = processPacket(receivedPacket);
answerPackets.forEach(answerPacket -> {
logger.debug("answering {}", answerPacket);
try {
outputStream.write(answerPacket.toBytes());
} catch (IOException e) {
logger.error("I/O Error: {}", e.getMessage());
}
});
}
} catch (IOException e) {
logger.error("I/O Error: {}", e.getMessage());
} catch (OwException e) {
Assert.fail("caught unexpected OwException");
}
}
}.start();
}
public void stopServer() throws IOException {
isRunning = false;
serverSocket.close();
}
private List<OwserverPacket> processPacket(OwserverPacket inputPacket) {
List<OwserverPacket> returnPackets = new ArrayList<>();
OwserverPacket returnPacket = new OwserverPacket(OwserverPacketType.RETURN);
switch (inputPacket.getMessageType()) {
case NOP:
returnPacket.setPayload("");
returnPackets.add(returnPacket);
break;
case DIRALL:
returnPacket.setPayload("/00.0123456789ab,/00.0123456789ac,/00.0123456789ad,/statistics");
returnPackets.add(returnPacket);
returnPacket = new OwserverPacket(OwserverPacketType.RETURN);
break;
case PRESENT:
switch (inputPacket.getPayloadString()) {
case "present":
break;
default:
returnPacket.setReturnCode(-1);
}
returnPacket.setPayload(inputPacket.getPayloadString());
returnPackets.add(returnPacket);
break;
case READ:
switch (inputPacket.getPayloadString()) {
case "testsensor/pages/page.ALL":
OwPageBuffer pageBuffer = new OwPageBuffer(8);
pageBuffer.setByte(5, 7, (byte) 31);
returnPacket.setPayload(pageBuffer);
returnPackets.add(returnPacket);
break;
case "testsensor/decimal":
returnPacket.setPayload(" 17.4");
returnPackets.add(returnPacket);
break;
case "testsensor/decimalarray":
returnPacket.setPayload(" 3834, 0");
returnPackets.add(returnPacket);
break;
default:
}
break;
case WRITE:
returnPackets.add(returnPacket);
break;
default:
}
return returnPackets;
}
}