/*
FPLUGReceiver.java
Copyright (c) 2015 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.deviceplugin.fplug.fplug;
import android.bluetooth.BluetoothSocket;
import android.util.Log;
import org.deviceconnect.android.deviceplugin.fplug.BuildConfig;
import java.io.IOException;
import java.util.ArrayList;
/**
* This class provides functions of receiving values from F-PLUG.
*
* @author NTT DOCOMO, INC.
*/
public class FPLUGReceiver extends Thread {
public interface FPLUGReceiverEventListener {
void onDisconnected();
void onReceiveResponse(FPLUGResponse response);
void onReceiveError(String message);
}
private final static String TAG = "FPLUGReceiver";
private int BUFFER_SIZE = 16;
private boolean mIsClose = false;
private boolean isWattHour = false;
private boolean isSuccessGetWattHour = false;
private boolean isPastValues = false;
private boolean isSuccessGetPastValues = false;
private byte[] totalBytes = null;
private int offset = 0;
private BluetoothSocket mSocket;
private String mAddress;
private FPLUGReceiverEventListener mListener;
public FPLUGReceiver(String address, BluetoothSocket socket, FPLUGReceiverEventListener listener) {
mAddress = address;
mSocket = socket;
mListener = listener;
}
@Override
public void run() {
while (!mIsClose) {
byte[] buf = new byte[BUFFER_SIZE];
int len;
try {
len = mSocket.getInputStream().read(buf);
if (BuildConfig.DEBUG) {
Log.d(TAG, "len:" + len + " str:" + toHexString(buf));
}
} catch (IOException e) {
if (BuildConfig.DEBUG) {
Log.e(TAG, "read break e:" + e);
}
mListener.onDisconnected();
break;
}
//handle multi read bytes
if (isWattHour) {
wattHourProcess(buf, len);
} else if (isPastValues) {
pastValuesProcess(buf, len);
} else {
parseCommand(buf);
}
}
}
public void close() {
mIsClose = true;
}
private void parseCommand(byte[] buf) {
int firstByte = buf[0] & 0xFF;
//echonet-lite
if (firstByte == 0x10) {
int ehd2 = buf[1] & 0xFF;
//specified
if (ehd2 == 0x81) {
int classCode = buf[5];
if (classCode == 0x22) {
int serviceByte = buf[10];
if (serviceByte == 0x71 || serviceByte == 0x51) {
handlePlugInitResponse(buf);
} else if (serviceByte == 0x72 || serviceByte == 0x52) {
handleWattResponse(buf);
} else {
Log.e(TAG, "unknown parameter");
}
} else if (classCode == 0x11) {
handleTemperature(buf);
} else if (classCode == 0x12) {
handleHumidity(buf);
} else if (classCode == 0x0D) {
handleIlluminance(buf);
} else {
Log.e(TAG, "unknown parameter");
}
}
//manual
else if (ehd2 == 0x82) {
int targetByte = buf[4] & 0xFF;
if (targetByte == 0x91) {
handleWattHourResponse(buf);
} else if (targetByte == 0x96) {
handlePastWattHourResponse(buf);
} else if (targetByte == 0x97) {
handlePastValuesResponse(buf);
} else {
Log.e(TAG, "unknown parameter");
}
} else {
Log.e(TAG, "unknown parameter");
}
}
//originals
else if (firstByte == 0x86) {
handlePairingResponse(buf);
} else if (firstByte == 0x87) {
handleSetDateResponse(buf);
} else if (firstByte == 0x85) {
handleLEDResponse(buf);
}
//unknown
else {
//When "init-plug" requested, 6 bytes by zero-filled comes after correct response.
if (BuildConfig.DEBUG) {
Log.e(TAG, "unknown parameter");
}
}
}
private void wattHourProcess(byte[] buf, int len) {
System.arraycopy(buf, 0, totalBytes, offset, len);
offset += len;
if (offset == 72) {
isWattHour = false;
if (BuildConfig.DEBUG) {
Log.d(TAG, "str:" + toHexString(totalBytes));
}
ArrayList<WattHour> dataList = new ArrayList<>();
int hoursAgo = 24;
byte buf1 = 0;
byte buf2 = 0;
int counter = 0;
for (byte bt : totalBytes) {
if (counter == 2) {
WattHour data = new WattHour();
if ((bt & 0xff) == 0x00) {
data.setReliable(true);
} else if ((bt & 0xff) == 0x01) {
data.setReliable(false);
}
data.setHoursAgo(hoursAgo);
data.setWatt((((int) buf2) * 256 + (buf1 & 0xff)));
dataList.add(data);
hoursAgo--;
counter = 0;
continue;
} else if (counter == 1) {
buf2 = bt;
} else {
buf1 = bt;
}
counter++;
}
if (isSuccessGetWattHour) {
FPLUGResponse response = new FPLUGResponse();
response.setAddress(mAddress);
response.setWattHourList(dataList);
mListener.onReceiveResponse(response);
} else {
mListener.onReceiveError("get watt hour failed");
}
}
}
private void pastValuesProcess(byte[] buf, int len) {
System.arraycopy(buf, 0, totalBytes, offset, len);
offset += len;
if (offset == 120) {
isPastValues = false;
if (BuildConfig.DEBUG) {
Log.d(TAG, "str:" + toHexString(totalBytes));
}
ArrayList<PastValues> dataList = new ArrayList<>();
int hoursAgo = 24;
byte temperatureByte1 = 0;
byte temperatureByte2 = 0;
byte humidityByte = 0;
byte illuminanceByte1 = 0;
byte illuminanceByte2;
int counter = 0;
for (byte bt : totalBytes) {
switch (counter) {
case 0:
temperatureByte1 = bt;
break;
case 1:
temperatureByte2 = bt;
break;
case 2:
humidityByte = bt;
break;
case 3:
illuminanceByte1 = bt;
break;
case 4:
illuminanceByte2 = bt;
PastValues data = new PastValues();
data.setHoursAgo(hoursAgo);
int temp = (((int) temperatureByte2) * 256 + (temperatureByte1 & 0xff));
double temperature = (double) temp / 10;
data.setTemperature(temperature);
int humidity = (humidityByte & 0xff);
data.setHumidity(humidity);
int illuminance = (illuminanceByte1 & 0xff);
illuminance += (illuminanceByte2 & 0xff) * 256;
data.setIlluminance(illuminance);
dataList.add(data);
hoursAgo--;
counter = 0;
continue;
default:
Log.e(TAG, "wrong index");
}
counter++;
}
if (isSuccessGetPastValues) {
FPLUGResponse response = new FPLUGResponse();
response.setAddress(mAddress);
response.setPastValuesList(dataList);
mListener.onReceiveResponse(response);
} else {
mListener.onReceiveError("get past values failed");
}
}
}
private void handlePlugInitResponse(byte[] buf) {
int serviceByte = buf[10];
if (serviceByte == 0x71) {
FPLUGResponse response = new FPLUGResponse();
response.setAddress(mAddress);
mListener.onReceiveResponse(response);
} else if (serviceByte == 0x51) {
mListener.onReceiveError("cancel pairing failed");
} else {
mListener.onReceiveError("unknown response parameter");
}
}
private void handlePairingResponse(byte[] buf) {
int type = buf[1] & 0xFF;
if (type == 0x00) {
FPLUGResponse response = new FPLUGResponse();
response.setAddress(mAddress);
mListener.onReceiveResponse(response);
} else if (type == 0x01) {
mListener.onReceiveError("cancel pairing failed");
} else {
mListener.onReceiveError("unknown response parameter");
}
}
private void handleWattHourResponse(byte[] buf) {
isWattHour = true;
totalBytes = new byte[72];
System.arraycopy(buf, 6, totalBytes, 0, BUFFER_SIZE - 6);
offset = 10;
if ((buf[5] & 0xff) == 0x00) {
isSuccessGetWattHour = true;
} else if ((buf[5] & 0xff) == 0x01) {
isSuccessGetWattHour = false;
}
}
private void handleTemperature(byte[] buf) {
int serviceByte = buf[10];
if (serviceByte == 0x72) {
int temp = (((int) buf[15]) * 256 + (buf[14] & 0xff));
double temperature = (double) temp / 10;
FPLUGResponse response = new FPLUGResponse();
response.setAddress(mAddress);
response.setTemperature(temperature);
mListener.onReceiveResponse(response);
} else if (serviceByte == 0x52) {
mListener.onReceiveError("get humidity failed");
} else {
mListener.onReceiveError("unknown response parameter");
}
}
private void handleHumidity(byte[] buf) {
int serviceByte = buf[10];
if (serviceByte == 0x72) {
int humidity = (buf[14] & 0xff);
FPLUGResponse response = new FPLUGResponse();
response.setAddress(mAddress);
response.setHumidity(humidity);
mListener.onReceiveResponse(response);
} else if (serviceByte == 0x52) {
mListener.onReceiveError("get humidity failed");
} else {
mListener.onReceiveError("unknown response parameter");
}
}
private void handleIlluminance(byte[] buf) {
int serviceByte = buf[10];
if (serviceByte == 0x72) {
int illuminance = (buf[14] & 0xff);
illuminance += (buf[15] & 0xff) * 256;
FPLUGResponse response = new FPLUGResponse();
response.setAddress(mAddress);
response.setIlluminance(illuminance);
mListener.onReceiveResponse(response);
} else if (serviceByte == 0x52) {
mListener.onReceiveError("get illuminance failed");
} else {
mListener.onReceiveError("unknown response parameter");
}
}
private void handleWattResponse(byte[] buf) {
int serviceByte = buf[10];
if (serviceByte == 0x72) {
int temp = (((int) buf[15]) * 256 + (buf[14] & 0xff));
double watt = (double) temp / 10;
FPLUGResponse response = new FPLUGResponse();
response.setAddress(mAddress);
response.setRealtimeWatt(watt);
mListener.onReceiveResponse(response);
} else if (serviceByte == 0x52) {
mListener.onReceiveError("get realtime watt failed");
} else {
mListener.onReceiveError("unknown response parameter");
}
}
private void handlePastWattHourResponse(byte[] buf) {
isWattHour = true;
totalBytes = new byte[72];
System.arraycopy(buf, 6, totalBytes, 0, BUFFER_SIZE - 6);
offset = 10;
if ((buf[5] & 0xff) == 0x00) {
isSuccessGetWattHour = true;
} else if ((buf[5] & 0xff) == 0x01) {
isSuccessGetWattHour = false;
}
}
private void handlePastValuesResponse(byte[] buf) {
isPastValues = true;
totalBytes = new byte[120];
System.arraycopy(buf, 6, totalBytes, 0, BUFFER_SIZE - 6);
offset = 10;
if ((buf[5] & 0xff) == 0x00) {
isSuccessGetPastValues = true;
} else if ((buf[5] & 0xff) == 0x01) {
isSuccessGetPastValues = false;
}
}
private void handleSetDateResponse(byte[] buf) {
int type = buf[1] & 0xFF;
if (type == 0x00) {
FPLUGResponse response = new FPLUGResponse();
response.setAddress(mAddress);
mListener.onReceiveResponse(response);
} else if (type == 0x01) {
mListener.onReceiveError("set date failed");
} else {
mListener.onReceiveError("unknown response parameter");
}
}
private void handleLEDResponse(byte[] buf) {
int type = buf[1] & 0xFF;
if (type == 0x00 || type == 0x01) {
FPLUGResponse response = new FPLUGResponse();
response.setAddress(mAddress);
mListener.onReceiveResponse(response);
} else {
mListener.onReceiveError("unknown response parameter");
}
}
private String toHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
String s = String.format("%02x", b);
sb.append(s).append(" ");
}
return sb.toString();
}
}