Skip to content

Commit

Permalink
Vorwerk support added
Browse files Browse the repository at this point in the history
It's now possible to supply vendor to the binding: Neato (default) or Vorwerk.
Replaced apache.codec import with a custom hex() function to allow a clean file install

Based on:
https://github.com/scurb/NeatoBinding
https://stackoverflow.com/questions/19540289/how-to-fix-the-java-security-cert-certificateexception-no-subject-alternative
https://www.mkyong.com/java/java-how-to-convert-bytes-to-hex/

Note:
This is a rework of my earlier fork found here:
90f9e93
  • Loading branch information
Pavion committed Mar 31, 2020
1 parent fb8899e commit ea3208a
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 12 deletions.
13 changes: 10 additions & 3 deletions bundles/org.openhab.binding.neato/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Disclaimer

This is a fork of a Neato Binding which is a part of openHAB addons found here:
https://github.com/openhab/openhab-addons/tree/2.5.x/bundles/org.openhab.binding.neato
This fork adds a new vendor option offering inofficial (and limited) support for Vorwerk vacuum cleaners.

# Neato Binding

This binding is used to connect your openHAB 2 system with Neato web (where you log in and find Your Neato's). The binding supports discovery via configuring your login and password to a bridge. From the binding, you will get status of your vacuum cleaners and also a command channel where you can control them. Since the binding uses a polling mechanism, there may be some latency depending on your setting regarding refresh time.
Expand Down Expand Up @@ -32,8 +38,9 @@ Neato Account Config

| Config | Description |
|----------|------------------------------------ |
| email | Email address tied to Neato Account |
| password | Password tied to Neato Account |
| email | Email address tied to your Account |
| password | Password tied to your Account |
| vendor | Your vendor (Neato or Vorwerk) |

Vacuum Cleaner Config

Expand Down Expand Up @@ -105,6 +112,6 @@ Frame label="Neato BotVac Connected" {
**neato.things**

```
neato:vacuumcleaner:fanndamm [ serial="vacuumcleaner-serial", secret="secret-string"]
neato:vacuumcleaner:fanndamm [ serial="vacuumcleaner-serial", secret="secret-string", vendor="neato"]
```

Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* used across the whole binding.
*
* @author Patrik Wimnell - Initial contribution
* @author Pavion - Vendor added
*/
public class NeatoBindingConstants {

Expand Down Expand Up @@ -48,6 +49,7 @@ public class NeatoBindingConstants {
public static final String CONFIG_SECRET = "secret";
public static final String CONFIG_SERIAL = "serial";
public static final String CONFIG_REFRESHTIME = "refresh";
public final static String CONFIG_VENDOR = "vendor";

public static final String PROPERTY_NAME = "robot-name";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,14 @@ public class NeatoRobot {
private NeatoState state;
private NeatoRobotInfo info;
private NeatoGeneralInfo generalInfo;
private String vendor;

private Gson gson = new Gson();

public NeatoRobot(NeatoRobotConfig config) {
this.serialNumber = config.getSerial();
this.secret = config.getSecret();
this.vendor = config.getVendor();
}

public NeatoState getState() {
Expand Down Expand Up @@ -106,10 +108,17 @@ private String callNeatoWS(String body) throws NeatoCommunicationException {
logger.debug("Calling Neato WS with body: {}", body);

InputStream stream = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));
String result = "";

String result = HttpUtil.executeUrl("POST",
"https://nucleo.neatocloud.com:4443/vendors/neato/robots/" + this.serialNumber + "/messages",
headers, stream, "text/html; charset=ISO-8859-1", 20000);
if (vendor.toLowerCase().trim().equals(VendorVorwerk.VENDOR_NAME)) {
result = VendorVorwerk.executeRequest("POST",
VendorVorwerk.NUCLEO_URL + "/vendors/vorwerk/robots/" + this.serialNumber + "/messages",
headers, body, "text/html; charset=ISO-8859-1", 20000);
} else {
result = HttpUtil.executeUrl("POST",
"https://nucleo.neatocloud.com:4443/vendors/neato/robots/" + this.serialNumber + "/messages",
headers, stream, "text/html; charset=ISO-8859-1", 20000);
}

return result;
} catch (IOException | NoSuchAlgorithmException | InvalidKeyException e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* Copyright (c) 2010-2020 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.neato.internal;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Properties;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

/**
*
* @author Florian Dietrich - Initial contribution
* @author Pavion - 2.5.0 adaptation
*
*/
public class VendorVorwerk {

public static final String BEEHIVE_URL = "https://vorwerk-beehive-production.herokuapp.com";
public static final String NUCLEO_URL = "https://nucleo.ksecosys.com";
public static final String VENDOR_NAME = "vorwerk";

public static String executeRequest(String httpMethod, String url, Properties httpHeaders, String content,
String contentType, int timeout) throws IOException {
URL requestUrl = new URL(url);
HttpsURLConnection connection = (HttpsURLConnection) requestUrl.openConnection();
applyNucleoSslConfiguration(connection);
connection.setRequestMethod(httpMethod);
for (String propName : httpHeaders.stringPropertyNames()) {
connection.addRequestProperty(propName, httpHeaders.getProperty(propName));
}
connection.setUseCaches(false);
connection.setDoOutput(true);
connection.setConnectTimeout(10000);
connection.setReadTimeout(10000);

if (content != null) {
DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
wr.writeBytes(content);
wr.flush();
wr.close();
}

BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));

StringBuilder sb = new StringBuilder();
String inputLine;
while ((inputLine = in.readLine()) != null) {
sb.append(inputLine);
}

in.close();
connection.disconnect();

return sb.toString();
}

/**
* Trust the self signed certificate.
*
* @param connection
*/
private static void applyNucleoSslConfiguration(HttpsURLConnection connection) {
try {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}

@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}

@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
} };

SSLContext sslctx = SSLContext.getInstance("SSL");
// sslctx.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
sslctx.init(null, trustAllCerts, new SecureRandom());
connection.setSSLSocketFactory(sslctx.getSocketFactory());

// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};

// Install the all-trusting host verifier
connection.setHostnameVerifier(allHostsValid);
} catch (NoSuchAlgorithmException e) {
} catch (KeyManagementException e) {
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
* The {@link NeatoAccountInformation} is the internal class for the neato web service account and information.
*
* @author Patrik Wimnell - Initial contribution
* @author Pavion - Vendor added
*/
public class NeatoAccountInformation {

private String vendor;
private String email;
private Object firstName;
private Object lastName;
Expand All @@ -39,6 +41,14 @@ public class NeatoAccountInformation {
@SerializedName("recent_firmwares")
private RecentFirmwares recentFirmwares;

public String getVendor() {
return vendor;
}

public void setVendor(String vendor) {
this.vendor = vendor;
}

public String getEmail() {
return email;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,22 @@
* Parameters used for bridge configuration.
*
* @author Jeff Lauterbach - Initial Contribution
* @author Pavion - Vendor added
*/
public class NeatoAccountConfig {

private String vendor;
private String email;
private String password;

public String getVendor() {
return vendor;
}

public void setVendor(String vendor) {
this.vendor = vendor;
}

public String getEmail() {
return email;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
* Config class for a Neato Robot
*
* @author Jeff Lauterbach - Initial Contribution
* @author Pavion - Vendor added
*
*/
public class NeatoRobotConfig {

private int refresh;
private String secret;
private String serial;
private String vendor;

public int getRefresh() {
return refresh;
Expand All @@ -48,9 +50,18 @@ public void setSerial(String serial) {
this.serial = serial;
}

public String getVendor() {
return vendor;
}

public void setVendor(String vendor) {
this.vendor = vendor;
}

@Override
public String toString() {
return "NeatoRobotConfig [refresh=" + refresh + ", secret=" + secret + ", serial=" + serial + "]";
return "NeatoRobotConfig [refresh=" + refresh + ", secret=" + secret + ", serial=" + serial + ", vendor="
+ vendor + "]";
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
*
* @author Patrik Wimnell - Initial contribution
* @author Jeff Lauterbach - Start discovery service from bridge
* @author Pavion - Vendor added
*/
public class NeatoAccountDiscoveryService extends AbstractDiscoveryService {

Expand Down Expand Up @@ -92,6 +93,7 @@ private void addThing(Robot robot) {
properties.put(NeatoBindingConstants.CONFIG_SERIAL, robot.getSerial());
properties.put(Thing.PROPERTY_MODEL_ID, robot.getModel());
properties.put(NeatoBindingConstants.PROPERTY_NAME, robot.getName());
properties.put(NeatoBindingConstants.CONFIG_VENDOR, handler.getVendor());

thingDiscovered(
DiscoveryResultBuilder.create(thingUID).withBridge(bridgeUID).withProperties(properties).build());
Expand Down

1 comment on commit ea3208a

@Pavion
Copy link
Owner Author

@Pavion Pavion commented on ea3208a Mar 31, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, wrong info in commit message:

Replaced apache.codec import with a custom hex() function to allow a clean file install is not part of this commit

Please sign in to comment.