Debugging MOzilla IOT impl

This commit is contained in:
BWS Systems
2019-05-24 15:22:34 -05:00
parent 79d5b5da28
commit 5f6bfae41a
41 changed files with 1319 additions and 152 deletions

View File

@@ -160,7 +160,7 @@ public class BridgeSettings extends BackupHandler {
}
theBridgeSettings.setSomfyAddress(theSomfyList);
theBridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true")));
// theBridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true")));
theBridgeSettings.setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false")));
theBridgeSettings.setButtonsleep(Integer.parseInt(System.getProperty("button.sleep", Configuration.DEFAULT_BUTTON_SLEEP)));
theBridgeSettings.setNestuser(System.getProperty("nest.user"));
@@ -223,9 +223,13 @@ public class BridgeSettings extends BackupHandler {
if(serverIpOverride != null) {
theBridgeSettings.setWebaddress(serverIpOverride);
theBridgeSettings.setUpnpConfigAddress(serverIpOverride);
}
}
/*
if(upnpStrictOverride != null)
theBridgeSettings.setUpnpStrict(Boolean.parseBoolean(upnpStrictOverride));
theBridgeSettings.setUpnpStrict(Boolean.parseBoolean(upnpStrictOverride));
*/
setupParams(Paths.get(theBridgeSettings.getConfigfile()), ".cfgbk", "habridge.config-");
bridgeSecurity.setSecurityData(theBridgeSettings.getSecurityData());

View File

@@ -90,9 +90,9 @@ public class BridgeSettingsDescriptor {
@SerializedName("openhabaddress")
@Expose
private IpList openhabaddress;
@SerializedName("moziotgateway")
@SerializedName("moziotaddress")
@Expose
private IpList moziotgateway;
private IpList moziotaddress;
@SerializedName("hubversion")
@Expose
private String hubversion;
@@ -120,6 +120,9 @@ public class BridgeSettingsDescriptor {
@SerializedName("tracestate")
@Expose
private boolean tracestate;
@SerializedName("upnporiginal")
@Expose
private boolean upnporiginal;
// @SerializedName("activeloggers")
// @Expose
// private List<NameValue> activeloggers;
@@ -142,11 +145,11 @@ public class BridgeSettingsDescriptor {
// Deprecated settings
private String haltoken;
private boolean upnpstrict;
// private boolean upnpstrict;
public BridgeSettingsDescriptor() {
super();
this.upnpstrict = true;
// this.upnpstrict = true;
this.useupnpiface = false;
this.userooms = false;
this.traceupnp = false;
@@ -175,6 +178,7 @@ public class BridgeSettingsDescriptor {
this.upnpsenddelay = Configuration.UPNP_SEND_DELAY;
this.broadlinkconfigured = false;
this.tracestate = false;
this.upnporiginal = false;
}
public String getUpnpConfigAddress() {
@@ -280,7 +284,7 @@ public class BridgeSettingsDescriptor {
public void setHarmonyAddress(IpList harmonyaddress) {
this.harmonyaddress = harmonyaddress;
}
/*
public boolean isUpnpStrict() {
return upnpstrict;
}
@@ -288,7 +292,7 @@ public class BridgeSettingsDescriptor {
public void setUpnpStrict(boolean upnpStrict) {
this.upnpstrict = upnpStrict;
}
*/
public boolean isTraceupnp() {
return traceupnp;
}
@@ -753,19 +757,19 @@ public class BridgeSettingsDescriptor {
this.tracestate = tracestate;
}
public IpList getMoziotgateway() {
return moziotgateway;
public IpList getMoziotaddress() {
return moziotaddress;
}
public void setMoziotgateway(IpList moziotgateway) {
this.moziotgateway = moziotgateway;
public void setMoziotgaddress(IpList moziotgateway) {
this.moziotaddress = moziotgateway;
}
public Boolean isValidMozIot() {
if (this.getMoziotgateway() == null || this.getMoziotgateway().getDevices().size() <= 0)
if (this.getMoziotaddress() == null || this.getMoziotaddress().getDevices().size() <= 0)
return false;
List<NamedIP> devicesList = this.getMoziotgateway().getDevices();
List<NamedIP> devicesList = this.getMoziotaddress().getDevices();
if (devicesList.get(0).getIp().contains(Configuration.DEFAULT_ADDRESS))
return false;
@@ -779,4 +783,12 @@ public class BridgeSettingsDescriptor {
public void setMoziotconfigured(boolean moziotconfigured) {
this.moziotconfigured = moziotconfigured;
}
public boolean isUpnporiginal() {
return upnporiginal;
}
public void setUpnporiginal(boolean upnporiginal) {
this.upnporiginal = upnporiginal;
}
}

View File

@@ -32,6 +32,7 @@ public class DeviceMapTypes {
public final static String[] SOMFY_DEVICE = { "somfyDevice", "Somfy Device"};
public final static String[] LIFX_DEVICE = { "lifxDevice", "LIFX Device"};
public final static String[] OPENHAB_DEVICE = { "openhabDevice", "OpenHAB Device"};
public final static String[] MOZIOT_DEVICE = { "moziotDevice", "Mozilla IOT Device"};
public final static String[] FHEM_DEVICE = { "fhemDevice", "FHEM Device"};
public final static String[] BROADLINK_DEVICE = { "broadlinkDevice", "Broadlink Device"};
@@ -67,6 +68,7 @@ public class DeviceMapTypes {
deviceMapTypes.add(FIBARO_SCENE);
deviceMapTypes.add(SOMFY_DEVICE);
deviceMapTypes.add(OPENHAB_DEVICE);
deviceMapTypes.add(MOZIOT_DEVICE);
deviceMapTypes.add(FHEM_DEVICE);
deviceMapTypes.add(BROADLINK_DEVICE);
}

View File

@@ -20,6 +20,7 @@ import com.bwssystems.HABridge.plugins.homewizard.HomeWizardHome;
import com.bwssystems.HABridge.plugins.http.HTTPHome;
import com.bwssystems.HABridge.plugins.hue.HueHome;
import com.bwssystems.HABridge.plugins.lifx.LifxHome;
import com.bwssystems.HABridge.plugins.moziot.MozIotHome;
import com.bwssystems.HABridge.plugins.mqtt.MQTTHome;
import com.bwssystems.HABridge.plugins.openhab.OpenHABHome;
import com.bwssystems.HABridge.plugins.somfy.SomfyHome;
@@ -120,7 +121,11 @@ public class HomeManager {
aHome = new OpenHABHome(bridgeSettings);
resourceList.put(DeviceMapTypes.OPENHAB_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.OPENHAB_DEVICE[DeviceMapTypes.typeIndex], aHome);
//setup the OpenHAB configuration if available
//setup the Mozilla IOT configuration if available
aHome = new MozIotHome(bridgeSettings);
resourceList.put(DeviceMapTypes.MOZIOT_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.MOZIOT_DEVICE[DeviceMapTypes.typeIndex], aHome);
//setup the FHEM configuration if available
aHome = new FHEMHome(bridgeSettings);
resourceList.put(DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex], aHome);
homeList.put(DeviceMapTypes.FHEM_DEVICE[DeviceMapTypes.typeIndex], aHome);

View File

@@ -321,6 +321,12 @@ public class DeviceResource {
return homeManager.findResource(DeviceMapTypes.OPENHAB_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.OPENHAB_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/moziot/devices", "application/json", (request, response) -> {
log.debug("Get MOzilla IOT devices");
response.status(HttpStatus.SC_OK);
return homeManager.findResource(DeviceMapTypes.MOZIOT_DEVICE[DeviceMapTypes.typeIndex]).getItems(DeviceMapTypes.MOZIOT_DEVICE[DeviceMapTypes.typeIndex]);
}, new JsonTransformer());
get (API_CONTEXT + "/fhem/devices", "application/json", (request, response) -> {
log.debug("Get FHEM devices");
response.status(HttpStatus.SC_OK);

View File

@@ -0,0 +1,8 @@
package com.bwssystems.HABridge.plugins.moziot;
public class Actions {
}

View File

@@ -0,0 +1,8 @@
package com.bwssystems.HABridge.plugins.moziot;
public class Events {
}

View File

@@ -0,0 +1,21 @@
package com.bwssystems.HABridge.plugins.moziot;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class JWT {
@SerializedName("jwt")
@Expose
private String jwt;
public String getJwt() {
return jwt;
}
public void setJwt(String jwt) {
this.jwt = jwt;
}
}

View File

@@ -0,0 +1,88 @@
package com.bwssystems.HABridge.plugins.moziot;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Level {
@SerializedName("title")
@Expose
private String title;
@SerializedName("type")
@Expose
private String type;
@SerializedName("@type")
@Expose
private String attype;
@SerializedName("unit")
@Expose
private String unit;
@SerializedName("minimum")
@Expose
private Integer minimum;
@SerializedName("maximum")
@Expose
private Integer maximum;
@SerializedName("links")
@Expose
private List<Link> links = null;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getAttype() {
return attype;
}
public void setAttype(String attype) {
this.attype = attype;
}
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
public Integer getMinimum() {
return minimum;
}
public void setMinimum(Integer minimum) {
this.minimum = minimum;
}
public Integer getMaximum() {
return maximum;
}
public void setMaximum(Integer maximum) {
this.maximum = maximum;
}
public List<Link> getLinks() {
return links;
}
public void setLinks(List<Link> links) {
this.links = links;
}
}

View File

@@ -0,0 +1,43 @@
package com.bwssystems.HABridge.plugins.moziot;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Link {
@SerializedName("rel")
@Expose
private String rel;
@SerializedName("href")
@Expose
private String href;
@SerializedName("mediaType")
@Expose
private String mediaType;
public String getRel() {
return rel;
}
public void setRel(String rel) {
this.rel = rel;
}
public String getHref() {
return href;
}
public void setHref(String href) {
this.href = href;
}
public String getMediaType() {
return mediaType;
}
public void setMediaType(String mediaType) {
this.mediaType = mediaType;
}
}

View File

@@ -0,0 +1,19 @@
package com.bwssystems.HABridge.plugins.moziot;
public class MozIotCommand {
private String url;
private String command;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
}

View File

@@ -0,0 +1,207 @@
package com.bwssystems.HABridge.plugins.moziot;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bwssystems.HABridge.BridgeSettings;
import com.bwssystems.HABridge.Home;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.CallItem;
import com.bwssystems.HABridge.api.hue.HueError;
import com.bwssystems.HABridge.api.hue.HueErrorResponse;
import com.bwssystems.HABridge.dao.DeviceDescriptor;
import com.bwssystems.HABridge.hue.BrightnessDecode;
import com.bwssystems.HABridge.hue.ColorData;
import com.bwssystems.HABridge.hue.ColorDecode;
import com.bwssystems.HABridge.hue.DeviceDataDecode;
import com.bwssystems.HABridge.hue.MultiCommandUtil;
import com.bwssystems.HABridge.hue.TimeDecode;
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
import com.bwssystems.HABridge.plugins.http.HTTPHome;
import com.google.gson.Gson;
public class MozIotHome implements Home {
private static final Logger log = LoggerFactory.getLogger(MozIotHome.class);
private Map<String, MozIotInstance> moziotMap;
private Boolean validMoziot;
private HTTPHandler httpClient;
private boolean closed;
public MozIotHome(BridgeSettings bridgeSettings) {
super();
closed = true;
createHome(bridgeSettings);
closed = false;
}
@Override
public String deviceHandler(CallItem anItem, MultiCommandUtil aMultiUtil, String lightId, int intensity,
Integer targetBri, Integer targetBriInc, ColorData colorData, DeviceDescriptor device, String body) {
String theUrl = anItem.getItem().getAsString();
String responseString = null;
if(theUrl != null && !theUrl.isEmpty()) {
MozIotCommand theCommand = null;
try {
theCommand = new Gson().fromJson(theUrl, MozIotCommand.class);
} catch(Exception e) {
log.warn("Cannot parse command to Mozilla IOT <<<" + theUrl + ">>>", e);
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
"Error on calling url to change device state", "/lights/"
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
return responseString;
}
String intermediate = theCommand.getUrl().substring(theCommand.getUrl().indexOf("://") + 3);
String hostPortion = intermediate.substring(0, intermediate.indexOf('/'));
String theUrlBody = intermediate.substring(intermediate.indexOf('/') + 1);
String hostAddr = null;
if (hostPortion.contains(":")) {
hostAddr = hostPortion.substring(0, intermediate.indexOf(':'));
} else
hostAddr = hostPortion;
MozIotInstance theHandler = findHandlerByAddress(hostAddr);
if(theHandler != null) {
String anUrl = BrightnessDecode.calculateReplaceIntensityValue(theUrlBody,
intensity, targetBri, targetBriInc, false);
if (colorData != null) {
anUrl = ColorDecode.replaceColorData(anUrl, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
}
anUrl = DeviceDataDecode.replaceDeviceData(anUrl, device);
anUrl = TimeDecode.replaceTimeValue(anUrl);
String aCommand = null;
if(theCommand.getCommand() != null && !theCommand.getCommand().isEmpty()) {
aCommand = BrightnessDecode.calculateReplaceIntensityValue(theCommand.getCommand(),
intensity, targetBri, targetBriInc, false);
if (colorData != null) {
aCommand = ColorDecode.replaceColorData(aCommand, colorData, BrightnessDecode.calculateIntensity(intensity, targetBri, targetBriInc), false);
}
aCommand = DeviceDataDecode.replaceDeviceData(aCommand, device);
aCommand = TimeDecode.replaceTimeValue(aCommand);
}
try {
boolean success = theHandler.callCommand(anUrl, aCommand, httpClient);
if(!success) {
log.warn("Comand had error to Mozilla IOT");
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
"Error on calling url to change device state", "/lights/"
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
}
} catch (Exception e) {
log.warn("Cannot send comand to Mozilla IOT", e);
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
"Error on calling url to change device state", "/lights/"
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
}
} else {
log.warn("Mozilla IOT Call could not complete, no address found: " + theUrl);
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
"Error on calling url to change device state", "/lights/"
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
}
} else {
log.warn("Mozilla IOT Call to be presented as http(s)://<ip_address>(:<port>)/payload, format of request unknown: " + theUrl);
responseString = new Gson().toJson(HueErrorResponse.createResponse("6", "/lights/" + lightId,
"Error on calling url to change device state", "/lights/"
+ lightId + "state", null, null).getTheErrors(), HueError[].class);
}
return responseString;
}
@Override
public Object getItems(String type) {
if(!validMoziot)
return null;
log.debug("consolidating devices for Mozilla IOT");
List<MozillaThing> theResponse = null;
Iterator<String> keys = moziotMap.keySet().iterator();
List<MozillaThing> deviceList = new ArrayList<MozillaThing>();
while(keys.hasNext()) {
String key = keys.next();
theResponse = moziotMap.get(key).getDevices(httpClient);
if(theResponse != null)
addMozIotDevices(deviceList, theResponse, key);
else {
log.warn("Cannot get devices for Mozilla IOT with name: " + key + ", skipping this Mozilla IOT.");
continue;
}
}
return deviceList;
}
private Boolean addMozIotDevices(List<MozillaThing> theDeviceList, List<MozillaThing> theSourceList, String theKey) {
Iterator<MozillaThing> devices = theSourceList.iterator();
while(devices.hasNext()) {
MozillaThing theDevice = devices.next();
theDeviceList.add(theDevice);
}
return true;
}
@Override
public Home createHome(BridgeSettings bridgeSettings) {
moziotMap = null;
validMoziot = bridgeSettings.getBridgeSettingsDescriptor().isValidMozIot();
log.info("Mozilla IOT Home created." + (validMoziot ? "" : " No Mozilla IOTs configured."));
if(validMoziot) {
moziotMap = new HashMap<String,MozIotInstance>();
httpClient = HTTPHome.getHandler();
Iterator<NamedIP> theList = bridgeSettings.getBridgeSettingsDescriptor().getMoziotaddress().getDevices().iterator();
while(theList.hasNext() && validMoziot) {
NamedIP aMoziot = theList.next();
try {
moziotMap.put(aMoziot.getName(), new MozIotInstance(aMoziot, httpClient));
} catch (Exception e) {
log.error("Cannot get Mozilla IOT (" + aMoziot.getName() + ") setup, Exiting with message: " + e.getMessage(), e);
validMoziot = false;
}
}
}
return this;
}
private MozIotInstance findHandlerByAddress(String hostAddress) {
MozIotInstance aHandler = null;
boolean found = false;
Iterator<String> keys = moziotMap.keySet().iterator();
while(keys.hasNext()) {
String key = keys.next();
aHandler = moziotMap.get(key);
if(aHandler != null && aHandler.getMozIotIP().getIp().equals(hostAddress)) {
found = true;
break;
}
}
if(!found)
aHandler = null;
return aHandler;
}
@Override
public void closeHome() {
log.debug("Closing Home.");
if(!closed && validMoziot) {
log.debug("Home is already closed....");
return;
}
if(httpClient != null)
httpClient.closeHandler();
moziotMap = null;
closed = true;
}
@Override
public void refresh() {
// noop
}
}

View File

@@ -0,0 +1,131 @@
package com.bwssystems.HABridge.plugins.moziot;
import java.util.ArrayList;
import java.util.List;
import com.bwssystems.HABridge.NamedIP;
import com.bwssystems.HABridge.api.NameValue;
import com.bwssystems.HABridge.plugins.http.HTTPHandler;
import com.google.gson.Gson;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpPost;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MozIotInstance {
private static final Logger log = LoggerFactory.getLogger(MozIotInstance.class);
private JWT moziotToken;
private NamedIP mozIotIP;
private NameValue[] headers;
public MozIotInstance(NamedIP theNamedIp, HTTPHandler httpClient) {
mozIotIP = theNamedIp;
headers = null;
gatewayLogin(httpClient);
}
public Boolean callCommand(String aCommand, String commandData, HTTPHandler httpClient) {
log.debug("calling Mozilla IOT: " + mozIotIP.getIp() + ":" + mozIotIP.getPort() + aCommand);
String aUrl = null;
if (mozIotIP.getSecure() != null && mozIotIP.getSecure())
aUrl = "https://";
else
aUrl = "http://";
headers = getAuthHeader();
aUrl = aUrl + mozIotIP.getIp() + ":" + mozIotIP.getPort() + "/" + aCommand;
String theData = httpClient.doHttpRequest(aUrl, HttpPut.METHOD_NAME, "application/json", commandData, headers);
log.debug("call Command return is: <" + theData + ">");
if (theData.contains("error") || theData.contains("ERROR") || theData.contains("Error"))
return false;
return true;
}
public List<MozillaThing> getDevices(HTTPHandler httpClient) {
log.debug("calling Mozilla IOT: " + mozIotIP.getIp() + ":" + mozIotIP.getPort());
List<MozillaThing> deviceList = null;
MozillaThing[] theThings;
String theUrl = null;
String theData;
if (mozIotIP.getSecure() != null && mozIotIP.getSecure())
theUrl = "https://";
else
theUrl = "http://";
headers = getAuthHeader();
theUrl = theUrl + mozIotIP.getIp() + ":" + mozIotIP.getPort() + "/things";
theData = httpClient.doHttpRequest(theUrl, HttpGet.METHOD_NAME, "application/json", null, headers);
if (theData != null) {
log.debug("GET Mozilla IOT Devices - data: " + theData);
try {
theThings = new Gson().fromJson(theData, MozillaThing[].class);
if (theThings != null && theThings.length > 0) {
deviceList = new ArrayList<MozillaThing>();
for (int i = 0; i < theThings.length; i++) {
deviceList.add(theThings[i]);
}
}
} catch (Exception e) {
log.warn("Cannot get an devices for Mozilla IOT " + mozIotIP.getName() + " Gson Parse Error.");
}
}
return deviceList;
}
private NameValue[] getAuthHeader() {
if (headers == null) {
headers = new NameValue[1];
headers[0] = new NameValue();
headers[0].setName("Authorization");
headers[0].setValue("Bearer " + moziotToken.getJwt());
}
return headers;
}
private void gatewayLogin(HTTPHandler httpClient) {
String aUrl = null;
if (mozIotIP.getSecure() != null && mozIotIP.getSecure())
aUrl = "https://";
else
aUrl = "http://";
aUrl = aUrl + mozIotIP.getIp() + ":" + mozIotIP.getPort() + "/login";
String commandData = "{\"email\": \"" + mozIotIP.getUsername() + "\", \"password\":\"" + mozIotIP.getPassword()
+ "\"}";
String theData = httpClient.doHttpRequest(aUrl, HttpPost.METHOD_NAME, "application/json", commandData, null);
if (theData != null) {
log.info("GET Mozilla login - data: " + theData);
try {
moziotToken = new Gson().fromJson(theData, JWT.class);
} catch (Exception e) {
log.warn("Cannot get login for Mozilla IOT " + mozIotIP.getName() + " Gson Parse Error.");
}
} else {
log.warn("Could not login " + mozIotIP.getName() + " error: <<<" + theData + ">>>");
}
}
public NamedIP getMozIotIP() {
return mozIotIP;
}
public void setMozIotIP(NamedIP mozIotIP) {
this.mozIotIP = mozIotIP;
}
protected void closeClient() {
}
public JWT getMoziotToken() {
return moziotToken;
}
public void setMoziotToken(JWT moziotToken) {
this.moziotToken = moziotToken;
}
}

View File

@@ -0,0 +1,154 @@
package com.bwssystems.HABridge.plugins.moziot;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class MozillaThing {
@SerializedName("name")
@Expose
private String name;
@SerializedName("type")
@Expose
private String type;
@SerializedName("@context")
@Expose
private String atcontext;
@SerializedName("@type")
@Expose
private List<String> attype = null;
@SerializedName("description")
@Expose
private String description;
@SerializedName("href")
@Expose
private String href;
@SerializedName("properties")
@Expose
private Properties properties;
@SerializedName("actions")
@Expose
private Actions actions;
@SerializedName("events")
@Expose
private Events events;
@SerializedName("links")
@Expose
private List<Link> links = null;
@SerializedName("layoutIndex")
@Expose
private Integer layoutIndex;
@SerializedName("selectedCapability")
@Expose
private String selectedCapability;
@SerializedName("iconHref")
@Expose
private Object iconHref;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getAtcontext() {
return atcontext;
}
public void setAtcontext(String atcontext) {
this.atcontext = atcontext;
}
public List<String> getAttype() {
return attype;
}
public void setAttype(List<String> attype) {
this.attype = attype;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getHref() {
return href;
}
public void setHref(String href) {
this.href = href;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public Actions getActions() {
return actions;
}
public void setActions(Actions actions) {
this.actions = actions;
}
public Events getEvents() {
return events;
}
public void setEvents(Events events) {
this.events = events;
}
public List<Link> getLinks() {
return links;
}
public void setLinks(List<Link> links) {
this.links = links;
}
public Integer getLayoutIndex() {
return layoutIndex;
}
public void setLayoutIndex(Integer layoutIndex) {
this.layoutIndex = layoutIndex;
}
public String getSelectedCapability() {
return selectedCapability;
}
public void setSelectedCapability(String selectedCapability) {
this.selectedCapability = selectedCapability;
}
public Object getIconHref() {
return iconHref;
}
public void setIconHref(Object iconHref) {
this.iconHref = iconHref;
}
}

View File

@@ -0,0 +1,55 @@
package com.bwssystems.HABridge.plugins.moziot;
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class On {
@SerializedName("title")
@Expose
private String title;
@SerializedName("type")
@Expose
private String type;
@SerializedName("attype")
@Expose
private String attype;
@SerializedName("links")
@Expose
private List<Link> links = null;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getAttype() {
return attype;
}
public void setAttype(String attype) {
this.attype = attype;
}
public List<Link> getLinks() {
return links;
}
public void setLinks(List<Link> links) {
this.links = links;
}
}

View File

@@ -0,0 +1,32 @@
package com.bwssystems.HABridge.plugins.moziot;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Properties {
@SerializedName("on")
@Expose
private On on;
@SerializedName("level")
@Expose
private Level level;
public On getOn() {
return on;
}
public void setOn(On on) {
this.on = on;
}
public Level getLevel() {
return level;
}
public void setLevel(Level level) {
this.level = level;
}
}

View File

@@ -17,13 +17,13 @@ import java.time.temporal.ChronoUnit;
import java.util.Enumeration;
import org.apache.http.conn.util.*;
public class UpnpListener {
private Logger log = LoggerFactory.getLogger(UpnpListener.class);
private MulticastSocket upnpMulticastSocket;
private int httpServerPort;
private String upnpConfigIP;
private boolean strict;
// private boolean strict;
private boolean upnpOriginal;
private boolean traceupnp;
private boolean useUpnpIface;
private BridgeControlDescriptor bridgeControl;
@@ -31,77 +31,67 @@ public class UpnpListener {
private String bridgeSNUUID;
private HuePublicConfig aHueConfig;
private Integer theUpnpSendDelay;
private String responseTemplate1 = "HTTP/1.1 200 OK\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"EXT:\r\n" +
"LOCATION: http://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"hue-bridgeid: %s\r\n" +
"ST: upnp:rootdevice\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n";
private String responseTemplate2 = "HTTP/1.1 200 OK\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"EXT:\r\n" +
"LOCATION: http://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"hue-bridgeid: %s\r\n" +
"ST: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String responseTemplate3 = "HTTP/1.1 200 OK\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"EXT:\r\n" +
"LOCATION: http://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"hue-bridgeid: %s\r\n" +
"ST: urn:schemas-upnp-org:device:basic:1\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String responseTemplateOriginal = "HTTP/1.1 200 OK\r\n" + "CACHE-CONTROL: max-age=86400\r\n" + "EXT:\r\n"
+ "LOCATION: http://%s:%s/description.xml\r\n" + "SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "ST: urn:schemas-upnp-org:device:basic:1\r\n" + "USN: uuid:"
+ HueConstants.UUID_PREFIX + "%s::urn:schemas-upnp-org:device:basic:1\r\n\r\n";
private String responseTemplate1 = "HTTP/1.1 200 OK\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
+ "EXT:\r\n" + "LOCATION: http://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "hue-bridgeid: %s\r\n" + "ST: upnp:rootdevice\r\n" + "USN: uuid:"
+ HueConstants.UUID_PREFIX + "%s::upnp:rootdevice\r\n\r\n";
private String responseTemplate2 = "HTTP/1.1 200 OK\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
+ "EXT:\r\n" + "LOCATION: http://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "hue-bridgeid: %s\r\n" + "ST: uuid:" + HueConstants.UUID_PREFIX
+ "%s\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String responseTemplate3 = "HTTP/1.1 200 OK\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
+ "EXT:\r\n" + "LOCATION: http://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "hue-bridgeid: %s\r\n" + "ST: urn:schemas-upnp-org:device:basic:1\r\n"
+ "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String notifyTemplate = "NOTIFY * HTTP/1.1\r\n" +
"HOST: %s:%s\r\n" +
"CACHE-CONTROL: max-age=100\r\n" +
"LOCATION: http://%s:%s/description.xml\r\n" +
"SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/" + HueConstants.API_VERSION + "\r\n" +
"NTS: ssdp:alive\r\n" +
"hue-bridgeid: %s\r\n" +
"NT: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n" +
"USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
private String notifyTemplate = "NOTIFY * HTTP/1.1\r\n" + "HOST: %s:%s\r\n" + "CACHE-CONTROL: max-age=100\r\n"
+ "LOCATION: http://%s:%s/description.xml\r\n" + "SERVER: Linux/3.14.0 UPnP/1.0 IpBridge/"
+ HueConstants.API_VERSION + "\r\n" + "NTS: ssdp:alive\r\n" + "hue-bridgeid: %s\r\n" + "NT: uuid:"
+ HueConstants.UUID_PREFIX + "%s\r\n" + "USN: uuid:" + HueConstants.UUID_PREFIX + "%s\r\n\r\n";
public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl, UDPDatagramSender aUdpDatagramSender) throws IOException {
public UpnpListener(BridgeSettingsDescriptor theSettings, BridgeControlDescriptor theControl,
UDPDatagramSender aUdpDatagramSender) throws IOException {
super();
upnpMulticastSocket = null;
httpServerPort = Integer.valueOf(theSettings.getServerPort());
upnpConfigIP = theSettings.getUpnpConfigAddress();
strict = theSettings.isUpnpStrict();
// strict = theSettings.isUpnpStrict();
upnpOriginal = theSettings.isUpnporiginal();
traceupnp = theSettings.isTraceupnp();
useUpnpIface = theSettings.isUseupnpiface();
theUpnpSendDelay = theSettings.getUpnpsenddelay();
bridgeControl = theControl;
aHueConfig = HuePublicConfig.createConfig("temp", upnpConfigIP, HueConstants.HUB_VERSION, theSettings.getHubmac());
aHueConfig = HuePublicConfig.createConfig("temp", upnpConfigIP, HueConstants.HUB_VERSION,
theSettings.getHubmac());
bridgeId = aHueConfig.getBridgeid();
bridgeSNUUID = aHueConfig.getSNUUIDFromMac();
try {
if(useUpnpIface)
upnpMulticastSocket = new MulticastSocket(new InetSocketAddress(upnpConfigIP, Configuration.UPNP_DISCOVERY_PORT));
if (useUpnpIface)
upnpMulticastSocket = new MulticastSocket(
new InetSocketAddress(upnpConfigIP, Configuration.UPNP_DISCOVERY_PORT));
else
upnpMulticastSocket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT);
} catch(IOException e){
log.error("Upnp Discovery Port is in use, or restricted by admin (try running with sudo or admin privs): " + Configuration.UPNP_DISCOVERY_PORT + " with message: " + e.getMessage());
throw(e);
upnpMulticastSocket = new MulticastSocket(Configuration.UPNP_DISCOVERY_PORT);
} catch (IOException e) {
log.error("Upnp Discovery Port is in use, or restricted by admin (try running with sudo or admin privs): "
+ Configuration.UPNP_DISCOVERY_PORT + " with message: " + e.getMessage());
throw (e);
}
}
public boolean startListening(){
public boolean startListening() {
log.info("UPNP Discovery Listener starting....");
Enumeration<NetworkInterface> ifs = null;
InetSocketAddress socketAddress = new InetSocketAddress(Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT);
InetSocketAddress socketAddress = new InetSocketAddress(Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT);
try {
ifs = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
ifs = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
log.error("Could not get network interfaces for this machine: " + e.getMessage());
return false;
}
@@ -116,14 +106,14 @@ public class UpnpListener {
InetAddress addr = addrs.nextElement();
log.debug(name + " ... has addr " + addr);
if (InetAddressUtils.isIPv4Address(addr.getHostAddress())) {
if(!useUpnpIface) {
if(traceupnp)
if (!useUpnpIface) {
if (traceupnp)
log.info("Traceupnp: Interface: " + name + " valid usable IP address: " + addr);
IPsPerNic++;
}
else if(addr.getHostAddress().equals(upnpConfigIP)) {
if(traceupnp)
log.info("Traceupnp: Interface: " + name + " matches upnp config address of IP address: " + addr);
} else if (addr.getHostAddress().equals(upnpConfigIP)) {
if (traceupnp)
log.info("Traceupnp: Interface: " + name + " matches upnp config address of IP address: "
+ addr);
IPsPerNic++;
}
}
@@ -162,13 +152,14 @@ public class UpnpListener {
try {
sendUpnpResponse(packet);
} catch (IOException e) {
log.warn("UpnpListener encountered an error sending upnp response packet. IP: " + packet.getAddress().getHostAddress() + " with message: " + e.getMessage());
log.warn("UpnpListener encountered an error sending upnp response packet. IP: "
+ packet.getAddress().getHostAddress() + " with message: " + e.getMessage());
log.debug("UpnpListener send upnp exception: ", e);
}
}
current = Instant.now();
if(ChronoUnit.MILLIS.between(previous, current) > Configuration.UPNP_NOTIFY_TIMEOUT) {
if (ChronoUnit.MILLIS.between(previous, current) > Configuration.UPNP_NOTIFY_TIMEOUT) {
sendUpnpNotify(socketAddress.getAddress());
previous = Instant.now();
}
@@ -203,32 +194,33 @@ public class UpnpListener {
/**
* ssdp discovery packet detection
*/
protected boolean isSSDPDiscovery(DatagramPacket packet){
//Only respond to discover request for strict upnp form
protected boolean isSSDPDiscovery(DatagramPacket packet) {
// Only respond to discover request for strict upnp form
String packetString = new String(packet.getData(), 0, packet.getLength());
if(packetString != null && packetString.startsWith("M-SEARCH * HTTP/1.1") && packetString.contains("\"ssdp:discover\"")){
if(strict && (packetString.contains("ST: urn:schemas-upnp-org:device:basic:1") || packetString.contains("ST: upnp:rootdevice") || packetString.contains("ST: ssdp:all")))
{
if(traceupnp) {
log.info("Traceupnp: SSDP M-SEARCH packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort());
}
else
log.debug("SSDP M-SEARCH packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: <<<" + packetString + ">>>");
if (packetString != null && packetString.startsWith("M-SEARCH * HTTP/1.1")
&& packetString.contains("\"ssdp:discover\"")) {
if ((packetString.contains("ST: urn:schemas-upnp-org:device:basic:1")
|| packetString.contains("ST: upnp:rootdevice") || packetString.contains("ST: ssdp:all"))) {
if (traceupnp) {
log.info("Traceupnp: SSDP M-SEARCH packet from " + packet.getAddress().getHostAddress() + ":"
+ packet.getPort());
} else
log.debug("SSDP M-SEARCH packet from " + packet.getAddress().getHostAddress() + ":"
+ packet.getPort() + ", body: <<<" + packetString + ">>>");
return true;
}
else if (!strict)
{
if(traceupnp) {
log.info("Traceupnp: SSDP M-SEARCH packet (!strict) from " + packet.getAddress().getHostAddress() + ":" + packet.getPort());
}
else
log.debug("SSDP M-SEARCH packet (!strict) from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: <<<" + packetString + ">>>");
return true;
}
}
else {
// log.debug("isSSDPDiscovery found message to not be valid - strict: " + strict);
// log.debug("SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: " + packetString);
} /*
* else if (!strict) { if(traceupnp) {
* log.info("Traceupnp: SSDP M-SEARCH packet (!strict) from " +
* packet.getAddress().getHostAddress() + ":" + packet.getPort()); } else
* log.debug("SSDP M-SEARCH packet (!strict) from " +
* packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: <<<"
* + packetString + ">>>"); return true; }
*/
} else {
// log.debug("isSSDPDiscovery found message to not be valid - strict: " +
// strict);
log.debug("SSDP packet from " + packet.getAddress().getHostAddress() + ":" + packet.getPort() + ", body: "
+ packetString);
}
return false;
}
@@ -245,65 +237,90 @@ public class UpnpListener {
} catch (InterruptedException e) {
// noop
}
discoveryResponse = String.format(responseTemplate1, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if(traceupnp) {
log.info("Traceupnp: send upnp discovery template 1 with response address: " + httpLocationAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
}
else
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort + " with discovery responseTemplate1 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
}
discoveryResponse = String.format(responseTemplate2, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
if(traceupnp) {
log.info("Traceupnp: send upnp discovery template 2 with response address: " + httpLocationAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
}
else
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort + " discovery responseTemplate2 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
if (upnpOriginal) {
discoveryResponse = String.format(responseTemplateOriginal, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if (traceupnp) {
log.info("Traceupnp: send upnp discovery template Original with response address: " + httpLocationAddress + ":"
+ httpServerPort + " to address: " + requester + ":" + sourcePort);
} else
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort
+ " with discovery responseTemplateOriginal is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
} else {
discoveryResponse = String.format(responseTemplate1, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if (traceupnp) {
log.info("Traceupnp: send upnp discovery template 1 with response address: " + httpLocationAddress + ":"
+ httpServerPort + " to address: " + requester + ":" + sourcePort);
} else
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort
+ " with discovery responseTemplate1 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
}
discoveryResponse = String.format(responseTemplate2, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID,
bridgeSNUUID);
if (traceupnp) {
log.info("Traceupnp: send upnp discovery template 2 with response address: " + httpLocationAddress + ":"
+ httpServerPort + " to address: " + requester + ":" + sourcePort);
} else
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort
+ " discovery responseTemplate2 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
try {
Thread.sleep(theUpnpSendDelay);
} catch (InterruptedException e) {
// noop
}
discoveryResponse = String.format(responseTemplate3, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if (traceupnp) {
log.info("Traceupnp: send upnp discovery template 3 with response address: " + httpLocationAddress + ":"
+ httpServerPort + " to address: " + requester + ":" + sourcePort);
} else
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort
+ " discovery responseTemplate3 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
}
discoveryResponse = String.format(responseTemplate3, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, httpLocationAddress, httpServerPort, bridgeId, bridgeSNUUID);
if(traceupnp) {
log.info("Traceupnp: send upnp discovery template 3 with response address: " + httpLocationAddress + ":" + httpServerPort + " to address: " + requester + ":" + sourcePort);
}
else
log.debug("sendUpnpResponse to address: " + requester + ":" + sourcePort + " discovery responseTemplate3 is <<<" + discoveryResponse + ">>>");
sendUDPResponse(discoveryResponse.getBytes(), requester, sourcePort);
}
private void sendUDPResponse(byte[] udpMessage, InetAddress requester, int sourcePort) throws IOException {
log.debug("Sending response string: <<<" + new String(udpMessage) + ">>>");
if(upnpMulticastSocket == null)
if (upnpMulticastSocket == null)
throw new IOException("Socket not initialized");
DatagramPacket response = new DatagramPacket(udpMessage, udpMessage.length, requester, sourcePort);
upnpMulticastSocket.send(response);
}
protected void sendUpnpNotify(InetAddress aSocketAddress) {
String notifyData = null;
notifyData = String.format(notifyTemplate, Configuration.UPNP_MULTICAST_ADDRESS, Configuration.UPNP_DISCOVERY_PORT, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
notifyData = String.format(notifyTemplate, Configuration.UPNP_MULTICAST_ADDRESS,
Configuration.UPNP_DISCOVERY_PORT, upnpConfigIP, httpServerPort, bridgeId, bridgeSNUUID, bridgeSNUUID);
log.debug("sendUpnpNotify notifyTemplate is <<<" + notifyData + ">>>");
DatagramPacket notifyPacket = new DatagramPacket(notifyData.getBytes(), notifyData.length(), aSocketAddress, Configuration.UPNP_DISCOVERY_PORT);
DatagramPacket notifyPacket = new DatagramPacket(notifyData.getBytes(), notifyData.length(), aSocketAddress,
Configuration.UPNP_DISCOVERY_PORT);
try {
upnpMulticastSocket.send(notifyPacket);
} catch (IOException e1) {
log.warn("UpnpListener encountered an error sending upnp notify packet. IP: " + notifyPacket.getAddress().getHostAddress() + " with message: " + e1.getMessage());
log.warn("UpnpListener encountered an error sending upnp notify packet. IP: "
+ notifyPacket.getAddress().getHostAddress() + " with message: " + e1.getMessage());
log.debug("UpnpListener send upnp notify exception: ", e1);
}
}
// added by https://github.com/pvint
// Ruthlessly stolen from https://stackoverflow.com/questions/22045165/java-datagrampacket-receive-how-to-determine-local-ip-interface
// Try to get a source IP that makes sense for the requestor to contact for use in the LOCATION header in replies
// Ruthlessly stolen from
// https://stackoverflow.com/questions/22045165/java-datagrampacket-receive-how-to-determine-local-ip-interface
// Try to get a source IP that makes sense for the requestor to contact for use
// in the LOCATION header in replies
private InetAddress getOutboundAddress(SocketAddress remoteAddress) throws SocketException {
DatagramSocket sock = new DatagramSocket();
// connect is needed to bind the socket and retrieve the local address