package org.deviceconnect.android.deviceplugin.alljoyn.profile;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Log;
import org.alljoyn.bus.BusException;
import org.alljoyn.bus.Variant;
import org.alljoyn.services.common.BusObjectDescription;
import org.allseen.LSF.ControllerService.Lamp;
import org.allseen.LSF.LampDetails;
import org.allseen.LSF.LampState;
import org.allseen.LSF.ResponseCode;
import org.deviceconnect.android.deviceplugin.alljoyn.AllJoynDeviceApplication;
import org.deviceconnect.android.deviceplugin.alljoyn.AllJoynServiceEntity;
import org.deviceconnect.android.deviceplugin.alljoyn.BuildConfig;
import org.deviceconnect.android.deviceplugin.alljoyn.OneShotSessionHandler;
import org.deviceconnect.android.deviceplugin.alljoyn.service.AllJoynService;
import org.deviceconnect.android.deviceplugin.alljoyn.util.ColorUtil;
import org.deviceconnect.android.message.MessageUtils;
import org.deviceconnect.android.profile.LightProfile;
import org.deviceconnect.android.profile.api.DConnectApi;
import org.deviceconnect.message.DConnectMessage;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Light profile for AllJoyn.
*
* @author NTT DOCOMO, INC.
*/
public class AllJoynLightProfile extends LightProfile {
private final static int TRANSITION_PERIOD = 10;
// TODO: Use property LampID in org.allseen.LSF.LampDetails instead.
private static final String LIGHT_ID_SELF = "self";
/**
* RGBの文字列の長さ.
*/
private static final int RGB_LENGTH = 6;
private enum LampServiceType {
TYPE_SINGLE_LAMP,
TYPE_LAMP_CONTROLLER,
TYPE_UNKNOWN,
}
private Map<String, FlashingExecutor> mFlashingMap = new HashMap<String, FlashingExecutor>();
public AllJoynLightProfile() {
addApi(mGetLightApi);
addApi(mPostLightApi);
addApi(mPutLightApi);
addApi(mDeleteLightApi);
}
AllJoynDeviceApplication getApplication() {
return (AllJoynDeviceApplication) getContext().getApplicationContext();
}
private final DConnectApi mGetLightApi = new DConnectApi() {
@Override
public Method getMethod() {
return Method.GET;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
final AllJoynServiceEntity service = ((AllJoynService) getService()).getEntity();
switch (getLampServiceType(service)) {
case TYPE_SINGLE_LAMP: {
onGetLightForSingleLamp(request, response, service);
return false;
}
case TYPE_LAMP_CONTROLLER: {
onGetLightForLampController(request, response, service);
return false;
}
case TYPE_UNKNOWN:
default: {
setUnsupportedError(response);
return true;
}
}
}
};
private void onGetLightForSingleLamp(@NonNull Intent request, @NonNull final Intent response
, @NonNull final AllJoynServiceEntity service) {
final AllJoynDeviceApplication app = getApplication();
OneShotSessionHandler.SessionJoinCallback callback = new OneShotSessionHandler.SessionJoinCallback() {
@Override
public void onSessionJoined(@NonNull String busName, short port, int sessionId) {
LampState proxy = app.getInterface(busName, sessionId, LampState.class);
if (proxy == null) {
MessageUtils.setUnknownError(response,
"Failed to obtain a proxy object for org.allseen.LSF.LampState .");
sendResponse(response);
return;
}
List<Bundle> lights = new ArrayList<>();
Bundle light = new Bundle();
try {
light.putString(PARAM_LIGHT_ID, LIGHT_ID_SELF);
light.putString(PARAM_NAME, service.serviceName);
light.putString(PARAM_CONFIG, "");
light.putBoolean(PARAM_ON, proxy.getOnOff());
lights.add(light);
} catch (BusException e) {
MessageUtils.setUnknownError(response, e.getLocalizedMessage());
sendResponse(response);
return;
}
response.putExtra(PARAM_LIGHTS, lights.toArray(new Bundle[lights.size()]));
setResultOK(response);
sendResponse(response);
}
@Override
public void onSessionFailed(@NonNull String busName, short port) {
MessageUtils.setUnknownError(response, "Failed to join session.");
sendResponse(response);
}
};
OneShotSessionHandler.run(getContext(), service.busName, service.port, callback);
}
private void onGetLightForLampController(@NonNull Intent request, @NonNull final Intent response
, @NonNull final AllJoynServiceEntity service) {
final AllJoynDeviceApplication app = getApplication();
OneShotSessionHandler.SessionJoinCallback callback = new OneShotSessionHandler.SessionJoinCallback() {
@Override
public void onSessionJoined(@NonNull String busName, short port, int sessionId) {
Lamp proxy = app.getInterface(busName, sessionId, Lamp.class);
if (proxy == null) {
MessageUtils.setUnknownError(response,
"Failed to obtain a proxy object for org.allseen.LSF.ControllerService.Lamp .");
sendResponse(response);
return;
}
List<Bundle> lights = new ArrayList<>();
try {
Lamp.GetAllLampIDs_return_value_uas
lampIDsResponse = proxy.getAllLampIDs();
if (lampIDsResponse.responseCode != ResponseCode.OK.getValue()) {
MessageUtils.setUnknownError(response,
"Failed to obtain lamp IDs (code: " + lampIDsResponse.responseCode + ").");
sendResponse(response);
return;
}
for (String lampId : lampIDsResponse.lampIDs) {
Bundle light = new Bundle();
light.putString(PARAM_LIGHT_ID, lampId);
Lamp.GetLampName_return_value_usss lampNameResponse =
proxy.getLampName(lampId, service.defaultLanguage);
if (lampNameResponse.responseCode != ResponseCode.OK.getValue()) {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Failed to obtain the lamp name (code: "
+ lampNameResponse.responseCode + "). Skipping this lamp...");
}
continue;
}
Lamp.GetLampState_return_value_usa_sv lampStateResponse =
proxy.getLampState(lampId);
if (lampStateResponse.responseCode != ResponseCode.OK.getValue()) {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Failed to obtain the on/off state (code: "
+ lampStateResponse.responseCode + "). Skipping this lamp...");
}
continue;
}
boolean isOn = lampStateResponse.lampState.get("OnOff")
.getObject(boolean.class);
light.putString(PARAM_NAME, lampNameResponse.lampName);
light.putString(PARAM_CONFIG, "");
light.putBoolean(PARAM_ON, isOn);
lights.add(light);
}
} catch (BusException e) {
MessageUtils.setUnknownError(response, e.getLocalizedMessage());
sendResponse(response);
return;
}
response.putExtra(PARAM_LIGHTS, lights.toArray(new Bundle[lights.size()]));
setResultOK(response);
sendResponse(response);
}
@Override
public void onSessionFailed(@NonNull String busName, short port) {
MessageUtils.setUnknownError(response, "Failed to join session.");
sendResponse(response);
}
};
OneShotSessionHandler.run(getContext(), service.busName, service.port, callback);
}
private final DConnectApi mPostLightApi = new DConnectApi() {
@Override
public Method getMethod() {
return Method.POST;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
final String serviceId = getServiceID(request);
final String lightId = getLightId(request);
final Double brightness = getBrightness(request);
final long[] flashing = getFlashing(request);
final AllJoynServiceEntity service = ((AllJoynService) getService()).getEntity();
int[] colors = getColors(request);
if (colors == null) {
MessageUtils.setInvalidRequestParameterError(response, "Parameter 'color' is invalid.");
return true;
}
switch (getLampServiceType(service)) {
case TYPE_SINGLE_LAMP: {
onPostLightForSingleLamp(request, response, service, serviceId, lightId, brightness, colors, flashing);
return false;
}
case TYPE_LAMP_CONTROLLER: {
onPostLightForLampController(request, response, service, serviceId, lightId, brightness, colors, flashing);
return false;
}
case TYPE_UNKNOWN:
default: {
setUnsupportedError(response);
return true;
}
}
}
};
private void onPostLightForSingleLamp(final Intent request, final Intent response, final AllJoynServiceEntity service,
final String serviceId, final String lightId, final Double brightness, final int[] color, final long[] flashing) {
final AllJoynDeviceApplication app = getApplication();
if (flashing != null) {
flashing(brightness, color, lightId, flashing); //do not check result of flashing
setResultOK(response);
sendResponse(response);
}else {
OneShotSessionHandler.SessionJoinCallback callback = new OneShotSessionHandler.SessionJoinCallback() {
@Override
public void onSessionJoined(final String busName, final short port, final int sessionId) {
final LampState proxyState = app.getInterface(busName, sessionId, LampState.class);
final LampDetails proxyDetails = app.getInterface(busName, sessionId, LampDetails.class);
if (proxyState == null) {
MessageUtils.setUnknownError(response,
"Failed to obtain a proxy object for org.allseen.LSF.LampState .");
sendResponse(response);
return;
}
if (proxyDetails == null) {
MessageUtils.setUnknownError(response,
"Failed to obtain a proxy object for org.allseen.LSF.LampDetails .");
sendResponse(response);
return;
}
try {
final HashMap<String, Variant> newStates = new HashMap<>();
// NOTE: Arithmetic operations in primitive types may lead to arithmetic
// overflow. To retain precision, BigDecimal objects are used.
newStates.put("OnOff", new Variant(true, "b"));
if (proxyDetails.getColor() && color != null) {
int[] hsb = ColorUtil.convertRGB_8_8_8_To_HSB_32_32_32(color);
newStates.put("Hue", new Variant(hsb[0], "u"));
newStates.put("Saturation", new Variant(hsb[1], "u"));
}
if (proxyDetails.getDimmable() && brightness != null) {
// [0, 1] -> [0, 0xffffffff]
BigDecimal tmp = BigDecimal.valueOf(0xffffffffL);
tmp = tmp.multiply(BigDecimal.valueOf(brightness));
long scaledVal = tmp.longValue();
int intScaledVal = ByteBuffer.allocate(8).putLong(scaledVal).getInt(4);
newStates.put("Brightness", new Variant(intScaledVal, "u"));
}
int responseCode = proxyState.transitionLampState(0, newStates, TRANSITION_PERIOD);
if (responseCode != ResponseCode.OK.getValue()) {
MessageUtils.setUnknownError(response,
"Failed to change lamp states (code: " + responseCode + ").");
sendResponse(response);
return;
}
} catch (BusException e) {
MessageUtils.setUnknownError(response, e.getLocalizedMessage());
sendResponse(response);
return;
}
setResultOK(response);
sendResponse(response);
}
@Override
public void onSessionFailed(final String busName, final short port) {
MessageUtils.setUnknownError(response, "Failed to join session.");
sendResponse(response);
}
};
OneShotSessionHandler.run(getContext(), service.busName, service.port, callback);
}
}
private void onPostLightForLampController(final Intent request, final Intent response,
final AllJoynServiceEntity service, final String serviceId, final String lightId,
final Double brightness, final int[] color, final long[] flashing) {
if (flashing != null) {
flashingForController(response, brightness, color, lightId, flashing); //do not check result of flashing
setResultOK(response);
sendResponse(response);
} else {
final AllJoynDeviceApplication app = getApplication();
OneShotSessionHandler.SessionJoinCallback callback = new OneShotSessionHandler.SessionJoinCallback() {
@Override
public void onSessionJoined(final String busName, final short port, final int sessionId) {
Lamp proxy = app.getInterface(busName, sessionId, Lamp.class);
if (proxy == null) {
MessageUtils.setUnknownError(response,
"Failed to obtain a proxy object for org.allseen.LSF.ControllerService.Lamp .");
sendResponse(response);
return;
}
try {
Lamp.GetAllLampIDs_return_value_uas getAllLampIDsResponse = proxy.getAllLampIDs();
if (getAllLampIDsResponse == null) {
MessageUtils.setUnknownError(response, "Failed to obtain lamp IDs.");
sendResponse(response);
return;
} else if (getAllLampIDsResponse.responseCode != ResponseCode.OK.getValue()) {
MessageUtils.setUnknownError(response,
"Failed to obtain lamp IDs (code: "
+ getAllLampIDsResponse.responseCode + ").");
sendResponse(response);
return;
}
String _lightId = lightId;
if (_lightId == null) {
_lightId = getAllLampIDsResponse.lampIDs[0];
} else if (!Arrays.asList(getAllLampIDsResponse.lampIDs).contains(_lightId)) {
MessageUtils.setInvalidRequestParameterError(response,
"A light with ID specified by 'lightId' not found.");
sendResponse(response);
return;
}
Lamp.GetLampDetails_return_value_usa_sv lampDetailsResponse = proxy.getLampDetails(_lightId);
if (lampDetailsResponse == null) {
MessageUtils.setUnknownError(response,
"Failed to obtain lamp details.");
sendResponse(response);
return;
} else if (lampDetailsResponse.responseCode != ResponseCode.OK.getValue()) {
MessageUtils.setUnknownError(response,
"Failed to obtain lamp details (code: " + lampDetailsResponse.responseCode + ").");
sendResponse(response);
return;
}
HashMap<String, Variant> newStates = new HashMap<>();
// NOTE: Arithmetic operations in primitive types may lead to arithmetic
// overflow. To retain precision, BigDecimal objects are used.
newStates.put("OnOff", new Variant(true, "b"));
if (lampDetailsResponse.lampDetails.containsKey("Color")) {
if (lampDetailsResponse.lampDetails.get("Color").getObject(boolean.class)
&& color != null) {
int[] hsb = ColorUtil.convertRGB_8_8_8_To_HSB_32_32_32(color);
newStates.put("Hue", new Variant(hsb[0], "u"));
newStates.put("Saturation", new Variant(hsb[1], "u"));
}
} else {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Color support is not described in the lamp details. " +
"Assuming it is not supported...");
}
}
if (lampDetailsResponse.lampDetails.containsKey("Dimmable")) {
if (lampDetailsResponse.lampDetails.get("Dimmable").getObject(boolean.class)
&& brightness != null) {
// [0, 1] -> [0, 0xffffffff]
BigDecimal tmp = BigDecimal.valueOf(0xffffffffL);
tmp = tmp.multiply(BigDecimal.valueOf(brightness));
long scaledVal = tmp.longValue();
int intScaledVal = ByteBuffer.allocate(8).putLong(scaledVal).getInt(4);
newStates.put("Brightness", new Variant(intScaledVal, "u"));
}
} else {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Dim support is not described in the lamp details. " +
"Assuming it is not supported...");
}
}
Lamp.TransitionLampState_return_value_us transLampStateResponse =
proxy.transitionLampState(_lightId, newStates, TRANSITION_PERIOD);
if (transLampStateResponse == null) {
MessageUtils.setUnknownError(response,
"Failed to change lamp states (not get transitionLampState).");
sendResponse(response);
return;
} else if (transLampStateResponse.responseCode != ResponseCode.OK.getValue()) {
MessageUtils.setUnknownError(response,
"Failed to change lamp states (code: " + transLampStateResponse.responseCode + ").");
sendResponse(response);
return;
}
} catch (BusException e) {
MessageUtils.setUnknownError(response, e.getLocalizedMessage());
sendResponse(response);
return;
}
setResultOK(response);
sendResponse(response);
}
@Override
public void onSessionFailed(@NonNull String busName, short port) {
MessageUtils.setUnknownError(response, "Failed to join session.");
sendResponse(response);
}
};
OneShotSessionHandler.run(getContext(), service.busName, service.port, callback);
}
}
private final DConnectApi mDeleteLightApi = new DConnectApi() {
@Override
public Method getMethod() {
return Method.DELETE;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
final String lightId = getLightId(request);
final AllJoynServiceEntity service = ((AllJoynService) getService()).getEntity();
switch (getLampServiceType(service)) {
case TYPE_SINGLE_LAMP: {
onDeleteLightForSingleLamp(request, response, service, lightId);
return false;
}
case TYPE_LAMP_CONTROLLER: {
onDeleteLightForLampController(request, response, service, lightId);
return false;
}
case TYPE_UNKNOWN:
default: {
setUnsupportedError(response);
return true;
}
}
}
};
private void onDeleteLightForSingleLamp(@NonNull Intent request, @NonNull final Intent response
, @NonNull final AllJoynServiceEntity service, @NonNull String lightId) {
final AllJoynDeviceApplication app = getApplication();
OneShotSessionHandler.SessionJoinCallback callback = new OneShotSessionHandler.SessionJoinCallback() {
@Override
public void onSessionJoined(@NonNull String busName, short port, int sessionId) {
LampState proxy = app.getInterface(busName, sessionId, LampState.class);
if (proxy == null) {
MessageUtils.setUnknownError(response,
"Failed to obtain a proxy object for org.allseen.LSF.LampState .");
sendResponse(response);
return;
}
try {
proxy.setOnOff(false);
} catch (BusException e) {
MessageUtils.setUnknownError(response, e.getLocalizedMessage());
sendResponse(response);
return;
}
setResultOK(response);
sendResponse(response);
}
@Override
public void onSessionFailed(@NonNull String busName, short port) {
MessageUtils.setUnknownError(response, "Failed to join session.");
sendResponse(response);
}
};
OneShotSessionHandler.run(getContext(), service.busName, service.port, callback);
}
private void onDeleteLightForLampController(@NonNull Intent request
, @NonNull final Intent response, @NonNull final AllJoynServiceEntity service
, @NonNull final String lightId) {
final AllJoynDeviceApplication app = getApplication();
OneShotSessionHandler.SessionJoinCallback callback = new OneShotSessionHandler.SessionJoinCallback() {
@Override
public void onSessionJoined(@NonNull String busName, short port, int sessionId) {
Lamp proxy = app.getInterface(busName, sessionId, Lamp.class);
if (proxy == null) {
MessageUtils.setUnknownError(response,
"Failed to obtain a proxy object for org.allseen.LSF.ControllerService.Lamp .");
sendResponse(response);
return;
}
try {
Lamp.GetAllLampIDs_return_value_uas getAllLampIDsResponse =
proxy.getAllLampIDs();
if (getAllLampIDsResponse == null) {
MessageUtils.setUnknownError(response, "Failed to obtain lamp IDs.");
sendResponse(response);
return;
} else if (getAllLampIDsResponse.responseCode != ResponseCode.OK.getValue()) {
MessageUtils.setUnknownError(response,
"Failed to obtain lamp IDs (code: "
+ getAllLampIDsResponse.responseCode + ").");
sendResponse(response);
return;
}
String _lightId = lightId;
if (_lightId == null) {
_lightId = getAllLampIDsResponse.lampIDs[0];
} else if (!Arrays.asList(getAllLampIDsResponse.lampIDs).contains(_lightId)) {
MessageUtils.setInvalidRequestParameterError(response,
"A light with ID specified by 'lightId' not found.");
sendResponse(response);
return;
}
Map<String, Variant> newStates = new HashMap<>();
newStates.put("OnOff", new Variant(false, "b"));
Lamp.TransitionLampState_return_value_us transLampStateResponse =
proxy.transitionLampState(_lightId, newStates, TRANSITION_PERIOD);
if (transLampStateResponse.responseCode != ResponseCode.OK.getValue()) {
MessageUtils.setUnknownError(response,
"Failed to turn off the light (code: " + transLampStateResponse.responseCode + ").");
sendResponse(response);
return;
}
} catch (BusException e) {
MessageUtils.setUnknownError(response, e.getLocalizedMessage());
sendResponse(response);
return;
}
setResultOK(response);
sendResponse(response);
}
@Override
public void onSessionFailed(@NonNull String busName, short port) {
MessageUtils.setUnknownError(response, "Failed to join session.");
sendResponse(response);
}
};
OneShotSessionHandler.run(getContext(), service.busName, service.port, callback);
}
private final DConnectApi mPutLightApi = new DConnectApi() {
@Override
public Method getMethod() {
return Method.PUT;
}
@Override
public boolean onRequest(final Intent request, final Intent response) {
final String serviceId = getServiceID(request);
final String name = getName(request);
final String lightId = getLightId(request);
final Double brightness = getBrightness(request);
final long[] flashing = getFlashing(request);
final AllJoynServiceEntity service = ((AllJoynService) getService()).getEntity();
if (service == null) {
MessageUtils.setNotFoundServiceError(response);
return true;
}
if (name == null || name.length() == 0) {
MessageUtils.setInvalidRequestParameterError(response,
"name is invalid.");
return true;
}
int[] colors = getColors(request);
if (colors == null) {
MessageUtils.setInvalidRequestParameterError(response, "Parameter 'color' is invalid.");
return true;
}
switch (getLampServiceType(service)) {
case TYPE_SINGLE_LAMP: {
onPutLightForSingleLamp(request, response, service, serviceId, lightId, name, brightness, colors, flashing);
return false;
}
case TYPE_LAMP_CONTROLLER: {
onPutLightForLampController(request, response, service, serviceId, lightId, name, brightness, colors, flashing);
return false;
}
case TYPE_UNKNOWN:
default: {
setUnsupportedError(response);
return true;
}
}
}
};
// TODO: Implement name change functionality using AllJoyn Config service.
private void onPutLightForSingleLamp(@NonNull Intent request, @NonNull final Intent response
, @NonNull AllJoynServiceEntity service, final String serviceId, @NonNull final String lightId, String name
, final Double brightness, final int[] color, final long[] flashing) {
if (brightness != null && (brightness < 0 || brightness > 1)) {
MessageUtils.setInvalidRequestParameterError(response,
"Parameter 'brightness' must be within range [0, 1].");
sendResponse(response);
return;
}
if (color != null && color.length != 3) {
MessageUtils.setInvalidRequestParameterError(response,
"Parameter 'color' must be a string representing " +
"an RGB hexadecimal (e.g. ff0000).");
sendResponse(response);
return;
}
if (flashing != null) {
flashing(brightness, color, lightId, flashing); //do not check result of flashing
setResultOK(response);
sendResponse(response);
} else {
final AllJoynDeviceApplication app = getApplication();
OneShotSessionHandler.SessionJoinCallback callback = new OneShotSessionHandler.SessionJoinCallback() {
@Override
public void onSessionJoined(@NonNull String busName, short port, int sessionId) {
LampState proxyState = app.getInterface(busName, sessionId, LampState.class);
LampDetails proxyDetails = app.getInterface(busName, sessionId, LampDetails.class);
if (proxyState == null) {
MessageUtils.setUnknownError(response,
"Failed to obtain a proxy object for org.allseen.LSF.LampState .");
sendResponse(response);
return;
}
if (proxyDetails == null) {
MessageUtils.setUnknownError(response,
"Failed to obtain a proxy object for org.allseen.LSF.LampDetails .");
sendResponse(response);
return;
}
try {
HashMap<String, Variant> newStates = new HashMap<>();
// NOTE: Arithmetic operations in primitive types may lead to arithmetic
// overflow. To retain precision, BigDecimal objects are used.
if (proxyDetails.getColor() && color != null) {
int[] hsb = ColorUtil.convertRGB_8_8_8_To_HSB_32_32_32(color);
newStates.put("Hue", new Variant(hsb[0], "u"));
newStates.put("Saturation", new Variant(hsb[1], "u"));
}
if (proxyDetails.getDimmable() && brightness != null) {
// [0, 1] -> [0, 0xffffffff]
BigDecimal tmp = BigDecimal.valueOf(0xffffffffL);
tmp = tmp.multiply(BigDecimal.valueOf(brightness));
long scaledVal = tmp.longValue();
int intScaledVal = ByteBuffer.allocate(8).putLong(scaledVal).getInt(4);
newStates.put("Brightness", new Variant(intScaledVal, "u"));
}
int responseCode = proxyState.transitionLampState(0, newStates, TRANSITION_PERIOD);
if (responseCode != ResponseCode.OK.getValue()) {
MessageUtils.setUnknownError(response,
"Failed to change lamp states (code: "
+ responseCode + ").");
sendResponse(response);
return;
}
} catch (BusException e) {
MessageUtils.setUnknownError(response, e.getLocalizedMessage());
sendResponse(response);
return;
}
setResultOK(response);
sendResponse(response);
}
@Override
public void onSessionFailed(@NonNull String busName, short port) {
MessageUtils.setUnknownError(response, "Failed to join session.");
sendResponse(response);
}
};
OneShotSessionHandler.run(getContext(), service.busName, service.port, callback);
}
}
private void onPutLightForLampController(@NonNull Intent request, @NonNull final Intent response
, @NonNull final AllJoynServiceEntity service, String serviceId, @NonNull final String lightId
, final String name, final Double brightness, final int[] color, final long[] flashing) {
if (brightness != null && (brightness < 0 || brightness > 1)) {
MessageUtils.setInvalidRequestParameterError(response,
"Parameter 'brightness' must be within range [0, 1].");
sendResponse(response);
return;
}
if (color != null && color.length != 3) {
MessageUtils.setInvalidRequestParameterError(response,
"Parameter 'color' must be a string representing " +
"an RGB hexadecimal (e.g. ff0000).");
sendResponse(response);
return;
}
if (flashing != null) {
flashingForController(response, brightness, color, lightId, flashing); //do not check result of flashing
setResultOK(response);
sendResponse(response);
} else {
final AllJoynDeviceApplication app = getApplication();
OneShotSessionHandler.SessionJoinCallback callback = new OneShotSessionHandler.SessionJoinCallback() {
@Override
public void onSessionJoined(@NonNull String busName, short port, int sessionId) {
Lamp proxy = app.getInterface(busName, sessionId, Lamp.class);
if (proxy == null) {
MessageUtils.setUnknownError(response,
"Failed to obtain a proxy object for org.allseen.LSF.ControllerService.Lamp .");
sendResponse(response);
return;
}
try {
Lamp.GetAllLampIDs_return_value_uas getAllLampIDsResponse =
proxy.getAllLampIDs();
if (getAllLampIDsResponse == null) {
MessageUtils.setUnknownError(response, "Failed to obtain lamp IDs.");
sendResponse(response);
return;
} else if (getAllLampIDsResponse.responseCode != ResponseCode.OK.getValue()) {
MessageUtils.setUnknownError(response,
"Failed to obtain lamp IDs (code: "
+ getAllLampIDsResponse.responseCode + ").");
sendResponse(response);
return;
}
String _lightId = lightId;
if (_lightId == null) {
_lightId = getAllLampIDsResponse.lampIDs[0];
} else if (!Arrays.asList(getAllLampIDsResponse.lampIDs).contains(_lightId)) {
MessageUtils.setInvalidRequestParameterError(response,
"A light with ID specified by 'lightId' not found.");
sendResponse(response);
return;
}
HashMap<String, Variant> newStates = new HashMap<>();
// NOTE: Arithmetic operations in primitive types may lead to arithmetic
// overflow. To retain precision, BigDecimal objects are used.
Lamp.GetLampDetails_return_value_usa_sv lampDetailsResponse =
proxy.getLampDetails(_lightId);
if (lampDetailsResponse.responseCode != ResponseCode.OK.getValue()) {
MessageUtils.setUnknownError(response,
"Failed to obtain lamp details (code: " + lampDetailsResponse.responseCode + ").");
sendResponse(response);
return;
}
if (lampDetailsResponse.lampDetails.containsKey("Color")) {
if (lampDetailsResponse.lampDetails.get("Color").getObject(boolean.class)
&& color != null) {
int[] hsb = ColorUtil.convertRGB_8_8_8_To_HSB_32_32_32(color);
newStates.put("Hue", new Variant(hsb[0], "u"));
newStates.put("Saturation", new Variant(hsb[1], "u"));
}
}
if (lampDetailsResponse.lampDetails.containsKey("Dimmable")) {
if (lampDetailsResponse.lampDetails.get("Dimmable").getObject(boolean.class)
&& brightness != null) {
// [0, 1] -> [0, 0xffffffff]
BigDecimal tmp = BigDecimal.valueOf(0xffffffffL);
tmp = tmp.multiply(BigDecimal.valueOf(brightness));
long scaledVal = tmp.longValue();
int intScaledVal = ByteBuffer.allocate(8).putLong(scaledVal).getInt(4);
newStates.put("Brightness", new Variant(intScaledVal, "u"));
}
}
Lamp.TransitionLampState_return_value_us transLampStateResponse =
proxy.transitionLampState(_lightId, newStates, TRANSITION_PERIOD);
if (transLampStateResponse.responseCode != ResponseCode.OK.getValue()) {
MessageUtils.setUnknownError(response,
"Failed to change lamp states (code: " + transLampStateResponse.responseCode + ").");
sendResponse(response);
return;
}
if (name != null) {
Lamp.SetLampName_return_value_uss lampNameResponse =
proxy.setLampName(_lightId, name, service.defaultLanguage);
if (lampNameResponse.responseCode != ResponseCode.OK.getValue()) {
MessageUtils.setUnknownError(response,
"Failed to change name (code: " + lampNameResponse.responseCode + ").");
sendResponse(response);
return;
}
}
} catch (BusException e) {
MessageUtils.setUnknownError(response, e.getLocalizedMessage());
sendResponse(response);
return;
}
setResultOK(response);
sendResponse(response);
}
@Override
public void onSessionFailed(@NonNull String busName, short port) {
MessageUtils.setUnknownError(response, "Failed to join session.");
sendResponse(response);
}
};
OneShotSessionHandler.run(getContext(), service.busName, service.port, callback);
}
}
private void flashing(final Double brightness, final int[] color, String lightId, long[] flashing) {
FlashingExecutor exe = mFlashingMap.get(lightId);
if (exe == null) {
exe = new FlashingExecutor();
mFlashingMap.put(lightId, exe);
}
exe.setLightControllable(new FlashingExecutor.LightControllable() {
@Override
public void changeLight(boolean isOn, final FlashingExecutor.CompleteListener listener) {
changeLightForSingleLamp(isOn, brightness, color);
listener.onComplete();
}
});
exe.start(flashing);
}
private void flashingForController(final Intent response, final Double brightness, final int[] color, final String lightId, long[] flashing) {
FlashingExecutor exe = mFlashingMap.get(lightId);
if (exe == null) {
exe = new FlashingExecutor();
mFlashingMap.put(lightId, exe);
}
exe.setLightControllable(new FlashingExecutor.LightControllable() {
@Override
public void changeLight(boolean isOn, final FlashingExecutor.CompleteListener listener) {
changeLightForController(isOn, brightness, color, lightId);
listener.onComplete();
}
});
exe.start(flashing);
}
private void changeLightForSingleLamp(final boolean isOn, final Double brightness, final int[] color) {
final AllJoynDeviceApplication app = getApplication();
final AllJoynServiceEntity service = ((AllJoynService) getService()).getEntity();
OneShotSessionHandler.SessionJoinCallback callback = new OneShotSessionHandler.SessionJoinCallback() {
@Override
public void onSessionJoined(final String busName, final short port, final int sessionId) {
final LampState proxyState = app.getInterface(busName, sessionId, LampState.class);
final LampDetails proxyDetails = app.getInterface(busName, sessionId, LampDetails.class);
if (proxyState == null) {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Failed to obtain a proxy object for org.allseen.LSF.LampState .");
}
return;
}
if (proxyDetails == null) {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Failed to obtain a proxy object for org.allseen.LSF.LampDetails .");
}
return;
}
try {
final HashMap<String, Variant> newStates = new HashMap<>();
// NOTE: Arithmetic operations in primitive types may lead to arithmetic
// overflow. To retain precision, BigDecimal objects are used.
newStates.put("OnOff", new Variant(isOn, "b"));
if (proxyDetails.getColor() && color != null) {
int[] hsb = ColorUtil.convertRGB_8_8_8_To_HSB_32_32_32(color);
newStates.put("Hue", new Variant(hsb[0], "u"));
newStates.put("Saturation", new Variant(hsb[1], "u"));
}
if (proxyDetails.getDimmable() && brightness != null) {
// [0, 1] -> [0, 0xffffffff]
BigDecimal tmp = BigDecimal.valueOf(0xffffffffL);
tmp = tmp.multiply(BigDecimal.valueOf(brightness));
long scaledVal = tmp.longValue();
int intScaledVal = ByteBuffer.allocate(8).putLong(scaledVal).getInt(4);
newStates.put("Brightness", new Variant(intScaledVal, "u"));
}
int responseCode = proxyState.transitionLampState(0, newStates, TRANSITION_PERIOD);
if (responseCode != ResponseCode.OK.getValue()) {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Failed to change lamp states (code: "
+ responseCode + ").");
}
}
} catch (BusException e) {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
e.getLocalizedMessage());
}
}
}
@Override
public void onSessionFailed(final String busName, final short port) {
}
};
OneShotSessionHandler.run(getContext(), service.busName, service.port, callback);
}
private void changeLightForController(final boolean isOn, final Double brightness, final int[] color, final String lightId) {
final AllJoynDeviceApplication app = getApplication();
final AllJoynServiceEntity service = ((AllJoynService) getService()).getEntity();
OneShotSessionHandler.SessionJoinCallback callback = new OneShotSessionHandler.SessionJoinCallback() {
@Override
public void onSessionJoined(final String busName, final short port, final int sessionId) {
Lamp proxy = app.getInterface(busName, sessionId, Lamp.class);
if (proxy == null) {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Failed to obtain a proxy object for org.allseen.LSF.ControllerService.Lamp .");
}
return;
}
try {
Lamp.GetAllLampIDs_return_value_uas getAllLampIDsResponse = proxy.getAllLampIDs();
if (getAllLampIDsResponse == null) {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Failed to obtain lamp IDs.");
}
return;
} else if (getAllLampIDsResponse.responseCode != ResponseCode.OK.getValue()) {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Failed to obtain lamp IDs (code: "
+ getAllLampIDsResponse.responseCode + ").");
}
return;
}
String _lightId = lightId;
if (_lightId == null) {
_lightId = getAllLampIDsResponse.lampIDs[0];
} else if (!Arrays.asList(getAllLampIDsResponse.lampIDs).contains(_lightId)) {
return;
}
Lamp.GetLampDetails_return_value_usa_sv lampDetailsResponse = proxy.getLampDetails(_lightId);
if (lampDetailsResponse == null) {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Failed to obtain lamp details.");
}
return;
} else if (lampDetailsResponse.responseCode != ResponseCode.OK.getValue()) {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Failed to obtain lamp details (code: " + lampDetailsResponse.responseCode + ").");
}
return;
}
final HashMap<String, Variant> newStates = new HashMap<>();
// NOTE: Arithmetic operations in primitive types may lead to arithmetic
// overflow. To retain precision, BigDecimal objects are used.
newStates.put("OnOff", new Variant(isOn, "b"));
if (lampDetailsResponse.lampDetails.containsKey("Color")) {
if (lampDetailsResponse.lampDetails.get("Color").getObject(boolean.class)
&& color != null) {
int[] hsb = ColorUtil.convertRGB_8_8_8_To_HSB_32_32_32(color);
newStates.put("Hue", new Variant(hsb[0], "u"));
newStates.put("Saturation", new Variant(hsb[1], "u"));
}
} else {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Color support is not described in the lamp details. " +
"Assuming it is not supported...");
}
}
if (lampDetailsResponse.lampDetails.containsKey("Dimmable")) {
if (lampDetailsResponse.lampDetails.get("Dimmable").getObject(boolean.class)
&& brightness != null) {
// [0, 1] -> [0, 0xffffffff]
BigDecimal tmp = BigDecimal.valueOf(0xffffffffL);
tmp = tmp.multiply(BigDecimal.valueOf(brightness));
long scaledVal = tmp.longValue();
int intScaledVal = ByteBuffer.allocate(8).putLong(scaledVal).getInt(4);
newStates.put("Brightness", new Variant(intScaledVal, "u"));
}
} else {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Dim support is not described in the lamp details. " +
"Assuming it is not supported...");
}
}
Lamp.TransitionLampState_return_value_us transLampStateResponse =
proxy.transitionLampState(_lightId, newStates, TRANSITION_PERIOD);
if (transLampStateResponse == null) {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Failed to change lamp states (not get transitionLampStatecode).");
}
} else if (transLampStateResponse.responseCode != ResponseCode.OK.getValue()) {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
"Failed to change lamp states (code: " + transLampStateResponse.responseCode + ").");
}
}
} catch (BusException e) {
if (BuildConfig.DEBUG) {
Log.w(AllJoynLightProfile.this.getClass().getSimpleName(),
e.getLocalizedMessage());
}
}
}
@Override
public void onSessionFailed(final String busName, final short port) {
}
};
OneShotSessionHandler.run(getContext(), service.busName, service.port, callback);
}
/**
* 成功レスポンス設定。
*
* @param response response
*/
private void setResultOK(final Intent response) {
setResult(response, DConnectMessage.RESULT_OK);
}
private boolean isSupportingInterfaces(final AllJoynServiceEntity service,
final String... interfaces) {
if (interfaces == null || interfaces.length == 0
|| service.proxyObjects == null) {
return false;
}
for (String ifaceCheck : interfaces) {
boolean found = false;
for (BusObjectDescription busObject : service.proxyObjects) {
if (Arrays.asList(busObject.interfaces).contains(ifaceCheck)) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
}
private LampServiceType getLampServiceType(@NonNull AllJoynServiceEntity service) {
if (isSupportingInterfaces(service, "org.allseen.LSF.ControllerService.Lamp")) {
// Can manage and control multiple lamps.
return LampServiceType.TYPE_LAMP_CONTROLLER;
} else if (isSupportingInterfaces(service, "org.allseen.LSF.LampState")) {
// Can control a single lamp.
return LampServiceType.TYPE_SINGLE_LAMP;
}
return LampServiceType.TYPE_UNKNOWN;
}
/**
* リクエストからcolorパラメータを取得する.
*
* @param request リクエスト
* @return colorパラメータ
*/
private String getColorString(final Intent request) {
return request.getStringExtra(PARAM_COLOR);
}
/**
* Get color parameter.
*
* @param colorParam color in string expression
* @param color Color parameter.
* @return true : Success, false : failure.
*/
private static boolean parseColorParam(final String colorParam, final int[] color) {
try {
String rr = colorParam.substring(0, 2);
String gg = colorParam.substring(2, 4);
String bb = colorParam.substring(4, 6);
if (colorParam.length() == RGB_LENGTH) {
if (rr == null || gg == null || bb == null) {
return false;
}
color[0] = Integer.parseInt(rr, 16);
color[1] = Integer.parseInt(gg, 16);
color[2] = Integer.parseInt(bb, 16);
} else {
return false;
}
} catch (NumberFormatException e) {
return false;
} catch (IllegalArgumentException e) {
return false;
}
return true;
}
private int[] getColors(final Intent request) {
int[] colors = new int[3];
String colorParam = getColorString(request);
if (colorParam != null && !parseColorParam(colorParam, colors)) {
return null;
}
return colors;
}
}