2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.asuswrt.internal.things;
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.*;
19 import java.util.Collection;
20 import java.util.List;
22 import java.util.concurrent.ScheduledFuture;
23 import java.util.concurrent.TimeUnit;
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;
54 import com.google.gson.JsonObject;
57 * The {@link AsuswrtRouter} is responsible for handling commands, which are sent to one of the channels.
59 * @author Christian Wild - Initial contribution
62 public class AsuswrtRouter extends BaseBridgeHandler {
63 private final Logger logger = LoggerFactory.getLogger(AsuswrtRouter.class);
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;
78 public AsuswrtErrorHandler errorHandler;
80 public AsuswrtRouter(Bridge bridge, HttpClient httpClient) {
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();
91 public void initialize() {
92 config = getConfigAs(AsuswrtConfiguration.class);
93 connector = new AsuswrtConnector(this);
95 // Initialize the handler.
96 setState(ThingStatus.UNKNOWN);
98 // background initialization (delay it a little bit):
99 startupJob = scheduler.schedule(this::delayedStartUp, 1000, TimeUnit.MILLISECONDS);
103 public void dispose() {
104 stopScheduler(startupJob);
105 stopScheduler(pollingJob);
106 stopScheduler(discoveryJob);
107 stopScheduler(reconnectJob);
111 public Collection<Class<? extends ThingHandlerService>> getServices() {
112 return List.of(AsuswrtDiscoveryService.class);
115 public void setDiscoveryService(AsuswrtDiscoveryService discoveryService) {
116 this.discoveryService = discoveryService;
124 * Delayed one-time startup job.
126 private void delayedStartUp() {
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;
137 logger.trace("({}) start polling scheduler with interval {} {}", getUID(), pollingInterval, timeUnit);
138 pollingJob = scheduler.scheduleWithFixedDelay(this::pollingJobAction, pollingInterval, pollingInterval,
141 stopScheduler(pollingJob);
145 protected void pollingJobAction() {
146 if (ThingStatus.ONLINE.equals(getState())) {
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;
157 logger.trace("({}) start reconnect scheduler in {} {}", getUID(), pollingInterval, timeUnit);
158 reconnectJob = scheduler.schedule(this::reconnectJobAction, pollingInterval, timeUnit);
161 protected void reconnectJobAction() {
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,
171 discoveryJob = scheduler.scheduleWithFixedDelay(discoveryService::startScan, 0, pollingInterval, timeUnit);
173 stopScheduler(discoveryJob);
180 * @param scheduler {@code ScheduledFeature<?>} which should be stopped
182 protected void stopScheduler(@Nullable ScheduledFuture<?> scheduler) {
183 if (scheduler != null) {
184 logger.trace("{} stopping scheduler {}", uid, scheduler);
185 scheduler.cancel(true);
194 * Connects to the router and sets the states.
196 @SuppressWarnings("null")
197 protected void connect() {
199 if (connector.cookieStore.cookieIsSet()) {
200 stopScheduler(reconnectJob);
201 queryDeviceData(false);
202 devicePropertiesChanged(deviceInfo);
203 setState(ThingStatus.ONLINE);
206 setState(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, errorHandler.getErrorMessage());
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);
217 * Queries device data asynchronously.
219 public void queryDeviceData() {
220 queryDeviceData(true);
224 * Sets routerInfo data and updates channels on receiving new data with the associated command.
226 * @param jsonObject contains the received data
227 * @param command the command that was sent
229 public void dataReceived(JsonObject jsonObject, String command) {
230 if (command.contains(CMD_GET_SYSINFO)) {
231 deviceInfo.setSysInfo(jsonObject);
232 devicePropertiesChanged(deviceInfo);
234 if (command.contains(CMD_GET_CLIENTLIST)) {
235 clientList.setData(jsonObject);
238 if (command.contains(CMD_GET_LANINFO)) {
239 interfaceList.setData(INTERFACE_LAN, jsonObject);
240 updateChild(THING_TYPE_INTERFACE, NETWORK_REPRESENTATION_PROPERTY, INTERFACE_LAN);
242 if (command.contains(CMD_GET_WANINFO)) {
243 interfaceList.setData(INTERFACE_WAN, jsonObject);
244 updateChild(THING_TYPE_INTERFACE, NETWORK_REPRESENTATION_PROPERTY, INTERFACE_WAN);
246 if (command.contains(CMD_GET_USAGE) || command.contains(CMD_GET_MEMUSAGE)
247 || command.contains(CMD_GET_CPUUSAGE)) {
248 deviceInfo.setUsageStats(jsonObject);
250 updateChannels(deviceInfo, clientList);
254 * Updates the router status.
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();
272 public void setState(ThingStatus thingStatus) {
273 setState(thingStatus, ThingStatusDetail.NONE, "");
276 /***********************************
280 ************************************/
282 public HttpClient getHttpClient() {
286 public AsuswrtConfiguration getConfiguration() {
290 public AsuswrtErrorHandler getErrorHandler() {
294 public ThingUID getUID() {
295 return thing.getUID();
298 public AsuswrtRouterInfo getDeviceInfo() {
302 public AsuswrtClientList getClients() {
306 public AsuswrtInterfaceList getInterfaces() {
307 return interfaceList;
310 public ThingStatus getState() {
311 return getThing().getStatus();
314 /***********************************
318 ************************************/
320 public void handleCommand(ChannelUID channelUID, Command command) {
321 if (command instanceof RefreshType) {
326 /***********************************
330 ************************************/
335 * If only one property must be changed, there is also a convenient method
336 * updateProperty(String name, String value).
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);
347 /***********************************
351 ************************************/
354 * Update all Channels
356 public void updateChannels(AsuswrtRouterInfo deviceInfo, AsuswrtClientList clientList) {
357 updateClientChannels(clientList);
358 updateUsageChannels(deviceInfo);
362 * Update Channel Usage
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));
380 * Update Client Channel
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()));
396 * @param channel chanelUID event belongs to
397 * @param event event-name is fired
399 protected void fireEvent(String channel, String event) {
400 triggerChannel(channel, event);
403 /***********************************
407 ************************************/
409 * Update all Child-Things with type Client
411 public void updateClients() {
412 updateChildThings(THING_TYPE_CLIENT);
416 * Update all Child-Things with type Interface
418 public void updateInterfaces() {
419 updateChildThings(THING_TYPE_INTERFACE);
423 * Update all Child-Things belonging to ThingTypeUID
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)) {
437 * Update Child single child with special representationProperty
439 * @param thingTypeToUpdate ThingTypeUID of Thing to update
440 * @param representationProperty Name of representationProperty
441 * @param propertyValue Value of representationProperty
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)) {
457 * Update Child-Thing (send refreshCommand)
459 * @param thing - Thing to update
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);
470 * Set State of all clients
472 * @param thingStatus new ThingStatus
474 public void updateChildStates(ThingStatus thingStatus) {
475 List<Thing> things = getThing().getThings();
476 for (Thing thing : things) {
477 updateChildState(thing, thingStatus);
482 * Set State of a Thing
484 * @param thing Thing to update
485 * @param thingStatus new ThingStatus
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, ""));
493 handler.bridgeStatusChanged(new ThingStatusInfo(thingStatus, ThingStatusDetail.NONE, ""));
498 /***********************************
502 ************************************/
505 * Get ChannelID including group
507 * @param group String channel-group
508 * @param channel String channel-name
509 * @return String channelID
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;
520 * Get Channel from ChannelID
522 * @param channelID String channelID
523 * @return String channel-name
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 + "#", "");