/**
* Copyright (c) 2010-2015, openHAB.org and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
*/
package be.devlaminck.openwebnet;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.myhome.fcrisciani.connector.MyHomeJavaConnector;
/**
* This thread reads every event on the bticino bus and then converts & publishes
* it to the openhab
*
* Based on code from Mauro Cicolella (as part of the FREEDOMOTIC framework)
* (https ://github.com/freedomotic/freedomotic/tree/master/plugins/devices/
* openwebnet) and on code of Flavio Fcrisciani
* (https://github.com/fcrisciani/java-myhome-library) released under EPL
*
* @author Tom De Vlaminck, Lago Moreno
* @serial 1.0
* @since 1.7.0
*/
public class MonitorSessionThread extends Thread {
private static final Logger logger = LoggerFactory.getLogger(MonitorSessionThread.class);
private OpenWebNet pluginReference = null;
private String ipAddress = null;
private Integer port = 0;
@Override
public void run() {
// connect to own gateway
logger.debug("Connecting to ipaddress {} on port {}", ipAddress, port);
pluginReference.myPlant = new MyHomeJavaConnector(ipAddress, port);
try {
pluginReference.myPlant.startMonitoring();
while (!Thread.interrupted()) {
try {
String readFrame = pluginReference.myPlant.readMonitoring();
if (readFrame != null) {
buildEventFromFrame(readFrame);
}
} catch (InterruptedException ex) {
logger.error("MonitorSessionThread.run, exception : " + ex.getMessage());
}
}
} catch (IOException ex) {
logger.error("MonitorSessionThread.run, exception : " + ex.getMessage());
}
logger.info("Stopped MonitorSessionThread thread");
}
public MonitorSessionThread(OpenWebNet pluginReference, String ipAddress, Integer port) {
this.pluginReference = pluginReference;
this.ipAddress = ipAddress;
this.port = port;
}
public void buildEventFromFrame(String frame) {
logger.info("Received OpenWebNet frame '" + frame + "' now translate it to an event.");
String who = null;
String what = null;
String where = null;
String objectClass = null;
String objectName = null;
String messageType = null;
String messageDescription = null;
String[] frameParts = null;
ProtocolRead event = null;
if (frame.isEmpty()) {
logger.error("Empty frame");
return;
}
int length = frame.length();
if (!frame.endsWith("##")) {
logger.error("Malformed frame " + frame + " " + frame.substring(length - 2, length));
return;
}
if (frame.equals(OWNUtilities.MSG_OPEN_ACK)) {
messageType = "ack";
return;
}
if (frame.equals(OWNUtilities.MSG_OPEN_NACK)) {
messageType = "nack";
return;
}
// Status request frame
if (frame.substring(0, 2).equalsIgnoreCase("*#")) {
// remove *# and ##
frame = frame.substring(2, length - 2);
// remove *# and ##
frameParts = frame.split("\\*"); // * is reserved so it must be
// escaped
who = frameParts[0];
where = frameParts[1];
objectName = who + "*" + where;
event = new ProtocolRead(frame);
if (who.equalsIgnoreCase("1")) {
objectClass = "Light";
objectName = who + "*" + where;
if (frameParts[2].equalsIgnoreCase("1")) {
String level = frameParts[3];
String speed = frameParts[4];
messageDescription = "Luminous intensity change";
if (level != null) {
event.addProperty("level", level);
}
if (speed != null) {
event.addProperty("speed", speed);
}
}
if (frameParts[2].equalsIgnoreCase("2")) {
String hour = frameParts[3];
String min = frameParts[4];
String sec = frameParts[5];
messageDescription = "Luminous intensity change";
if (hour != null) {
event.addProperty("hour", hour);
}
if (min != null) {
event.addProperty("min", min);
}
if (sec != null) {
event.addProperty("sec", sec);
}
}
}
// POWER MANAGEMENT
if (who.equalsIgnoreCase("3")) {
objectClass = "Powermeter";
objectName = who + "*" + where;
String voltage = null;
String current = null;
String power = null;
String energy = null;
if (frameParts[3].equalsIgnoreCase("0")) {
voltage = frameParts[3];
current = frameParts[4];
power = frameParts[5];
energy = frameParts[6];
messageDescription = "Load control status";
if (voltage != null) {
event.addProperty("voltage", voltage);
}
if (current != null) {
event.addProperty("current", current);
}
if (power != null) {
event.addProperty("power", power);
}
if (energy != null) {
event.addProperty("energy", energy);
}
}
if (frameParts[3].equalsIgnoreCase("1")) {
voltage = frameParts[3];
if (voltage != null) {
event.addProperty("voltage", voltage);
}
messageDescription = "Voltage status";
}
if (frameParts[3].equalsIgnoreCase("2")) {
current = frameParts[3];
if (current != null) {
event.addProperty("current", current);
}
messageDescription = "Current status";
}
if (frameParts[3].equalsIgnoreCase("3")) {
power = frameParts[3];
if (power != null) {
event.addProperty("power", power);
}
messageDescription = "Power status";
}
if (frameParts[3].equalsIgnoreCase("4")) {
energy = frameParts[3];
if (energy != null) {
event.addProperty("energy", energy);
}
messageDescription = "Energy status";
}
}
// TERMOREGULATION
if (who.equalsIgnoreCase("4")) {
messageType = "thermoregulation";
objectClass = "Thermo";
objectName = who + "*" + where;
if (frameParts[2].equalsIgnoreCase("0")) {
String temperature = frameParts[3];
temperature = OWNUtilities.convertTemperature(temperature);
messageDescription = "Temperature value";
if (temperature != null) {
event.addProperty("temperature", temperature);
}
} else {
logger.debug("other temperature message");
}
}
// GATEWAY CONTROL
if (who.equalsIgnoreCase("13")) {
objectClass = "Gateway";
objectName = who;
String hour = null;
String minute = null;
String second = null;
String timeZone = null;
String dayWeek = null;
String day = null;
String month = null;
String year = null;
String version = null;
String release = null;
String build = null;
if (frameParts[2].equalsIgnoreCase("0")) {
hour = frameParts[3];
minute = frameParts[4];
second = frameParts[5];
timeZone = frameParts[6]; // aggiungere funzione conversione
messageType = "gatewayControl";
messageDescription = "Time request";
if (hour != null) {
event.addProperty("hour", hour);
}
if (minute != null) {
event.addProperty("minute", minute);
}
if (second != null) {
event.addProperty("second", second);
}
if (timeZone != null) {
event.addProperty("timeZone", timeZone);
}
}
if (frameParts[2].equalsIgnoreCase("1")) {
dayWeek = OWNUtilities.dayName(frameParts[3]);
day = frameParts[4];
month = frameParts[5];
year = frameParts[6];
messageType = "gatewayControl";
messageDescription = "Date request";
if (dayWeek != null) {
event.addProperty("dayWeek", dayWeek);
}
if (day != null) {
event.addProperty("day", day);
}
if (month != null) {
event.addProperty("month", month);
}
if (year != null) {
event.addProperty("year", year);
}
}
if (frameParts[2].equalsIgnoreCase("10")) {
String ip1 = frameParts[3];
String ip2 = frameParts[4];
String ip3 = frameParts[5];
String ip4 = frameParts[6];
messageType = "gatewayControl";
messageDescription = "IP request";
event.addProperty("ip-address", ip1 + "." + ip2 + "." + ip3 + "." + ip4);
}
if (frameParts[2].equalsIgnoreCase("11")) {
String netmask1 = frameParts[3];
String netmask2 = frameParts[4];
String netmask3 = frameParts[5];
String netmask4 = frameParts[6];
messageType = "gatewayControl";
messageDescription = "Netmask request";
event.addProperty("netmask", netmask1 + "." + netmask2 + "." + netmask3 + "." + netmask4);
}
if (frameParts[2].equalsIgnoreCase("12")) {
String mac1 = frameParts[3];
String mac2 = frameParts[4];
String mac3 = frameParts[5];
String mac4 = frameParts[6];
String mac5 = frameParts[7];
String mac6 = frameParts[8];
messageType = "gatewayControl";
messageDescription = "MAC request";
event.addProperty("mac-address",
mac1 + ":" + mac2 + ":" + mac3 + ":" + mac4 + ":" + mac5 + ":" + mac6);
}
if (frameParts[2].equalsIgnoreCase("15")) {
String model = OWNUtilities.gatewayModel(frameParts[3]);
messageType = "gatewayControl";
messageDescription = "Model request";
event.addProperty("model", model);
}
if (frameParts[2].equalsIgnoreCase("16")) {
version = frameParts[3];
release = frameParts[4];
build = frameParts[5];
messageType = "gatewayControl";
messageDescription = "Firmware version request";
event.addProperty("firmware - version", version + "." + release + "." + build);
}
if (frameParts[2].equalsIgnoreCase("17")) {
String days = frameParts[3];
String hours = frameParts[4];
String minutes = frameParts[5];
String seconds = frameParts[6];
messageType = "gatewayControl";
messageDescription = "Uptime request";
event.addProperty("uptime ", days + "D:" + hours + "H:" + minutes + "m:" + seconds + "s");
}
if (frameParts[2].equalsIgnoreCase("22")) {
hour = frameParts[3];
minute = frameParts[4];
second = frameParts[5];
timeZone = frameParts[6];
String weekDay = OWNUtilities.dayName(frameParts[7]);
day = frameParts[8];
month = frameParts[9];
year = frameParts[10];
messageType = "gatewayControl";
messageDescription = "Date&Time request";
event.addProperty("date", weekDay + " " + day + "/" + month + "/" + year);
event.addProperty("time", hour + ":" + minute + ":" + second + " (" + timeZone + ")");
}
if (frameParts[2].equalsIgnoreCase("23")) {
version = frameParts[3];
release = frameParts[4];
build = frameParts[5];
messageType = "gatewayControl";
messageDescription = "Kernel version request";
event.addProperty("kernel - version", version + "." + release + "." + build);
}
if (frameParts[2].equalsIgnoreCase("24")) {
version = frameParts[3];
release = frameParts[4];
build = frameParts[5];
messageType = "gatewayControl";
messageDescription = "Distribution version request";
event.addProperty("distribution - version", version + "." + release + "." + build);
}
}
// Basic and evolved CEN
if (who.equalsIgnoreCase("15")) {
objectClass = "CEN_Basic_Evolved";
objectName = who + "*" + where;
what = frameParts[2];
String[] what_parts = what.split("#");
if (what_parts.length == 1) {
// push button n
event.addProperty("push_button_n", what_parts[0]);
// type of pressure
event.addProperty("pressure", "Virtual pressure");
} else if (what_parts.length == 2) {
// push button n
event.addProperty("push_button_n", what_parts[0]);
if (what_parts[0].equalsIgnoreCase("1")) {
// type of pressure
event.addProperty("pressure", "Virtual release after short pressure");
}
if (what_parts[0].equalsIgnoreCase("2")) {
// type of pressure
event.addProperty("pressure", "Virtual release after an extended pressure");
}
if (what_parts[0].equalsIgnoreCase("3")) {
// type of pressure
event.addProperty("pressure", "Virtual extended pressure");
}
} else {
logger.debug("other CEN Basic or Evolved message");
}
}
event.addProperty("who", who);
if (where != null) {
event.addProperty("where", where);
}
if (messageDescription != null) {
event.addProperty("messageDescription", messageDescription);
}
if (messageType != null) {
event.addProperty("messageType", messageType);
}
// notify event
logger.info(OWNUtilities.getDateTime() + " Rx: " + frame + " " + "(" + messageDescription + ")");
// Notify all the listeners an event has been received
pluginReference.notifyEvent(event);
}
// Command frame
if (!(frame.substring(0, 2).equalsIgnoreCase("*#")) && (frame.substring(0, 1).equalsIgnoreCase("*"))) {
// remove delimiter chars * and ##
frame = frame.substring(1, length - 2);
frameParts = frame.split("\\*"); // * is reserved so it must be
// escaped
who = frameParts[0];
what = frameParts[1];
// Burglar Central Unit Status Request = *#5##
if (frameParts.length >= 3) {
where = frameParts[2];
} else {
where = "";
}
event = new ProtocolRead(frame);
objectName = who + "*" + where;
boolean virtual_where = false;
String[] what_parts;
switch (Integer.parseInt(who)) {
// LIGHTING
case 1:
messageType = "Lighting";
objectClass = "Light";
// For virtual configuration we receive for light on 1000#1
// so assuming the second part is the what
what_parts = what.split("#");
if (what_parts.length > 1) {
virtual_where = true;
// take the last part for the what
what = what_parts[what_parts.length - 1];
}
switch (Integer.parseInt(what)) {
// Light OFF
case 0:
messageDescription = "Light OFF";
break;
// Light ON
case 1:
messageDescription = "Light ON";
break;
default:
if (Integer.parseInt(what) >= 2 && Integer.parseInt(what) <= 10) {
messageDescription = "Light Dimmer";
}
break;
}
break; // close LIGHTING switch
// AUTOMATION
case 2:
messageType = "Automation";
objectClass = "Automation";
switch (Integer.parseInt(what)) {
case 0:
messageDescription = "Automation STOP";
break;
case 1:
messageDescription = "Automation UP";
break;
case 2:
messageDescription = "Automation DOWN";
break;
}
break; // close AUTOMATION switch
// POWER MANAGEMENT
case 3:
objectClass = "Powermeter";
messageType = "Power management";
switch (Integer.parseInt(what)) {
case 0:
messageDescription = "Load disable";
break;
case 1:
messageDescription = "Load enable";
break;
case 2:
messageDescription = "Load forced";
break;
case 3:
messageDescription = "Stop load forced";
break;
}
break; // close POWER MANAGEMENT switch
// THERMOREGULATION
case 4:
messageType = "thermoregulation";
objectClass = "Thermo";
switch (Integer.parseInt(what)) {
case 0:
messageDescription = "Conditioning";
break;
case 1:
messageDescription = "Heating";
break;
case 20:
messageDescription = "Remote Control disabled";
break;
case 21:
messageDescription = "Remote Control enabled";
break;
case 22:
messageDescription = "At least one Probe OFF";
break;
case 23:
messageDescription = "At least one Probe in protection";
break;
case 24:
messageDescription = "At least one Probe in manual";
break;
case 30:
messageDescription = "Failure discovered";
break;
case 31:
messageDescription = "Central Unit battery KO";
break;
case 103:
messageDescription = "OFF Heating";
break;
case 110:
messageDescription = "Manual Heating";
break;
case 111:
messageDescription = "Automatic Heating";
break;
case 202:
messageDescription = "AntiFreeze";
break;
case 203:
messageDescription = "OFF Conditioning";
break;
case 210:
messageDescription = "Manual Conditioning";
break;
case 211:
messageDescription = "Automatic Conditioning";
break;
case 302:
messageDescription = "Thermal Protection";
break;
case 303:
messageDescription = "Generic OFF";
break;
case 311:
messageDescription = "Automatic Generic";
break;
}
break; // close THERMOREGULATION switch
// BURGLAR ALARM
case 5:
messageType = "alarm";
objectClass = "Alarm";
switch (Integer.parseInt(what)) {
case 0:
messageDescription = "System on maintenance";
break;
case 4:
messageDescription = "Battery fault";
break;
case 5:
messageDescription = "Battery OK";
break;
case 6:
messageDescription = "No Network";
break;
case 7:
messageDescription = "Network OK";
break;
case 8:
messageDescription = "System engaged";
break;
case 9:
messageDescription = "System disengaged";
break;
case 10:
messageDescription = "Battery KO";
break;
case 11: // prelevare la sottostringa di where #N
{
messageDescription = "Zone " + where + " engaged";
}
break;
case 12: // prelevare la sottostringa di where #N
{
messageDescription = "Aux " + where + " in Technical alarm ON";
}
break;
case 13: // prelevare la sottostringa di where #N
{
messageDescription = "Aux " + where + " in Technical alarm RESET";
}
break;
case 15: // prelevare la sottostringa di where #N
{
messageDescription = "Zone " + where + " in Intrusion alarm";
}
break;
case 16: // prelevare la sottostringa di where #N
{
messageDescription = "Zone " + where + " in Tampering alarm";
}
break;
case 17: // prelevare la sottostringa di where #N
{
messageDescription = "Zone " + where + " in Anti-panic alarm";
}
break;
case 18: // prelevare la sottostringa di where #N
{
messageDescription = "Zone " + where + " divided";
}
break;
case 31: // prelevare la sottostringa di where #N
{
messageDescription = "Silent alarm from aux " + where;
}
break;
}
break; // close BURGLAR ALARM switch
// SOUND SYSTEM
case 16:
messageType = "Sound System";
objectClass = "Sound";
switch (Integer.parseInt(what)) {
case 0:
messageDescription = "ON Baseband";
break;
case 3:
messageDescription = "ON Stereo channel";
break;
case 10:
messageDescription = "OFF Baseband";
break;
case 13:
messageDescription = "OFF Stereo channel";
break;
case 30:
messageDescription = "Sleep on baseband";
break;
case 33:
messageDescription = "Sleep on stereo channel";
break;
}
break; // close SOUND SYSTEM switch
// CEN (Basic & Evolved)
case 15:
messageType = "CEN Basic and Evolved";
objectClass = "CEN";
what_parts = what.split("#");
if (what_parts.length == 1) {
// type of pressure
messageDescription = "Virtual pressure";
} else if (what_parts.length == 2) {
if (what_parts[0].equalsIgnoreCase("1")) {
// type of pressure
messageDescription = "Virtual release after short pressure";
} else if (what_parts[0].equalsIgnoreCase("2")) {
// type of pressure
messageDescription = "Virtual release after an extended pressure";
} else if (what_parts[0].equalsIgnoreCase("3")) {
// type of pressure
messageDescription = "Virtual extended pressure";
}
} else {
messageDescription = "other CEN Basic or Evolved message";
}
} // close switch(who)
if (who != null) {
event.addProperty("who", who);
}
if (what != null) {
event.addProperty("what", what);
}
if (where != null) {
event.addProperty("where", where);
// Indicate virtual where message
event.addProperty("virtual", virtual_where ? "true" : "false");
}
if (messageType != null) {
event.addProperty("messageType", messageType);
}
if (messageDescription != null) {
event.addProperty("messageDescription", messageDescription);
}
if (objectClass != null) {
event.addProperty("object.class", objectClass);
}
if (objectName != null) {
event.addProperty("object.name", objectName);
}
logger.info("Frame " + frame + " is " + messageType + " message. Notify it as OpenHab event "
+ messageDescription == "No Description set" ? "" : messageDescription); // for debug
// Notify all the listeners an event has been received
pluginReference.notifyEvent(event);
}
}
}