]> git.basschouten.com Git - openhab-addons.git/blob
cb8624915a491d7fa3095a75fec6d12417265bcb
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.boschindego.internal.handler;
14
15 import static org.openhab.binding.boschindego.internal.BoschIndegoBindingConstants.*;
16
17 import java.io.IOException;
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.List;
21 import java.util.Set;
22 import java.util.concurrent.ConcurrentHashMap;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jetty.client.HttpClient;
26 import org.openhab.binding.boschindego.internal.AuthorizationController;
27 import org.openhab.binding.boschindego.internal.AuthorizationListener;
28 import org.openhab.binding.boschindego.internal.AuthorizationProvider;
29 import org.openhab.binding.boschindego.internal.IndegoController;
30 import org.openhab.binding.boschindego.internal.discovery.IndegoDiscoveryService;
31 import org.openhab.binding.boschindego.internal.dto.response.DevicePropertiesResponse;
32 import org.openhab.binding.boschindego.internal.exceptions.IndegoAuthenticationException;
33 import org.openhab.binding.boschindego.internal.exceptions.IndegoException;
34 import org.openhab.core.auth.client.oauth2.OAuthClientService;
35 import org.openhab.core.auth.client.oauth2.OAuthException;
36 import org.openhab.core.auth.client.oauth2.OAuthFactory;
37 import org.openhab.core.auth.client.oauth2.OAuthResponseException;
38 import org.openhab.core.thing.Bridge;
39 import org.openhab.core.thing.ChannelUID;
40 import org.openhab.core.thing.ThingStatus;
41 import org.openhab.core.thing.ThingStatusDetail;
42 import org.openhab.core.thing.binding.BaseBridgeHandler;
43 import org.openhab.core.thing.binding.ThingHandlerService;
44 import org.openhab.core.types.Command;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 /**
49  * The {@link BoschAccountHandler} is responsible for handling commands, which are
50  * sent to one of the channels.
51  *
52  * @author Jacob Laursen - Initial contribution
53  */
54 @NonNullByDefault
55 public class BoschAccountHandler extends BaseBridgeHandler implements AuthorizationListener {
56
57     private final Logger logger = LoggerFactory.getLogger(BoschAccountHandler.class);
58     private final OAuthFactory oAuthFactory;
59     private final Set<AuthorizationListener> authorizationListeners = ConcurrentHashMap.newKeySet();
60
61     private OAuthClientService oAuthClientService;
62     private AuthorizationController authorizationController;
63     private IndegoController controller;
64
65     public BoschAccountHandler(Bridge bridge, HttpClient httpClient, OAuthFactory oAuthFactory) {
66         super(bridge);
67
68         this.oAuthFactory = oAuthFactory;
69
70         oAuthClientService = oAuthFactory.createOAuthClientService(thing.getUID().getAsString(), BSK_TOKEN_URI,
71                 BSK_AUTH_URI, BSK_CLIENT_ID, null, BSK_SCOPE, false);
72         authorizationController = new AuthorizationController(oAuthClientService, this);
73         controller = new IndegoController(httpClient, authorizationController);
74     }
75
76     @Override
77     public void initialize() {
78         OAuthClientService oAuthClientService = oAuthFactory.getOAuthClientService(thing.getUID().getAsString());
79         if (oAuthClientService == null) {
80             throw new IllegalStateException("OAuth handle doesn't exist");
81         }
82         authorizationController.setOAuthClientService(oAuthClientService);
83         this.oAuthClientService = oAuthClientService;
84
85         updateStatus(ThingStatus.UNKNOWN);
86
87         scheduler.execute(() -> {
88             try {
89                 authorizationController.getAccessToken();
90                 updateStatus(ThingStatus.ONLINE);
91             } catch (OAuthException | OAuthResponseException e) {
92                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
93                         "@text/offline.conf-error.oauth2-unauthorized");
94             } catch (IOException e) {
95                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
96                         "@text/offline.comm-error.oauth2-authorization-failed");
97             }
98         });
99     }
100
101     @Override
102     public void dispose() {
103         oAuthFactory.ungetOAuthService(thing.getUID().getAsString());
104         authorizationListeners.clear();
105     }
106
107     @Override
108     public void handleCommand(ChannelUID channelUID, Command command) {
109     }
110
111     @Override
112     public void handleRemoval() {
113         oAuthFactory.deleteServiceAndAccessToken(thing.getUID().getAsString());
114         super.handleRemoval();
115     }
116
117     public AuthorizationProvider getAuthorizationProvider() {
118         return authorizationController;
119     }
120
121     public void registerAuthorizationListener(AuthorizationListener listener) {
122         if (!authorizationListeners.add(listener)) {
123             throw new IllegalStateException("Attempt to register already registered authorization listener");
124         }
125     }
126
127     public void unregisterAuthorizationListener(AuthorizationListener listener) {
128         if (!authorizationListeners.remove(listener)) {
129             throw new IllegalStateException("Attempt to unregister authorization listener which is not registered");
130         }
131     }
132
133     public void onSuccessfulAuthorization() {
134         updateStatus(ThingStatus.ONLINE);
135     }
136
137     public void onFailedAuthorization(Throwable throwable) {
138         logger.debug("Authorization failure", throwable);
139         if (throwable instanceof IndegoAuthenticationException) {
140             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
141                     "@text/offline.comm-error.authentication-failure");
142         } else {
143             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, throwable.getMessage());
144         }
145     }
146
147     public void onAuthorizationFlowCompleted() {
148         // Ignore
149     }
150
151     @Override
152     public Collection<Class<? extends ThingHandlerService>> getServices() {
153         return List.of(IndegoDiscoveryService.class);
154     }
155
156     public void authorize(String authCode) throws IndegoAuthenticationException {
157         logger.info("Attempting to authorize using authorization code");
158
159         try {
160             oAuthClientService.getAccessTokenResponseByAuthorizationCode(authCode, BSK_REDIRECT_URI);
161         } catch (OAuthException | OAuthResponseException | IOException e) {
162             throw new IndegoAuthenticationException("Failed to authorize by authorization code " + authCode, e);
163         }
164
165         logger.info("Authorization completed successfully");
166
167         updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE, "@text/online.authorization-completed");
168
169         authorizationListeners.forEach(l -> l.onAuthorizationFlowCompleted());
170     }
171
172     public Collection<DevicePropertiesResponse> getDevices() throws IndegoException {
173         Collection<String> serialNumbers = controller.getSerialNumbers();
174         List<DevicePropertiesResponse> devices = new ArrayList<DevicePropertiesResponse>(serialNumbers.size());
175
176         for (String serialNumber : serialNumbers) {
177             DevicePropertiesResponse properties = controller.getDeviceProperties(serialNumber);
178             if (properties.serialNumber == null) {
179                 properties.serialNumber = serialNumber;
180             }
181             devices.add(properties);
182         }
183
184         return devices;
185     }
186 }