]> git.basschouten.com Git - openhab-addons.git/blob
9607c7b6a46f773a2c85952a99219c979999368f
[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.freeboxos.internal.api.rest;
14
15 import java.io.IOException;
16 import java.net.URI;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Locale;
20 import java.util.Map;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.eclipse.jetty.websocket.api.Session;
25 import org.eclipse.jetty.websocket.api.StatusCode;
26 import org.eclipse.jetty.websocket.api.WebSocketListener;
27 import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
28 import org.eclipse.jetty.websocket.client.WebSocketClient;
29 import org.openhab.binding.freeboxos.internal.api.ApiHandler;
30 import org.openhab.binding.freeboxos.internal.api.FreeboxException;
31 import org.openhab.binding.freeboxos.internal.api.rest.LanBrowserManager.LanHost;
32 import org.openhab.binding.freeboxos.internal.api.rest.VmManager.VirtualMachine;
33 import org.openhab.binding.freeboxos.internal.handler.HostHandler;
34 import org.openhab.binding.freeboxos.internal.handler.VmHandler;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 import com.google.gson.JsonElement;
39
40 import inet.ipaddr.mac.MACAddress;
41
42 /**
43  * The {@link WebSocketManager} is the Java class register to the websocket server and handle notifications
44  *
45  * @author GaĆ«l L'hopital - Initial contribution
46  */
47 @NonNullByDefault
48 public class WebSocketManager extends RestManager implements WebSocketListener {
49     private static final String HOST_UNREACHABLE = "lan_host_l3addr_unreachable";
50     private static final String HOST_REACHABLE = "lan_host_l3addr_reachable";
51     private static final String VM_CHANGED = "vm_state_changed";
52     private static final Register REGISTRATION = new Register("register",
53             List.of(VM_CHANGED, HOST_REACHABLE, HOST_UNREACHABLE));
54     private static final String WS_PATH = "ws/event";
55
56     private final Logger logger = LoggerFactory.getLogger(WebSocketManager.class);
57     private final Map<MACAddress, HostHandler> lanHosts = new HashMap<>();
58     private final Map<Integer, VmHandler> vms = new HashMap<>();
59     private final ApiHandler apiHandler;
60
61     private volatile @Nullable Session wsSession;
62
63     private record Register(String action, List<String> events) {
64
65     }
66
67     public WebSocketManager(FreeboxOsSession session) throws FreeboxException {
68         super(session, LoginManager.Permission.NONE, session.getUriBuilder().path(WS_PATH));
69         this.apiHandler = session.getApiHandler();
70     }
71
72     private static enum Action {
73         REGISTER,
74         NOTIFICATION,
75         UNKNOWN;
76     }
77
78     private static record WebSocketResponse(boolean success, Action action, String event, String source,
79             @Nullable JsonElement result) {
80         public String getEvent() {
81             return source + "_" + event;
82         }
83     }
84
85     public void openSession(@Nullable String sessionToken) throws FreeboxException {
86         WebSocketClient client = new WebSocketClient(apiHandler.getHttpClient());
87         URI uri = getUriBuilder().scheme(getUriBuilder().build().getScheme().contains("s") ? "wss" : "ws").build();
88         ClientUpgradeRequest request = new ClientUpgradeRequest();
89         request.setHeader(ApiHandler.AUTH_HEADER, sessionToken);
90
91         try {
92             client.start();
93             client.connect(this, uri, request);
94         } catch (Exception e) {
95             throw new FreeboxException(e, "Exception connecting websocket client");
96         }
97     }
98
99     public void closeSession() {
100         logger.debug("Awaiting closure from remote");
101         Session localSession = wsSession;
102         if (localSession != null) {
103             localSession.close();
104         }
105     }
106
107     @Override
108     public void onWebSocketConnect(@NonNullByDefault({}) Session wsSession) {
109         this.wsSession = wsSession;
110         logger.debug("Websocket connection establisehd");
111         try {
112             wsSession.getRemote().sendString(apiHandler.serialize(REGISTRATION));
113         } catch (IOException e) {
114             logger.warn("Error connecting to websocket: {}", e.getMessage());
115         }
116     }
117
118     @Override
119     public void onWebSocketText(@NonNullByDefault({}) String message) {
120         Session localSession = wsSession;
121         if (message.toLowerCase(Locale.US).contains("bye") && localSession != null) {
122             localSession.close(StatusCode.NORMAL, "Thanks");
123             return;
124         }
125
126         WebSocketResponse result = apiHandler.deserialize(WebSocketResponse.class, message);
127         if (result.success) {
128             switch (result.action) {
129                 case REGISTER:
130                     logger.debug("Event registration successfull");
131                     break;
132                 case NOTIFICATION:
133                     handleNotification(result);
134                     break;
135                 default:
136                     logger.warn("Unhandled notification received: {}", result.action);
137             }
138         }
139     }
140
141     private void handleNotification(WebSocketResponse result) {
142         JsonElement json = result.result;
143         if (json != null) {
144             switch (result.getEvent()) {
145                 case VM_CHANGED:
146                     VirtualMachine vm = apiHandler.deserialize(VirtualMachine.class, json.toString());
147                     logger.debug("Received notification for VM {}", vm.id());
148                     VmHandler vmHandler = vms.get(vm.id());
149                     if (vmHandler != null) {
150                         vmHandler.updateVmChannels(vm);
151                     }
152                     break;
153                 case HOST_UNREACHABLE, HOST_REACHABLE:
154                     LanHost host = apiHandler.deserialize(LanHost.class, json.toString());
155                     MACAddress mac = host.getMac();
156                     logger.debug("Received notification for LanHost {}", mac.toColonDelimitedString());
157                     HostHandler hostHandler = lanHosts.get(mac);
158                     if (hostHandler != null) {
159                         hostHandler.updateConnectivityChannels(host);
160                     }
161                     break;
162                 default:
163                     logger.warn("Unhandled event received: {}", result.getEvent());
164             }
165         } else {
166             logger.warn("Empty json element in notification");
167         }
168     }
169
170     @Override
171     public void onWebSocketClose(int statusCode, @NonNullByDefault({}) String reason) {
172         logger.debug("Socket Closed: [{}] - reason {}", statusCode, reason);
173         this.wsSession = null;
174     }
175
176     @Override
177     public void onWebSocketError(@NonNullByDefault({}) Throwable cause) {
178         logger.warn("Error on websocket: {}", cause.getMessage());
179     }
180
181     @Override
182     public void onWebSocketBinary(byte @Nullable [] payload, int offset, int len) {
183         /* do nothing */
184     }
185
186     public void registerListener(MACAddress mac, HostHandler hostHandler) {
187         lanHosts.put(mac, hostHandler);
188     }
189
190     public void unregisterListener(MACAddress mac) {
191         lanHosts.remove(mac);
192     }
193
194     public void registerVm(int clientId, VmHandler vmHandler) {
195         vms.put(clientId, vmHandler);
196     }
197
198     public void unregisterVm(int clientId) {
199         vms.remove(clientId);
200     }
201 }