[somfytahoma] Open to other portals (#10611)
* [somfytahoma] Open to other portals Signed-off-by: Laurent Garnier <lg.hc@free.fr> * Review comment: suppress the advanced setting for cookie handling Signed-off-by: Laurent Garnier <lg.hc@free.fr>pull/10665/head
parent
22eebc797a
commit
892221ccad
|
@ -5,13 +5,14 @@ This binding integrates the
|
|||
and
|
||||
[Somfy Connexoon](https://www.somfy.fr/produits/1811429/)
|
||||
home automation systems.
|
||||
Any home automation system based on the OverKiz API is potentially supported.
|
||||
|
||||
## Supported Things
|
||||
|
||||
Currently these things are supported:
|
||||
|
||||
- bridge (Somfy Tahoma bridge, which can discover gateways, roller shutters, awnings, switches and action groups)
|
||||
- gateways (Somfy Tahoma gateway - gateway status)
|
||||
- bridge (cloud bridge, which can discover gateways, roller shutters, awnings, switches and action groups)
|
||||
- gateways (gateway status)
|
||||
- gates (control gate, get state)
|
||||
- roller shutters (UP, DOWN, STOP control of a roller shutter). IO Homecontrol devices are allowed to set exact position of a shutter (0-100%)
|
||||
- blinds (UP, DOWN, STOP control of a blind). IO Homecontrol devices are allowed to set exact position of a blinds (0-100%) as well as orientation of slats (0-100%)
|
||||
|
@ -46,10 +47,10 @@ Both Somfy Tahoma and Somfy Connexoon gateways have been confirmed working.
|
|||
|
||||
To start a discovery, just
|
||||
|
||||
- Add a new Somfy Tahoma bridge thing.
|
||||
- Configure the bridge with your email (login) and password to the TahomaLink cloud portal.
|
||||
- Add a new bridge thing.
|
||||
- Configure the bridge selecting your cloud portal (www.tahomalink.com by default) and setting your email (login) and password to the cloud portal.
|
||||
|
||||
If the supplied TahomaLink credentials are correct, the automatic discovery can be used to scan and detect roller shutters, awnings, switches and action groups that will appear in your Inbox.
|
||||
If the supplied credentials are correct, the automatic discovery can be used to scan and detect roller shutters, awnings, switches and action groups that will appear in your Inbox.
|
||||
|
||||
## Thing Configuration
|
||||
|
||||
|
@ -61,7 +62,7 @@ Please see the example below.
|
|||
| Thing | Channel | Note |
|
||||
|-------------------------------------------------------------------------------|------------------------------|-----------------------------------------------------------------------------------------------------------------------------|
|
||||
| bridge | N.A | bridge does not expose any channel |
|
||||
| gateway | status | status of your Tahoma gateway |
|
||||
| gateway | status | status of your gateway |
|
||||
| gateway | scenarios | used to run the scenarios defined in the cloud portal |
|
||||
| gate | gate_command | used for controlling your gate (open, close, stop, pedestrian) |
|
||||
| gate | gate_state | get state of your gate (open, closed, pedestrian) |
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.openhab.core.thing.ThingTypeUID;
|
|||
* used across the whole binding.
|
||||
*
|
||||
* @author Ondrej Pecta - Initial contribution
|
||||
* @author Laurent Garnier - Other portals integration
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SomfyTahomaBindingConstants {
|
||||
|
@ -280,13 +281,14 @@ public class SomfyTahomaBindingConstants {
|
|||
public static final String SHUTTER = "shutter";
|
||||
|
||||
// Constants
|
||||
public static final String TAHOMA_API_URL = "https://www.tahomalink.com/enduser-mobile-web/enduserAPI/";
|
||||
public static final String TAHOMA_EVENTS_URL = TAHOMA_API_URL + "events/";
|
||||
public static final String SETUP_URL = TAHOMA_API_URL + "setup/";
|
||||
public static final String TAHOMA_PORTAL = "www.tahomalink.com";
|
||||
public static final String API_BASE_URL = "/enduser-mobile-web/enduserAPI/";
|
||||
public static final String EVENTS_URL = "events/";
|
||||
public static final String SETUP_URL = "setup/";
|
||||
public static final String GATEWAYS_URL = SETUP_URL + "gateways/";
|
||||
public static final String DEVICES_URL = SETUP_URL + "devices/";
|
||||
public static final String REFRESH_URL = DEVICES_URL + "states/refresh";
|
||||
public static final String EXEC_URL = TAHOMA_API_URL + "exec/";
|
||||
public static final String EXEC_URL = "exec/";
|
||||
public static final String DELETE_URL = EXEC_URL + "current/setup/";
|
||||
public static final String TAHOMA_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36";
|
||||
public static final int TAHOMA_TIMEOUT = 5;
|
||||
|
@ -415,6 +417,7 @@ public class SomfyTahomaBindingConstants {
|
|||
put(29, "TAHOMA_V2");
|
||||
put(30, "KIZBOX_V2_3H");
|
||||
put(31, "KIZBOX_V2_2H");
|
||||
put(32, "COZYTOUCH");
|
||||
put(34, "CONNEXOON");
|
||||
put(35, "JSW_CAMERA");
|
||||
put(37, "KIZBOX_MINI_DAUGHTERBOARD");
|
||||
|
|
|
@ -13,15 +13,18 @@
|
|||
package org.openhab.binding.somfytahoma.internal.config;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.somfytahoma.internal.SomfyTahomaBindingConstants;
|
||||
|
||||
/**
|
||||
* The {@link SomfyTahomaConfig} is is the base class for configuration
|
||||
* information held by devices and modules.
|
||||
*
|
||||
* @author Ondrej Pecta - Initial contribution
|
||||
* @author Laurent Garnier - new parameter portalUrl
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SomfyTahomaConfig {
|
||||
private String cloudPortal = SomfyTahomaBindingConstants.TAHOMA_PORTAL;
|
||||
private String email = "";
|
||||
private String password = "";
|
||||
private int refresh = 30;
|
||||
|
@ -29,6 +32,10 @@ public class SomfyTahomaConfig {
|
|||
private int retries = 10;
|
||||
private int retryDelay = 1000;
|
||||
|
||||
public String getCloudPortal() {
|
||||
return cloudPortal;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
@ -53,6 +60,10 @@ public class SomfyTahomaConfig {
|
|||
return retryDelay;
|
||||
}
|
||||
|
||||
public void setCloudPortal(String cloudPortal) {
|
||||
this.cloudPortal = cloudPortal;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ import com.google.gson.JsonSyntaxException;
|
|||
* sent to one of the channels.
|
||||
*
|
||||
* @author Ondrej Pecta - Initial contribution
|
||||
* @author Laurent Garnier - Other portals integration
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
|
||||
|
@ -193,8 +194,6 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
|
|||
}
|
||||
|
||||
public synchronized void login() {
|
||||
String url;
|
||||
|
||||
if (thingConfig.getEmail().isEmpty() || thingConfig.getPassword().isEmpty()) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"Can not access device as username and/or password are null");
|
||||
|
@ -214,11 +213,10 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
|
|||
reLoginNeeded = false;
|
||||
|
||||
try {
|
||||
url = TAHOMA_API_URL + "login";
|
||||
String urlParameters = "userId=" + urlEncode(thingConfig.getEmail()) + "&userPassword="
|
||||
+ urlEncode(thingConfig.getPassword());
|
||||
|
||||
ContentResponse response = sendRequestBuilder(url, HttpMethod.POST)
|
||||
ContentResponse response = sendRequestBuilder("login", HttpMethod.POST)
|
||||
.content(new StringContentProvider(urlParameters),
|
||||
"application/x-www-form-urlencoded; charset=UTF-8")
|
||||
.send();
|
||||
|
@ -235,7 +233,7 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
|
|||
} else if (data.isSuccess()) {
|
||||
logger.debug("SomfyTahoma version: {}", data.getVersion());
|
||||
String id = registerEvents();
|
||||
if (id != null && !id.equals(UNAUTHORIZED)) {
|
||||
if (id != null && !UNAUTHORIZED.equals(id)) {
|
||||
eventsId = id;
|
||||
logger.debug("Events id: {}", eventsId);
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
|
@ -254,7 +252,8 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
|
|||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Received invalid data (login)");
|
||||
} catch (ExecutionException e) {
|
||||
if (isAuthenticationChallenge(e)) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Authentication challenge");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
|
||||
"Error logging in (check your credentials)");
|
||||
setTooManyRequests();
|
||||
} else {
|
||||
logger.debug("Cannot get login cookie", e);
|
||||
|
@ -270,14 +269,15 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
|
|||
}
|
||||
|
||||
private void setTooManyRequests() {
|
||||
logger.debug("Too many requests error, suspending activity for {} seconds", SUSPEND_TIME);
|
||||
logger.debug("Too many requests or bad credentials for the cloud portal, suspending activity for {} seconds",
|
||||
SUSPEND_TIME);
|
||||
tooManyRequests = true;
|
||||
scheduler.schedule(this::enableLogin, SUSPEND_TIME, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private @Nullable String registerEvents() {
|
||||
SomfyTahomaRegisterEventsResponse response = invokeCallToURL(TAHOMA_EVENTS_URL + "register", "",
|
||||
HttpMethod.POST, SomfyTahomaRegisterEventsResponse.class);
|
||||
SomfyTahomaRegisterEventsResponse response = invokeCallToURL(EVENTS_URL + "register", "", HttpMethod.POST,
|
||||
SomfyTahomaRegisterEventsResponse.class);
|
||||
return response != null ? response.getId() : null;
|
||||
}
|
||||
|
||||
|
@ -294,8 +294,8 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
|
|||
}
|
||||
|
||||
private List<SomfyTahomaEvent> getEvents() {
|
||||
SomfyTahomaEvent[] response = invokeCallToURL(TAHOMA_API_URL + "events/" + eventsId + "/fetch", "",
|
||||
HttpMethod.POST, SomfyTahomaEvent[].class);
|
||||
SomfyTahomaEvent[] response = invokeCallToURL(EVENTS_URL + eventsId + "/fetch", "", HttpMethod.POST,
|
||||
SomfyTahomaEvent[].class);
|
||||
return response != null ? List.of(response) : List.of();
|
||||
}
|
||||
|
||||
|
@ -357,13 +357,13 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
|
|||
}
|
||||
|
||||
public List<SomfyTahomaActionGroup> listActionGroups() {
|
||||
SomfyTahomaActionGroup[] list = invokeCallToURL(TAHOMA_API_URL + "actionGroups", "", HttpMethod.GET,
|
||||
SomfyTahomaActionGroup[] list = invokeCallToURL("actionGroups", "", HttpMethod.GET,
|
||||
SomfyTahomaActionGroup[].class);
|
||||
return list != null ? List.of(list) : List.of();
|
||||
}
|
||||
|
||||
public @Nullable SomfyTahomaSetup getSetup() {
|
||||
SomfyTahomaSetup setup = invokeCallToURL(TAHOMA_API_URL + "setup", "", HttpMethod.GET, SomfyTahomaSetup.class);
|
||||
SomfyTahomaSetup setup = invokeCallToURL("setup", "", HttpMethod.GET, SomfyTahomaSetup.class);
|
||||
if (setup != null) {
|
||||
saveDevicePlaces(setup.getDevices());
|
||||
}
|
||||
|
@ -591,7 +591,7 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
|
|||
private void logout() {
|
||||
try {
|
||||
eventsId = "";
|
||||
sendGetToTahomaWithCookie(TAHOMA_API_URL + "logout");
|
||||
sendGetToTahomaWithCookie("logout");
|
||||
} catch (ExecutionException | TimeoutException e) {
|
||||
logger.debug("Cannot send logout command!", e);
|
||||
} catch (InterruptedException e) {
|
||||
|
@ -626,7 +626,7 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
|
|||
|
||||
private String sendMethodToTahomaWithCookie(String url, HttpMethod method, String urlParameters)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
logger.trace("Sending {} to url: {} with data: {}", method.asString(), url, urlParameters);
|
||||
logger.trace("Sending {} to url: {} with data: {}", method.asString(), getApiFullUrl(url), urlParameters);
|
||||
Request request = sendRequestBuilder(url, method);
|
||||
if (!urlParameters.isEmpty()) {
|
||||
request = request.content(new StringContentProvider(urlParameters), "application/json;charset=UTF-8");
|
||||
|
@ -644,10 +644,15 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
|
|||
return response.getContentAsString();
|
||||
}
|
||||
|
||||
private Request sendRequestBuilder(String url, HttpMethod method) {
|
||||
return httpClient.newRequest(url).method(method).header(HttpHeader.ACCEPT_LANGUAGE, "en-US,en")
|
||||
.header(HttpHeader.ACCEPT_ENCODING, "gzip, deflate").header("X-Requested-With", "XMLHttpRequest")
|
||||
.timeout(TAHOMA_TIMEOUT, TimeUnit.SECONDS).agent(TAHOMA_AGENT);
|
||||
private Request sendRequestBuilder(String subUrl, HttpMethod method) {
|
||||
return httpClient.newRequest(getApiFullUrl(subUrl)).method(method)
|
||||
.header(HttpHeader.ACCEPT_LANGUAGE, "en-US,en").header(HttpHeader.ACCEPT_ENCODING, "gzip, deflate")
|
||||
.header("X-Requested-With", "XMLHttpRequest").timeout(TAHOMA_TIMEOUT, TimeUnit.SECONDS)
|
||||
.agent(TAHOMA_AGENT);
|
||||
}
|
||||
|
||||
private String getApiFullUrl(String subUrl) {
|
||||
return "https://" + thingConfig.getCloudPortal() + API_BASE_URL + subUrl;
|
||||
}
|
||||
|
||||
public void sendCommand(String io, String command, String params, String url) {
|
||||
|
@ -672,7 +677,7 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
|
|||
}
|
||||
|
||||
private boolean sendCommandInternal(String io, String command, String params, String url) {
|
||||
String value = params.equals("[]") ? command : command + " " + params.replace("\"", "");
|
||||
String value = "[]".equals(params) ? command : command + " " + params.replace("\"", "");
|
||||
String urlParameters = "{\"label\":\"" + getThingLabelByURL(io) + " - " + value
|
||||
+ " - openHAB\",\"actions\":[{\"deviceURL\":\"" + io + "\",\"commands\":[{\"name\":\"" + command
|
||||
+ "\",\"parameters\":" + params + "}]}]}";
|
||||
|
@ -799,11 +804,10 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
|
|||
@Override
|
||||
public void handleConfigurationUpdate(Map<String, Object> configurationParameters) {
|
||||
super.handleConfigurationUpdate(configurationParameters);
|
||||
if (configurationParameters.containsKey("email")) {
|
||||
thingConfig.setEmail(configurationParameters.get("email").toString());
|
||||
}
|
||||
if (configurationParameters.containsKey("password")) {
|
||||
thingConfig.setPassword(configurationParameters.get("password").toString());
|
||||
if (configurationParameters.containsKey("email") || configurationParameters.containsKey("password")
|
||||
|| configurationParameters.containsKey("portalUrl")) {
|
||||
reLoginNeeded = true;
|
||||
tooManyRequests = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -841,11 +845,11 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
|
|||
if (isAuthenticationChallenge(e)) {
|
||||
reLogin();
|
||||
} else {
|
||||
logger.debug("Cannot call url: {} with params: {}!", url, urlParameters, e);
|
||||
logger.debug("Cannot call url: {} with params: {}!", getApiFullUrl(url), urlParameters, e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||
}
|
||||
} catch (TimeoutException e) {
|
||||
logger.debug("Timeout when calling url: {} with params: {}!", url, urlParameters, e);
|
||||
logger.debug("Timeout when calling url: {} with params: {}!", getApiFullUrl(url), urlParameters, e);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||
} catch (InterruptedException e) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>SomfyTahoma Binding</name>
|
||||
<description>This is the binding for SomfyTahoma.</description>
|
||||
<description>This is the binding for Somfy Tahoma and Somfy Connexoon home automation systems and for any other system
|
||||
based on the OverKiz API.</description>
|
||||
|
||||
</binding:binding>
|
||||
|
|
|
@ -19,26 +19,42 @@
|
|||
</config-description>
|
||||
|
||||
<config-description uri="bridge-type:somfytahoma:bridge">
|
||||
<parameter name="cloudPortal" type="text" required="false">
|
||||
<label>Cloud Portal</label>
|
||||
<description>Cloud portal to connect to</description>
|
||||
<options>
|
||||
<option value="www.tahomalink.com">Somfy TaHoma / Somfy Connexoon IO / Somfy (Europe)</option>
|
||||
<option value="ha201-1.overkiz.com">Somfy Connexoon RTS / Somfy (Australia)</option>
|
||||
<option value="ha401-1.overkiz.com">Somfy (North America)</option>
|
||||
<option value="ha110-1.overkiz.com">Cozytouch</option>
|
||||
<option value="ha101-1.overkiz.com">eedomus</option>
|
||||
<option value="ha117-1.overkiz.com">Hi Kumo</option>
|
||||
<option value="ha112-1.overkiz.com">Rexel Energeasy Connect</option>
|
||||
</options>
|
||||
<default>www.tahomalink.com</default>
|
||||
<limitToOptions>false</limitToOptions>
|
||||
</parameter>
|
||||
|
||||
<parameter name="email" type="text" required="true">
|
||||
<label>Email Address</label>
|
||||
<description>Email address for TahomaLink portal</description>
|
||||
<description>Email address for the portal</description>
|
||||
</parameter>
|
||||
|
||||
<parameter name="password" type="text" required="true">
|
||||
<context>password</context>
|
||||
<label>Password</label>
|
||||
<description>Password for TahomaLink portal</description>
|
||||
<description>Password for the portal</description>
|
||||
</parameter>
|
||||
|
||||
<parameter name="refresh" type="integer" required="false" min="10">
|
||||
<label>Refresh</label>
|
||||
<description>Specifies the refresh time in seconds for polling events from Tahoma cloud</description>
|
||||
<description>Specifies the refresh time in seconds for polling events from the cloud</description>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
|
||||
<parameter name="statusTimeout" type="integer" required="false" min="60">
|
||||
<label>Status Timeout</label>
|
||||
<description>Specifies the timeout in seconds after which the status is got from Tahoma cloud</description>
|
||||
<description>Specifies the timeout in seconds after which the status is got from the cloud</description>
|
||||
<default>300</default>
|
||||
</parameter>
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
<!-- Bridge -->
|
||||
<bridge-type id="bridge">
|
||||
<label>Somfy Tahoma Bridge</label>
|
||||
<description>Somfy Tahoma bridge enabling communication with Somfy devices</description>
|
||||
<label>Bridge</label>
|
||||
<description>Bridge enabling communication with devices through a cloud portal</description>
|
||||
|
||||
<config-description-ref uri="bridge-type:somfytahoma:bridge"/>
|
||||
</bridge-type>
|
||||
|
|
Loading…
Reference in New Issue