]> git.basschouten.com Git - openhab-addons.git/blob
2113b08785e69f8538762dcd78b7745cb675a978
[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.folderwatcher.internal.handler;
14
15 import static org.openhab.binding.folderwatcher.internal.FolderWatcherBindingConstants.CHANNEL_NEWFILE;
16
17 import java.io.File;
18 import java.io.IOException;
19 import java.nio.file.FileVisitResult;
20 import java.nio.file.FileVisitor;
21 import java.nio.file.Files;
22 import java.nio.file.Path;
23 import java.nio.file.Paths;
24 import java.nio.file.attribute.BasicFileAttributes;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.concurrent.ScheduledFuture;
28 import java.util.concurrent.TimeUnit;
29
30 import org.eclipse.jdt.annotation.NonNullByDefault;
31 import org.eclipse.jdt.annotation.Nullable;
32 import org.openhab.binding.folderwatcher.internal.common.WatcherCommon;
33 import org.openhab.binding.folderwatcher.internal.config.LocalFolderWatcherConfiguration;
34 import org.openhab.core.OpenHAB;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingStatus;
38 import org.openhab.core.thing.ThingStatusDetail;
39 import org.openhab.core.thing.binding.BaseThingHandler;
40 import org.openhab.core.types.Command;
41 import org.openhab.core.types.RefreshType;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 /**
46  * The {@link LocalFolderWatcherHandler} is responsible for handling commands, which are
47  * sent to one of the channels.
48  *
49  * @author Alexandr Salamatov - Initial contribution
50  */
51 @NonNullByDefault
52 public class LocalFolderWatcherHandler extends BaseThingHandler {
53     private final Logger logger = LoggerFactory.getLogger(LocalFolderWatcherHandler.class);
54     private LocalFolderWatcherConfiguration config = new LocalFolderWatcherConfiguration();
55     private File currentLocalListingFile = new File(OpenHAB.getUserDataFolder() + File.separator + "FolderWatcher"
56             + File.separator + thing.getUID().getAsString().replace(':', '_') + ".data");
57     private @Nullable ScheduledFuture<?> executionJob;
58     private List<String> previousLocalListing = new ArrayList<>();
59
60     public LocalFolderWatcherHandler(Thing thing) {
61         super(thing);
62     }
63
64     @Override
65     public void handleCommand(ChannelUID channelUID, Command command) {
66         logger.debug("Channel {} triggered with command {}", channelUID.getId(), command);
67         if (command instanceof RefreshType) {
68             refreshFolderInformation();
69         }
70     }
71
72     @Override
73     public void initialize() {
74         config = getConfigAs(LocalFolderWatcherConfiguration.class);
75         updateStatus(ThingStatus.UNKNOWN);
76
77         if (!Files.isDirectory(Paths.get(config.localDir))) {
78             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Local directory is not valid");
79             return;
80         }
81         try {
82             previousLocalListing = WatcherCommon.initStorage(currentLocalListingFile, config.localDir);
83         } catch (IOException e) {
84             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, e.getMessage());
85             logger.debug("Can't write file {}: {}", currentLocalListingFile, e.getMessage());
86             return;
87         }
88
89         if (config.pollIntervalLocal > 0) {
90             updateStatus(ThingStatus.ONLINE);
91             executionJob = scheduler.scheduleWithFixedDelay(this::refreshFolderInformation, config.pollIntervalLocal,
92                     config.pollIntervalLocal, TimeUnit.SECONDS);
93         } else {
94             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
95                     "Polling interval can't be null or negative");
96             return;
97         }
98     }
99
100     @Override
101     public void dispose() {
102         ScheduledFuture<?> executionJob = this.executionJob;
103         if (executionJob != null) {
104             executionJob.cancel(true);
105             this.executionJob = null;
106         }
107     }
108
109     private void refreshFolderInformation() {
110         final String rootDir = config.localDir;
111         try {
112             List<String> currentLocalListing = new ArrayList<>();
113
114             Files.walkFileTree(Paths.get(rootDir), new FileVisitor<@Nullable Path>() {
115                 @Override
116                 public FileVisitResult preVisitDirectory(@Nullable Path dir, @Nullable BasicFileAttributes attrs)
117                         throws IOException {
118                     if (dir != null) {
119                         if (!dir.equals(Paths.get(rootDir)) && !config.listRecursiveLocal) {
120                             return FileVisitResult.SKIP_SUBTREE;
121                         }
122                     }
123                     return FileVisitResult.CONTINUE;
124                 }
125
126                 @Override
127                 public FileVisitResult visitFile(@Nullable Path file, @Nullable BasicFileAttributes attrs)
128                         throws IOException {
129                     if (file != null) {
130                         if (Files.isHidden(file) && !config.listHiddenLocal) {
131                             return FileVisitResult.CONTINUE;
132                         }
133                         currentLocalListing.add(file.toAbsolutePath().toString());
134                     }
135                     return FileVisitResult.CONTINUE;
136                 }
137
138                 @Override
139                 public FileVisitResult visitFileFailed(@Nullable Path file, @Nullable IOException exc)
140                         throws IOException {
141                     return FileVisitResult.CONTINUE;
142                 }
143
144                 @Override
145                 public FileVisitResult postVisitDirectory(@Nullable Path dir, @Nullable IOException exc)
146                         throws IOException {
147                     return FileVisitResult.CONTINUE;
148                 }
149             });
150
151             List<String> diffLocalListing = new ArrayList<>(currentLocalListing);
152             diffLocalListing.removeAll(previousLocalListing);
153             diffLocalListing.forEach(file -> triggerChannel(CHANNEL_NEWFILE, file));
154
155             if (!diffLocalListing.isEmpty()) {
156                 WatcherCommon.saveNewListing(diffLocalListing, currentLocalListingFile);
157             }
158             previousLocalListing = new ArrayList<>(currentLocalListing);
159         } catch (IOException e) {
160             logger.debug("File manipulation error: {}", e.getMessage());
161         }
162     }
163 }