]> git.basschouten.com Git - openhab-addons.git/blob
4c4b3903c7ad8887aed3399d997ad29b52422189
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 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.systeminfo.internal.model;
14
15 import java.math.BigDecimal;
16 import java.math.RoundingMode;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.core.library.types.DecimalType;
24 import org.openhab.core.library.types.PercentType;
25 import org.openhab.core.library.types.StringType;
26 import org.osgi.service.component.annotations.Component;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import oshi.SystemInfo;
31 import oshi.hardware.CentralProcessor;
32 import oshi.hardware.ComputerSystem;
33 import oshi.hardware.Display;
34 import oshi.hardware.GlobalMemory;
35 import oshi.hardware.HWDiskStore;
36 import oshi.hardware.HardwareAbstractionLayer;
37 import oshi.hardware.NetworkIF;
38 import oshi.hardware.PowerSource;
39 import oshi.hardware.Sensors;
40 import oshi.software.os.OSFileStore;
41 import oshi.software.os.OSProcess;
42 import oshi.software.os.OperatingSystem;
43 import oshi.util.EdidUtil;
44
45 /**
46  * This implementation of {@link SysteminfoInterface} is using the open source library OSHI to provide system
47  * information. OSHI is a free JNA-based (native) Operating System and Hardware Information library for Java.
48  *
49  * @author Svilen Valkanov - Initial contribution
50  * @author Lyubomir Papazov - Move the initialization logic that could potentially take long time to the
51  *         initializeSysteminfo method
52  * @author Christoph Weitkamp - Update to OSHI 3.13.0 - Replaced deprecated method
53  *         CentralProcessor#getSystemSerialNumber()
54  * @author Wouter Born - Update to OSHI 4.0.0 and add null annotations
55  * @author Mark Herwege - Add dynamic creation of extra channels
56  *
57  * @see <a href="https://github.com/oshi/oshi">OSHI GitHub repository</a>
58  */
59 @NonNullByDefault
60 @Component(service = SysteminfoInterface.class)
61 public class OSHISysteminfo implements SysteminfoInterface {
62
63     private final Logger logger = LoggerFactory.getLogger(OSHISysteminfo.class);
64
65     private @NonNullByDefault({}) HardwareAbstractionLayer hal;
66
67     // Dynamic objects (may be queried repeatedly)
68     private @NonNullByDefault({}) GlobalMemory memory;
69     private @NonNullByDefault({}) CentralProcessor cpu;
70     private @NonNullByDefault({}) Sensors sensors;
71
72     // Static objects, should be recreated on each request
73     private @NonNullByDefault({}) ComputerSystem computerSystem;
74     private @NonNullByDefault({}) OperatingSystem operatingSystem;
75     private @NonNullByDefault({}) List<NetworkIF> networks;
76     private @NonNullByDefault({}) List<Display> displays;
77     private @NonNullByDefault({}) List<OSFileStore> fileStores;
78     private @NonNullByDefault({}) List<PowerSource> powerSources;
79     private @NonNullByDefault({}) List<HWDiskStore> drives;
80
81     // Array containing cpu tick info to calculate CPU load, according to oshi doc:
82     // 8 long values representing time spent in User, Nice, System, Idle, IOwait, IRQ, SoftIRQ, and Steal states
83     private long[] ticks = new long[8];
84     // Map containing previous process state to calculate load by process
85     private Map<Integer, OSProcess> processTicks = new HashMap<>();
86
87     public static final int PRECISION_AFTER_DECIMAL_SIGN = 1;
88
89     /**
90      * Some of the methods used in this constructor execute native code and require execute permissions
91      *
92      */
93     public OSHISysteminfo() {
94         logger.debug("OSHISysteminfo service is created");
95     }
96
97     @Override
98     public void initializeSysteminfo() {
99         logger.debug("OSHISysteminfo service starts initializing");
100
101         SystemInfo systemInfo = new SystemInfo();
102         hal = systemInfo.getHardware();
103
104         // Doesn't need regular update, they may be queried repeatedly
105         memory = hal.getMemory();
106         cpu = hal.getProcessor();
107         sensors = hal.getSensors();
108
109         computerSystem = hal.getComputerSystem();
110         operatingSystem = systemInfo.getOperatingSystem();
111         networks = hal.getNetworkIFs();
112         displays = hal.getDisplays();
113         fileStores = operatingSystem.getFileSystem().getFileStores();
114         powerSources = hal.getPowerSources();
115         drives = hal.getDiskStores();
116     }
117
118     private <T> T getDevice(List<@Nullable T> devices, int index) throws DeviceNotFoundException {
119         if (devices.size() <= index) {
120             throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
121         }
122         return (T) devices.get(index);
123     }
124
125     private <T> T getDevice(T @Nullable [] devices, int index) throws DeviceNotFoundException {
126         if (devices == null || devices.length <= index) {
127             throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
128         }
129         return devices[index];
130     }
131
132     private OSProcess getProcess(int pid) throws DeviceNotFoundException {
133         OSProcess process = operatingSystem.getProcess(pid);
134         if (process == null) {
135             throw new DeviceNotFoundException("Error while getting information for process with PID " + pid);
136         }
137         return process;
138     }
139
140     @Override
141     public StringType getOsFamily() {
142         String osFamily = operatingSystem.getFamily();
143         return new StringType(osFamily);
144     }
145
146     @Override
147     public StringType getOsManufacturer() {
148         String osManufacturer = operatingSystem.getManufacturer();
149         return new StringType(osManufacturer);
150     }
151
152     @Override
153     public StringType getOsVersion() {
154         String osVersion = operatingSystem.getVersionInfo().toString();
155         return new StringType(osVersion);
156     }
157
158     @Override
159     public StringType getCpuName() {
160         String name = cpu.getProcessorIdentifier().getName();
161         return new StringType(name);
162     }
163
164     @Override
165     public StringType getCpuDescription() {
166         String model = cpu.getProcessorIdentifier().getModel();
167         String family = cpu.getProcessorIdentifier().getFamily();
168         String serialNumber = computerSystem.getSerialNumber();
169         String identifier = cpu.getProcessorIdentifier().getIdentifier();
170         String vendor = cpu.getProcessorIdentifier().getVendor();
171         String architecture = cpu.getProcessorIdentifier().isCpu64bit() ? "64 bit" : "32 bit";
172         String descriptionFormatString = "Model: %s %s,family: %s, vendor: %s, sn: %s, identifier: %s ";
173         String description = String.format(descriptionFormatString, model, architecture, family, vendor, serialNumber,
174                 identifier);
175
176         return new StringType(description);
177     }
178
179     @Override
180     public DecimalType getCpuLogicalCores() {
181         int logicalProcessorCount = cpu.getLogicalProcessorCount();
182         return new DecimalType(logicalProcessorCount);
183     }
184
185     @Override
186     public DecimalType getCpuPhysicalCores() {
187         int physicalProcessorCount = cpu.getPhysicalProcessorCount();
188         return new DecimalType(physicalProcessorCount);
189     }
190
191     @Override
192     public DecimalType getMemoryTotal() {
193         long totalMemory = memory.getTotal();
194         totalMemory = getSizeInMB(totalMemory);
195         return new DecimalType(totalMemory);
196     }
197
198     @Override
199     public DecimalType getMemoryAvailable() {
200         long availableMemory = memory.getAvailable();
201         availableMemory = getSizeInMB(availableMemory);
202         return new DecimalType(availableMemory);
203     }
204
205     @Override
206     public DecimalType getMemoryUsed() {
207         long totalMemory = memory.getTotal();
208         long availableMemory = memory.getAvailable();
209         long usedMemory = totalMemory - availableMemory;
210         usedMemory = getSizeInMB(usedMemory);
211         return new DecimalType(usedMemory);
212     }
213
214     @Override
215     public DecimalType getStorageTotal(int index) throws DeviceNotFoundException {
216         OSFileStore fileStore = getDevice(fileStores, index);
217         fileStore.updateAttributes();
218         long totalSpace = fileStore.getTotalSpace();
219         totalSpace = getSizeInMB(totalSpace);
220         return new DecimalType(totalSpace);
221     }
222
223     @Override
224     public DecimalType getStorageAvailable(int index) throws DeviceNotFoundException {
225         OSFileStore fileStore = getDevice(fileStores, index);
226         fileStore.updateAttributes();
227         long freeSpace = fileStore.getUsableSpace();
228         freeSpace = getSizeInMB(freeSpace);
229         return new DecimalType(freeSpace);
230     }
231
232     @Override
233     public DecimalType getStorageUsed(int index) throws DeviceNotFoundException {
234         OSFileStore fileStore = getDevice(fileStores, index);
235         fileStore.updateAttributes();
236         long totalSpace = fileStore.getTotalSpace();
237         long freeSpace = fileStore.getUsableSpace();
238         long usedSpace = totalSpace - freeSpace;
239         usedSpace = getSizeInMB(usedSpace);
240         return new DecimalType(usedSpace);
241     }
242
243     @Override
244     public @Nullable DecimalType getStorageAvailablePercent(int deviceIndex) throws DeviceNotFoundException {
245         OSFileStore fileStore = getDevice(fileStores, deviceIndex);
246         fileStore.updateAttributes();
247         long totalSpace = fileStore.getTotalSpace();
248         long freeSpace = fileStore.getUsableSpace();
249         if (totalSpace > 0) {
250             double freePercentDecimal = (double) freeSpace / (double) totalSpace;
251             BigDecimal freePercent = getPercentsValue(freePercentDecimal);
252             return new DecimalType(freePercent);
253         } else {
254             return null;
255         }
256     }
257
258     @Override
259     public @Nullable DecimalType getStorageUsedPercent(int deviceIndex) throws DeviceNotFoundException {
260         OSFileStore fileStore = getDevice(fileStores, deviceIndex);
261         fileStore.updateAttributes();
262         long totalSpace = fileStore.getTotalSpace();
263         long freeSpace = fileStore.getUsableSpace();
264         long usedSpace = totalSpace - freeSpace;
265         if (totalSpace > 0) {
266             double usedPercentDecimal = (double) usedSpace / (double) totalSpace;
267             BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
268             return new DecimalType(usedPercent);
269         } else {
270             return null;
271         }
272     }
273
274     @Override
275     public StringType getStorageName(int index) throws DeviceNotFoundException {
276         OSFileStore fileStore = getDevice(fileStores, index);
277         String name = fileStore.getName();
278         return new StringType(name);
279     }
280
281     @Override
282     public StringType getStorageType(int deviceIndex) throws DeviceNotFoundException {
283         OSFileStore fileStore = getDevice(fileStores, deviceIndex);
284         String type = fileStore.getType();
285         return new StringType(type);
286     }
287
288     @Override
289     public StringType getStorageDescription(int index) throws DeviceNotFoundException {
290         OSFileStore fileStore = getDevice(fileStores, index);
291         String description = fileStore.getDescription();
292         return new StringType(description);
293     }
294
295     @Override
296     public StringType getNetworkIp(int index) throws DeviceNotFoundException {
297         NetworkIF netInterface = getDevice(networks, index);
298         netInterface.updateAttributes();
299         String[] ipAddresses = netInterface.getIPv4addr();
300         String ipv4 = getDevice(ipAddresses, 0);
301         return new StringType(ipv4);
302     }
303
304     @Override
305     public StringType getNetworkName(int index) throws DeviceNotFoundException {
306         NetworkIF netInterface = getDevice(networks, index);
307         String name = netInterface.getName();
308         return new StringType(name);
309     }
310
311     @Override
312     public StringType getNetworkDisplayName(int index) throws DeviceNotFoundException {
313         NetworkIF netInterface = getDevice(networks, index);
314         String adapterName = netInterface.getDisplayName();
315         return new StringType(adapterName);
316     }
317
318     @Override
319     public StringType getDisplayInformation(int index) throws DeviceNotFoundException {
320         Display display = getDevice(displays, index);
321
322         byte[] edid = display.getEdid();
323         String manufacturer = EdidUtil.getManufacturerID(edid);
324         String product = EdidUtil.getProductID(edid);
325         String serialNumber = EdidUtil.getSerialNo(edid);
326         int width = EdidUtil.getHcm(edid);
327         int height = EdidUtil.getVcm(edid);
328
329         String edidFormatString = "Product %s, manufacturer %s, SN: %s, Width: %d, Height: %d";
330         String edidInfo = String.format(edidFormatString, product, manufacturer, serialNumber, width, height);
331         return new StringType(edidInfo);
332     }
333
334     @Override
335     public @Nullable DecimalType getSensorsCpuTemperature() {
336         BigDecimal cpuTemp = new BigDecimal(sensors.getCpuTemperature());
337         cpuTemp = cpuTemp.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
338         return cpuTemp.signum() == 1 ? new DecimalType(cpuTemp) : null;
339     }
340
341     @Override
342     public @Nullable DecimalType getSensorsCpuVoltage() {
343         BigDecimal cpuVoltage = new BigDecimal(sensors.getCpuVoltage());
344         cpuVoltage = cpuVoltage.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
345         return cpuVoltage.signum() == 1 ? new DecimalType(cpuVoltage) : null;
346     }
347
348     @Override
349     public @Nullable DecimalType getSensorsFanSpeed(int index) throws DeviceNotFoundException {
350         int[] fanSpeeds = sensors.getFanSpeeds();
351         int speed = 0; // 0 means unable to measure speed
352         if (index < fanSpeeds.length) {
353             speed = fanSpeeds[index];
354         } else {
355             throw new DeviceNotFoundException();
356         }
357         return speed > 0 ? new DecimalType(speed) : null;
358     }
359
360     @Override
361     public @Nullable DecimalType getBatteryRemainingTime(int index) throws DeviceNotFoundException {
362         PowerSource powerSource = getDevice(powerSources, index);
363         powerSource.updateAttributes();
364         double remainingTimeInSeconds = powerSource.getTimeRemainingEstimated();
365         // The getTimeRemaining() method returns (-1.0) if is calculating or (-2.0) if the time is unlimited.
366         BigDecimal remainingTime = getTimeInMinutes(remainingTimeInSeconds);
367         return remainingTime.signum() == 1 ? new DecimalType(remainingTime) : null;
368     }
369
370     @Override
371     public DecimalType getBatteryRemainingCapacity(int index) throws DeviceNotFoundException {
372         PowerSource powerSource = getDevice(powerSources, index);
373         powerSource.updateAttributes();
374         double remainingCapacity = powerSource.getRemainingCapacityPercent();
375         BigDecimal remainingCapacityPercents = getPercentsValue(remainingCapacity);
376         return new DecimalType(remainingCapacityPercents);
377     }
378
379     @Override
380     public StringType getBatteryName(int index) throws DeviceNotFoundException {
381         PowerSource powerSource = getDevice(powerSources, index);
382         String name = powerSource.getName();
383         return new StringType(name);
384     }
385
386     @Override
387     public @Nullable DecimalType getMemoryAvailablePercent() {
388         long availableMemory = memory.getAvailable();
389         long totalMemory = memory.getTotal();
390         if (totalMemory > 0) {
391             double freePercentDecimal = (double) availableMemory / (double) totalMemory;
392             BigDecimal freePercent = getPercentsValue(freePercentDecimal);
393             return new DecimalType(freePercent);
394         } else {
395             return null;
396         }
397     }
398
399     @Override
400     public @Nullable DecimalType getMemoryUsedPercent() {
401         long availableMemory = memory.getAvailable();
402         long totalMemory = memory.getTotal();
403         long usedMemory = totalMemory - availableMemory;
404         if (totalMemory > 0) {
405             double usedPercentDecimal = (double) usedMemory / (double) totalMemory;
406             BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
407             return new DecimalType(usedPercent);
408         } else {
409             return null;
410         }
411     }
412
413     @Override
414     public StringType getDriveName(int deviceIndex) throws DeviceNotFoundException {
415         HWDiskStore drive = getDevice(drives, deviceIndex);
416         String name = drive.getName();
417         return new StringType(name);
418     }
419
420     @Override
421     public StringType getDriveModel(int deviceIndex) throws DeviceNotFoundException {
422         HWDiskStore drive = getDevice(drives, deviceIndex);
423         String model = drive.getModel();
424         return new StringType(model);
425     }
426
427     @Override
428     public StringType getDriveSerialNumber(int deviceIndex) throws DeviceNotFoundException {
429         HWDiskStore drive = getDevice(drives, deviceIndex);
430         String serialNumber = drive.getSerial();
431         return new StringType(serialNumber);
432     }
433
434     @Override
435     public @Nullable DecimalType getSwapTotal() {
436         long swapTotal = memory.getVirtualMemory().getSwapTotal();
437         swapTotal = getSizeInMB(swapTotal);
438         return new DecimalType(swapTotal);
439     }
440
441     @Override
442     public @Nullable DecimalType getSwapAvailable() {
443         long swapTotal = memory.getVirtualMemory().getSwapTotal();
444         long swapUsed = memory.getVirtualMemory().getSwapUsed();
445         long swapAvailable = swapTotal - swapUsed;
446         swapAvailable = getSizeInMB(swapAvailable);
447         return new DecimalType(swapAvailable);
448     }
449
450     @Override
451     public @Nullable DecimalType getSwapUsed() {
452         long swapUsed = memory.getVirtualMemory().getSwapUsed();
453         swapUsed = getSizeInMB(swapUsed);
454         return new DecimalType(swapUsed);
455     }
456
457     @Override
458     public @Nullable DecimalType getSwapAvailablePercent() {
459         long swapTotal = memory.getVirtualMemory().getSwapTotal();
460         long swapUsed = memory.getVirtualMemory().getSwapUsed();
461         long swapAvailable = swapTotal - swapUsed;
462         if (swapTotal > 0) {
463             double swapAvailablePercentDecimal = (double) swapAvailable / (double) swapTotal;
464             BigDecimal swapAvailablePercent = getPercentsValue(swapAvailablePercentDecimal);
465             return new DecimalType(swapAvailablePercent);
466         } else {
467             return null;
468         }
469     }
470
471     @Override
472     public @Nullable DecimalType getSwapUsedPercent() {
473         long swapTotal = memory.getVirtualMemory().getSwapTotal();
474         long swapUsed = memory.getVirtualMemory().getSwapUsed();
475         if (swapTotal > 0) {
476             double swapUsedPercentDecimal = (double) swapUsed / (double) swapTotal;
477             BigDecimal swapUsedPercent = getPercentsValue(swapUsedPercentDecimal);
478             return new DecimalType(swapUsedPercent);
479         } else {
480             return null;
481         }
482     }
483
484     private long getSizeInMB(long sizeInBytes) {
485         return Math.round(sizeInBytes / (1024D * 1024));
486     }
487
488     private BigDecimal getPercentsValue(double decimalFraction) {
489         BigDecimal result = new BigDecimal(decimalFraction * 100);
490         result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
491         return result;
492     }
493
494     private BigDecimal getTimeInMinutes(double timeInSeconds) {
495         BigDecimal timeInMinutes = new BigDecimal(timeInSeconds / 60);
496         timeInMinutes = timeInMinutes.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.UP);
497         return timeInMinutes;
498     }
499
500     @Override
501     public @Nullable PercentType getSystemCpuLoad() {
502         PercentType load = (ticks[0] > 0) ? new PercentType(getPercentsValue(cpu.getSystemCpuLoadBetweenTicks(ticks)))
503                 : null;
504         ticks = cpu.getSystemCpuLoadTicks();
505         return load;
506     }
507
508     /**
509      * {@inheritDoc}
510      *
511      * This information is available only on Mac and Linux OS.
512      */
513     @Override
514     public @Nullable DecimalType getCpuLoad1() {
515         BigDecimal avarageCpuLoad = getAvarageCpuLoad(1);
516         return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
517     }
518
519     /**
520      * {@inheritDoc}
521      *
522      * This information is available only on Mac and Linux OS.
523      */
524     @Override
525     public @Nullable DecimalType getCpuLoad5() {
526         BigDecimal avarageCpuLoad = getAvarageCpuLoad(5);
527         return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
528     }
529
530     /**
531      * {@inheritDoc}
532      *
533      * This information is available only on Mac and Linux OS.
534      */
535     @Override
536     public @Nullable DecimalType getCpuLoad15() {
537         BigDecimal avarageCpuLoad = getAvarageCpuLoad(15);
538         return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
539     }
540
541     private BigDecimal getAvarageCpuLoad(int timeInMinutes) {
542         // This parameter is specified in OSHI Javadoc
543         int index;
544         switch (timeInMinutes) {
545             case 1:
546                 index = 0;
547                 break;
548             case 5:
549                 index = 1;
550                 break;
551             case 15:
552                 index = 2;
553                 break;
554             default:
555                 index = 2;
556         }
557         double processorLoads[] = cpu.getSystemLoadAverage(index + 1);
558         BigDecimal result = new BigDecimal(processorLoads[index]);
559         result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
560         return result;
561     }
562
563     @Override
564     public DecimalType getCpuUptime() {
565         long seconds = operatingSystem.getSystemUptime();
566         return new DecimalType(getTimeInMinutes(seconds));
567     }
568
569     @Override
570     public DecimalType getCpuThreads() {
571         int threadCount = operatingSystem.getThreadCount();
572         return new DecimalType(threadCount);
573     }
574
575     @Override
576     public StringType getNetworkMac(int networkIndex) throws DeviceNotFoundException {
577         NetworkIF network = getDevice(networks, networkIndex);
578         String mac = network.getMacaddr();
579         return new StringType(mac);
580     }
581
582     @Override
583     public DecimalType getNetworkPacketsReceived(int networkIndex) throws DeviceNotFoundException {
584         NetworkIF network = getDevice(networks, networkIndex);
585         network.updateAttributes();
586         long packRecv = network.getPacketsRecv();
587         return new DecimalType(packRecv);
588     }
589
590     @Override
591     public DecimalType getNetworkPacketsSent(int networkIndex) throws DeviceNotFoundException {
592         NetworkIF network = getDevice(networks, networkIndex);
593         network.updateAttributes();
594         long packSent = network.getPacketsSent();
595         return new DecimalType(packSent);
596     }
597
598     @Override
599     public DecimalType getNetworkDataSent(int networkIndex) throws DeviceNotFoundException {
600         NetworkIF network = getDevice(networks, networkIndex);
601         network.updateAttributes();
602         long bytesSent = network.getBytesSent();
603         return new DecimalType(getSizeInMB(bytesSent));
604     }
605
606     @Override
607     public DecimalType getNetworkDataReceived(int networkIndex) throws DeviceNotFoundException {
608         NetworkIF network = getDevice(networks, networkIndex);
609         network.updateAttributes();
610         long bytesRecv = network.getBytesRecv();
611         return new DecimalType(getSizeInMB(bytesRecv));
612     }
613
614     @Override
615     public int getCurrentProcessID() {
616         return operatingSystem.getProcessId();
617     }
618
619     @Override
620     public @Nullable StringType getProcessName(int pid) throws DeviceNotFoundException {
621         if (pid > 0) {
622             OSProcess process = getProcess(pid);
623             String name = process.getName();
624             return new StringType(name);
625         } else {
626             return null;
627         }
628     }
629
630     @Override
631     public @Nullable DecimalType getProcessCpuUsage(int pid) throws DeviceNotFoundException {
632         if (pid > 0) {
633             OSProcess process = getProcess(pid);
634             DecimalType load = (processTicks.containsKey(pid))
635                     ? new DecimalType(getPercentsValue(process.getProcessCpuLoadBetweenTicks(processTicks.get(pid))))
636                     : null;
637             processTicks.put(pid, process);
638             return load;
639         } else {
640             return null;
641         }
642     }
643
644     @Override
645     public @Nullable DecimalType getProcessMemoryUsage(int pid) throws DeviceNotFoundException {
646         if (pid > 0) {
647             OSProcess process = getProcess(pid);
648             long memortInBytes = process.getResidentSetSize();
649             long memoryInMB = getSizeInMB(memortInBytes);
650             return new DecimalType(memoryInMB);
651         } else {
652             return null;
653         }
654     }
655
656     @Override
657     public @Nullable StringType getProcessPath(int pid) throws DeviceNotFoundException {
658         if (pid > 0) {
659             OSProcess process = getProcess(pid);
660             String path = process.getPath();
661             return new StringType(path);
662         } else {
663             return null;
664         }
665     }
666
667     @Override
668     public @Nullable DecimalType getProcessThreads(int pid) throws DeviceNotFoundException {
669         if (pid > 0) {
670             OSProcess process = getProcess(pid);
671             int threadCount = process.getThreadCount();
672             return new DecimalType(threadCount);
673         } else {
674             return null;
675         }
676     }
677
678     @Override
679     public int getNetworkIFCount() {
680         return networks.size();
681     }
682
683     @Override
684     public int getDisplayCount() {
685         return displays.size();
686     }
687
688     @Override
689     public int getFileOSStoreCount() {
690         return fileStores.size();
691     }
692
693     @Override
694     public int getPowerSourceCount() {
695         return powerSources.size();
696     }
697
698     @Override
699     public int getDriveCount() {
700         return drives.size();
701     }
702
703     @Override
704     public int getFanCount() {
705         return sensors.getFanSpeeds().length;
706     }
707 }