]> git.basschouten.com Git - openhab-addons.git/blob
205976298427bb83a7be676d030a2dea7e5866b3
[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.asuswrt.internal.things;
14
15 import static org.openhab.binding.asuswrt.internal.constants.AsuswrtBindingConstants.*;
16 import static org.openhab.binding.asuswrt.internal.constants.AsuswrtBindingSettings.*;
17 import static org.openhab.binding.asuswrt.internal.helpers.AsuswrtUtils.*;
18
19 import java.util.Collection;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.concurrent.ScheduledFuture;
23 import java.util.concurrent.TimeUnit;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.eclipse.jetty.client.HttpClient;
28 import org.openhab.binding.asuswrt.internal.AsuswrtDiscoveryService;
29 import org.openhab.binding.asuswrt.internal.api.AsuswrtConnector;
30 import org.openhab.binding.asuswrt.internal.helpers.AsuswrtErrorHandler;
31 import org.openhab.binding.asuswrt.internal.helpers.AsuswrtUtils;
32 import org.openhab.binding.asuswrt.internal.structures.AsuswrtClientList;
33 import org.openhab.binding.asuswrt.internal.structures.AsuswrtConfiguration;
34 import org.openhab.binding.asuswrt.internal.structures.AsuswrtInterfaceList;
35 import org.openhab.binding.asuswrt.internal.structures.AsuswrtRouterInfo;
36 import org.openhab.core.library.unit.Units;
37 import org.openhab.core.thing.Bridge;
38 import org.openhab.core.thing.ChannelUID;
39 import org.openhab.core.thing.Thing;
40 import org.openhab.core.thing.ThingStatus;
41 import org.openhab.core.thing.ThingStatusDetail;
42 import org.openhab.core.thing.ThingStatusInfo;
43 import org.openhab.core.thing.ThingTypeUID;
44 import org.openhab.core.thing.ThingUID;
45 import org.openhab.core.thing.binding.BaseBridgeHandler;
46 import org.openhab.core.thing.binding.ThingHandler;
47 import org.openhab.core.thing.binding.ThingHandlerService;
48 import org.openhab.core.types.Command;
49 import org.openhab.core.types.RefreshType;
50 import org.openhab.core.types.UnDefType;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 import com.google.gson.JsonObject;
55
56 /**
57  * The {@link AsuswrtRouter} is responsible for handling commands, which are sent to one of the channels.
58  *
59  * @author Christian Wild - Initial contribution
60  */
61 @NonNullByDefault
62 public class AsuswrtRouter extends BaseBridgeHandler {
63     private final Logger logger = LoggerFactory.getLogger(AsuswrtRouter.class);
64
65     private @Nullable ScheduledFuture<?> startupJob;
66     private @Nullable ScheduledFuture<?> pollingJob;
67     private @Nullable ScheduledFuture<?> reconnectJob;
68     private @Nullable ScheduledFuture<?> discoveryJob;
69     private @NonNullByDefault({}) AsuswrtDiscoveryService discoveryService;
70     private @Nullable AsuswrtConnector connector;
71     private AsuswrtConfiguration config;
72     private AsuswrtRouterInfo deviceInfo;
73     private AsuswrtInterfaceList interfaceList = new AsuswrtInterfaceList();
74     private AsuswrtClientList clientList = new AsuswrtClientList();
75     private final HttpClient httpClient;
76     private final String uid;
77
78     public AsuswrtErrorHandler errorHandler;
79
80     public AsuswrtRouter(Bridge bridge, HttpClient httpClient) {
81         super(bridge);
82         Thing thing = getThing();
83         uid = thing.getUID().toString();
84         errorHandler = new AsuswrtErrorHandler();
85         this.httpClient = httpClient;
86         deviceInfo = new AsuswrtRouterInfo();
87         config = new AsuswrtConfiguration();
88     }
89
90     @Override
91     public void initialize() {
92         config = getConfigAs(AsuswrtConfiguration.class);
93         connector = new AsuswrtConnector(this);
94
95         // Initialize the handler.
96         setState(ThingStatus.UNKNOWN);
97
98         // background initialization (delay it a little bit):
99         startupJob = scheduler.schedule(this::delayedStartUp, 1000, TimeUnit.MILLISECONDS);
100     }
101
102     @Override
103     public void dispose() {
104         stopScheduler(startupJob);
105         stopScheduler(pollingJob);
106         stopScheduler(discoveryJob);
107         stopScheduler(reconnectJob);
108     }
109
110     @Override
111     public Collection<Class<? extends ThingHandlerService>> getServices() {
112         return List.of(AsuswrtDiscoveryService.class);
113     }
114
115     public void setDiscoveryService(AsuswrtDiscoveryService discoveryService) {
116         this.discoveryService = discoveryService;
117     }
118
119     /*
120      * Scheduler
121      */
122
123     /**
124      * Delayed one-time startup job.
125      */
126     private void delayedStartUp() {
127         connect();
128     }
129
130     public void startPollingJob() {
131         int pollingInterval = AsuswrtUtils.getValueOrDefault(config.pollingInterval, POLLING_INTERVAL_S_DEFAULT);
132         TimeUnit timeUnit = TimeUnit.SECONDS;
133         if (pollingInterval > 0) {
134             if (pollingInterval < POLLING_INTERVAL_S_MIN) {
135                 pollingInterval = POLLING_INTERVAL_S_MIN;
136             }
137             logger.trace("({}) start polling scheduler with interval {} {}", getUID(), pollingInterval, timeUnit);
138             pollingJob = scheduler.scheduleWithFixedDelay(this::pollingJobAction, pollingInterval, pollingInterval,
139                     timeUnit);
140         } else {
141             stopScheduler(pollingJob);
142         }
143     }
144
145     protected void pollingJobAction() {
146         if (ThingStatus.ONLINE.equals(getState())) {
147             queryDeviceData();
148         }
149     }
150
151     protected void startReconnectScheduler() {
152         int pollingInterval = config.reconnectInterval;
153         TimeUnit timeUnit = TimeUnit.SECONDS;
154         if (pollingInterval < RECONNECT_INTERVAL_S) {
155             pollingInterval = RECONNECT_INTERVAL_S;
156         }
157         logger.trace("({}) start reconnect scheduler in {} {}", getUID(), pollingInterval, timeUnit);
158         reconnectJob = scheduler.schedule(this::reconnectJobAction, pollingInterval, timeUnit);
159     }
160
161     protected void reconnectJobAction() {
162         connect();
163     }
164
165     protected void startDiscoveryScheduler() {
166         int pollingInterval = config.discoveryInterval;
167         TimeUnit timeUnit = TimeUnit.SECONDS;
168         if (config.autoDiscoveryEnabled && pollingInterval > 0) {
169             logger.trace("{} starting bridge discovery sheduler with interval {} {}", getUID(), pollingInterval,
170                     timeUnit);
171             discoveryJob = scheduler.scheduleWithFixedDelay(discoveryService::startScan, 0, pollingInterval, timeUnit);
172         } else {
173             stopScheduler(discoveryJob);
174         }
175     }
176
177     /**
178      * Stops a scheduler.
179      *
180      * @param scheduler {@code ScheduledFeature<?>} which should be stopped
181      */
182     protected void stopScheduler(@Nullable ScheduledFuture<?> scheduler) {
183         if (scheduler != null) {
184             logger.trace("{} stopping scheduler {}", uid, scheduler);
185             scheduler.cancel(true);
186         }
187     }
188
189     /*
190      * Functions
191      */
192
193     /**
194      * Connects to the router and sets the states.
195      */
196     @SuppressWarnings("null")
197     protected void connect() {
198         connector.login();
199         if (connector.cookieStore.cookieIsSet()) {
200             stopScheduler(reconnectJob);
201             queryDeviceData(false);
202             devicePropertiesChanged(deviceInfo);
203             setState(ThingStatus.ONLINE);
204             startPollingJob();
205         } else {
206             setState(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, errorHandler.getErrorMessage());
207         }
208     }
209
210     @SuppressWarnings("null")
211     public void queryDeviceData(Boolean asyncRequest) {
212         connector.queryDeviceData(CMD_GET_SYSINFO + CMD_GET_USAGE + CMD_GET_LANINFO + CMD_GET_WANINFO
213                 + CMD_GET_CLIENTLIST + CMD_GET_TRAFFIC, asyncRequest);
214     }
215
216     /**
217      * Queries device data asynchronously.
218      */
219     public void queryDeviceData() {
220         queryDeviceData(true);
221     }
222
223     /**
224      * Sets routerInfo data and updates channels on receiving new data with the associated command.
225      *
226      * @param jsonObject contains the received data
227      * @param command the command that was sent
228      */
229     public void dataReceived(JsonObject jsonObject, String command) {
230         if (command.contains(CMD_GET_SYSINFO)) {
231             deviceInfo.setSysInfo(jsonObject);
232             devicePropertiesChanged(deviceInfo);
233         }
234         if (command.contains(CMD_GET_CLIENTLIST)) {
235             clientList.setData(jsonObject);
236             updateClients();
237         }
238         if (command.contains(CMD_GET_LANINFO)) {
239             interfaceList.setData(INTERFACE_LAN, jsonObject);
240             updateChild(THING_TYPE_INTERFACE, NETWORK_REPRESENTATION_PROPERTY, INTERFACE_LAN);
241         }
242         if (command.contains(CMD_GET_WANINFO)) {
243             interfaceList.setData(INTERFACE_WAN, jsonObject);
244             updateChild(THING_TYPE_INTERFACE, NETWORK_REPRESENTATION_PROPERTY, INTERFACE_WAN);
245         }
246         if (command.contains(CMD_GET_USAGE) || command.contains(CMD_GET_MEMUSAGE)
247                 || command.contains(CMD_GET_CPUUSAGE)) {
248             deviceInfo.setUsageStats(jsonObject);
249         }
250         updateChannels(deviceInfo, clientList);
251     }
252
253     /**
254      * Updates the router status.
255      */
256     public void setState(ThingStatus thingStatus, ThingStatusDetail statusDetail, String text) {
257         if (!thingStatus.equals(getThing().getStatus())) {
258             updateStatus(thingStatus, statusDetail, text);
259             updateChildStates(thingStatus);
260             if (ThingStatus.OFFLINE.equals(thingStatus)) {
261                 stopScheduler(pollingJob);
262                 // Set channels to undef
263                 getThing().getChannels().forEach(c -> updateState(c.getUID(), UnDefType.UNDEF));
264                 startReconnectScheduler();
265             }
266         }
267     }
268
269     /**
270      * Upate RouterStatus
271      */
272     public void setState(ThingStatus thingStatus) {
273         setState(thingStatus, ThingStatusDetail.NONE, "");
274     }
275
276     /***********************************
277      *
278      * PUBLIC GETs
279      *
280      ************************************/
281
282     public HttpClient getHttpClient() {
283         return httpClient;
284     }
285
286     public AsuswrtConfiguration getConfiguration() {
287         return config;
288     }
289
290     public AsuswrtErrorHandler getErrorHandler() {
291         return errorHandler;
292     }
293
294     public ThingUID getUID() {
295         return thing.getUID();
296     }
297
298     public AsuswrtRouterInfo getDeviceInfo() {
299         return deviceInfo;
300     }
301
302     public AsuswrtClientList getClients() {
303         return clientList;
304     }
305
306     public AsuswrtInterfaceList getInterfaces() {
307         return interfaceList;
308     }
309
310     public ThingStatus getState() {
311         return getThing().getStatus();
312     }
313
314     /***********************************
315      *
316      * COMMAND HANDLER
317      *
318      ************************************/
319     @Override
320     public void handleCommand(ChannelUID channelUID, Command command) {
321         if (command instanceof RefreshType) {
322             queryDeviceData();
323         }
324     }
325
326     /***********************************
327      *
328      * PROPERTIES
329      *
330      ************************************/
331
332     /**
333      * UPDATE PROPERTIES
334      *
335      * If only one property must be changed, there is also a convenient method
336      * updateProperty(String name, String value).
337      */
338     public void devicePropertiesChanged(AsuswrtRouterInfo deviceInfo) {
339         /* device properties */
340         Map<String, String> properties = editProperties();
341         properties.put(Thing.PROPERTY_MAC_ADDRESS, interfaceList.getByName(INTERFACE_WAN).getMAC());
342         properties.put(Thing.PROPERTY_MODEL_ID, deviceInfo.getProductId());
343         properties.put(Thing.PROPERTY_FIRMWARE_VERSION, deviceInfo.getFirmwareVersion());
344         updateProperties(properties);
345     }
346
347     /***********************************
348      *
349      * CHANNELS
350      *
351      ************************************/
352
353     /**
354      * Update all Channels
355      */
356     public void updateChannels(AsuswrtRouterInfo deviceInfo, AsuswrtClientList clientList) {
357         updateClientChannels(clientList);
358         updateUsageChannels(deviceInfo);
359     }
360
361     /**
362      * Update Channel Usage
363      */
364     public void updateUsageChannels(AsuswrtRouterInfo deviceInfo) {
365         updateState(getChannelID(CHANNEL_GROUP_SYSINFO, CHANNEL_MEM_TOTAL),
366                 getQuantityType(deviceInfo.getMemUsage().getTotal(), Units.MEGABYTE));
367         updateState(getChannelID(CHANNEL_GROUP_SYSINFO, CHANNEL_MEM_FREE),
368                 getQuantityType(deviceInfo.getMemUsage().getFree(), Units.MEGABYTE));
369         updateState(getChannelID(CHANNEL_GROUP_SYSINFO, CHANNEL_MEM_USED),
370                 getQuantityType(deviceInfo.getMemUsage().getUsed(), Units.MEGABYTE));
371         updateState(getChannelID(CHANNEL_GROUP_SYSINFO, CHANNEL_MEM_FREE_PERCENT),
372                 getQuantityType(deviceInfo.getMemUsage().getFreePercent(), Units.PERCENT));
373         updateState(getChannelID(CHANNEL_GROUP_SYSINFO, CHANNEL_MEM_USED_PERCENT),
374                 getQuantityType(deviceInfo.getMemUsage().getUsedPercent(), Units.PERCENT));
375         updateState(getChannelID(CHANNEL_GROUP_SYSINFO, CHANNEL_CPU_USED_PERCENT),
376                 getQuantityType(deviceInfo.getCpuAverage().getUsedPercent(), Units.PERCENT));
377     }
378
379     /**
380      * Update Client Channel
381      */
382     public void updateClientChannels(AsuswrtClientList clientList) {
383         updateState(getChannelID(CHANNEL_GROUP_CLIENTS, CHANNEL_CLIENTS_KNOWN),
384                 getStringType(clientList.getClientList()));
385         updateState(getChannelID(CHANNEL_GROUP_CLIENTS, CHANNEL_CLIENTS_ONLINE),
386                 getStringType(clientList.getOnlineClients().getClientList()));
387         updateState(getChannelID(CHANNEL_GROUP_CLIENTS, CHANNEL_CLIENTS_COUNT),
388                 getDecimalType(clientList.getOnlineClients().getCount()));
389         updateState(getChannelID(CHANNEL_GROUP_CLIENTS, CHANNEL_CLIENTS_ONLINE_MAC),
390                 getStringType(clientList.getOnlineClients().getMacAddresses()));
391     }
392
393     /**
394      * Fire Event
395      *
396      * @param channel chanelUID event belongs to
397      * @param event event-name is fired
398      */
399     protected void fireEvent(String channel, String event) {
400         triggerChannel(channel, event);
401     }
402
403     /***********************************
404      *
405      * CHILD THINGS
406      *
407      ************************************/
408     /**
409      * Update all Child-Things with type Client
410      */
411     public void updateClients() {
412         updateChildThings(THING_TYPE_CLIENT);
413     }
414
415     /**
416      * Update all Child-Things with type Interface
417      */
418     public void updateInterfaces() {
419         updateChildThings(THING_TYPE_INTERFACE);
420     }
421
422     /**
423      * Update all Child-Things belonging to ThingTypeUID
424      */
425     public void updateChildThings(ThingTypeUID thingTypeToUpdate) {
426         ThingTypeUID thingTypeUID;
427         List<Thing> things = getThing().getThings();
428         for (Thing thing : things) {
429             thingTypeUID = thing.getThingTypeUID();
430             if (thingTypeToUpdate.equals(thingTypeUID)) {
431                 updateChild(thing);
432             }
433         }
434     }
435
436     /**
437      * Update Child single child with special representationProperty
438      *
439      * @param thingTypeToUpdate ThingTypeUID of Thing to update
440      * @param representationProperty Name of representationProperty
441      * @param propertyValue Value of representationProperty
442      */
443     public void updateChild(ThingTypeUID thingTypeToUpdate, String representationProperty, String propertyValue) {
444         List<Thing> things = getThing().getThings();
445         for (Thing thing : things) {
446             ThingTypeUID thingTypeUID = thing.getThingTypeUID();
447             if (thingTypeToUpdate.equals(thingTypeUID)) {
448                 String thingProperty = thing.getProperties().get(representationProperty);
449                 if (propertyValue.equals(thingProperty)) {
450                     updateChild(thing);
451                 }
452             }
453         }
454     }
455
456     /**
457      * Update Child-Thing (send refreshCommand)
458      *
459      * @param thing - Thing to update
460      */
461     public void updateChild(Thing thing) {
462         ThingHandler handler = thing.getHandler();
463         if (handler != null) {
464             ChannelUID cUid = new ChannelUID(thing.getUID(), CHANNELS_ALL);
465             handler.handleCommand(cUid, RefreshType.REFRESH);
466         }
467     }
468
469     /**
470      * Set State of all clients
471      *
472      * @param thingStatus new ThingStatus
473      */
474     public void updateChildStates(ThingStatus thingStatus) {
475         List<Thing> things = getThing().getThings();
476         for (Thing thing : things) {
477             updateChildState(thing, thingStatus);
478         }
479     }
480
481     /**
482      * Set State of a Thing
483      *
484      * @param thing Thing to update
485      * @param thingStatus new ThingStatus
486      */
487     public void updateChildState(Thing thing, ThingStatus thingStatus) {
488         ThingHandler handler = thing.getHandler();
489         if (handler != null) {
490             if (ThingStatus.OFFLINE.equals(thingStatus)) {
491                 handler.bridgeStatusChanged(new ThingStatusInfo(thingStatus, ThingStatusDetail.BRIDGE_OFFLINE, ""));
492             } else {
493                 handler.bridgeStatusChanged(new ThingStatusInfo(thingStatus, ThingStatusDetail.NONE, ""));
494             }
495         }
496     }
497
498     /***********************************
499      *
500      * FUNCTIONS
501      *
502      ************************************/
503
504     /**
505      * Get ChannelID including group
506      *
507      * @param group String channel-group
508      * @param channel String channel-name
509      * @return String channelID
510      */
511     protected String getChannelID(String group, String channel) {
512         ThingTypeUID thingTypeUID = thing.getThingTypeUID();
513         if (CHANNEL_GROUP_THING_SET.contains(thingTypeUID) && group.length() > 0) {
514             return group + "#" + channel;
515         }
516         return channel;
517     }
518
519     /**
520      * Get Channel from ChannelID
521      *
522      * @param channelID String channelID
523      * @return String channel-name
524      */
525     protected String getChannelFromID(ChannelUID channelID) {
526         String channel = channelID.getIdWithoutGroup();
527         channel = channel.replace(CHANNEL_GROUP_SYSINFO + "#", "");
528         channel = channel.replace(CHANNEL_GROUP_CLIENTS + "#", "");
529         return channel;
530     }
531 }