Fixed merge issues

This commit is contained in:
Ziver Koc 2019-11-12 14:26:37 +01:00
parent 81a85320a2
commit 09ae1b2667
37 changed files with 22 additions and 24 deletions

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,267 @@
/*
* 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 Logger logger = LogUtil.getLogger();
private static String CONFIG_TELLSTICK_COM_PORT = "tellstick.com_port";
private static final long TRANSMISSION_UNIQUENESS_TTL = 1000; // milliseconds
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(CONFIG_TELLSTICK_COM_PORT) != null;
}
@Override
public void initialize() throws Exception {
initialize(HalContext.getStringProperty(CONFIG_TELLSTICK_COM_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;
}
}