mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-30 00:51:58 +01:00
Start refactoring AuthService to handle direct Enedis service call and cleaner code
Signed-off-by: Laurent ARNAL <laurent@clae.net>
This commit is contained in:
parent
cc827a0166
commit
73d36c30a7
@ -35,7 +35,7 @@ public interface LinkyAccountHandler {
|
|||||||
* @param reqCode The unique code passed by Smartthings to obtain the refresh and access tokens
|
* @param reqCode The unique code passed by Smartthings to obtain the refresh and access tokens
|
||||||
* @return returns the name of the Smartthings user that is authorized
|
* @return returns the name of the Smartthings user that is authorized
|
||||||
*/
|
*/
|
||||||
String authorize(String redirectUrl, String reqCode) throws LinkyException;
|
String authorize(String redirectUrl, String reqState, String reqCode) throws LinkyException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats the Url to use to call Smartthings to authorize the application.
|
* Formats the Url to use to call Smartthings to authorize the application.
|
||||||
|
@ -17,18 +17,22 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
import java.util.Map;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServlet;
|
import javax.servlet.http.HttpServlet;
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
|
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
|
||||||
|
import org.openhab.core.auth.client.oauth2.OAuthClientService;
|
||||||
|
import org.openhab.core.auth.client.oauth2.OAuthException;
|
||||||
|
import org.openhab.core.auth.client.oauth2.OAuthFactory;
|
||||||
|
import org.openhab.core.auth.client.oauth2.OAuthResponseException;
|
||||||
import org.osgi.framework.BundleContext;
|
import org.osgi.framework.BundleContext;
|
||||||
import org.osgi.service.component.ComponentContext;
|
import org.osgi.service.component.ComponentContext;
|
||||||
import org.osgi.service.component.annotations.Activate;
|
|
||||||
import org.osgi.service.component.annotations.Component;
|
|
||||||
import org.osgi.service.component.annotations.Deactivate;
|
import org.osgi.service.component.annotations.Deactivate;
|
||||||
import org.osgi.service.component.annotations.Reference;
|
import org.osgi.service.component.annotations.Reference;
|
||||||
import org.osgi.service.http.HttpService;
|
import org.osgi.service.http.HttpService;
|
||||||
@ -41,28 +45,38 @@ import org.slf4j.LoggerFactory;
|
|||||||
* @author Gaël L'hopital - Initial contribution
|
* @author Gaël L'hopital - Initial contribution
|
||||||
* @author Laurent Arnal - Rewrite addon to use official dataconect API
|
* @author Laurent Arnal - Rewrite addon to use official dataconect API
|
||||||
*/
|
*/
|
||||||
@Component(service = LinkyAuthService.class, configurationPid = "binding.internal.authService")
|
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
public class LinkyAuthService {
|
public class LinkyAuthService implements LinkyAccountHandler {
|
||||||
|
|
||||||
private static final String TEMPLATE_PATH = "templates/";
|
private static final String TEMPLATE_PATH = "templates/";
|
||||||
private static final String TEMPLATE_INDEX = TEMPLATE_PATH + "index.html";
|
private static final String TEMPLATE_INDEX = TEMPLATE_PATH + "index.html";
|
||||||
private static final String ERROR_UKNOWN_BRIDGE = "Returned 'state' by doesn't match any Bridges. Has the bridge been removed?";
|
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(LinkyAuthService.class);
|
private final Logger logger = LoggerFactory.getLogger(LinkyAuthService.class);
|
||||||
|
|
||||||
private @NonNullByDefault({}) HttpService httpService;
|
private @NonNullByDefault({}) HttpService httpService;
|
||||||
private @NonNullByDefault({}) BundleContext bundleContext;
|
private @NonNullByDefault({}) BundleContext bundleContext;
|
||||||
private @Nullable LinkyAccountHandler accountHandler;
|
|
||||||
|
|
||||||
@Activate
|
private final boolean oAuthSupport = true;
|
||||||
protected void activate(ComponentContext componentContext, Map<String, Object> properties) {
|
private @Nullable OAuthClientService oAuthService;
|
||||||
|
|
||||||
|
public LinkyAuthService(final @Reference OAuthFactory oAuthFactory, final @Reference HttpService httpService,
|
||||||
|
final @Reference ComponentContext componentContext) {
|
||||||
|
|
||||||
|
this.httpService = httpService;
|
||||||
|
|
||||||
|
this.oAuthService = oAuthFactory.createOAuthClientService(LinkyBindingConstants.BINDING_ID,
|
||||||
|
LinkyBindingConstants.ENEDIS_API_TOKEN_URL, LinkyBindingConstants.ENEDIS_AUTH_AUTHORIZE_URL,
|
||||||
|
LinkyBindingConstants.clientId, LinkyBindingConstants.clientSecret, LinkyBindingConstants.LINKY_SCOPES,
|
||||||
|
true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bundleContext = componentContext.getBundleContext();
|
bundleContext = componentContext.getBundleContext();
|
||||||
|
|
||||||
httpService.registerServlet(LinkyBindingConstants.LINKY_ALIAS, createServlet(), new Hashtable<>(),
|
httpService.registerServlet(LinkyBindingConstants.LINKY_ALIAS, createServlet(), new Hashtable<>(),
|
||||||
httpService.createDefaultHttpContext());
|
httpService.createDefaultHttpContext());
|
||||||
httpService.registerResources(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS,
|
httpService.registerResources(LinkyBindingConstants.LINKY_ALIAS + LinkyBindingConstants.LINKY_IMG_ALIAS,
|
||||||
"web", null);
|
"web", null);
|
||||||
|
|
||||||
} catch (NamespaceException | ServletException | IOException e) {
|
} catch (NamespaceException | ServletException | IOException e) {
|
||||||
logger.warn("Error during linky servlet startup", e);
|
logger.warn("Error during linky servlet startup", e);
|
||||||
}
|
}
|
||||||
@ -104,54 +118,123 @@ public class LinkyAuthService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Call with Linky redirect uri returned State and Code values to get the refresh and access tokens and persist
|
public String authorize(String redirectUri, String reqState, String reqCode) throws LinkyException {
|
||||||
* these values
|
// Will work only in case of direct oAuth2 authentification to enedis
|
||||||
*
|
// this is not the case in v1 as we go trough MyElectricalData
|
||||||
* @param servletBaseURL the servlet base, which will be the Linky redirect url
|
|
||||||
* @param state The Linky returned state value
|
if (oAuthSupport) {
|
||||||
* @param code The Linky returned code value
|
try {
|
||||||
* @return returns the name of the Linky user that is authorized
|
OAuthClientService oAuthService = this.oAuthService;
|
||||||
*/
|
if (oAuthService == null) {
|
||||||
public String authorize(String servletBaseURL, String state, String code) throws LinkyException {
|
throw new OAuthException("OAuth service is not initialized");
|
||||||
LinkyAccountHandler accountHandler = getLinkyAccountHandler();
|
}
|
||||||
if (accountHandler == null) {
|
logger.debug("Make call to Enedis to get access token.");
|
||||||
logger.debug(
|
final AccessTokenResponse credentials = oAuthService
|
||||||
"Linky redirected with state '{}' but no matching bridge was found. Possible bridge has been removed.",
|
.getAccessTokenByClientCredentials(LinkyBindingConstants.LINKY_SCOPES);
|
||||||
state);
|
|
||||||
throw new LinkyException(ERROR_UKNOWN_BRIDGE);
|
final String user = updateProperties(credentials);
|
||||||
} else {
|
logger.debug("Authorized for user: {}", user);
|
||||||
return accountHandler.authorize(servletBaseURL, code);
|
return user;
|
||||||
|
} catch (RuntimeException | OAuthException | IOException e) {
|
||||||
|
// updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
|
||||||
|
throw new LinkyException("Error during oAuth authorize :" + e.getMessage(), e);
|
||||||
|
} catch (final OAuthResponseException e) {
|
||||||
|
throw new LinkyException("\"Error during oAuth authorize :" + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback for MyElectricalData
|
||||||
|
else {
|
||||||
|
/*
|
||||||
|
* String token = EnedisHttpApi.getToken(httpClient, LinkyBindingConstants.clientId, reqCode);
|
||||||
|
*
|
||||||
|
* logger.debug("token: {}", token);
|
||||||
|
*
|
||||||
|
* Collection<Thing> col = this.thingRegistry.getAll();
|
||||||
|
* for (Thing thing : col) {
|
||||||
|
* if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
|
||||||
|
* Configuration config = thing.getConfiguration();
|
||||||
|
* String prmId = (String) config.get("prmId");
|
||||||
|
*
|
||||||
|
* if (!prmId.equals(reqCode)) {
|
||||||
|
* continue;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* config.put("token", token);
|
||||||
|
* LinkyHandler handler = (LinkyHandler) thing.getHandler();
|
||||||
|
* if (handler != null) {
|
||||||
|
* handler.saveConfiguration(config);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* return token;
|
||||||
|
*/
|
||||||
|
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* @param listener Adds the given handler
|
public boolean isAuthorized() {
|
||||||
*/
|
final AccessTokenResponse accessTokenResponse = getAccessTokenResponse();
|
||||||
public void setLinkyAccountHandler(LinkyAccountHandler accountHandler) {
|
|
||||||
this.accountHandler = accountHandler;
|
return accessTokenResponse != null && accessTokenResponse.getAccessToken() != null
|
||||||
|
&& accessTokenResponse.getRefreshToken() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private @Nullable AccessTokenResponse getAccessTokenResponse() {
|
||||||
* @param handler Removes the given handler
|
try {
|
||||||
*/
|
OAuthClientService oAuthService = this.oAuthService;
|
||||||
public void unsetLinkyAccountHandler(LinkyAccountHandler accountHandler) {
|
return oAuthService == null ? null : oAuthService.getAccessTokenResponse();
|
||||||
this.accountHandler = null;
|
} catch (OAuthException | IOException | OAuthResponseException | RuntimeException e) {
|
||||||
|
logger.debug("Exception checking authorization: ", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private String updateProperties(AccessTokenResponse credentials) {
|
||||||
* @param listener Adds the given handler
|
return credentials.getAccessToken();
|
||||||
*/
|
|
||||||
public @Nullable LinkyAccountHandler getLinkyAccountHandler() {
|
|
||||||
return this.accountHandler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Reference
|
@Override
|
||||||
protected void setHttpService(HttpService httpService) {
|
public String formatAuthorizationUrl(String redirectUri) {
|
||||||
this.httpService = httpService;
|
// Will work only in case of direct oAuth2 authentification to enedis
|
||||||
|
// this is not the case in v1 as we go trough MyElectricalData
|
||||||
|
if (oAuthSupport) {
|
||||||
|
try {
|
||||||
|
OAuthClientService oAuthService = this.oAuthService;
|
||||||
|
if (oAuthService == null) {
|
||||||
|
throw new OAuthException("OAuth service is not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
String uri = oAuthService.getAuthorizationUrl(redirectUri, null, "Linky");
|
||||||
|
return uri;
|
||||||
|
} catch (final OAuthException e) {
|
||||||
|
logger.debug("Error constructing AuthorizationUrl: ", e);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void unsetHttpService(HttpService httpService) {
|
@Override
|
||||||
this.httpService = null;
|
public List<String> getAllPrmId() {
|
||||||
|
List<String> result = new ArrayList<String>();
|
||||||
|
/*
|
||||||
|
* Collection<Thing> col = this.thingRegistry.getAll();
|
||||||
|
*
|
||||||
|
* for (Thing thing : col) {
|
||||||
|
* if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
|
||||||
|
* Configuration config = thing.getConfiguration();
|
||||||
|
*
|
||||||
|
* String prmId = (String) config.get("prmId");
|
||||||
|
* result.add(prmId);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -83,25 +83,19 @@ public class LinkyAuthServlet extends HttpServlet {
|
|||||||
try {
|
try {
|
||||||
handleLinkyRedirect(replaceMap, servletBaseURLSecure, req.getQueryString());
|
handleLinkyRedirect(replaceMap, servletBaseURLSecure, req.getQueryString());
|
||||||
|
|
||||||
LinkyAccountHandler accountHandler = linkyAuthService.getLinkyAccountHandler();
|
|
||||||
|
|
||||||
resp.setContentType(CONTENT_TYPE);
|
resp.setContentType(CONTENT_TYPE);
|
||||||
|
|
||||||
StringBuffer optionBuffer = new StringBuffer();
|
StringBuffer optionBuffer = new StringBuffer();
|
||||||
|
|
||||||
if (accountHandler != null) {
|
List<String> prmIds = linkyAuthService.getAllPrmId();
|
||||||
List<String> prmIds = accountHandler.getAllPrmId();
|
for (String prmId : prmIds) {
|
||||||
for (String prmId : prmIds) {
|
optionBuffer.append("<option value=\"" + prmId + "\">" + prmId + "</option>");
|
||||||
optionBuffer.append("<option value=\"" + prmId + "\">" + prmId + "</option>");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceMap.put(KEY_PRMID_OPTION, optionBuffer.toString());
|
replaceMap.put(KEY_PRMID_OPTION, optionBuffer.toString());
|
||||||
replaceMap.put(KEY_REDIRECT_URI, servletBaseURLSecure);
|
replaceMap.put(KEY_REDIRECT_URI, servletBaseURLSecure);
|
||||||
replaceMap.put(KEY_RETRIEVE_TOKEN_URI, servletBaseURLSecure + "?state=OK");
|
replaceMap.put(KEY_RETRIEVE_TOKEN_URI, servletBaseURLSecure + "?state=OK");
|
||||||
if (accountHandler != null) {
|
replaceMap.put(KEY_AUTHORIZE_URI, linkyAuthService.formatAuthorizationUrl(servletBaseURLSecure));
|
||||||
replaceMap.put(KEY_AUTHORIZE_URI, accountHandler.formatAuthorizationUrl(servletBaseURLSecure));
|
|
||||||
}
|
|
||||||
resp.getWriter().append(replaceKeysFromMap(indexTemplate, replaceMap));
|
resp.getWriter().append(replaceKeysFromMap(indexTemplate, replaceMap));
|
||||||
resp.getWriter().close();
|
resp.getWriter().close();
|
||||||
} catch (LinkyException ex) {
|
} catch (LinkyException ex) {
|
||||||
|
@ -30,6 +30,9 @@ public class LinkyBindingConstants {
|
|||||||
|
|
||||||
public static final String BINDING_ID = "linky";
|
public static final String BINDING_ID = "linky";
|
||||||
|
|
||||||
|
public static final String clientId = "_88uJnEjEs_IMf4bjGZJV6gGxYga";
|
||||||
|
public static final String clientSecret = "6lsPfCmu0fEXuKYy3e0e6w8ydIca";
|
||||||
|
|
||||||
// List of all Thing Type UIDs
|
// List of all Thing Type UIDs
|
||||||
public static final ThingTypeUID THING_TYPE_LINKY = new ThingTypeUID(BINDING_ID, "linky");
|
public static final ThingTypeUID THING_TYPE_LINKY = new ThingTypeUID(BINDING_ID, "linky");
|
||||||
|
|
||||||
@ -69,16 +72,20 @@ public class LinkyBindingConstants {
|
|||||||
/**
|
/**
|
||||||
* Smartthings scopes needed by this binding to work.
|
* Smartthings scopes needed by this binding to work.
|
||||||
*/
|
*/
|
||||||
public static final String LINKY_SCOPES = Stream.of("r:devices:*", "w:devices:*", "x:devices:*", "r:hubs:*",
|
public static final String LINKY_SCOPES = Stream.of("am_application_scope", "default")
|
||||||
"r:locations:*", "w:locations:*", "x:locations:*", "r:scenes:*", "x:scenes:*", "r:rules:*", "w:rules:*",
|
.collect(Collectors.joining(" "));
|
||||||
"r:installedapps", "w:installedapps").collect(Collectors.joining(" "));
|
// "r:devices:*", "w:devices:*", "x:devices:*", "r:hubs:*",
|
||||||
|
// "r:locations:*", "w:locations:*", "x:locations:*", "r:scenes:*", "x:scenes:*", "r:rules:*", "w:rules:*",
|
||||||
|
// "r:installedapps", "w:installedapps"
|
||||||
|
|
||||||
// List of Linky services related urls, information
|
// List of Linky services related urls, information
|
||||||
public static final String LINKY_ACCOUNT_URL = "https://www.myelectricaldata.fr/";
|
public static final String LINKY_ACCOUNT_URL = "https://www.myelectricaldata.fr/";
|
||||||
public static final String LINKY_AUTHORIZE_URL = LINKY_ACCOUNT_URL + "v1/oauth2/authorize";
|
public static final String LINKY_AUTHORIZE_URL = LINKY_ACCOUNT_URL + "v1/oauth2/authorize";
|
||||||
public static final String LINKY_API_TOKEN_URL = LINKY_ACCOUNT_URL + "token";
|
public static final String LINKY_API_TOKEN_URL = LINKY_ACCOUNT_URL + "token";
|
||||||
|
|
||||||
public static final String ENEDIS_ACCOUNT_URL = "https://mon-compte-particulier.enedis.fr/";
|
public static final String ENEDIS_API_ACCOUNT_URL = "https://ext.prod-sandbox.api.enedis.fr/";
|
||||||
public static final String ENEDIS_AUTHORIZE_URL = ENEDIS_ACCOUNT_URL + "dataconnect/v1/oauth2/authorize";
|
public static final String ENEDIS_API_TOKEN_URL = ENEDIS_API_ACCOUNT_URL + "oauth2/v3/token";
|
||||||
public static final String ENEDIS_API_TOKEN_URL = ENEDIS_ACCOUNT_URL + "token";
|
|
||||||
|
public static final String ENEDIS_AUTH_ACCOUNT_URL = "https://mon-compte-particulier.enedis.fr/";
|
||||||
|
public static final String ENEDIS_AUTH_AUTHORIZE_URL = ENEDIS_AUTH_ACCOUNT_URL + "dataconnect/v1/oauth2/authorize";
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
|||||||
public class LinkyConfiguration {
|
public class LinkyConfiguration {
|
||||||
public String token = "";
|
public String token = "";
|
||||||
public String prmId = "";
|
public String prmId = "";
|
||||||
|
public String clientId = "";
|
||||||
|
public String clientSecret = "";
|
||||||
|
|
||||||
public boolean seemsValid() {
|
public boolean seemsValid() {
|
||||||
return !prmId.isBlank();
|
return !prmId.isBlank();
|
||||||
|
@ -12,16 +12,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.openhab.binding.linky.internal;
|
package org.openhab.binding.linky.internal;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.KeyManagementException;
|
import java.security.KeyManagementException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.net.ssl.TrustManager;
|
import javax.net.ssl.TrustManager;
|
||||||
@ -30,19 +26,12 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
|||||||
import org.eclipse.jdt.annotation.Nullable;
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.eclipse.jetty.client.HttpClient;
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
import org.openhab.binding.linky.internal.api.EnedisHttpApi;
|
|
||||||
import org.openhab.binding.linky.internal.handler.LinkyHandler;
|
import org.openhab.binding.linky.internal.handler.LinkyHandler;
|
||||||
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
|
|
||||||
import org.openhab.core.auth.client.oauth2.OAuthClientService;
|
|
||||||
import org.openhab.core.auth.client.oauth2.OAuthException;
|
|
||||||
import org.openhab.core.auth.client.oauth2.OAuthFactory;
|
import org.openhab.core.auth.client.oauth2.OAuthFactory;
|
||||||
import org.openhab.core.auth.client.oauth2.OAuthResponseException;
|
|
||||||
import org.openhab.core.config.core.Configuration;
|
|
||||||
import org.openhab.core.i18n.LocaleProvider;
|
import org.openhab.core.i18n.LocaleProvider;
|
||||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||||
import org.openhab.core.io.net.http.TrustAllTrustManager;
|
import org.openhab.core.io.net.http.TrustAllTrustManager;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
import org.openhab.core.thing.ThingRegistry;
|
|
||||||
import org.openhab.core.thing.ThingTypeUID;
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
||||||
import org.openhab.core.thing.binding.ThingHandler;
|
import org.openhab.core.thing.binding.ThingHandler;
|
||||||
@ -51,6 +40,7 @@ import org.osgi.service.component.ComponentContext;
|
|||||||
import org.osgi.service.component.annotations.Activate;
|
import org.osgi.service.component.annotations.Activate;
|
||||||
import org.osgi.service.component.annotations.Component;
|
import org.osgi.service.component.annotations.Component;
|
||||||
import org.osgi.service.component.annotations.Reference;
|
import org.osgi.service.component.annotations.Reference;
|
||||||
|
import org.osgi.service.http.HttpService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -67,7 +57,7 @@ import com.google.gson.JsonDeserializer;
|
|||||||
*/
|
*/
|
||||||
@NonNullByDefault
|
@NonNullByDefault
|
||||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.linky")
|
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.linky")
|
||||||
public class LinkyHandlerFactory extends BaseThingHandlerFactory implements LinkyAccountHandler {
|
public class LinkyHandlerFactory extends BaseThingHandlerFactory {
|
||||||
private static final DateTimeFormatter LINKY_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
|
private static final DateTimeFormatter LINKY_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
|
||||||
private static final DateTimeFormatter LINKY_LOCALDATE_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd");
|
private static final DateTimeFormatter LINKY_LOCALDATE_FORMATTER = DateTimeFormatter.ofPattern("uuuu-MM-dd");
|
||||||
private static final DateTimeFormatter LINKY_LOCALDATETIME_FORMATTER = DateTimeFormatter
|
private static final DateTimeFormatter LINKY_LOCALDATETIME_FORMATTER = DateTimeFormatter
|
||||||
@ -75,9 +65,6 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
|
|||||||
private static final int REQUEST_BUFFER_SIZE = 8000;
|
private static final int REQUEST_BUFFER_SIZE = 8000;
|
||||||
private static final int RESPONSE_BUFFER_SIZE = 200000;
|
private static final int RESPONSE_BUFFER_SIZE = 200000;
|
||||||
|
|
||||||
private final String clientId = "e551937c-5250-48bc-b4a6-2323af68db92";
|
|
||||||
private final String clientSecret = "";
|
|
||||||
|
|
||||||
private final Logger logger = LoggerFactory.getLogger(LinkyHandlerFactory.class);
|
private final Logger logger = LoggerFactory.getLogger(LinkyHandlerFactory.class);
|
||||||
private final Gson gson = new GsonBuilder()
|
private final Gson gson = new GsonBuilder()
|
||||||
.registerTypeAdapter(ZonedDateTime.class,
|
.registerTypeAdapter(ZonedDateTime.class,
|
||||||
@ -100,43 +87,36 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
|
|||||||
|
|
||||||
private final LocaleProvider localeProvider;
|
private final LocaleProvider localeProvider;
|
||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
private final LinkyAuthService authService;
|
|
||||||
private final boolean oAuthSupport = false;
|
|
||||||
private @Nullable OAuthClientService oAuthService;
|
|
||||||
private final ThingRegistry thingRegistry;
|
|
||||||
|
|
||||||
@Activate
|
@Activate
|
||||||
public LinkyHandlerFactory(final @Reference LocaleProvider localeProvider,
|
public LinkyHandlerFactory(final @Reference LocaleProvider localeProvider,
|
||||||
final @Reference HttpClientFactory httpClientFactory, final @Reference LinkyAuthService authService,
|
final @Reference HttpClientFactory httpClientFactory, final @Reference OAuthFactory oAuthFactory,
|
||||||
final @Reference OAuthFactory oAuthFactory, final @Reference ThingRegistry thingRegistry) {
|
final @Reference HttpService httpService, ComponentContext componentContext) {
|
||||||
this.localeProvider = localeProvider;
|
this.localeProvider = localeProvider;
|
||||||
SslContextFactory sslContextFactory = new SslContextFactory.Client();
|
SslContextFactory sslContextFactory = new SslContextFactory.Client();
|
||||||
try {
|
try {
|
||||||
SSLContext sslContext = SSLContext.getInstance("SSL");
|
SSLContext sslContext = SSLContext.getInstance("SSL");
|
||||||
sslContext.init(null, new TrustManager[] { TrustAllTrustManager.getInstance() }, null);
|
sslContext.init(null, new TrustManager[] { TrustAllTrustManager.getInstance() }, null);
|
||||||
sslContextFactory.setSslContext(sslContext);
|
sslContextFactory.setSslContext(sslContext);
|
||||||
|
|
||||||
|
LinkyAuthService authService = new LinkyAuthService(oAuthFactory, httpService, componentContext);
|
||||||
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
logger.warn("An exception occurred while requesting the SSL encryption algorithm : '{}'", e.getMessage(),
|
logger.warn("An exception occurred while requesting the SSL encryption algorithm : '{}'", e.getMessage(),
|
||||||
e);
|
e);
|
||||||
} catch (KeyManagementException e) {
|
} catch (KeyManagementException e) {
|
||||||
logger.warn("An exception occurred while initialising the SSL context : '{}'", e.getMessage(), e);
|
logger.warn("An exception occurred while initialising the SSL context : '{}'", e.getMessage(), e);
|
||||||
}
|
}
|
||||||
this.thingRegistry = thingRegistry;
|
|
||||||
this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID, sslContextFactory);
|
this.httpClient = httpClientFactory.createHttpClient(LinkyBindingConstants.BINDING_ID, sslContextFactory);
|
||||||
httpClient.setFollowRedirects(false);
|
httpClient.setFollowRedirects(false);
|
||||||
httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE);
|
httpClient.setRequestBufferSize(REQUEST_BUFFER_SIZE);
|
||||||
this.authService = authService;
|
|
||||||
|
|
||||||
this.oAuthService = oAuthFactory.createOAuthClientService(LinkyBindingConstants.BINDING_ID,
|
|
||||||
LinkyBindingConstants.LINKY_API_TOKEN_URL, LinkyBindingConstants.LINKY_AUTHORIZE_URL, clientId,
|
|
||||||
clientSecret, LinkyBindingConstants.LINKY_SCOPES, true);
|
|
||||||
this.authService.setLinkyAccountHandler(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void activate(ComponentContext componentContext) {
|
protected void activate(ComponentContext componentContext) {
|
||||||
super.activate(componentContext);
|
super.activate(componentContext);
|
||||||
try {
|
try {
|
||||||
|
|
||||||
httpClient.start();
|
httpClient.start();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.warn("Unable to start Jetty HttpClient {}", e.getMessage());
|
logger.warn("Unable to start Jetty HttpClient {}", e.getMessage());
|
||||||
@ -168,124 +148,4 @@ public class LinkyHandlerFactory extends BaseThingHandlerFactory implements Link
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAuthorized() {
|
|
||||||
final AccessTokenResponse accessTokenResponse = getAccessTokenResponse();
|
|
||||||
|
|
||||||
return accessTokenResponse != null && accessTokenResponse.getAccessToken() != null
|
|
||||||
&& accessTokenResponse.getRefreshToken() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private @Nullable AccessTokenResponse getAccessTokenResponse() {
|
|
||||||
try {
|
|
||||||
OAuthClientService oAuthService = this.oAuthService;
|
|
||||||
return oAuthService == null ? null : oAuthService.getAccessTokenResponse();
|
|
||||||
} catch (OAuthException | IOException | OAuthResponseException | RuntimeException e) {
|
|
||||||
logger.debug("Exception checking authorization: ", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String authorize(String redirectUri, String reqCode) throws LinkyException {
|
|
||||||
// Will work only in case of direct oAuth2 authentification to enedis
|
|
||||||
// this is not the case in v1 as we go trough MyElectricalData
|
|
||||||
|
|
||||||
if (oAuthSupport) {
|
|
||||||
try {
|
|
||||||
OAuthClientService oAuthService = this.oAuthService;
|
|
||||||
if (oAuthService == null) {
|
|
||||||
throw new OAuthException("OAuth service is not initialized");
|
|
||||||
}
|
|
||||||
logger.debug("Make call to Smartthings to get access token.");
|
|
||||||
final AccessTokenResponse credentials = oAuthService.getAccessTokenResponseByAuthorizationCode(reqCode,
|
|
||||||
redirectUri);
|
|
||||||
final String user = updateProperties(credentials);
|
|
||||||
logger.debug("Authorized for user: {}", user);
|
|
||||||
return user;
|
|
||||||
} catch (RuntimeException | OAuthException | IOException e) {
|
|
||||||
// updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
|
|
||||||
throw new LinkyException("Error during oAuth authorize :" + e.getMessage(), e);
|
|
||||||
} catch (final OAuthResponseException e) {
|
|
||||||
throw new LinkyException("\"Error during oAuth authorize :" + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Fallback for MyElectricalData
|
|
||||||
else {
|
|
||||||
String token = EnedisHttpApi.getToken(httpClient, clientId, reqCode);
|
|
||||||
|
|
||||||
logger.debug("token: {}", token);
|
|
||||||
|
|
||||||
Collection<Thing> col = this.thingRegistry.getAll();
|
|
||||||
for (Thing thing : col) {
|
|
||||||
if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
|
|
||||||
Configuration config = thing.getConfiguration();
|
|
||||||
String prmId = (String) config.get("prmId");
|
|
||||||
|
|
||||||
if (!prmId.equals(reqCode)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.put("token", token);
|
|
||||||
LinkyHandler handler = (LinkyHandler) thing.getHandler();
|
|
||||||
if (handler != null) {
|
|
||||||
handler.saveConfiguration(config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String updateProperties(AccessTokenResponse credentials) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String formatAuthorizationUrl(String redirectUri) {
|
|
||||||
// Will work only in case of direct oAuth2 authentification to enedis
|
|
||||||
// this is not the case in v1 as we go trough MyElectricalData
|
|
||||||
if (oAuthSupport) {
|
|
||||||
try {
|
|
||||||
OAuthClientService oAuthService = this.oAuthService;
|
|
||||||
if (oAuthService == null) {
|
|
||||||
throw new OAuthException("OAuth service is not initialized");
|
|
||||||
}
|
|
||||||
|
|
||||||
String uri = oAuthService.getAuthorizationUrl(redirectUri, null, "Linky");
|
|
||||||
return uri;
|
|
||||||
} catch (final OAuthException e) {
|
|
||||||
logger.debug("Error constructing AuthorizationUrl: ", e);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Fallback for MyElectricalData
|
|
||||||
else {
|
|
||||||
String uri = LinkyBindingConstants.ENEDIS_AUTHORIZE_URL;
|
|
||||||
uri = uri + "?";
|
|
||||||
uri = uri + "&client_id=" + clientId;
|
|
||||||
uri = uri + "&duration=" + "P36M";
|
|
||||||
uri = uri + "&response_type=" + "code";
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getAllPrmId() {
|
|
||||||
Collection<Thing> col = this.thingRegistry.getAll();
|
|
||||||
List<String> result = new ArrayList<String>();
|
|
||||||
for (Thing thing : col) {
|
|
||||||
if (LinkyBindingConstants.THING_TYPE_LINKY.equals(thing.getThingTypeUID())) {
|
|
||||||
Configuration config = thing.getConfiguration();
|
|
||||||
|
|
||||||
String prmId = (String) config.get("prmId");
|
|
||||||
result.add(prmId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,16 @@
|
|||||||
<description>Your Enedis token (can be left empty, use the connection page to automatically fill it
|
<description>Your Enedis token (can be left empty, use the connection page to automatically fill it
|
||||||
http://youopenhab/connectlinky)</description>
|
http://youopenhab/connectlinky)</description>
|
||||||
</parameter>
|
</parameter>
|
||||||
|
|
||||||
|
<parameter name="clientId" type="text" required="false">
|
||||||
|
<label>clientId</label>
|
||||||
|
<description>Your Enedis clientId</description>
|
||||||
|
</parameter>
|
||||||
|
<parameter name="clientSecret" type="text" required="false">
|
||||||
|
<label>clientSecret</label>
|
||||||
|
<description>Your Enedis clientSecret</description>
|
||||||
|
</parameter>
|
||||||
|
|
||||||
</config-description>
|
</config-description>
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user