/** * * Copyright (c) 2009-2016 Freedomotic team * http://freedomotic.com * * This file is part of Freedomotic * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Freedomotic; see the file COPYING. If not, see * <http://www.gnu.org/licenses/>. */ package com.freedomotic.plugins.devices.x10.gateways; import com.freedomotic.app.Freedomotic; import com.freedomotic.events.ProtocolRead; import com.freedomotic.plugins.devices.x10.X10; import com.freedomotic.plugins.devices.x10.X10AbstractGateway; import com.freedomotic.plugins.devices.x10.X10Event; import com.freedomotic.serial.SerialConnectionProvider; import com.freedomotic.serial.SerialDataConsumer; import java.io.IOException; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; /** * An implementation of a reader/writer for an X10 transciever named Xannura * Home PMIX35 You can find the technical manual at * http://freedomotic.googlecode.com/files/PMIX35.pdf * * @author Enrico Nicoletti (enrico.nicoletti84@gmail.com) */ public final class PMix35Gateway implements X10AbstractGateway, SerialDataConsumer { private static SerialConnectionProvider usb = null; //PMIX strings for ack, nack and null messages private static final String PMIX_ACK = "$<9000!4A#".trim(); private static final String PMIX_NACK = "$<9000?4A#".trim(); private static final String PMIX_NULL = "$<900029#".trim(); private String lastReceived = ""; private X10Event x10Event = new X10Event(); private X10 plugin; public PMix35Gateway(X10 plugin) { this.plugin = plugin; connect(); } /** * Connection with the PMIX35 over serial port */ @Override public void connect() { if (usb == null) { Properties config = new Properties(); config.setProperty("hello-message", "$>9000PXD3#"); //hello message defined by pmix15 comm protocol config.setProperty("hello-reply", "$<9000VP"); //expected reply to hello-message starts with this string config.setProperty("polling-message", "$>9000RQCE#"); //$>9000RQcs# forces read data using this string //The computer always take the initiative to read, the PMIX35 is a passive gateway config.setProperty("polling-time", "1000"); //millisecs between reads. config.setProperty("port", "/dev/ttyUSB0"); usb = new SerialConnectionProvider(config); //instantiating a new serial connection with the previous parameters usb.addListener(this); usb.connect(); if (usb.isConnected()) { plugin.setDescription("Connected to " + usb.getPortName()); } else { plugin.setDescription("Unable to connect to " + config.getProperty("port")); plugin.stop(); } } } @Override public void disconnect() { if (usb != null) { usb.disconnect(); if (!usb.isConnected()) { plugin.setDescription("Disconnected"); } } } @Override public String send(String message) throws IOException { String reply = usb.send(message); return reply; } /** * Parse the PMIX35 readed line to find X10 standard codes * * @param readed * @return */ @Override public String parseReaded(String readed) { //if nothing is readed. Likely the PMIX35 is no longer connected to usb if (readed.isEmpty()) { usb.disconnect(); return ""; } //if readed is a noise detection/network impedance value discard this line if (readed.contains(PMIX_NULL) || readed.contains(PMIX_ACK) || readed.contains(PMIX_NACK)) { return ""; } //removing the prifix ($<9000) and the suffix (CS#) //CS is the checksum (2 characters) readed = readed.substring(6, readed.length() - 3); //it's a command echo if (readed.startsWith("LE")) { System.out.println(readed); return ""; } //if the message is a network noise detection value if (readed.startsWith("ND")) { boolean isNoisy = (new Integer(readed.substring(2, 4)) == 1); //noise on the network //System.err.println("Powerline network is really noisy. Possible loose of data"); //if the message is a network impedance value readed = readed.substring(4); } else if (readed.startsWith("NI")) { int impedance = new Integer(readed.substring(2, 6)); //Network inmpedance readed = readed.substring(6); } //check again for command echo as we can have ND00LE A01... or NI0000LE A01... if (readed.startsWith("LE")) { System.out.println(readed); return ""; } //otherwise remains an X10 standard string (EG: A01 AON) readed = readed.substring(10).trim(); return readed; } /** * A PMIX35 message is composed by a start string + the x10 message + a * checksum + PMIX end of line (the # character) For example: <ul> <li> * A01A01 AONAON -> $>9000LW A01A01 AONAON**# </ul> ** corresponds to the * checksum (2 characters). LW means line write in PMIX35 lingo * * @param the string to translate in PMIX35 format * @return the encoded string ready to be sent to the PMIX35 */ @Override public String composeMessage(String housecode, String address, String command) { //Adding the standard prefix String message = "$>9000LW " + housecode + address + housecode + address + " " + housecode + command + housecode + command; //Calculate the chacksum int sum = 0; for (int i = 0; i < message.length(); i++) { sum += message.charAt(i); } //we need only the last to char of the checksum String cs = Integer.toHexString(sum); cs = cs.substring(cs.length() - 2); cs = cs.toUpperCase(); //Adding the end of line character and construction the whole encoded string return message + cs + "#\r\n"; } @Override public String getName() { return "PMIX35"; } @Override public void onDataAvailable(String readed) { String[] tokens = readed.split("#"); for (int i = 0; i < tokens.length; i++) { String line = parseReaded(tokens[i] + "#"); if (!line.isEmpty() && !line.equals(lastReceived)) { lastReceived = line; System.out.println(lastReceived); if (X10.isAnX10Address(line)) { x10Event.setAddress(line); } else { if (X10.isAnX10Command(line)) { x10Event.setFunction(line); if (x10Event.isEventComplete()) { System.out.println("send x10 event " + x10Event); x10Event.send(); } } } } } } @Override public void read() { try { send("$>9000RQCE#"); } catch (IOException ex) { Logger.getLogger(PMix35Gateway.class.getName()).log(Level.SEVERE, null, ex); } } }