/*
HueDeviceService
Copyright (c) 2014 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.deviceplugin.hue;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import com.philips.lighting.hue.sdk.PHAccessPoint;
import com.philips.lighting.hue.sdk.PHBridgeSearchManager;
import com.philips.lighting.hue.sdk.PHHueSDK;
import com.philips.lighting.hue.sdk.PHSDKListener;
import com.philips.lighting.model.PHBridge;
import com.philips.lighting.model.PHBridgeResourcesCache;
import com.philips.lighting.model.PHHueError;
import com.philips.lighting.model.PHHueParsingError;
import org.deviceconnect.android.deviceplugin.hue.profile.HueSystemProfile;
import org.deviceconnect.android.deviceplugin.hue.service.HueService;
import org.deviceconnect.android.message.DConnectMessageService;
import org.deviceconnect.android.profile.SystemProfile;
import org.deviceconnect.android.service.DConnectService;
import org.json.hue.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
/**
* 本デバイスプラグインのプロファイルをDeviceConnectに登録するサービス.
*
* @author NTT DOCOMO, INC.
*/
public class HueDeviceService extends DConnectMessageService {
/**
* デバッグフラグ.
*/
private static final boolean DEBUG = BuildConfig.DEBUG;
/**
* デバッグタグ.
*/
private static final String TAG = "hue.dplugin";
/**
* ロガー.
*/
private final Logger mLogger = Logger.getLogger("hue.dplugin");
/**
* Hueのデータを管理ヘルパークラス.
*/
private HueDBHelper mHueDBHelper;
@Override
public void onCreate() {
super.onCreate();
initHueSDK();
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(mConnectivityReceiver, filter);
}
@Override
public void onDestroy() {
unregisterReceiver(mConnectivityReceiver);
destroyHueSDK();
super.onDestroy();
}
@Override
protected void onManagerUninstalled() {
// Managerアンインストール検知時の処理。
if (BuildConfig.DEBUG) {
mLogger.info("Plug-in : onManagerUninstalled");
}
}
@Override
protected void onManagerTerminated() {
// Manager正常終了通知受信時の処理。
if (BuildConfig.DEBUG) {
mLogger.info("Plug-in : onManagerTerminated");
}
}
@Override
protected void onDevicePluginReset() {
// Device Plug-inへのReset要求受信時の処理。
if (BuildConfig.DEBUG) {
mLogger.info("Plug-in : onDevicePluginReset");
}
reconnectAccessPoints();
}
@Override
protected SystemProfile getSystemProfile() {
return new HueSystemProfile();
}
/**
* HueSDKを初期化します.
*/
private void initHueSDK() {
if (DEBUG) {
Log.i(TAG, "HueDeviceService#initHueSDK");
}
mHueDBHelper = new HueDBHelper(getApplicationContext());
// hue SDKの初期化
PHHueSDK hueSDK = PHHueSDK.getInstance();
hueSDK.setAppName(HueConstants.APNAME);
hueSDK.setDeviceName(HueConstants.APNAME);
hueSDK.getNotificationManager().registerSDKListener(mPhListener);
if (DEBUG) {
Log.i(TAG, "@@@@@@ PHHueSDK version: " + hueSDK.getSDKVersion());
Log.i(TAG, "@@@@@@ PHHueSDK App Name: " + hueSDK.getAppName());
Log.i(TAG, "@@@@@@ PHHueSDK Device Name: " + hueSDK.getDeviceName());
}
reconnectAccessPoints();
}
/**
* HueSDKを破棄します.
*/
private void destroyHueSDK() {
if (DEBUG) {
Log.i(TAG, "HueDeviceService#destroyHueSDK");
}
// hue SDKの後始末
PHHueSDK hueSDK = PHHueSDK.getInstance();
hueSDK.getNotificationManager().unregisterSDKListener(mPhListener);
hueSDK.disableAllHeartbeat();
hueSDK.destroySDK();
}
/**
* Hueサービスを追加します.
*
* @param hueSDK Hue SDK
* @param accessPoint アクセスポイント
*/
private void addHueService(final PHHueSDK hueSDK, final PHAccessPoint accessPoint) {
HueService service = (HueService) getServiceProvider().getService(accessPoint.getIpAddress());
if (service == null) {
service = new HueService(accessPoint);
getServiceProvider().addService(service);
}
service.setOnline(hueSDK.isAccessPointConnected(accessPoint));
}
/**
* 登録されているアクセスポイントに再接続を行います.
*/
private void reconnectAccessPoints() {
if (DEBUG) {
mLogger.fine("HueDeviceService#reconnectAccessPoints");
}
PHHueSDK hueSDK = PHHueSDK.getInstance();
List<PHAccessPoint> accessPoints = mHueDBHelper.getAccessPoints();
for (PHAccessPoint accessPoint : accessPoints) {
if (DEBUG) {
mLogger.info("AccessPoint: " + accessPoint.getMacAddress() + " " + accessPoint.getIpAddress());
}
if (!hueSDK.isAccessPointConnected(accessPoint)) {
hueSDK.connect(accessPoint);
}
addHueService(hueSDK, accessPoint);
}
PHBridgeSearchManager sm = (PHBridgeSearchManager) hueSDK.getSDKService(PHHueSDK.SEARCH_BRIDGE);
sm.search(true, true);
}
/**
* DBに格納されているアクセスポイント情報を削除します.
*
* @param service Hueサービス
*/
public void removeHueService(final HueService service) {
mHueDBHelper.removeAccessPointByIpAddress(service.getId());
}
/**
* 接続されている全てのブリッジを切断します。
*
* @param flag serviceのonline状態を変更フラグ(trueの時は変更する)
*/
private void disconnectAllBridges(final boolean flag) {
PHHueSDK hueSDK = PHHueSDK.getInstance();
List<PHBridge> bridges = new ArrayList<PHBridge>(hueSDK.getAllBridges());
for (PHBridge bridge : bridges) {
hueSDK.disableHeartbeat(bridge);
hueSDK.disconnect(bridge);
if (flag) {
PHBridgeResourcesCache cache = bridge.getResourceCache();
String ipAddress = cache.getBridgeConfiguration().getIpAddress();
DConnectService service = getServiceProvider().getService(ipAddress);
if (service != null) {
service.setOnline(false);
}
}
}
}
/**
* Hueのブリッジとの通信を管理するリスナー.
*/
private final PHSDKListener mPhListener = new PHSDKListener() {
@Override
public void onAccessPointsFound(final List<PHAccessPoint> accessPoints) {
if (DEBUG) {
Log.i(TAG, "PHSDKListener:#onAccessPointsFound: accessPoint" +
"=" + accessPoints);
}
PHHueSDK hueSDK = PHHueSDK.getInstance();
if (accessPoints != null && accessPoints.size() > 0) {
hueSDK.getAccessPointsFound().clear();
hueSDK.getAccessPointsFound().addAll(accessPoints);
for (PHAccessPoint accessPoint : accessPoints) {
PHAccessPoint ap = mHueDBHelper.getAccessPointByMacAddress(accessPoint.getMacAddress());
if (ap != null) {
accessPoint.setUsername(ap.getUsername());
}
addHueService(hueSDK, accessPoint);
}
} else {
PHBridgeSearchManager sm = (PHBridgeSearchManager) hueSDK.getSDKService(PHHueSDK.SEARCH_BRIDGE);
sm.search(false, false, true);
}
}
@Override
public void onCacheUpdated(final List<Integer> cacheNotificationsList, final PHBridge bridge) {
if (DEBUG) {
Log.i(TAG, "PHSDKListener:#onCacheUpdated: cacheNotificationsList=" + cacheNotificationsList + ", bridge=" + bridge);
}
}
@Override
public void onBridgeConnected(final PHBridge phBridge, final String userName) {
if (DEBUG) {
Log.i(TAG, "PHSDKListener:#onBridgeConnected: bridge=" + phBridge + ", userName=" + userName);
}
String ipAddress = phBridge.getResourceCache().getBridgeConfiguration().getIpAddress();
String macAddress = phBridge.getResourceCache().getBridgeConfiguration().getMacAddress();
PHHueSDK hueSDK = PHHueSDK.getInstance();
hueSDK.setSelectedBridge(phBridge);
hueSDK.addBridge(phBridge);
hueSDK.enableHeartbeat(phBridge, PHHueSDK.HB_INTERVAL);
hueSDK.getLastHeartbeat().put(ipAddress, System.currentTimeMillis());
DConnectService service = getServiceProvider().getService(ipAddress);
if (service != null) {
service.setOnline(true);
}
// 接続されたアクセスポイント情報をDBに格納
PHAccessPoint accessPoint = new PHAccessPoint();
accessPoint.setUsername(userName);
accessPoint.setIpAddress(ipAddress);
accessPoint.setMacAddress(macAddress);
if (!mHueDBHelper.hasAccessPoint(accessPoint)) {
mHueDBHelper.addAccessPoint(accessPoint);
} else {
mHueDBHelper.updateAccessPoint(accessPoint);
}
}
@Override
public void onAuthenticationRequired(final PHAccessPoint accessPoint) {
if (DEBUG) {
Log.i(TAG, "PHSDKListener:#onAuthenticationRequired: accessPoint=" + accessPoint);
}
}
@Override
public void onConnectionResumed(final PHBridge bridge) {
if (DEBUG) {
Log.i(TAG, "PHSDKListener:#onConnectionResumed: bridge=" + bridge);
}
String ipAddress = bridge.getResourceCache().getBridgeConfiguration().getIpAddress();
PHHueSDK hueSDK = PHHueSDK.getInstance();
for (int i = 0; i < hueSDK.getDisconnectedAccessPoint().size(); i++) {
if (hueSDK.getDisconnectedAccessPoint().get(i).getIpAddress().equals(ipAddress)) {
hueSDK.getDisconnectedAccessPoint().remove(i);
}
}
DConnectService service = getServiceProvider().getService(ipAddress);
if (service != null) {
service.setOnline(true);
}
}
@Override
public void onConnectionLost(final PHAccessPoint accessPoint) {
if (DEBUG) {
Log.i(TAG, "PHSDKListener:#onConnectionLost: accessPoint=" + accessPoint);
}
PHHueSDK hueSDK = PHHueSDK.getInstance();
if (!hueSDK.getDisconnectedAccessPoint().contains(accessPoint)) {
hueSDK.getDisconnectedAccessPoint().add(accessPoint);
}
DConnectService service = getServiceProvider().getService(accessPoint.getIpAddress());
if (service != null) {
service.setOnline(false);
}
}
@Override
public void onError(final int code, final String message) {
if (DEBUG) {
Log.e(TAG, "PHSDKListener:#onError: code=" + code + ", message=" + message);
}
if (code == PHHueError.AUTHENTICATION_FAILED) {
disconnectAllBridges(false);
reconnectAccessPoints();
}
}
@Override
public void onParsingErrors(final List<PHHueParsingError> errors) {
if (DEBUG) {
Log.e(TAG, "PHSDKListener:#onParsingErrors");
for (PHHueParsingError error : errors) {
Log.e(TAG, "--");
Log.e(TAG, "code: " + error.getCode() + ", " + error.getMessage());
JSONObject obj = error.getJSONContext();
if (obj != null) {
Log.e(TAG, "" + obj.toString(4));
}
}
Log.e(TAG, "==");
}
}
};
/**
* ネットワーク状況が変わった通知を受けるレシーバー.
*/
private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
String action = intent.getAction();
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
ConnectivityManager conn = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = conn.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
reconnectAccessPoints();
} else {
disconnectAllBridges(true);
}
}
}
};
}