Mooved Tellstick plugin to plugin folder and added all targets to build xml

This commit is contained in:
Ziver Koc 2017-05-29 17:02:31 +02:00
parent efdbc56859
commit da290e570f
25 changed files with 155 additions and 11 deletions

96
plugins/tellstick/build.xml Executable file
View file

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="Tellstick Plugin" >
<!-- ________________________ PROPERTIES AND SETTINGS ________________________ -->
<!--common properties-->
<property name="root" value="." />
<property name="srcDir" value="${root}/src" />
<property name="testDir" value="${root}/test" />
<property name="libDir" value="${root}/lib" />
<property name="buildRoot" value="${root}/build" />
<property name="compileDir" value="${buildRoot}/production" />
<property name="compileTestDir" value="${buildRoot}/test" />
<property name="releaseDir" value="${buildRoot}/release" />
<property name="releaseJar" value="tellstick.jar" />
<property name="reportsDir" value="../../${buildRoot}/reports" /> <!-- Use Hal reports folder -->
<!-- ________________________ CLASSPATH ________________________ -->
<!--define standard arguments for javac-->
<presetdef name="javac">
<javac includeantruntime="false" />
</presetdef>
<!--classpath included when building-->
<path id="classpath.build">
<fileset dir="${libDir}">
<include name="**/*.jar"/>
</fileset>
<pathelement location="${compileDir}" />
<!-- Hal core -->
<pathelement location="../../build/production" />
<fileset dir="../../lib">
<include name="**/*.jar"/>
</fileset>
</path>
<path id="classpath.test">
<pathelement location="${compileTestDir}" />
<pathelement location="../../${compileTestDir}" />
<!--include libraries used for building-->
<path refid="classpath.build"/>
</path>
<!-- ________________________ EXECUTION TARGETS ________________________ -->
<target name="test" depends="build-test">
<mkdir dir="${reportsDir}" />
<junit printsummary="yes" haltonfailure="false" fork="true">
<classpath refid="classpath.test" />
<formatter type="plain" usefile="false" /> <!-- to screen -->
<formatter type="xml" /> <!-- to file -->
<batchtest todir="${reportsDir}" skipNonTests="true">
<fileset dir="${compileTestDir}" includes="**/*Test*.class" />
</batchtest>
</junit>
</target>
<!-- ________________________ BUILD TARGETS ________________________ -->
<!--clean all build paths-->
<target name="clean">
<delete includeemptydirs="true" failonerror="false">
<fileset dir="${buildRoot}" includes="**/*"/>
</delete>
</target>
<!--build product code-->
<target name="build">
<mkdir dir="${compileDir}" />
<javac srcdir="${srcDir}" destdir="${compileDir}" debug="yes" debugLevel="lines,vars,source" fork="yes">
<classpath refid="classpath.build" />
<include name="**/*.java" />
</javac>
<copy todir="${compileDir}">
<fileset dir="${srcDir}"
excludes="**/*.java" />
</copy>
</target>
<target name="build-test" depends="build">
<mkdir dir="${compileTestDir}" />
<javac srcdir="${testDir}" destdir="${compileTestDir}" debug="yes" debugLevel="lines,vars,source" fork="yes">
<classpath refid="classpath.test" />
<include name="**/*.java" />
</javac>
</target>
<target name="package" depends="build">
<jar destfile="${releaseDir}/${releaseJar}" basedir="${compileDir}" />
</target>
</project>

View file

@ -0,0 +1,13 @@
package se.hal.plugin.tellstick;
/**
* This interface represents a device configuration and links it to a protocol.
*
* Created by Ziver on 2016-08-18.
*/
public interface TellstickDevice {
String getProtocolName(); // TODO: could be implemented in a better way
String getModelName(); // TODO: could be implemented in a better way
}

View file

@ -0,0 +1,38 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Ziver Koc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package se.hal.plugin.tellstick;
/**
* Indicates that the implementing class is a protocol that can have group events.
* More specifically that on transmission will affect multiple devices.
*/
public interface TellstickDeviceGroup {
/**
* Protocols should extend this method if it has group functionality.
* @return true if this object and the input object belongs to the same group.
*/
public boolean equalsGroup(TellstickDeviceGroup obj);
}

View file

@ -0,0 +1,133 @@
/*
* Copyright (c) 2015 Ziver
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package se.hal.plugin.tellstick;
import se.hal.plugin.tellstick.TellstickProtocol.TellstickDecodedEntry;
import se.hal.plugin.tellstick.protocol.NexaSelfLearningProtocol;
import se.hal.plugin.tellstick.protocol.Oregon0x1A2DProtocol;
import zutil.converter.Converter;
import zutil.log.LogUtil;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Created by Ziver on 2015-02-18.
*
* Protocol Specification: http://developer.telldus.com/doxygen/TellStick.html
*/
public class TellstickParser {
private static final Logger logger = LogUtil.getLogger();
private static HashMap<String, TellstickProtocol> protocolMap;
static {
registerProtocol(NexaSelfLearningProtocol.class);
registerProtocol(Oregon0x1A2DProtocol.class);
}
private int firmwareVersion = -1;
/**
* Will decode the given data and return a list with device and data objects
* @param data
* @return a list with decoded objects or empty list if there was an error
*/
public List<TellstickDecodedEntry> decode(String data) {
if (data.startsWith("+W")) {
data = data.substring(2);
HashMap<String, String> map = new HashMap<String, String>();
String[] parameters = data.split(";");
for (String parameter : parameters) {
String[] keyValue = parameter.split(":");
map.put(keyValue[0], keyValue[1]);
}
TellstickProtocol protocol =
getProtocolInstance(map.get("protocol"), map.get("model"));
if (protocol != null) {
String binData = map.get("data");
logger.finest("Decoding: " + protocol);
List<TellstickDecodedEntry> list = protocol.decode(Converter.hexToByte(binData));
if (list == null)
list = Collections.EMPTY_LIST;
return list;
} else {
logger.warning("Unknown protocol: " + data);
}
} else if (data.startsWith("+S") || data.startsWith("+T")) {
// This is confirmation of send commands
synchronized (this) {
this.notifyAll();
}
} else if (data.startsWith("+V")) {
if (data.length() > 2)
firmwareVersion = Integer.parseInt(data.substring(2));
}else {
logger.severe("Unknown prefix: " + data);
}
return Collections.EMPTY_LIST;
}
/**
* This method blocks until a send command confirmation is received.
*/
public synchronized void waitSendConformation(){
try {
this.wait();
} catch (InterruptedException e) {
logger.log(Level.SEVERE, null, e);
}
}
public int getFirmwareVersion() {
return firmwareVersion;
}
public static void registerProtocol(Class<? extends TellstickProtocol> protClass) {
try {
registerProtocol( protClass.newInstance() );
} catch (Exception e) {
logger.log(Level.SEVERE, null, e);
}
}
public static void registerProtocol(TellstickProtocol protObj) {
if (protocolMap == null)
protocolMap = new HashMap<>();
protocolMap.put(
protObj.getProtocolName() + "-" + protObj.getModelName(),
protObj);
}
public static TellstickProtocol getProtocolInstance(String protocol, String model) {
return protocolMap.get(protocol + "-" + model);
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2015 Ziver
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package se.hal.plugin.tellstick;
import se.hal.intf.HalDeviceData;
import se.hal.intf.HalEventConfig;
import se.hal.intf.HalEventData;
import se.hal.plugin.tellstick.cmd.TellstickCmd;
import java.util.List;
/**
* Created by Ziver on 2015-02-18.
*/
public abstract class TellstickProtocol {
private final String protocol;
private final String model;
public TellstickProtocol(String protocol, String model){
this.protocol = protocol;
this.model = model;
}
public String getProtocolName(){
return protocol;
}
public String getModelName(){
return model;
}
public TellstickCmd encode(HalEventConfig deviceConfig, HalEventData deviceData){ return null; }
public abstract List<TellstickDecodedEntry> decode(byte[] data);
public static class TellstickDecodedEntry {
private TellstickDevice device;
private HalDeviceData data;
public TellstickDecodedEntry(TellstickDevice device, HalDeviceData data){
this.device = device;
this.data = data;
}
public TellstickDevice getDevice(){
return device;
}
public HalDeviceData getData(){
return data;
}
}
}

View file

@ -0,0 +1,270 @@
/*
* Copyright (c) 2015 Ziver
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package se.hal.plugin.tellstick;
import com.fazecast.jSerialComm.SerialPort;
import se.hal.HalContext;
import se.hal.intf.*;
import se.hal.plugin.tellstick.TellstickProtocol.TellstickDecodedEntry;
import se.hal.plugin.tellstick.cmd.TellstickCmd;
import zutil.log.LogUtil;
import zutil.struct.TimedHashSet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Tellstick serial port controller, this class handles all tellstick
* communication and reporting to Hal
*/
public class TellstickSerialComm implements Runnable,
HalSensorController, HalEventController, HalAutoScannableController {
private static final long TRANSMISSION_UNIQUENESS_TTL = 1000; // milliseconds
private static final Logger logger = LogUtil.getLogger();
private static TellstickSerialComm instance; // Todo: Don't like this but it is the best I could come up with
private SerialPort serial;
private InputStream in;
private OutputStream out;
private TimedHashSet<String> set; // To check for duplicate transmissions
protected TellstickParser parser;
private HalSensorReportListener sensorListener;
private HalEventReportListener eventListener;
private List<TellstickDevice> registeredDevices;
public TellstickSerialComm() {
set = new TimedHashSet<>(TRANSMISSION_UNIQUENESS_TTL);
parser = new TellstickParser();
registeredDevices = Collections.synchronizedList(new ArrayList<TellstickDevice>());
}
@Override
public boolean isAvailable() {
return HalContext.getStringProperty("tellstick.com_port") != null;
}
@Override
public void initialize() throws Exception {
// Read properties
String port = HalContext.getStringProperty("tellstick.com_port");
if (port == null)
port = "COM1"; // defaults
initialize(port);
}
public void initialize(String portName) throws Exception {
if (instance != null)
throw new IllegalStateException("There is a previous TellstickSerialComm instance, only one allowed");
instance = this;
logger.info("Connecting to com port... ("+ portName +")");
serial = SerialPort.getCommPort(portName);
serial.setBaudRate(9600);
if(!serial.openPort())
throw new IOException("Could not open port: "+portName);
serial.setComPortTimeouts(
SerialPort.TIMEOUT_READ_BLOCKING, 0, 0);
in = serial.getInputStream();
out = serial.getOutputStream();
Executors.newSingleThreadExecutor().execute(this);
}
public void close() {
if(serial != null) {
try {
serial.closePort();
in.close();
out.close();
} catch (IOException e) {
logger.log(Level.SEVERE, null, e);
}
}
serial = null;
in = null;
out = null;
instance = null;
}
public void run() {
try {
String data;
while (in != null && (data = readLine()) != null) {
handleLine(data);
}
} catch (IOException e) {
logger.log(Level.SEVERE, null, e);
}
}
/**
* There seems to be an issue with read(...) methods, only read() is working
*/
private String readLine() throws IOException {
StringBuilder str = new StringBuilder(50);
int c;
while((c = in.read()) >= 0){
switch(c) {
case -1:
return null;
case '\n':
case '\r':
if(str.length() > 0)
return str.toString();
break;
default:
str.append((char)c);
}
}
return str.toString();
}
protected void handleLine(String data){
List<TellstickDecodedEntry> decodeList = parser.decode(data);
for (TellstickDecodedEntry entry : decodeList) {
if (entry.getData().getTimestamp() < 0)
entry.getData().setTimestamp(System.currentTimeMillis());
boolean registered = registeredDevices.contains(entry.getDevice());
if (registered && !set.contains(data) || // check for duplicates transmissions of registered devices
!registered && set.contains(data)) { // required duplicate transmissions before reporting unregistered devices
//Check for registered device that are in the same group
if (entry.getDevice() instanceof TellstickDeviceGroup) {
TellstickDeviceGroup groupProtocol = (TellstickDeviceGroup) entry.getDevice();
for (int i = 0; i < registeredDevices.size(); ++i) { // Don't use foreach for concurrency reasons
TellstickDevice childDevice = registeredDevices.get(i);
if (childDevice instanceof TellstickDeviceGroup &&
groupProtocol.equalsGroup((TellstickDeviceGroup)childDevice) &&
!entry.getDevice().equals(childDevice)) {
reportEvent(childDevice, entry.getData());
}
}
}
// Report source event
reportEvent(entry.getDevice(), entry.getData());
}
}
set.add(data);
}
private void reportEvent(TellstickDevice tellstickDevice, HalDeviceData deviceData){
if (sensorListener != null && tellstickDevice instanceof HalSensorConfig)
sensorListener.reportReceived((HalSensorConfig) tellstickDevice, (HalSensorData) deviceData);
else if (eventListener != null && tellstickDevice instanceof HalEventConfig)
eventListener.reportReceived((HalEventConfig) tellstickDevice, (HalEventData) deviceData);
}
@Override
public void send(HalEventConfig deviceConfig, HalEventData deviceData) {
if(deviceConfig instanceof TellstickDevice) {
TellstickDevice tellstickDevice = (TellstickDevice) deviceConfig;
TellstickProtocol prot = TellstickParser.getProtocolInstance(
tellstickDevice.getProtocolName(),
tellstickDevice.getModelName());
TellstickCmd cmd = prot.encode(deviceConfig, deviceData);
if (cmd != null)
write(cmd.getTransmissionString());
parser.waitSendConformation();
deviceData.setTimestamp(System.currentTimeMillis());
}
}
private void write(String data) {
if (data == null)
return;
try {
for(int i=0; i<data.length();i++)
out.write(0xFF & data.charAt(i));
out.write('\n');
out.flush();
} catch (IOException e) {
logger.log(Level.SEVERE, null, e);
}
}
@Override
public void register(HalEventConfig event) {
if(event instanceof TellstickDevice)
registeredDevices.add((TellstickDevice) event);
else throw new IllegalArgumentException(
"Device config is not an instance of "+TellstickDevice.class+": "+event.getClass());
}
@Override
public void register(HalSensorConfig sensor) {
if(sensor instanceof TellstickDevice)
registeredDevices.add((TellstickDevice) sensor);
else throw new IllegalArgumentException(
"Device config is not an instance of "+TellstickDevice.class+": "+sensor.getClass());
}
public <T> List<T> getRegisteredDevices(Class<T> clazz){
ArrayList<T> list = new ArrayList<>();
for (TellstickDevice device : registeredDevices){
if (clazz.isAssignableFrom(device.getClass()))
list.add((T) device);
}
return list;
}
@Override
public void deregister(HalEventConfig event) {
registeredDevices.remove(event);
}
@Override
public void deregister(HalSensorConfig sensor) {
registeredDevices.remove(sensor);
}
@Override
public int size() {
return registeredDevices.size();
}
@Override
public void setListener(HalEventReportListener listener) {
eventListener = listener;
}
@Override
public void setListener(HalSensorReportListener listener) {
sensorListener = listener;
}
public static TellstickSerialComm getInstance(){
return instance;
}
}

View file

@ -0,0 +1,10 @@
package se.hal.plugin.tellstick.cmd;
/**
* Created by Ziver on 2016-08-29.
*/
public interface TellstickCmd {
String getTransmissionString();
}

View file

@ -0,0 +1,92 @@
package se.hal.plugin.tellstick.cmd;
import java.nio.charset.StandardCharsets;
/**
* Created by Ziver on 2016-08-29.
*/
public class TellstickCmdExtendedSend implements TellstickCmd{
private static int OFFSET_TIMINGS = 1;
private static int OFFSET_PULSE_LENGTH = 5;
private static int OFFSET_PULSES = 6;
private byte[] cmd = new byte[79];
private int length = 0;
/**
* @param timing set first timing in us
* @return an instance of itself
*/
public TellstickCmdExtendedSend setPulls0Timing(int timing){
setPullsTiming(0, timing); return this;
}
/**
* @param timing set second timing in us
* @return an instance of itself
*/
public TellstickCmdExtendedSend setPulls1Timing(int timing){
setPullsTiming(1, timing); return this;
}
/**
* @param timing set third timing in us
* @return an instance of itself
*/
public TellstickCmdExtendedSend setPulls2Timing(int timing){
setPullsTiming(2, timing); return this;
}
/**
* @param timing set fourth timing in us
* @return an instance of itself
*/
public TellstickCmdExtendedSend setPulls3Timing(int timing){
setPullsTiming(3, timing); return this;
}
/**
* @param i an index from 0 to 3
* @param timing set first pulls length in us between 0-2550
* @return an instance of itself
*/
private void setPullsTiming(int i, int timing){ // TODO: should probably have high and low timing to be called pulls
if (0 > timing || timing > 2550)
throw new IllegalArgumentException("Invalid pulls "+timing+" must be between 0-2550" );
cmd[OFFSET_TIMINGS + i] = (byte)(timing/10);
}
public TellstickCmdExtendedSend addPulls0(){
addPulls(0); return this;
}
public TellstickCmdExtendedSend addPulls1(){
addPulls(1); return this;
}
public TellstickCmdExtendedSend addPulls2(){
addPulls(2); return this;
}
public TellstickCmdExtendedSend addPulls3(){
addPulls(3); return this;
}
private void addPulls(int i) {
if (OFFSET_PULSES+(length/4) > cmd.length)
throw new IndexOutOfBoundsException("Maximum length "+cmd.length+" reached");
switch (length % 4){
case 0:
cmd[OFFSET_PULSES+ length/4] |= 0b1100_0000 & (i << 6); break;
case 1:
cmd[OFFSET_PULSES+ length/4] |= 0b0011_0000 & (i << 4); break;
case 2:
cmd[OFFSET_PULSES+ length/4] |= 0b0000_1100 & (i << 2); break;
case 3:
cmd[OFFSET_PULSES+ length/4] |= 0b0000_0011 & (i); break;
}
length++;
}
public String getTransmissionString(){
cmd[0] = 'T';
cmd[OFFSET_PULSE_LENGTH] = (byte)length;
cmd[OFFSET_PULSES+(int)Math.ceil(length/4.0)] = '+';
return new String(cmd, 0, OFFSET_PULSES+(int)Math.ceil(length/4.0)+1, StandardCharsets.ISO_8859_1);
}
}

View file

@ -0,0 +1,35 @@
package se.hal.plugin.tellstick.cmd;
import java.nio.charset.StandardCharsets;
/**
* Created by Ziver on 2016-08-29.
*/
public class TellstickCmdSend implements TellstickCmd{
private static int OFFSET_PULSES = 1;
private byte[] cmd = new byte[79];
private int length = 0;
/**
* @param timing adds a pulls timing in us between 0-2550
* @return an instance of itself
*/
private void addPulls(int timing) { // TODO: should probably have high and low timing to be called pulls
if (OFFSET_PULSES+length > cmd.length)
throw new IndexOutOfBoundsException("Maximum length "+cmd.length+" reached");
if (0 > timing || timing > 2550)
throw new IllegalArgumentException("Invalid pulls "+timing+" must be between 0-2550" );
cmd[OFFSET_PULSES+length] = (byte)(timing/10);
length++;
}
public String getTransmissionString(){
cmd[0] = 'S';
cmd[OFFSET_PULSES+length] = '+';
return new String(cmd, 0, OFFSET_PULSES+length+1, StandardCharsets.ISO_8859_1);
}
}

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2015 Ziver
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package se.hal.plugin.tellstick.device;
import se.hal.intf.HalEventConfig;
import se.hal.intf.HalEventController;
import se.hal.intf.HalEventData;
import se.hal.intf.HalSensorData;
import se.hal.plugin.tellstick.TellstickDevice;
import se.hal.plugin.tellstick.TellstickDeviceGroup;
import se.hal.plugin.tellstick.TellstickSerialComm;
import se.hal.plugin.tellstick.protocol.NexaSelfLearningProtocol;
import se.hal.struct.devicedata.SwitchEventData;
import se.hal.struct.devicedata.TemperatureSensorData;
import zutil.parser.binary.BinaryStruct;
import zutil.ui.Configurator;
/**
* Created by Ziver on 2015-02-18.
*/
public class NexaSelfLearning implements HalEventConfig,TellstickDevice,TellstickDeviceGroup {
@Configurator.Configurable("House code")
private int house = 0;
@Configurator.Configurable("Group code")
private boolean group = false;
@Configurator.Configurable("Unit code")
private int unit = 0;
public NexaSelfLearning() { }
public NexaSelfLearning(int house, boolean group, int unit) {
this.house = house;
this.group = group;
this.unit = unit;
}
public int getHouse() {
return house;
}
public void setHouse(int house) {
this.house = house;
}
public boolean getGroup() {
return group;
}
public void setGroup(boolean group) {
this.group = group;
}
public int getUnit() {
return unit;
}
public void setUnit(int unit) {
this.unit = unit;
}
public String toString(){
return "house:"+house+
", group:"+group+
", unit:"+unit;
}
@Override
public boolean equals(Object obj){
if(obj instanceof NexaSelfLearning)
return ((NexaSelfLearning) obj).house == house &&
((NexaSelfLearning) obj).group == group &&
((NexaSelfLearning)obj).unit == unit;
return false;
}
@Override
public boolean equalsGroup(TellstickDeviceGroup obj) {
if(obj instanceof NexaSelfLearning)
return ((NexaSelfLearning) obj).house == house &&
(((NexaSelfLearning) obj).group || group );
return false;
}
@Override
public Class<? extends HalEventController> getEventControllerClass() {
return TellstickSerialComm.class;
}
@Override
public Class<? extends HalEventData> getEventDataClass() {
return SwitchEventData.class;
}
@Override
public String getProtocolName() { return NexaSelfLearningProtocol.PROTOCOL; }
@Override
public String getModelName() { return NexaSelfLearningProtocol.MODEL; }
}

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2015 Ziver
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package se.hal.plugin.tellstick.device;
import se.hal.intf.HalEventConfig;
import se.hal.intf.HalEventController;
import se.hal.intf.HalEventData;
import se.hal.plugin.tellstick.TellstickDevice;
import se.hal.plugin.tellstick.TellstickDeviceGroup;
import se.hal.plugin.tellstick.TellstickSerialComm;
import se.hal.plugin.tellstick.protocol.NexaSelfLearningProtocol;
import se.hal.struct.devicedata.DimmerEventData;
import se.hal.struct.devicedata.SwitchEventData;
import zutil.ui.Configurator;
/**
* Created by Ziver on 2015-02-18.
*/
public class NexaSelfLearningDimmer implements HalEventConfig,TellstickDevice {
@Configurator.Configurable("House code")
private int house = 0;
@Configurator.Configurable("Unit code")
private int unit = 0;
public NexaSelfLearningDimmer() { }
public NexaSelfLearningDimmer(int house, int unit) {
this.house = house;
this.unit = unit;
}
public int getHouse() {
return house;
}
public void setHouse(int house) {
this.house = house;
}
public int getUnit() {
return unit;
}
public void setUnit(int unit) {
this.unit = unit;
}
public String toString(){
return "house:"+house+
", unit:"+unit;
}
@Override
public boolean equals(Object obj){
if(obj instanceof NexaSelfLearningDimmer)
return ((NexaSelfLearningDimmer) obj).house == house &&
((NexaSelfLearningDimmer)obj).unit == unit;
return false;
}
@Override
public Class<? extends HalEventController> getEventControllerClass() {
return TellstickSerialComm.class;
}
@Override
public Class<? extends HalEventData> getEventDataClass() {
return DimmerEventData.class;
}
@Override
public String getProtocolName() { return NexaSelfLearningProtocol.PROTOCOL; }
@Override
public String getModelName() { return NexaSelfLearningProtocol.MODEL; }
}

View file

@ -0,0 +1,101 @@
package se.hal.plugin.tellstick.device;
import se.hal.intf.HalSensorConfig;
import se.hal.intf.HalSensorController;
import se.hal.intf.HalSensorData;
import se.hal.plugin.tellstick.TellstickDevice;
import se.hal.plugin.tellstick.TellstickSerialComm;
import se.hal.plugin.tellstick.protocol.Oregon0x1A2DProtocol;
import se.hal.struct.devicedata.HumiditySensorData;
import se.hal.struct.devicedata.LightSensorData;
import se.hal.struct.devicedata.PowerConsumptionSensorData;
import se.hal.struct.devicedata.TemperatureSensorData;
import zutil.log.LogUtil;
import zutil.ui.Configurator;
import java.util.logging.Logger;
/**
* Created by Ziver on 2015-11-19.
*/
public class Oregon0x1A2D implements HalSensorConfig,TellstickDevice {
private static final Logger logger = LogUtil.getLogger();
public enum OregonSensorType{
HUMIDITY,LIGHT,POWER,TEMPERATURE
}
@Configurator.Configurable("Address")
private int address = 0;
@Configurator.Configurable("Report Interval(ms)")
private int interval = 60*1000; // default 1 min
@Configurator.Configurable("Sensor Type")
private OregonSensorType sensorType;
public Oregon0x1A2D() { }
public Oregon0x1A2D(int address, OregonSensorType sensorType) {
this.address = address;
this.sensorType = sensorType;
}
public int getAddress() {
return address;
}
@Override
public long getDataInterval() {
return interval;
}
public OregonSensorType getSensorType() {
return sensorType;
}
@Override
public boolean equals(Object obj){
if(! (obj instanceof Oregon0x1A2D))
return false;
return ((Oregon0x1A2D)obj).address == this.address &&
((Oregon0x1A2D)obj).sensorType == this.sensorType;
}
public String toString(){
return "address:"+address+",sensorType:"+ sensorType;
}
@Override
public AggregationMethod getAggregationMethod() {
if (sensorType == OregonSensorType.POWER)
return AggregationMethod.SUM;
return AggregationMethod.AVERAGE;
}
@Override
public Class<? extends HalSensorController> getSensorControllerClass() {
return TellstickSerialComm.class;
}
@Override
public Class<? extends HalSensorData> getSensorDataClass() {
if (sensorType != null) {
switch (sensorType) {
case HUMIDITY:
return HumiditySensorData.class;
case LIGHT:
return LightSensorData.class;
case POWER:
return PowerConsumptionSensorData.class;
case TEMPERATURE:
return TemperatureSensorData.class;
}
}
return TemperatureSensorData.class;
}
@Override
public String getProtocolName() { return Oregon0x1A2DProtocol.PROTOCOL; }
@Override
public String getModelName() { return Oregon0x1A2DProtocol.MODEL; }
}

View file

@ -0,0 +1,10 @@
{
"version": 1.0,
"name": "Tellstick",
"interfaces": [
{"se.hal.intf.HalAutoScannableController": "se.hal.plugin.tellstick.TellstickSerialComm"},
{"se.hal.intf.HalSensorConfig": "se.hal.plugin.tellstick.device.Oregon0x1A2D"},
{"se.hal.intf.HalEventConfig": "se.hal.plugin.tellstick.device.NexaSelfLearning"}
]
}

View file

@ -0,0 +1,173 @@
/*
* Copyright (c) 2015 Ziver
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package se.hal.plugin.tellstick.protocol;
import se.hal.intf.HalEventConfig;
import se.hal.intf.HalEventData;
import se.hal.plugin.tellstick.TellstickSerialComm;
import se.hal.plugin.tellstick.cmd.TellstickCmd;
import se.hal.plugin.tellstick.cmd.TellstickCmdExtendedSend;
import se.hal.plugin.tellstick.TellstickProtocol;
import se.hal.plugin.tellstick.device.NexaSelfLearning;
import se.hal.plugin.tellstick.device.NexaSelfLearningDimmer;
import se.hal.struct.devicedata.DimmerEventData;
import se.hal.struct.devicedata.SwitchEventData;
import zutil.ByteUtil;
import zutil.log.LogUtil;
import zutil.parser.binary.BinaryStruct;
import zutil.parser.binary.BinaryStructInputStream;
import zutil.parser.binary.BinaryStructOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Created by Ziver on 2015-02-18.
* @see <a href="https://github.com/telldus/telldus/blob/master/telldus-core/service/ProtocolNexa.cpp">ProtocolNexa.cpp Tellstick Reference</a>
*/
public class NexaSelfLearningProtocol extends TellstickProtocol {
private static final Logger logger = LogUtil.getLogger();
public static final String PROTOCOL = "arctech";
public static final String MODEL = "selflearning";
private static class NexaSLTransmissionStruct implements BinaryStruct{
@BinaryField(index=10, length=26)
int house = 0;
@BinaryField(index=20, length=1)
boolean group = false;
@BinaryField(index = 30, length = 1)
boolean enable = false;
@BinaryField(index=40, length=4)
int unit = 0;
}
private static class NexaSLTransmissionDimmerStruct extends NexaSLTransmissionStruct {
@BinaryField(index = 50, length = 4)
int dimLevel = 0;
}
public NexaSelfLearningProtocol() {
super(PROTOCOL, MODEL);
}
@Override
public TellstickCmd encode(HalEventConfig deviceConfig, HalEventData deviceData){
if ( ! (deviceConfig instanceof NexaSelfLearning || deviceConfig instanceof NexaSelfLearningDimmer)){
logger.severe("Device config is not instance of NexaSelfLearning: "+deviceConfig.getClass());
return null;
}
if ( ! (deviceData instanceof SwitchEventData || deviceData instanceof DimmerEventData)){
logger.severe("Device data is not an instance of SwitchEventData or DimmerEventData: "+deviceData.getClass());
return null;
}
// Create transmission struct
NexaSLTransmissionStruct struct;
if (deviceData instanceof DimmerEventData) {
struct = new NexaSLTransmissionDimmerStruct();
struct.house = ((NexaSelfLearningDimmer) deviceConfig).getHouse();
struct.unit = ((NexaSelfLearningDimmer) deviceConfig).getUnit();
((NexaSLTransmissionDimmerStruct)struct).dimLevel = (int)(deviceData.getData()*16);
}
else {
struct = new NexaSLTransmissionStruct();
struct.house = ((NexaSelfLearning) deviceConfig).getHouse();
struct.group = ((NexaSelfLearning) deviceConfig).getGroup();
struct.unit = ((NexaSelfLearning) deviceConfig).getUnit();
struct.enable = ((SwitchEventData) deviceData).isOn();
}
// Generate transmission string
try {
TellstickCmdExtendedSend cmd = new TellstickCmdExtendedSend();
cmd.setPulls0Timing(1270);
cmd.setPulls1Timing(2550);
cmd.setPulls2Timing(240);
cmd.addPulls2().addPulls1(); // preamble
byte[] data = BinaryStructOutputStream.serialize(struct);
for (byte b : data){
for (int i=7; i>=0; --i){
if (ByteUtil.getBits(b, i, 1) == 0) // 0
cmd.addPulls2().addPulls2().addPulls2().addPulls0(); // 0b1010_1000
else // 1
cmd.addPulls2().addPulls0().addPulls2().addPulls2(); // 0b1000_1010
}
}
cmd.addPulls2().addPulls2(); // postemble?
return cmd;
} catch (IOException e) {
logger.log(Level.SEVERE, null, e);
}
return null;
}
@Override
public List<TellstickDecodedEntry> decode(byte[] data){
// Data positions
// house = 0xFFFFFFC0
// group = 0x00000020
// method = 0x00000010
// unit = 0x0000000F
// ----------------h------------ g m --u-
// 0x2CE81990 - 00101100_11101000_00011001_10 0 1 0000 - ON
// 0x2CE81980 - 00101100_11101000_00011001_10 0 0 0000 - OFF
NexaSLTransmissionStruct struct = new NexaSLTransmissionStruct();
BinaryStructInputStream.read(struct, data);
ArrayList<TellstickDecodedEntry> list = new ArrayList<>();
/* for (NexaSelfLearningDimmer device : TellstickSerialComm.getInstance().getRegisteredDevices(NexaSelfLearningDimmer.class)) {
if (device.getHouse() == struct.house && device.getUnit() == struct.unit){
NexaSLTransmissionDimmerStruct dimmerStruct = new NexaSLTransmissionDimmerStruct();
BinaryStructInputStream.read(dimmerStruct, data);
list.add(new TellstickDecodedEntry(
new NexaSelfLearningDimmer(dimmerStruct.house, dimmerStruct.unit),
new DimmerEventData(dimmerStruct.dimLevel)
));
}
}
if (list.isEmpty()) { // No dimmer stored, we assume it is a switch event then...
*/
list.add(new TellstickDecodedEntry(
new NexaSelfLearning(struct.house, struct.group, struct.unit),
new SwitchEventData(struct.enable, System.currentTimeMillis())
));
/* }*/
return list;
}
}

View file

@ -0,0 +1,121 @@
package se.hal.plugin.tellstick.protocol;
import se.hal.intf.HalSensorData;
import se.hal.plugin.tellstick.TellstickProtocol;
import se.hal.plugin.tellstick.TellstickSerialComm;
import se.hal.plugin.tellstick.device.Oregon0x1A2D;
import se.hal.plugin.tellstick.device.Oregon0x1A2D.OregonSensorType;
import se.hal.struct.devicedata.HumiditySensorData;
import se.hal.struct.devicedata.LightSensorData;
import se.hal.struct.devicedata.PowerConsumptionSensorData;
import se.hal.struct.devicedata.TemperatureSensorData;
import zutil.converter.Converter;
import zutil.log.LogUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
/**
* Created by Ziver on 2015-11-19.
* @see <a href="https://github.com/telldus/telldus/blob/master/telldus-core/service/ProtocolOregon.cpp">ProtocolOregon.cpp Tellstick Reference</a>
*/
public class Oregon0x1A2DProtocol extends TellstickProtocol {
private static final Logger logger = LogUtil.getLogger();
public static final String PROTOCOL = "oregon";
public static final String MODEL = "0x1A2D";
public Oregon0x1A2DProtocol(){
super(PROTOCOL, MODEL);
}
@Override
public List<TellstickDecodedEntry> decode(byte[] data) {
/*
Nibble(s) Details
0..3 Sensor ID This 16-bit value is unique to each sensor, or sometimes a group of sensors.
4 Channel Some sensors use the coding 1 << (ch 1), where ch is 1, 2 or 3.
5..6 Rolling Code Value changes randomly every time the sensor is reset
7 Flags 1 Bit value 0x4 is the battery low flag
8..[n-5] Sensor-specific Data Usually in BCD format
[n-3]..[n-4] Checksum The 8-bit sum of nibbles 0..[n-5]
*/
//Example: class:sensor;protocol:oregon;model:0x1A2D;data:20BA000000002700;
// int channel = (data[0] >> 4) & 0x7; // channel not used
int address = data[1] & 0xFF;
int temp3 = (data[2] >> 4) & 0xF;
int temp1 = (data[3] >> 4) & 0xF;
int temp2 = data[3] & 0xF;
int hum2 = (data[4] >> 4) & 0xF;
boolean negative = (data[4] & (1 << 3)) > 0;
int hum1 = data[5] & 0xF;
int checksum = data[6];
int calcChecksum = 0;
for (int i = 0; i < 6; i++) {
calcChecksum += ((data[i] >> 4) & 0xF) + (data[i] & 0xF);
}
calcChecksum += 0x1 + 0xA + 0x2 + 0xD - 0xA;
if (calcChecksum != checksum) {
logger.fine("Checksum failed, address: "+address+", data: "+ Converter.toHexString(data));
return null;
}
double temperature = ((temp1 * 100) + (temp2 * 10) + temp3)/10.0;
if (negative)
temperature = -temperature;
double humidity = (hum1 * 10.0) + hum2;
// Create return objects
long timestamp = System.currentTimeMillis();
boolean humidityFound=false, temperatureFound=false;
ArrayList<TellstickDecodedEntry> list = new ArrayList<>();
for (Oregon0x1A2D device : TellstickSerialComm.getInstance().getRegisteredDevices(Oregon0x1A2D.class)) {
if (device.getAddress() != address)
continue;
HalSensorData dataObj;
OregonSensorType sensorType = device.getSensorType();
if (sensorType == null)
sensorType = OregonSensorType.POWER;
switch (sensorType){
case HUMIDITY:
dataObj = new HumiditySensorData(humidity, timestamp);
humidityFound = true;
break;
case LIGHT:
dataObj = new LightSensorData(temperature, timestamp);
temperatureFound = true;
break;
case TEMPERATURE:
dataObj = new TemperatureSensorData(temperature, timestamp);
temperatureFound = true;
break;
default:
case POWER:
dataObj = new PowerConsumptionSensorData(temperature, timestamp);
temperatureFound = true;
break;
}
list.add(new TellstickDecodedEntry(device, dataObj));
}
// Add new sensors if we did not find a registered one
if (!temperatureFound)
list.add(new TellstickDecodedEntry(
new Oregon0x1A2D(address, OregonSensorType.TEMPERATURE),
new TemperatureSensorData(temperature, timestamp)));
if (!humidityFound && humidity>0.0)
list.add(new TellstickDecodedEntry(
new Oregon0x1A2D(address, OregonSensorType.HUMIDITY),
new HumiditySensorData(humidity, timestamp)));
return list;
}
}

View file

@ -0,0 +1,115 @@
package se.hal.plugin.tellstick;
import org.junit.Before;
import org.junit.Test;
import se.hal.intf.*;
import se.hal.struct.devicedata.DimmerEventData;
import se.hal.struct.devicedata.SwitchEventData;
import se.hal.struct.devicedata.TemperatureSensorData;
import zutil.converter.Converter;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
/**
* Created by Ziver on 2015-11-19.
*/
public class TelstickSerialCommEventTest {
@Before
public void init(){
TellstickParser.registerProtocol(TestEvent.class);
}
//############# Non crashing TC
@Test
public void startup(){
TellstickSerialComm tellstick = new TellstickSerialComm();
tellstick.handleLine("+V2");
}
@Test
public void unregisteredListener(){
TellstickSerialComm tellstick = new TellstickSerialComm();
tellstick.handleLine("+Wclass:sensor;protocol:test-prot;model:test-model;data:1234;");
}
//############ Normal TCs
@Test
public void receiveUnregisteredEvent(){
// Setup
TellstickSerialComm tellstick = new TellstickSerialComm();
final ArrayList<HalEventConfig> list = new ArrayList<>();
tellstick.setListener(new HalEventReportListener() {
@Override
public void reportReceived(HalEventConfig e, HalEventData d) {
list.add(e);
}
});
// Execution
tellstick.handleLine("+Wclass:sensor;protocol:test-prot;model:test-model;data:2345;");
assertEquals("Events first transmission", 0, list.size());
tellstick.handleLine("+Wclass:sensor;protocol:test-prot;model:test-model;data:2345;");
assertEquals("Events Second transmission", 1, list.size());
}
@Test
public void receiveEvent(){
// Setup
TellstickSerialComm tellstick = new TellstickSerialComm();
final ArrayList<HalEventConfig> list = new ArrayList<>();
tellstick.setListener(new HalEventReportListener() {
@Override
public void reportReceived(HalEventConfig e, HalEventData d) {
list.add(e);
}
});
// Execution
TestEvent event = new TestEvent();
event.testData = 0xAAAA;
tellstick.register(event);
tellstick.handleLine("+Wclass:sensor;protocol:test-prot;model:test-model;data:AAAA;");
// Verification
assertEquals("Nr of received events", 1, list.size());
assertEquals("Data", event.testData, ((TestEvent)list.get(0)).testData);
}
private static class TestEvent extends TellstickProtocol implements HalEventConfig,TellstickDevice {
public int testData;
public TestEvent(){
super("test-prot", "test-model");
}
@Override
public List<TellstickDecodedEntry> decode(byte[] data) {
testData = Converter.toInt(data);
ArrayList<TellstickDecodedEntry> list = new ArrayList<>();
list.add(new TellstickDecodedEntry(
this, new DimmerEventData(testData, System.currentTimeMillis())
));
return list;
}
@Override
public Class<? extends HalEventController> getEventControllerClass() { return null; }
@Override
public Class<? extends HalEventData> getEventDataClass() {
return null;
}
@Override
public boolean equals(Object obj) {return testData == ((TestEvent)obj).testData;}
}
}

View file

@ -0,0 +1,53 @@
package se.hal.plugin.tellstick;
import se.hal.plugin.tellstick.device.NexaSelfLearning;
import se.hal.plugin.tellstick.protocol.NexaSelfLearningProtocol;
import se.hal.struct.devicedata.SwitchEventData;
/**
* Created by Ziver on 2015-11-19.
*/
public class TelstickSerialCommNexaOnOffTest {
public static void main(String[] args) {
try {
System.out.println("Connecting to db...");
TellstickSerialComm comm = new TellstickSerialComm();
// http://developer.telldus.com/doxygen/TellStick.html
comm.initialize("COM8");
//comm.connect("/dev/ttyUSB1");
Thread.sleep(1000);
NexaSelfLearning nexaDevice = new NexaSelfLearning();
//nexa.setHouse(11772006);
nexaDevice.setHouse(14160770);
nexaDevice.setGroup(false);
nexaDevice.setUnit(1);
SwitchEventData nexaData = new SwitchEventData();
System.out.println("Up and Running");
while(true) {
Thread.sleep(2000);
nexaData.turnOn();
nexaDevice.setUnit(0);
comm.send(nexaDevice, nexaData);
Thread.sleep(2000);
nexaDevice.setUnit(1);
comm.send(nexaDevice, nexaData);
Thread.sleep(2000);
nexaData.turnOff();
nexaDevice.setUnit(0);
comm.send(nexaDevice, nexaData);
Thread.sleep(2000);
nexaDevice.setUnit(1);
comm.send(nexaDevice, nexaData);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,104 @@
package se.hal.plugin.tellstick;
import org.junit.Before;
import org.junit.Test;
import se.hal.intf.*;
import se.hal.struct.devicedata.SwitchEventData;
import se.hal.struct.devicedata.TemperatureSensorData;
import zutil.converter.Converter;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
/**
* Created by Ziver on 2015-11-19.
*/
public class TelstickSerialCommSensorTest {
@Before
public void init(){
TellstickParser.registerProtocol(TestSensor.class);
}
//############ Normal TCs
@Test
public void receiveUnregisteredSensor(){
// Setup
TellstickSerialComm tellstick = new TellstickSerialComm();
final ArrayList<HalSensorConfig> list = new ArrayList<>();
tellstick.setListener(new HalSensorReportListener() {
@Override
public void reportReceived(HalSensorConfig e, HalSensorData d) {
list.add(e);
}
});
// Execution
tellstick.handleLine("+Wclass:sensor;protocol:test-prot;model:test-model;data:2345;");
assertEquals("Sensors first transmission", 0, list.size());
tellstick.handleLine("+Wclass:sensor;protocol:test-prot;model:test-model;data:2345;");
assertEquals("Sensors Second transmission", 1, list.size());
}
@Test
public void receiveSensor(){
// Setup
TellstickSerialComm tellstick = new TellstickSerialComm();
final ArrayList<HalSensorConfig> list = new ArrayList<>();
tellstick.setListener(new HalSensorReportListener() {
@Override
public void reportReceived(HalSensorConfig e, HalSensorData d) {
list.add(e);
}
});
// Execution
TestSensor sensor = new TestSensor();
sensor.testData = 0xAAAA;
tellstick.register(sensor);
tellstick.handleLine("+Wclass:sensor;protocol:test-prot;model:test-model;data:AAAA;");
// Verification
assertEquals("Nr of received sensors", 1, list.size());
assertEquals("Data", sensor.testData, ((TestSensor)list.get(0)).testData);
}
private static class TestSensor extends TellstickProtocol implements HalSensorConfig,TellstickDevice {
public int testData;
public TestSensor(){
super("test-prot", "test-model");
}
@Override
public List<TellstickDecodedEntry> decode(byte[] data) {
testData = Converter.toInt(data);
ArrayList<TellstickDecodedEntry> list = new ArrayList<>();
list.add(new TellstickDecodedEntry(
this, new TemperatureSensorData(testData, 0)
));
return list;
}
@Override
public boolean equals(Object obj) {return testData == ((TestSensor)obj).testData;}
@Override
public long getDataInterval() { return 0; }
@Override
public AggregationMethod getAggregationMethod() { return null; }
@Override
public Class<? extends HalSensorController> getSensorControllerClass() { return null; }
@Override
public Class<? extends HalSensorData> getSensorDataClass() {
return null;
}
}
}

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2015 Ziver
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package se.hal.plugin.tellstick.protocol;
import se.hal.plugin.tellstick.TellstickProtocol;
import se.hal.plugin.tellstick.TellstickProtocol.TellstickDecodedEntry;
import se.hal.plugin.tellstick.device.NexaSelfLearning;
import se.hal.plugin.tellstick.device.NexaSelfLearningDimmer;
import se.hal.struct.devicedata.DimmerEventData;
import se.hal.struct.devicedata.SwitchEventData;
import zutil.converter.Converter;
import java.nio.charset.StandardCharsets;
import java.util.List;
import static org.junit.Assert.*;
public class NexaSelfLearningTest {
@org.junit.Test
public void testSwitchEncode() throws Exception {
NexaSelfLearning nexaDevice = new NexaSelfLearning();
nexaDevice.setHouse(11_772_006);
nexaDevice.setUnit(3);
SwitchEventData nexaData = new SwitchEventData(true, System.currentTimeMillis());
byte[] expected = Converter.toBytes(new char[]{
84, // T
127, 255, 24, 0, // timings
132, // length
154, 138, 136, 170, 136, 168, 170, 138, 136, 168,
168, 170, 136, 170, 138, 138, 138, 138, 138, 136,
168, 170, 138, 136, 168, 170, 138, 136, 170, 138,
136, 168, 170,
43}); // +
NexaSelfLearningProtocol nexaProt = new NexaSelfLearningProtocol();
byte[] actual = nexaProt.encode(nexaDevice, nexaData).getTransmissionString()
.getBytes(StandardCharsets.ISO_8859_1);
System.out.println("Expected: "+Converter.toHexString(expected).toUpperCase());
System.out.println("Actual : "+Converter.toHexString(actual).toUpperCase());
assertArrayEquals(expected, actual);
}
@org.junit.Test
public void testDimmerEncode() throws Exception {
NexaSelfLearningDimmer nexaDevice = new NexaSelfLearningDimmer();
nexaDevice.setHouse(11_772_006);
nexaDevice.setUnit(3);
DimmerEventData nexaData = new DimmerEventData(0.5, System.currentTimeMillis());
byte[] expected = Converter.toBytes(new char[]{
84, // T
127, 255, 24, 0, // timings
164, // length, 32 extra timings
154, 138, 136, 170, 136, 168, 170, 138, 136, 168,
168, 170, 136, 170, 138, 138, 138, 138, 138, 136,
168, 170, 138, 136, 168, 170, 138,
138, 138, 138, 136, 168,
168, 170, 138, 138, 138, 138, 138, 138, 138, // Dimer value
43}); // +
NexaSelfLearningProtocol nexaProt = new NexaSelfLearningProtocol();
byte[] actual = nexaProt.encode(nexaDevice, nexaData).getTransmissionString()
.getBytes(StandardCharsets.ISO_8859_1);
System.out.println("Expected: "+Converter.toHexString(expected).toUpperCase());
System.out.println("Actual : "+Converter.toHexString(actual).toUpperCase());
assertArrayEquals(expected, actual);
}
@org.junit.Test
public void decode_ON() throws Exception {
TellstickDecodedEntry nexa = decode("0x2CE81990");
assertEquals("House Code", 11772006, ((NexaSelfLearning)nexa.getDevice()).getHouse());
assertEquals("Unit Code", 0, ((NexaSelfLearning)nexa.getDevice()).getUnit());
assertTrue("Enabled", ((SwitchEventData)nexa.getData()).isOn());
}
@org.junit.Test
public void decode_OFF() throws Exception {
TellstickDecodedEntry nexa = decode("0x2CE81980");
assertEquals("House Code", 11772006, ((NexaSelfLearning)nexa.getDevice()).getHouse());
assertEquals("Unit Code", 0, ((NexaSelfLearning)nexa.getDevice()).getUnit());
assertFalse("Enabled", ((SwitchEventData)nexa.getData()).isOn());
}
private TellstickDecodedEntry decode(String data){
NexaSelfLearningProtocol nexaProt = new NexaSelfLearningProtocol();
List<TellstickDecodedEntry> list = nexaProt.decode(Converter.hexToByte(data));
return list.get(0);
}
}

View file

@ -34,6 +34,10 @@
</fileset>
</path>
<!-- ________________________ EXECUTION TARGETS ________________________ -->
<target name="test" />
<!-- ________________________ BUILD TARGETS ________________________ -->
<!--clean all build paths-->