package io.guh.nymeaapp; import java.util.List; import java.util.ArrayList; import java.util.UUID; import java.util.HashMap; import android.util.Log; import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.content.Intent; import android.content.IntentFilter; import android.content.BroadcastReceiver; import android.content.ServiceConnection; import android.content.ComponentName; import android.content.Context; import android.service.controls.Control; import android.service.controls.DeviceTypes; import io.reactivex.processors.ReplayProcessor; import org.json.*; // Helper class to establish a connection to the NymeaAppService and interact // with that using IBinder and ServiceBroadcastListener public class NymeaAppServiceConnection implements ServiceConnection { private static final String TAG = "nymea-app: NymeaAppServiceConnection"; private IBinder m_service; private Context m_context; private boolean m_connected = false; private HashMap m_nymeaHosts = new HashMap(); public NymeaAppServiceConnection(Context context) { super(); m_context = context; } final public boolean connected() { return m_connected; } public void onConnectedChanged(boolean connected) {}; final public boolean isReady(UUID nymeaId) { return m_nymeaHosts.get(nymeaId).isReady; } public void onReadyChanged(UUID nymeaId, boolean ready) {} public final HashMap getHosts() { return m_nymeaHosts; } final public Thing getThing(UUID thingId) { for (HashMap.Entry entry : m_nymeaHosts.entrySet()) { Thing thing = entry.getValue().things.get(thingId); if (thing != null) { return thing; } } return null; } final public UUID hostForThing(UUID thingId) { for (HashMap.Entry entry : m_nymeaHosts.entrySet()) { Thing thing = entry.getValue().things.get(thingId); if (thing != null) { return entry.getKey(); } } return null; } public void onError() {} public void onUpdate(UUID nymeaId, UUID thingId) {} final public void executeAction(UUID nymeaId, UUID thingId, UUID actionTypeId, String paramValue) { try { JSONObject params = new JSONObject(); params.put("nymeaId", nymeaId.toString()); params.put("thingId", thingId.toString()); params.put("actionTypeId", actionTypeId.toString()); JSONArray actionParams = new JSONArray(); JSONObject param = new JSONObject(); param.put("paramTypeId", actionTypeId.toString()); param.put("value", paramValue); actionParams.put(param); params.put("params", actionParams); Parcel parcel = createRequest("ExecuteAction", params); Parcel retParcel = Parcel.obtain(); m_service.transact(1, parcel, retParcel, 0); } catch (Exception e) { Log.d(TAG, "Error calling executeAction on NymeaAppService"); } } @Override public void onServiceConnected(ComponentName className, IBinder service) { Log.d(TAG, "Connected to NymeaAppService"); m_service = service; registerServiceBroadcastReceiver(); try { Parcel parcel = createRequest("GetInstances"); Parcel retParcel = Parcel.obtain(); m_service.transact(1, parcel, retParcel, 0); JSONObject reply = new JSONObject(retParcel.readString()); Log.d(TAG, "Instaces received: " + reply.toString()); JSONArray instances = reply.getJSONArray("instances"); for (int i = 0; i < instances.length(); i++) { JSONObject instanceMap = instances.getJSONObject(i); NymeaHost nymeaHost = new NymeaHost(); nymeaHost.id = UUID.fromString(instanceMap.getString("id")); nymeaHost.name = instanceMap.getString("name"); nymeaHost.isReady = instanceMap.getBoolean("isReady"); m_nymeaHosts.put(nymeaHost.id, nymeaHost); if (nymeaHost.isReady) { fetchThings(nymeaHost.id); } } } catch (JSONException e) { Log.d(TAG, "Error while processing JSON in communication with NymeaAppService: " + e.toString()); onError(); return; } catch (RemoteException e) { Log.d(TAG, "Error communicating with NymeaAppService: " + e.toString()); onError(); return; } m_connected = true; onConnectedChanged(m_connected); } @Override public void onServiceDisconnected(ComponentName arg0) { m_service = null; for (int i = 0; i < m_nymeaHosts.size(); i++) { m_nymeaHosts.get(i).isReady = false; } m_connected = false; onConnectedChanged(m_connected); } public void registerServiceBroadcastReceiver() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(NymeaAppService.NYMEA_APP_BROADCAST); m_context.registerReceiver(serviceMessageReceiver, intentFilter); Log.d(TAG, "Registered broadcast receiver"); } private BroadcastReceiver serviceMessageReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (NymeaAppService.NYMEA_APP_BROADCAST.equals(intent.getAction())) { String payload = intent.getStringExtra("data"); try { processBroadcast(payload); } catch(JSONException e) { Log.d(TAG, "Error parsing broadcast JSON: " + e.toString()); } } } }; private void processBroadcast(String payload) throws JSONException { JSONObject data = new JSONObject(payload); JSONObject params = data.getJSONObject("params"); // Log.d(TAG, "Broadcast received from NymeaAppService: " + data.getString("notification")); Log.d(TAG, params.toString()); if (data.getString("notification").equals("ThingStateChanged")) { UUID nymeaId = UUID.fromString(params.getString("nymeaId")); UUID thingId = UUID.fromString(params.getString("thingId")); UUID stateTypeId = UUID.fromString(params.getString("stateTypeId")); String value = params.getString("value"); // Log.d(TAG, "Thing state changed: " + thingId + " stateTypeId: " + stateTypeId + " value: " + value); Thing thing = getThing(thingId); if (thing != null) { thing.stateById(stateTypeId).value = value; onUpdate(nymeaId, thingId); } else { Log.d(TAG, "Got a state change notification for a thing we don't know!"); } } if (data.getString("notification").equals("ReadyStateChanged")) { UUID nymeaId = UUID.fromString(params.getString("nymeaId")); NymeaHost host = m_nymeaHosts.get(nymeaId); host.isReady = params.getBoolean("isReady"); if (host.isReady) { Log.d(TAG, "Host is ready. Fetching things..."); fetchThings(nymeaId); } else { Log.d(TAG, "Host is not ready yet..."); } } } private void fetchThings(UUID nymeaId) { Log.d(TAG, "Fetching things"); String thingsList; try { JSONObject params = new JSONObject(); params.put("nymeaId", nymeaId.toString()); Parcel parcel = createRequest("GetThings", params); Parcel retParcel = Parcel.obtain(); m_service.transact(1, parcel, retParcel, 0); thingsList = retParcel.readString(); } catch (Exception e) { Log.d(TAG, "Error fetching things from NymeaAppService: " + e.toString()); onError(); return; } try { JSONObject result = new JSONObject(thingsList); for (int i = 0; i < result.getJSONArray("things").length(); i++) { JSONObject entry = result.getJSONArray("things").getJSONObject(i); Thing thing = new Thing(); thing.id = UUID.fromString(entry.getString("id")); thing.name = entry.getString("name"); thing.className = entry.getString("className"); JSONArray ifaces = entry.getJSONArray("interfaces"); for (int j = 0; j < ifaces.length(); j++) { thing.interfaces.add(ifaces.get(j)); } JSONArray states = entry.getJSONArray("states"); for (int j = 0; j < states.length(); j++) { JSONObject stateMap = states.getJSONObject(j); State s = new State(); s.typeId = UUID.fromString(stateMap.getString("stateTypeId")); s.name = stateMap.getString("name"); s.displayName = stateMap.getString("displayName"); s.value = stateMap.getString("value"); thing.states.add(s); } JSONArray actions = entry.getJSONArray("actions"); for (int j = 0; j < actions.length(); j++) { JSONObject actionMap = actions.getJSONObject(j); Action a = new Action(); a.typeId = UUID.fromString(actionMap.getString("actionTypeId")); a.name = actionMap.getString("name"); a.displayName = actionMap.getString("displayName"); thing.actions.add(a); } m_nymeaHosts.get(nymeaId).things.put(thing.id, thing); } } catch (Exception e) { Log.d(TAG, "Error parsing JSON from NymeaAppService: " + e.toString()); Log.d(TAG, thingsList); m_service = null; onError(); return; } Log.d(TAG, "Things fetched: " + m_nymeaHosts.get(nymeaId).things.size()); m_nymeaHosts.get(nymeaId).isReady = true; onReadyChanged(nymeaId, true); } private Parcel createRequest(String method) throws JSONException { return createRequest(method, null); } private Parcel createRequest(String method, JSONObject params) throws JSONException { Parcel ret = Parcel.obtain(); JSONObject payload = new JSONObject(); payload.put("method", method); if (params != null) { payload.put("params", params); } Log.d(TAG, "Parcel payload: " + payload.toString()); ret.writeString(payload.toString()); return ret; } }