]> git.basschouten.com Git - openhab-addons.git/blob
647a78250449a98bedce81b5693f97869bf2b3b4
[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.hpprinter.internal;
14
15 import static org.openhab.binding.hpprinter.internal.HPPrinterBindingConstants.*;
16
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.ScheduledFuture;
21 import java.util.concurrent.TimeUnit;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.eclipse.jetty.client.HttpClient;
26 import org.openhab.binding.hpprinter.internal.api.HPFeatures;
27 import org.openhab.binding.hpprinter.internal.api.HPProductUsageFeatures;
28 import org.openhab.binding.hpprinter.internal.api.HPProductUsageFeatures.PrinterType;
29 import org.openhab.binding.hpprinter.internal.api.HPProperties;
30 import org.openhab.binding.hpprinter.internal.api.HPScannerStatus;
31 import org.openhab.binding.hpprinter.internal.api.HPScannerStatusFeatures;
32 import org.openhab.binding.hpprinter.internal.api.HPServerResult;
33 import org.openhab.binding.hpprinter.internal.api.HPServerResult.RequestStatus;
34 import org.openhab.binding.hpprinter.internal.api.HPStatus;
35 import org.openhab.binding.hpprinter.internal.api.HPUsage;
36 import org.openhab.binding.hpprinter.internal.api.HPWebServerClient;
37 import org.openhab.core.library.CoreItemFactory;
38 import org.openhab.core.library.types.DecimalType;
39 import org.openhab.core.library.types.OnOffType;
40 import org.openhab.core.library.types.QuantityType;
41 import org.openhab.core.library.types.StringType;
42 import org.openhab.core.library.unit.MetricPrefix;
43 import org.openhab.core.library.unit.Units;
44 import org.openhab.core.thing.Channel;
45 import org.openhab.core.thing.ChannelUID;
46 import org.openhab.core.thing.ThingStatus;
47 import org.openhab.core.thing.ThingStatusDetail;
48 import org.openhab.core.thing.ThingUID;
49 import org.openhab.core.thing.binding.builder.ChannelBuilder;
50 import org.openhab.core.types.State;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 /**
55  * The {@link HPPrinterBinder} connects the binding to the Web Server Client
56  * classes.
57  *
58  * @author Stewart Cossey - Initial contribution
59  */
60 @NonNullByDefault
61 public class HPPrinterBinder {
62     private final Logger logger = LoggerFactory.getLogger(HPPrinterBinder.class);
63
64     private static final int OFFLINE_CHECK_INTERVAL = 15;
65
66     private HPPrinterHandler handler;
67     private ScheduledExecutorService scheduler;
68
69     private final int statusCheckInterval;
70     private final int usageCheckInterval;
71     private final HPWebServerClient printerClient;
72
73     private @Nullable ScheduledFuture<?> statusScheduler;
74     private @Nullable ScheduledFuture<?> scannerStatusScheduler;
75     private @Nullable ScheduledFuture<?> usageScheduler;
76     private @Nullable ScheduledFuture<?> offlineScheduler;
77
78     private boolean handlerDisposed;
79
80     /**
81      * Creates a new HP Printer Binder object
82      *
83      * @param handler {HPPrinterBinderEvent} The Event handler for the binder.
84      * @param httpClient {HttpClient} The HttpClient object to use to perform HTTP
85      *            requests.
86      * @param scheduler {ScheduledExecutorService} The scheduler service object.
87      * @param config {HPPrinterConfiguration} The configuration object.
88      */
89     public HPPrinterBinder(HPPrinterHandler handler, HttpClient httpClient, ScheduledExecutorService scheduler,
90             HPPrinterConfiguration config) {
91         this.handler = handler;
92         this.scheduler = scheduler;
93         usageCheckInterval = config.usageInterval;
94         statusCheckInterval = config.statusInterval;
95         String ipAddress = config.ipAddress;
96         if (ipAddress == null) {
97             throw new IllegalStateException("ip-address should have been validated already and may not be empty.");
98         }
99         printerClient = new HPWebServerClient(httpClient, ipAddress);
100         handlerDisposed = false;
101     }
102
103     public void retrieveProperties() {
104         HPServerResult<HPProperties> result = printerClient.getProperties();
105
106         if (result.getStatus() == RequestStatus.SUCCESS) {
107             handler.updateProperties(result.getData().getProperties());
108         }
109     }
110
111     public synchronized void channelsChanged() {
112         logger.trace("Channels have been changed");
113         closeInternal();
114         open();
115     }
116
117     /**
118      * Dynamically add channels to the Thing based on the Embedded Web Server Usage
119      * Feed
120      */
121     public void dynamicallyAddChannels(ThingUID thingUid) {
122         logger.debug("Building dynamic channels based on printer");
123
124         HPServerResult<HPFeatures> featureResult = printerClient.getProductUsageFeatures();
125         if (featureResult.getStatus() == RequestStatus.SUCCESS) {
126             final List<Channel> channels = new ArrayList<>();
127
128             HPFeatures features = featureResult.getData();
129
130             if (features.getProductStatusSupported()) {
131                 channels.add(ChannelBuilder
132                         .create(new ChannelUID(thingUid, CGROUP_STATUS, CHANNEL_STATUS), CoreItemFactory.STRING)
133                         .withLabel("Status").withDescription("Printer status").withType(chanTypeStatus).build());
134
135                 channels.add(ChannelBuilder
136                         .create(new ChannelUID(thingUid, CGROUP_STATUS, CHANNEL_TRAYEMPTYOROPEN),
137                                 CoreItemFactory.SWITCH)
138                         .withLabel("Tray Empty/Open").withDescription("The tray is empty or open")
139                         .withType(chanTypeReadSwitch).build());
140             }
141
142             if (features.getScannerStatusSupported()) {
143                 HPServerResult<HPScannerStatusFeatures> result = printerClient.getScannerFeatures();
144                 if (result.getStatus() == RequestStatus.SUCCESS) {
145                     HPScannerStatusFeatures feature = result.getData();
146
147                     if (feature.hasStatus()) {
148                         channels.add(ChannelBuilder
149                                 .create(new ChannelUID(thingUid, CGROUP_STATUS, CHANNEL_SCANNER_STATUS),
150                                         CoreItemFactory.STRING)
151                                 .withLabel("Scanner").withDescription("Scanner status").withType(chanTypeStatus)
152                                 .build());
153                     }
154
155                     if (feature.hasAdf()) {
156                         channels.add(ChannelBuilder
157                                 .create(new ChannelUID(thingUid, CGROUP_STATUS, CHANNEL_SCANNER_ADFLOADED),
158                                         CoreItemFactory.SWITCH)
159                                 .withLabel("ADF Loaded").withDescription("The automatic document feeder is loaded")
160                                 .withType(chanTypeReadSwitch).build());
161                     }
162                 }
163             }
164
165             if (features.getProductUsageSupported()) {
166                 HPServerResult<HPProductUsageFeatures> result = printerClient.getProductFeatures();
167                 if (result.getStatus() == RequestStatus.SUCCESS) {
168                     HPProductUsageFeatures feature = result.getData();
169
170                     channels.add(ChannelBuilder
171                             .create(new ChannelUID(thingUid, CGROUP_INK, CHANNEL_BLACK_LEVEL),
172                                     HPPrinterBindingConstants.ITEM_TYPE_INK)
173                             .withLabel("Black Level").withDescription("Shows the amount of Black Ink/Toner remaining")
174                             .withType(chanTypeInkLevel).build());
175
176                     channels.add(ChannelBuilder
177                             .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_TOTAL_PAGES), CoreItemFactory.NUMBER)
178                             .withLabel("Total Lifetime Pages")
179                             .withDescription("The amount of pages printed over the printer lifetime")
180                             .withType(chanTypeTotals).build());
181
182                     if (feature.hasCumulativeMarking()) {
183                         channels.add(ChannelBuilder
184                                 .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_BLACK_MARKING),
185                                         HPPrinterBindingConstants.ITEM_TYPE_CUMLMARK)
186                                 .withLabel("Black Marking Used").withDescription("The amount of Black Marking used")
187                                 .withType(chanTypeMarking).build());
188                     }
189
190                     switch (feature.getType()) {
191                         case SINGLECOLOR:
192                             if (feature.hasCumulativeMarking()) {
193                                 channels.add(ChannelBuilder
194                                         .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_COLOR_MARKING),
195                                                 HPPrinterBindingConstants.ITEM_TYPE_CUMLMARK)
196                                         .withLabel("Colour Marking Used")
197                                         .withDescription("The amount of Colour Marking used").withType(chanTypeMarking)
198                                         .build());
199                             }
200
201                             channels.add(ChannelBuilder
202                                     .create(new ChannelUID(thingUid, CGROUP_INK, CHANNEL_COLOR_LEVEL),
203                                             HPPrinterBindingConstants.ITEM_TYPE_INK)
204                                     .withLabel("Color Level")
205                                     .withDescription("Shows the amount of Colour Ink/Toner remaining")
206                                     .withType(chanTypeInkLevel).build());
207
208                             channels.add(ChannelBuilder
209                                     .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_TOTAL_COLORPAGES),
210                                             CoreItemFactory.NUMBER)
211                                     .withLabel("Total Colour Pages")
212                                     .withDescription("The amount of colour pages printed").withType(chanTypeTotals)
213                                     .build());
214
215                             channels.add(ChannelBuilder
216                                     .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_TOTAL_MONOPAGES),
217                                             CoreItemFactory.NUMBER)
218                                     .withLabel("Total Monochrome Pages")
219                                     .withDescription("The amount of monochrome pages printed").withType(chanTypeTotals)
220                                     .build());
221
222                             if (feature.hasPagesRemaining()) {
223                                 channels.add(ChannelBuilder
224                                         .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_BLACK_PAGES_REMAINING),
225                                                 CoreItemFactory.NUMBER)
226                                         .withLabel("Black Pages Remaining")
227                                         .withDescription("Estimated Black pages remaining")
228                                         .withType(chanTypeTotalsAdvanced).build());
229
230                                 channels.add(ChannelBuilder
231                                         .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_COLOR_PAGES_REMAINING),
232                                                 CoreItemFactory.NUMBER)
233                                         .withLabel("Colour Pages Remaining")
234                                         .withDescription("Estimated Colour pages remaining")
235                                         .withType(chanTypeTotalsAdvanced).build());
236                             }
237
238                             break;
239
240                         case MULTICOLOR:
241                             if (feature.hasCumulativeMarking()) {
242                                 channels.add(ChannelBuilder
243                                         .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_CYAN_MARKING),
244                                                 HPPrinterBindingConstants.ITEM_TYPE_CUMLMARK)
245                                         .withLabel("Cyan Marking Used")
246                                         .withDescription("The amount of Cyan Marking used").withType(chanTypeMarking)
247                                         .build());
248
249                                 channels.add(ChannelBuilder
250                                         .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_MAGENTA_MARKING),
251                                                 HPPrinterBindingConstants.ITEM_TYPE_CUMLMARK)
252                                         .withLabel("Magenta Marking Used")
253                                         .withDescription("The amount of Magenta Marking used").withType(chanTypeMarking)
254                                         .build());
255
256                                 channels.add(ChannelBuilder
257                                         .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_YELLOW_MARKING),
258                                                 HPPrinterBindingConstants.ITEM_TYPE_CUMLMARK)
259                                         .withLabel("Yellow Marking Used")
260                                         .withDescription("The amount of Yellow Marking used").withType(chanTypeMarking)
261                                         .build());
262                             }
263
264                             if (feature.hasPagesRemaining()) {
265                                 channels.add(ChannelBuilder
266                                         .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_BLACK_PAGES_REMAINING),
267                                                 CoreItemFactory.NUMBER)
268                                         .withLabel("Black Pages Remaining")
269                                         .withDescription("Estimated Black pages remaining")
270                                         .withType(chanTypeTotalsAdvanced).build());
271
272                                 channels.add(ChannelBuilder
273                                         .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_CYAN_PAGES_REMAINING),
274                                                 CoreItemFactory.NUMBER)
275                                         .withLabel("Cyan Pages Remaining")
276                                         .withDescription("Estimated Cyan pages remaining")
277                                         .withType(chanTypeTotalsAdvanced).build());
278
279                                 channels.add(ChannelBuilder
280                                         .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_MAGENTA_PAGES_REMAINING),
281                                                 CoreItemFactory.NUMBER)
282                                         .withLabel("Magenta Pages Remaining")
283                                         .withDescription("Estimated Magenta pages remaining")
284                                         .withType(chanTypeTotalsAdvanced).build());
285
286                                 channels.add(ChannelBuilder
287                                         .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_YELLOW_PAGES_REMAINING),
288                                                 CoreItemFactory.NUMBER)
289                                         .withLabel("Yellow Pages Remaining")
290                                         .withDescription("Estimated Yellow pages remaining")
291                                         .withType(chanTypeTotalsAdvanced).build());
292                             }
293
294                             channels.add(ChannelBuilder
295                                     .create(new ChannelUID(thingUid, CGROUP_INK, CHANNEL_CYAN_LEVEL),
296                                             HPPrinterBindingConstants.ITEM_TYPE_INK)
297                                     .withLabel("Cyan Level")
298                                     .withDescription("Shows the amount of Cyan Ink/Toner remaining")
299                                     .withType(chanTypeInkLevel).build());
300
301                             channels.add(ChannelBuilder
302                                     .create(new ChannelUID(thingUid, CGROUP_INK, CHANNEL_MAGENTA_LEVEL),
303                                             HPPrinterBindingConstants.ITEM_TYPE_INK)
304                                     .withLabel("Magenta Level")
305                                     .withDescription("Shows the amount of Magenta Ink/Toner remaining")
306                                     .withType(chanTypeInkLevel).build());
307
308                             channels.add(ChannelBuilder
309                                     .create(new ChannelUID(thingUid, CGROUP_INK, CHANNEL_YELLOW_LEVEL),
310                                             HPPrinterBindingConstants.ITEM_TYPE_INK)
311                                     .withLabel("Yellow Level")
312                                     .withDescription("Shows the amount of Yellow Ink/Toner remaining")
313                                     .withType(chanTypeInkLevel).build());
314
315                             channels.add(ChannelBuilder
316                                     .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_TOTAL_COLORPAGES),
317                                             CoreItemFactory.NUMBER)
318                                     .withLabel("Total Colour Pages")
319                                     .withDescription("The amount of colour pages printed").withType(chanTypeTotals)
320                                     .build());
321
322                             channels.add(ChannelBuilder
323                                     .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_TOTAL_MONOPAGES),
324                                             CoreItemFactory.NUMBER)
325                                     .withLabel("Total Monochrome Pages")
326                                     .withDescription("The amount of monochrome pages printed").withType(chanTypeTotals)
327                                     .build());
328
329                             break;
330
331                         default:
332                             if (feature.hasPagesRemaining()) {
333                                 channels.add(ChannelBuilder
334                                         .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_BLACK_PAGES_REMAINING),
335                                                 CoreItemFactory.NUMBER)
336                                         .withLabel("Black Pages Remaining")
337                                         .withDescription("Estimated Black pages remaining")
338                                         .withType(chanTypeTotalsAdvanced).build());
339                             }
340                     }
341
342                     if (feature.hasJamEvents()) {
343                         channels.add(ChannelBuilder
344                                 .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_JAM_EVENTS),
345                                         CoreItemFactory.NUMBER)
346                                 .withLabel("Jams").withDescription("The amount of times the paper has jammed")
347                                 .withType(chanTypeTotalsAdvanced).build());
348                     }
349
350                     if (feature.hasMispickEvents()) {
351                         channels.add(ChannelBuilder
352                                 .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_MISPICK_EVENTS),
353                                         CoreItemFactory.NUMBER)
354                                 .withLabel("Missed Picks")
355                                 .withDescription("The amount of times the paper failed to feed into the printer")
356                                 .withType(chanTypeTotalsAdvanced).build());
357                     }
358
359                     if (feature.hasSubscriptionCount()) {
360                         channels.add(ChannelBuilder
361                                 .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_SUBSCRIPTION),
362                                         CoreItemFactory.NUMBER)
363                                 .withLabel("Subscription Count")
364                                 .withDescription("The amount of times an item has been printed in subscription")
365                                 .withType(chanTypeTotalsAdvanced).build());
366                     }
367
368                     if (feature.hasTotalFrontPanelCancelPresses()) {
369                         channels.add(ChannelBuilder
370                                 .create(new ChannelUID(thingUid, CGROUP_USAGE, CHANNEL_FRONT_PANEL_CANCEL),
371                                         CoreItemFactory.NUMBER)
372                                 .withLabel("Front Panel Cancel Count")
373                                 .withDescription("The amount of times a print has been cancelled from the Front Panel")
374                                 .withType(chanTypeTotalsAdvanced).build());
375                     }
376
377                     // Other
378                     if (feature.hasCloudPrint()) {
379                         channels.add(ChannelBuilder
380                                 .create(new ChannelUID(thingUid, CGROUP_OTHER, CHANNEL_CLOUD_PRINT),
381                                         CoreItemFactory.NUMBER)
382                                 .withLabel("Cloud Print Count")
383                                 .withDescription(
384                                         "The amount of times a document has been printed via Google Cloud Print")
385                                 .withType(chanTypeTotalsAdvanced).build());
386                     }
387
388                     // Scan
389                     if (feature.hasScanADF()) {
390                         channels.add(ChannelBuilder
391                                 .create(new ChannelUID(thingUid, CGROUP_SCAN, CHANNEL_TOTAL_ADF),
392                                         CoreItemFactory.NUMBER)
393                                 .withLabel("Document Feeder Count")
394                                 .withDescription("Times scanned via the Document Feeder").withType(chanTypeTotals)
395                                 .build());
396                     }
397
398                     if (feature.hasScanFlatbed()) {
399                         channels.add(ChannelBuilder
400                                 .create(new ChannelUID(thingUid, CGROUP_SCAN, CHANNEL_TOTAL_FLATBED),
401                                         CoreItemFactory.NUMBER)
402                                 .withLabel("Flatbed Count").withDescription("Times scanned via the Flatbed/Glass")
403                                 .withType(chanTypeTotals).build());
404                     }
405
406                     if (feature.hasScanToEmail()) {
407                         channels.add(ChannelBuilder
408                                 .create(new ChannelUID(thingUid, CGROUP_SCAN, CHANNEL_TOTAL_TOEMAIL),
409                                         CoreItemFactory.NUMBER)
410                                 .withLabel("Scan to Email Count").withDescription("Times scanned using Scan to Email")
411                                 .withType(chanTypeTotalsAdvanced).build());
412                     }
413
414                     if (feature.hasScanToFolder()) {
415                         channels.add(ChannelBuilder
416                                 .create(new ChannelUID(thingUid, CGROUP_SCAN, CHANNEL_TOTAL_TOFOLDER),
417                                         CoreItemFactory.NUMBER)
418                                 .withLabel("Scan to Folder Count").withDescription("Times scanned using Scan to Folder")
419                                 .withType(chanTypeTotalsAdvanced).build());
420                     }
421
422                     if (feature.hasScanToHost()) {
423                         channels.add(ChannelBuilder
424                                 .create(new ChannelUID(thingUid, CGROUP_SCAN, CHANNEL_TOTAL_TOHOST),
425                                         CoreItemFactory.NUMBER)
426                                 .withLabel("Scan to Host Count")
427                                 .withDescription("Times scanned using Scan to Host Device")
428                                 .withType(chanTypeTotalsAdvanced).build());
429                     }
430
431                     // Scanner Engine
432                     if (feature.hasScannerEngine()) {
433                         if (feature.hasScanADF()) {
434                             channels.add(ChannelBuilder
435                                     .create(new ChannelUID(thingUid, CGROUP_SCANNER, CHANNEL_TOTAL_ADF),
436                                             CoreItemFactory.NUMBER)
437                                     .withLabel("Document Feeder Count")
438                                     .withDescription("Times scanned via the Document Feeder").withType(chanTypeTotals)
439                                     .build());
440                         }
441
442                         if (feature.hasScanFlatbed()) {
443                             channels.add(ChannelBuilder
444                                     .create(new ChannelUID(thingUid, CGROUP_SCANNER, CHANNEL_TOTAL_FLATBED),
445                                             CoreItemFactory.NUMBER)
446                                     .withLabel("Flatbed Count").withDescription("Times scanned via the Flatbed/Glass")
447                                     .withType(chanTypeTotals).build());
448                         }
449
450                         channels.add(ChannelBuilder
451                                 .create(new ChannelUID(thingUid, CGROUP_SCANNER, CHANNEL_JAM_EVENTS),
452                                         CoreItemFactory.NUMBER)
453                                 .withLabel("Jams").withDescription("The amount of times the paper has jammed")
454                                 .withType(chanTypeTotalsAdvanced).build());
455
456                         channels.add(ChannelBuilder
457                                 .create(new ChannelUID(thingUid, CGROUP_SCANNER, CHANNEL_MISPICK_EVENTS),
458                                         CoreItemFactory.NUMBER)
459                                 .withLabel("Missed Picks")
460                                 .withDescription("The amount of times the paper failed to feed into the printer")
461                                 .withType(chanTypeTotalsAdvanced).build());
462
463                     }
464
465                     // Copy Engine
466                     if (feature.hasCopyApplication()) {
467                         if (feature.hasScanADF()) {
468                             channels.add(ChannelBuilder
469                                     .create(new ChannelUID(thingUid, CGROUP_COPY, CHANNEL_TOTAL_ADF),
470                                             CoreItemFactory.NUMBER)
471                                     .withLabel("Document Feeder Count")
472                                     .withDescription("Times scanned via the Document Feeder").withType(chanTypeTotals)
473                                     .build());
474                         }
475
476                         if (feature.hasScanFlatbed()) {
477                             channels.add(ChannelBuilder
478                                     .create(new ChannelUID(thingUid, CGROUP_COPY, CHANNEL_TOTAL_FLATBED),
479                                             CoreItemFactory.NUMBER)
480                                     .withLabel("Flatbed Count").withDescription("Times scanned via the Flatbed/Glass")
481                                     .withType(chanTypeTotals).build());
482                         }
483
484                         channels.add(ChannelBuilder
485                                 .create(new ChannelUID(thingUid, CGROUP_COPY, CHANNEL_TOTAL_PAGES),
486                                         CoreItemFactory.NUMBER)
487                                 .withLabel("Total Pages").withDescription("The amount of pages copied")
488                                 .withType(chanTypeTotals).build());
489
490                         if (feature.getType() == PrinterType.MULTICOLOR
491                                 || feature.getType() == PrinterType.SINGLECOLOR) {
492                             channels.add(ChannelBuilder
493                                     .create(new ChannelUID(thingUid, CGROUP_COPY, CHANNEL_TOTAL_COLORPAGES),
494                                             CoreItemFactory.NUMBER)
495                                     .withLabel("Total Colour Pages")
496                                     .withDescription("The amount of colour pages copied").withType(chanTypeTotals)
497                                     .build());
498
499                             channels.add(ChannelBuilder
500                                     .create(new ChannelUID(thingUid, CGROUP_COPY, CHANNEL_TOTAL_MONOPAGES),
501                                             CoreItemFactory.NUMBER)
502                                     .withLabel("Total Monochrome Pages")
503                                     .withDescription("The amount of monochrome pages copied").withType(chanTypeTotals)
504                                     .build());
505                         }
506                     }
507
508                     // App Usage
509                     if (feature.hasPrintApplication()) {
510                         channels.add(ChannelBuilder
511                                 .create(new ChannelUID(thingUid, CGROUP_APP, CHANNEL_TOTAL_WIN), CoreItemFactory.NUMBER)
512                                 .withLabel("Windows").withType(chanTypeTotalsAdvanced).build());
513
514                         channels.add(ChannelBuilder
515                                 .create(new ChannelUID(thingUid, CGROUP_APP, CHANNEL_TOTAL_ANDROID),
516                                         CoreItemFactory.NUMBER)
517                                 .withLabel("Android").withType(chanTypeTotalsAdvanced).build());
518
519                         channels.add(ChannelBuilder
520                                 .create(new ChannelUID(thingUid, CGROUP_APP, CHANNEL_TOTAL_IOS), CoreItemFactory.NUMBER)
521                                 .withLabel("iOS").withType(chanTypeTotalsAdvanced).build());
522
523                         channels.add(ChannelBuilder
524                                 .create(new ChannelUID(thingUid, CGROUP_APP, CHANNEL_TOTAL_OSX), CoreItemFactory.NUMBER)
525                                 .withLabel("OSX").withType(chanTypeTotalsAdvanced).build());
526
527                         channels.add(ChannelBuilder
528                                 .create(new ChannelUID(thingUid, CGROUP_APP, CHANNEL_TOTAL_SAMSUNG),
529                                         CoreItemFactory.NUMBER)
530                                 .withLabel("Samsung").withType(chanTypeTotalsAdvanced).build());
531
532                         if (feature.hasPrintApplicationChrome()) {
533                             channels.add(ChannelBuilder
534                                     .create(new ChannelUID(thingUid, CGROUP_APP, CHANNEL_TOTAL_CHROME),
535                                             CoreItemFactory.NUMBER)
536                                     .withLabel("Chrome").withType(chanTypeTotalsAdvanced).build());
537                         }
538                     }
539                 }
540             }
541
542             handler.binderAddChannels(channels);
543         }
544     }
545
546     /**
547      * Opens the connection to the Embedded Web Server
548      */
549     public void open() {
550         runOfflineScheduler();
551     }
552
553     /**
554      * Public method to close the connection to the Embedded Web Server
555      *
556      * Set handlerDisposed to prevent call-backs to the handler after it has been disposed
557      * Then call the closeinternal() method
558      */
559     public void close() {
560         handlerDisposed = true;
561         closeInternal();
562     }
563
564     /**
565      * Private (internal) method to close the connection to the Embedded Web Server
566      */
567     private void closeInternal() {
568         stopBackgroundSchedules();
569
570         final ScheduledFuture<?> localOfflineScheduler = offlineScheduler;
571
572         if (localOfflineScheduler != null) {
573             localOfflineScheduler.cancel(true);
574             offlineScheduler = null;
575         }
576     }
577
578     /**
579      * The device has gone offline
580      */
581     private void goneOffline() {
582         handler.updateStatus(ThingStatus.OFFLINE);
583
584         closeInternal();
585         runOfflineScheduler();
586     }
587
588     private void runOfflineScheduler() {
589         offlineScheduler = scheduler.scheduleWithFixedDelay(this::checkOnline, 0, OFFLINE_CHECK_INTERVAL,
590                 TimeUnit.SECONDS);
591     }
592
593     private synchronized void stopBackgroundSchedules() {
594         logger.debug("Stopping Interval Refreshes");
595         ScheduledFuture<?> localUsageScheduler = usageScheduler;
596         if (localUsageScheduler != null) {
597             localUsageScheduler.cancel(true);
598         }
599
600         ScheduledFuture<?> localStatusScheduler = statusScheduler;
601         if (localStatusScheduler != null) {
602             localStatusScheduler.cancel(true);
603         }
604
605         ScheduledFuture<?> localScannerStatusScheduler = scannerStatusScheduler;
606         if (localScannerStatusScheduler != null) {
607             localScannerStatusScheduler.cancel(true);
608         }
609     }
610
611     private synchronized void startBackgroundSchedules() {
612         stopBackgroundSchedules();
613         logger.debug("Starting Interval Refreshes");
614
615         usageScheduler = scheduler.scheduleWithFixedDelay(this::checkUsage, 0, usageCheckInterval, TimeUnit.SECONDS);
616
617         // Only schedule below items when there channels are linked
618         if (handler.areStatusChannelsLinked(new String[] { CHANNEL_STATUS, CHANNEL_TRAYEMPTYOROPEN })) {
619             statusScheduler = scheduler.scheduleWithFixedDelay(this::checkStatus, 0, statusCheckInterval,
620                     TimeUnit.SECONDS);
621         }
622
623         if (handler.areStatusChannelsLinked(new String[] { CHANNEL_SCANNER_STATUS, CHANNEL_SCANNER_ADFLOADED })) {
624             scannerStatusScheduler = scheduler.scheduleWithFixedDelay(this::checkScannerStatus, 0, statusCheckInterval,
625                     TimeUnit.SECONDS);
626         }
627     }
628
629     private void checkScannerStatus() {
630         HPServerResult<HPScannerStatus> result = printerClient.getScannerStatus();
631
632         if (handlerDisposed) {
633             return;
634         }
635         if (result.getStatus() == RequestStatus.SUCCESS) {
636             handler.updateState(CGROUP_STATUS, CHANNEL_SCANNER_STATUS,
637                     new StringType(result.getData().getScannerStatus()));
638             handler.updateState(CGROUP_STATUS, CHANNEL_SCANNER_ADFLOADED,
639                     convertToOnOffType(result.getData().getAdfLoaded()));
640         } else {
641             goneOffline();
642         }
643     }
644
645     private void checkStatus() {
646         HPServerResult<HPStatus> result = printerClient.getStatus();
647
648         if (handlerDisposed) {
649             return;
650         }
651         if (result.getStatus() == RequestStatus.SUCCESS) {
652             handler.updateState(CGROUP_STATUS, CHANNEL_STATUS, new StringType(result.getData().getPrinterStatus()));
653             handler.updateState(CGROUP_STATUS, CHANNEL_TRAYEMPTYOROPEN,
654                     convertToOnOffType(result.getData().getTrayEmptyOrOpen()));
655         } else {
656             goneOffline();
657         }
658     }
659
660     private static State convertToOnOffType(Boolean bool) {
661         if (bool) {
662             return OnOffType.ON;
663         } else {
664             return OnOffType.OFF;
665         }
666     }
667
668     private void checkUsage() {
669         HPServerResult<HPUsage> result = printerClient.getUsage();
670
671         if (handlerDisposed) {
672             return;
673         }
674         if (result.getStatus() == RequestStatus.SUCCESS) {
675             // Inks
676             handler.updateState(CGROUP_INK, CHANNEL_BLACK_LEVEL,
677                     new QuantityType<>(result.getData().getInkBlack(), Units.PERCENT));
678             handler.updateState(CGROUP_INK, CHANNEL_COLOR_LEVEL,
679                     new QuantityType<>(result.getData().getInkColor(), Units.PERCENT));
680             handler.updateState(CGROUP_INK, CHANNEL_CYAN_LEVEL,
681                     new QuantityType<>(result.getData().getInkCyan(), Units.PERCENT));
682             handler.updateState(CGROUP_INK, CHANNEL_MAGENTA_LEVEL,
683                     new QuantityType<>(result.getData().getInkMagenta(), Units.PERCENT));
684             handler.updateState(CGROUP_INK, CHANNEL_YELLOW_LEVEL,
685                     new QuantityType<>(result.getData().getInkYellow(), Units.PERCENT));
686
687             handler.updateState(CGROUP_USAGE, CHANNEL_JAM_EVENTS, new DecimalType(result.getData().getJamEvents()));
688             handler.updateState(CGROUP_USAGE, CHANNEL_SUBSCRIPTION,
689                     new DecimalType(result.getData().getTotalSubscriptionImpressions()));
690             handler.updateState(CGROUP_USAGE, CHANNEL_TOTAL_COLORPAGES,
691                     new DecimalType(result.getData().getTotalColorImpressions()));
692             handler.updateState(CGROUP_USAGE, CHANNEL_TOTAL_MONOPAGES,
693                     new DecimalType(result.getData().getTotalMonochromeImpressions()));
694             handler.updateState(CGROUP_USAGE, CHANNEL_TOTAL_PAGES,
695                     new DecimalType(result.getData().getTotalImpressions()));
696             handler.updateState(CGROUP_USAGE, CHANNEL_MISPICK_EVENTS,
697                     new DecimalType(result.getData().getMispickEvents()));
698             handler.updateState(CGROUP_USAGE, CHANNEL_FRONT_PANEL_CANCEL,
699                     new DecimalType(result.getData().getFrontPanelCancelCount()));
700
701             handler.updateState(CGROUP_USAGE, CHANNEL_BLACK_MARKING,
702                     new QuantityType<>(result.getData().getInkBlackMarking(), MetricPrefix.MILLI(Units.LITRE)));
703             handler.updateState(CGROUP_USAGE, CHANNEL_COLOR_MARKING,
704                     new QuantityType<>(result.getData().getInkColorMarking(), MetricPrefix.MILLI(Units.LITRE)));
705             handler.updateState(CGROUP_USAGE, CHANNEL_CYAN_MARKING,
706                     new QuantityType<>(result.getData().getInkCyanMarking(), MetricPrefix.MILLI(Units.LITRE)));
707             handler.updateState(CGROUP_USAGE, CHANNEL_MAGENTA_MARKING,
708                     new QuantityType<>(result.getData().getInkMagentaMarking(), MetricPrefix.MILLI(Units.LITRE)));
709             handler.updateState(CGROUP_USAGE, CHANNEL_YELLOW_MARKING,
710                     new QuantityType<>(result.getData().getInkYellowMarking(), MetricPrefix.MILLI(Units.LITRE)));
711
712             handler.updateState(CGROUP_USAGE, CHANNEL_BLACK_PAGES_REMAINING,
713                     new DecimalType(result.getData().getInkBlackPagesRemaining()));
714
715             handler.updateState(CGROUP_USAGE, CHANNEL_COLOR_PAGES_REMAINING,
716                     new DecimalType(result.getData().getInkColorPagesRemaining()));
717
718             handler.updateState(CGROUP_USAGE, CHANNEL_CYAN_PAGES_REMAINING,
719                     new DecimalType(result.getData().getInkCyanPagesRemaining()));
720
721             handler.updateState(CGROUP_USAGE, CHANNEL_MAGENTA_PAGES_REMAINING,
722                     new DecimalType(result.getData().getInkMagentaPagesRemaining()));
723
724             handler.updateState(CGROUP_USAGE, CHANNEL_YELLOW_PAGES_REMAINING,
725                     new DecimalType(result.getData().getInkYellowPagesRemaining()));
726
727             // Scan
728             handler.updateState(CGROUP_SCAN, CHANNEL_TOTAL_ADF, new DecimalType(result.getData().getScanAdfCount()));
729
730             handler.updateState(CGROUP_SCAN, CHANNEL_TOTAL_FLATBED,
731                     new DecimalType(result.getData().getScanFlatbedCount()));
732
733             handler.updateState(CGROUP_SCAN, CHANNEL_TOTAL_TOEMAIL,
734                     new DecimalType(result.getData().getScanToEmailCount()));
735
736             handler.updateState(CGROUP_SCAN, CHANNEL_TOTAL_TOFOLDER,
737                     new DecimalType(result.getData().getScanToFolderCount()));
738
739             handler.updateState(CGROUP_SCAN, CHANNEL_TOTAL_TOHOST,
740                     new DecimalType(result.getData().getScanToHostCount()));
741
742             // Scanner
743             handler.updateState(CGROUP_SCANNER, CHANNEL_TOTAL_ADF,
744                     new DecimalType(result.getData().getScannerAdfCount()));
745             handler.updateState(CGROUP_SCANNER, CHANNEL_TOTAL_FLATBED,
746                     new DecimalType(result.getData().getScannerFlatbedCount()));
747             handler.updateState(CGROUP_SCANNER, CHANNEL_JAM_EVENTS,
748                     new DecimalType(result.getData().getScannerJamEvents()));
749             handler.updateState(CGROUP_SCANNER, CHANNEL_MISPICK_EVENTS,
750                     new DecimalType(result.getData().getScannerMispickEvents()));
751
752             // Copy
753             handler.updateState(CGROUP_COPY, CHANNEL_TOTAL_ADF, new DecimalType(result.getData().getCopyAdfCount()));
754             handler.updateState(CGROUP_COPY, CHANNEL_TOTAL_FLATBED,
755                     new DecimalType(result.getData().getCopyFlatbedCount()));
756             handler.updateState(CGROUP_COPY, CHANNEL_TOTAL_PAGES,
757                     new DecimalType(result.getData().getCopyTotalImpressions()));
758             handler.updateState(CGROUP_COPY, CHANNEL_TOTAL_COLORPAGES,
759                     new DecimalType(result.getData().getCopyTotalColorImpressions()));
760             handler.updateState(CGROUP_COPY, CHANNEL_TOTAL_MONOPAGES,
761                     new DecimalType(result.getData().getCopyTotalMonochromeImpressions()));
762
763             // App Usage
764             handler.updateState(CGROUP_APP, CHANNEL_TOTAL_WIN, new DecimalType(result.getData().getAppWindowsCount()));
765             handler.updateState(CGROUP_APP, CHANNEL_TOTAL_OSX, new DecimalType(result.getData().getAppOSXCount()));
766             handler.updateState(CGROUP_APP, CHANNEL_TOTAL_IOS, new DecimalType(result.getData().getAppIosCount()));
767             handler.updateState(CGROUP_APP, CHANNEL_TOTAL_ANDROID,
768                     new DecimalType(result.getData().getAppAndroidCount()));
769             handler.updateState(CGROUP_APP, CHANNEL_TOTAL_SAMSUNG,
770                     new DecimalType(result.getData().getAppSamsungCount()));
771             handler.updateState(CGROUP_APP, CHANNEL_TOTAL_CHROME,
772                     new DecimalType(result.getData().getAppChromeCount()));
773
774             // Other
775             handler.updateState(CGROUP_OTHER, CHANNEL_CLOUD_PRINT,
776                     new DecimalType(result.getData().getCloudPrintImpressions()));
777
778         } else {
779             goneOffline();
780         }
781     }
782
783     private void goneOnline() {
784         handler.updateStatus(ThingStatus.ONLINE);
785
786         final ScheduledFuture<?> localOfflineScheduler = offlineScheduler;
787
788         if (localOfflineScheduler != null) {
789             localOfflineScheduler.cancel(true);
790             offlineScheduler = null;
791         }
792         retrieveProperties();
793
794         startBackgroundSchedules();
795     }
796
797     private void checkOnline() {
798         HPServerResult<HPStatus> result = printerClient.getStatus();
799
800         if (handlerDisposed) {
801             return;
802         }
803         if (result.getStatus() == RequestStatus.SUCCESS) {
804             goneOnline();
805         } else if (result.getStatus() == RequestStatus.TIMEOUT) {
806             handler.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, result.getErrorMessage());
807         } else {
808             handler.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, result.getErrorMessage());
809         }
810     }
811 }