2 * Copyright (c) 2010-2020 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.systeminfo.internal.model;
15 import java.math.BigDecimal;
17 import org.apache.commons.lang.ArrayUtils;
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.openhab.core.library.types.DecimalType;
21 import org.openhab.core.library.types.StringType;
22 import org.osgi.service.component.annotations.Component;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
26 import oshi.SystemInfo;
27 import oshi.hardware.CentralProcessor;
28 import oshi.hardware.ComputerSystem;
29 import oshi.hardware.Display;
30 import oshi.hardware.GlobalMemory;
31 import oshi.hardware.HWDiskStore;
32 import oshi.hardware.HardwareAbstractionLayer;
33 import oshi.hardware.NetworkIF;
34 import oshi.hardware.PowerSource;
35 import oshi.hardware.Sensors;
36 import oshi.software.os.OSFileStore;
37 import oshi.software.os.OSProcess;
38 import oshi.software.os.OperatingSystem;
39 import oshi.util.EdidUtil;
42 * This implementation of {@link SysteminfoInterface} is using the open source library OSHI to provide system
43 * information. OSHI is a free JNA-based (native) Operating System and Hardware Information library for Java.
45 * @author Svilen Valkanov - Initial contribution
46 * @author Lyubomir Papazov - Move the initialization logic that could potentially take long time to the
47 * initializeSysteminfo method
48 * @author Christoph Weitkamp - Update to OSHI 3.13.0 - Replaced deprecated method
49 * CentralProcessor#getSystemSerialNumber()
50 * @author Wouter Born - Update to OSHI 4.0.0 and add null annotations
52 * @see <a href="https://github.com/oshi/oshi">OSHI GitHub repository</a>
55 @Component(service = SysteminfoInterface.class)
56 public class OSHISysteminfo implements SysteminfoInterface {
58 private final Logger logger = LoggerFactory.getLogger(OSHISysteminfo.class);
60 private @NonNullByDefault({}) HardwareAbstractionLayer hal;
62 // Dynamic objects (may be queried repeatedly)
63 private @NonNullByDefault({}) GlobalMemory memory;
64 private @NonNullByDefault({}) CentralProcessor cpu;
65 private @NonNullByDefault({}) Sensors sensors;
67 // Static objects, should be recreated on each request
68 private @NonNullByDefault({}) ComputerSystem computerSystem;
69 private @NonNullByDefault({}) OperatingSystem operatingSystem;
70 private @NonNullByDefault({}) NetworkIF[] networks;
71 private @NonNullByDefault({}) Display[] displays;
72 private @NonNullByDefault({}) OSFileStore[] fileStores;
73 private @NonNullByDefault({}) PowerSource[] powerSources;
74 private @NonNullByDefault({}) HWDiskStore[] drives;
76 public static final int PRECISION_AFTER_DECIMAL_SIGN = 1;
79 * Some of the methods used in this constructor execute native code and require execute permissions
82 public OSHISysteminfo() {
83 logger.debug("OSHISysteminfo service is created");
87 public void initializeSysteminfo() {
88 logger.debug("OSHISysteminfo service starts initializing");
90 SystemInfo systemInfo = new SystemInfo();
91 hal = systemInfo.getHardware();
93 // Doesn't need regular update, they may be queried repeatedly
94 memory = hal.getMemory();
95 cpu = hal.getProcessor();
96 sensors = hal.getSensors();
98 computerSystem = hal.getComputerSystem();
99 operatingSystem = systemInfo.getOperatingSystem();
100 networks = hal.getNetworkIFs();
101 displays = hal.getDisplays();
102 fileStores = operatingSystem.getFileSystem().getFileStores();
103 powerSources = hal.getPowerSources();
104 drives = hal.getDiskStores();
107 private Object getDevice(Object @Nullable [] devices, int index) throws DeviceNotFoundException {
108 if ((devices == null) || (devices.length <= index)) {
109 throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
111 return devices[index];
114 private OSProcess getProcess(int pid) throws DeviceNotFoundException {
115 OSProcess process = operatingSystem.getProcess(pid);
116 if (process == null) {
117 throw new DeviceNotFoundException("Error while getting information for process with PID " + pid);
123 public StringType getOsFamily() {
124 String osFamily = operatingSystem.getFamily();
125 return new StringType(osFamily);
129 public StringType getOsManufacturer() {
130 String osManufacturer = operatingSystem.getManufacturer();
131 return new StringType(osManufacturer);
135 public StringType getOsVersion() {
136 String osVersion = operatingSystem.getVersionInfo().toString();
137 return new StringType(osVersion);
141 public StringType getCpuName() {
142 String name = cpu.getProcessorIdentifier().getName();
143 return new StringType(name);
147 public StringType getCpuDescription() {
148 String model = cpu.getProcessorIdentifier().getModel();
149 String family = cpu.getProcessorIdentifier().getFamily();
150 String serialNumber = computerSystem.getSerialNumber();
151 String identifier = cpu.getProcessorIdentifier().getIdentifier();
152 String vendor = cpu.getProcessorIdentifier().getVendor();
153 String architecture = cpu.getProcessorIdentifier().isCpu64bit() ? "64 bit" : "32 bit";
154 String descriptionFormatString = "Model: %s %s,family: %s, vendor: %s, sn: %s, identifier: %s ";
155 String description = String.format(descriptionFormatString, model, architecture, family, vendor, serialNumber,
158 return new StringType(description);
162 public DecimalType getCpuLogicalCores() {
163 int logicalProcessorCount = cpu.getLogicalProcessorCount();
164 return new DecimalType(logicalProcessorCount);
168 public DecimalType getCpuPhysicalCores() {
169 int physicalProcessorCount = cpu.getPhysicalProcessorCount();
170 return new DecimalType(physicalProcessorCount);
174 public DecimalType getMemoryTotal() {
175 long totalMemory = memory.getTotal();
176 totalMemory = getSizeInMB(totalMemory);
177 return new DecimalType(totalMemory);
181 public DecimalType getMemoryAvailable() {
182 long availableMemory = memory.getAvailable();
183 availableMemory = getSizeInMB(availableMemory);
184 return new DecimalType(availableMemory);
188 public DecimalType getMemoryUsed() {
189 long totalMemory = memory.getTotal();
190 long availableMemory = memory.getAvailable();
191 long usedMemory = totalMemory - availableMemory;
192 usedMemory = getSizeInMB(usedMemory);
193 return new DecimalType(usedMemory);
197 public DecimalType getStorageTotal(int index) throws DeviceNotFoundException {
198 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, index);
199 fileStore.updateAtrributes();
200 long totalSpace = fileStore.getTotalSpace();
201 totalSpace = getSizeInMB(totalSpace);
202 return new DecimalType(totalSpace);
206 public DecimalType getStorageAvailable(int index) throws DeviceNotFoundException {
207 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, index);
208 fileStore.updateAtrributes();
209 long freeSpace = fileStore.getUsableSpace();
210 freeSpace = getSizeInMB(freeSpace);
211 return new DecimalType(freeSpace);
215 public DecimalType getStorageUsed(int index) throws DeviceNotFoundException {
216 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, index);
217 fileStore.updateAtrributes();
218 long totalSpace = fileStore.getTotalSpace();
219 long freeSpace = fileStore.getUsableSpace();
220 long usedSpace = totalSpace - freeSpace;
221 usedSpace = getSizeInMB(usedSpace);
222 return new DecimalType(usedSpace);
226 public @Nullable DecimalType getStorageAvailablePercent(int deviceIndex) throws DeviceNotFoundException {
227 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, deviceIndex);
228 fileStore.updateAtrributes();
229 long totalSpace = fileStore.getTotalSpace();
230 long freeSpace = fileStore.getUsableSpace();
231 if (totalSpace > 0) {
232 double freePercentDecimal = (double) freeSpace / (double) totalSpace;
233 BigDecimal freePercent = getPercentsValue(freePercentDecimal);
234 return new DecimalType(freePercent);
241 public @Nullable DecimalType getStorageUsedPercent(int deviceIndex) throws DeviceNotFoundException {
242 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, deviceIndex);
243 fileStore.updateAtrributes();
244 long totalSpace = fileStore.getTotalSpace();
245 long freeSpace = fileStore.getUsableSpace();
246 long usedSpace = totalSpace - freeSpace;
247 if (totalSpace > 0) {
248 double usedPercentDecimal = (double) usedSpace / (double) totalSpace;
249 BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
250 return new DecimalType(usedPercent);
257 public StringType getStorageName(int index) throws DeviceNotFoundException {
258 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, index);
259 String name = fileStore.getName();
260 return new StringType(name);
264 public StringType getStorageType(int deviceIndex) throws DeviceNotFoundException {
265 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, deviceIndex);
266 String type = fileStore.getType();
267 return new StringType(type);
271 public StringType getStorageDescription(int index) throws DeviceNotFoundException {
272 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, index);
273 String description = fileStore.getDescription();
274 return new StringType(description);
278 public StringType getNetworkIp(int index) throws DeviceNotFoundException {
279 NetworkIF netInterface = (NetworkIF) getDevice(networks, index);
280 netInterface.updateAttributes();
281 String[] ipAddresses = netInterface.getIPv4addr();
282 String ipv4 = (String) getDevice(ipAddresses, 0);
283 return new StringType(ipv4);
287 public StringType getNetworkName(int index) throws DeviceNotFoundException {
288 NetworkIF netInterface = (NetworkIF) getDevice(networks, index);
289 String name = netInterface.getName();
290 return new StringType(name);
294 public StringType getNetworkDisplayName(int index) throws DeviceNotFoundException {
295 NetworkIF netInterface = (NetworkIF) getDevice(networks, index);
296 String adapterName = netInterface.getDisplayName();
297 return new StringType(adapterName);
301 public StringType getDisplayInformation(int index) throws DeviceNotFoundException {
302 Display display = (Display) getDevice(displays, index);
304 byte[] edid = display.getEdid();
305 String manufacturer = EdidUtil.getManufacturerID(edid);
306 String product = EdidUtil.getProductID(edid);
307 String serialNumber = EdidUtil.getSerialNo(edid);
308 int width = EdidUtil.getHcm(edid);
309 int height = EdidUtil.getVcm(edid);
311 String edidFormatString = "Product %s, manufacturer %s, SN: %s, Width: %d, Height: %d";
312 String edidInfo = String.format(edidFormatString, product, manufacturer, serialNumber, width, height);
313 return new StringType(edidInfo);
317 public @Nullable DecimalType getSensorsCpuTemperature() {
318 BigDecimal cpuTemp = new BigDecimal(sensors.getCpuTemperature());
319 cpuTemp = cpuTemp.setScale(PRECISION_AFTER_DECIMAL_SIGN, BigDecimal.ROUND_HALF_UP);
320 return cpuTemp.signum() == 1 ? new DecimalType(cpuTemp) : null;
324 public @Nullable DecimalType getSensorsCpuVoltage() {
325 BigDecimal cpuVoltage = new BigDecimal(sensors.getCpuVoltage());
326 cpuVoltage = cpuVoltage.setScale(PRECISION_AFTER_DECIMAL_SIGN, BigDecimal.ROUND_HALF_UP);
327 return cpuVoltage.signum() == 1 ? new DecimalType(cpuVoltage) : null;
331 public @Nullable DecimalType getSensorsFanSpeed(int index) throws DeviceNotFoundException {
332 int[] fanSpeeds = sensors.getFanSpeeds();
333 int speed = (int) getDevice(ArrayUtils.toObject(fanSpeeds), index);
334 return speed > 0 ? new DecimalType(speed) : null;
338 public @Nullable DecimalType getBatteryRemainingTime(int index) throws DeviceNotFoundException {
339 PowerSource powerSource = (PowerSource) getDevice(powerSources, index);
340 powerSource.updateAttributes();
341 double remainingTimeInSeconds = powerSource.getTimeRemainingEstimated();
342 // The getTimeRemaining() method returns (-1.0) if is calculating or (-2.0) if the time is unlimited.
343 BigDecimal remainingTime = getTimeInMinutes(remainingTimeInSeconds);
344 return remainingTime.signum() == 1 ? new DecimalType(remainingTime) : null;
348 public DecimalType getBatteryRemainingCapacity(int index) throws DeviceNotFoundException {
349 PowerSource powerSource = (PowerSource) getDevice(powerSources, index);
350 powerSource.updateAttributes();
351 double remainingCapacity = powerSource.getRemainingCapacityPercent();
352 BigDecimal remainingCapacityPercents = getPercentsValue(remainingCapacity);
353 return new DecimalType(remainingCapacityPercents);
357 public StringType getBatteryName(int index) throws DeviceNotFoundException {
358 PowerSource powerSource = (PowerSource) getDevice(powerSources, index);
359 String name = powerSource.getName();
360 return new StringType(name);
364 public @Nullable DecimalType getMemoryAvailablePercent() {
365 long availableMemory = memory.getAvailable();
366 long totalMemory = memory.getTotal();
367 if (totalMemory > 0) {
368 double freePercentDecimal = (double) availableMemory / (double) totalMemory;
369 BigDecimal freePercent = getPercentsValue(freePercentDecimal);
370 return new DecimalType(freePercent);
377 public @Nullable DecimalType getMemoryUsedPercent() {
378 long availableMemory = memory.getAvailable();
379 long totalMemory = memory.getTotal();
380 long usedMemory = totalMemory - availableMemory;
381 if (totalMemory > 0) {
382 double usedPercentDecimal = (double) usedMemory / (double) totalMemory;
383 BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
384 return new DecimalType(usedPercent);
391 public StringType getDriveName(int deviceIndex) throws DeviceNotFoundException {
392 HWDiskStore drive = (HWDiskStore) getDevice(drives, deviceIndex);
393 String name = drive.getName();
394 return new StringType(name);
398 public StringType getDriveModel(int deviceIndex) throws DeviceNotFoundException {
399 HWDiskStore drive = (HWDiskStore) getDevice(drives, deviceIndex);
400 String model = drive.getModel();
401 return new StringType(model);
405 public StringType getDriveSerialNumber(int deviceIndex) throws DeviceNotFoundException {
406 HWDiskStore drive = (HWDiskStore) getDevice(drives, deviceIndex);
407 String serialNumber = drive.getSerial();
408 return new StringType(serialNumber);
412 public @Nullable DecimalType getSwapTotal() {
413 long swapTotal = memory.getVirtualMemory().getSwapTotal();
414 swapTotal = getSizeInMB(swapTotal);
415 return new DecimalType(swapTotal);
419 public @Nullable DecimalType getSwapAvailable() {
420 long swapTotal = memory.getVirtualMemory().getSwapTotal();
421 long swapUsed = memory.getVirtualMemory().getSwapUsed();
422 long swapAvailable = swapTotal - swapUsed;
423 swapAvailable = getSizeInMB(swapAvailable);
424 return new DecimalType(swapAvailable);
428 public @Nullable DecimalType getSwapUsed() {
429 long swapUsed = memory.getVirtualMemory().getSwapUsed();
430 swapUsed = getSizeInMB(swapUsed);
431 return new DecimalType(swapUsed);
435 public @Nullable DecimalType getSwapAvailablePercent() {
436 long swapTotal = memory.getVirtualMemory().getSwapTotal();
437 long swapUsed = memory.getVirtualMemory().getSwapUsed();
438 long swapAvailable = swapTotal - swapUsed;
440 double swapAvailablePercentDecimal = (double) swapAvailable / (double) swapTotal;
441 BigDecimal swapAvailablePercent = getPercentsValue(swapAvailablePercentDecimal);
442 return new DecimalType(swapAvailablePercent);
449 public @Nullable DecimalType getSwapUsedPercent() {
450 long swapTotal = memory.getVirtualMemory().getSwapTotal();
451 long swapUsed = memory.getVirtualMemory().getSwapUsed();
453 double swapUsedPercentDecimal = (double) swapUsed / (double) swapTotal;
454 BigDecimal swapUsedPercent = getPercentsValue(swapUsedPercentDecimal);
455 return new DecimalType(swapUsedPercent);
461 private long getSizeInMB(long sizeInBytes) {
462 return Math.round(sizeInBytes / (1024D * 1024));
465 private BigDecimal getPercentsValue(double decimalFraction) {
466 BigDecimal result = new BigDecimal(decimalFraction * 100);
467 result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, BigDecimal.ROUND_HALF_UP);
471 private BigDecimal getTimeInMinutes(double timeInSeconds) {
472 BigDecimal timeInMinutes = new BigDecimal(timeInSeconds / 60);
473 timeInMinutes = timeInMinutes.setScale(PRECISION_AFTER_DECIMAL_SIGN, BigDecimal.ROUND_UP);
474 return timeInMinutes;
480 * This information is available only on Mac and Linux OS.
483 public @Nullable DecimalType getCpuLoad1() {
484 BigDecimal avarageCpuLoad = getAvarageCpuLoad(1);
485 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
491 * This information is available only on Mac and Linux OS.
494 public @Nullable DecimalType getCpuLoad5() {
495 BigDecimal avarageCpuLoad = getAvarageCpuLoad(5);
496 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
502 * This information is available only on Mac and Linux OS.
505 public @Nullable DecimalType getCpuLoad15() {
506 BigDecimal avarageCpuLoad = getAvarageCpuLoad(15);
507 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
510 private BigDecimal getAvarageCpuLoad(int timeInMunutes) {
511 // This parameter is specified in OSHI Javadoc
513 switch (timeInMunutes) {
526 double processorLoads[] = cpu.getSystemLoadAverage(index + 1);
527 BigDecimal result = new BigDecimal(processorLoads[index]);
528 result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, BigDecimal.ROUND_HALF_UP);
533 public DecimalType getCpuUptime() {
534 long seconds = operatingSystem.getSystemUptime();
535 return new DecimalType(getTimeInMinutes(seconds));
539 public DecimalType getCpuThreads() {
540 int threadCount = operatingSystem.getThreadCount();
541 return new DecimalType(threadCount);
545 public StringType getNetworkMac(int networkIndex) throws DeviceNotFoundException {
546 NetworkIF network = (NetworkIF) getDevice(networks, networkIndex);
547 String mac = network.getMacaddr();
548 return new StringType(mac);
552 public DecimalType getNetworkPacketsReceived(int networkIndex) throws DeviceNotFoundException {
553 NetworkIF network = (NetworkIF) getDevice(networks, networkIndex);
554 network.updateAttributes();
555 long packRecv = network.getPacketsRecv();
556 return new DecimalType(packRecv);
560 public DecimalType getNetworkPacketsSent(int networkIndex) throws DeviceNotFoundException {
561 NetworkIF network = (NetworkIF) getDevice(networks, networkIndex);
562 network.updateAttributes();
563 long packSent = network.getPacketsSent();
564 return new DecimalType(packSent);
568 public DecimalType getNetworkDataSent(int networkIndex) throws DeviceNotFoundException {
569 NetworkIF network = (NetworkIF) getDevice(networks, networkIndex);
570 network.updateAttributes();
571 long bytesSent = network.getBytesSent();
572 return new DecimalType(getSizeInMB(bytesSent));
576 public DecimalType getNetworkDataReceived(int networkIndex) throws DeviceNotFoundException {
577 NetworkIF network = (NetworkIF) getDevice(networks, networkIndex);
578 network.updateAttributes();
579 long bytesRecv = network.getBytesRecv();
580 return new DecimalType(getSizeInMB(bytesRecv));
584 public @Nullable StringType getProcessName(int pid) throws DeviceNotFoundException {
586 OSProcess process = getProcess(pid);
587 String name = process.getName();
588 return new StringType(name);
595 public @Nullable DecimalType getProcessCpuUsage(int pid) throws DeviceNotFoundException {
597 OSProcess process = getProcess(pid);
598 double cpuUsageRaw = (process.getKernelTime() + process.getUserTime()) / process.getUpTime();
599 BigDecimal cpuUsage = getPercentsValue(cpuUsageRaw);
600 return new DecimalType(cpuUsage);
607 public @Nullable DecimalType getProcessMemoryUsage(int pid) throws DeviceNotFoundException {
609 OSProcess process = getProcess(pid);
610 long memortInBytes = process.getResidentSetSize();
611 long memoryInMB = getSizeInMB(memortInBytes);
612 return new DecimalType(memoryInMB);
619 public @Nullable StringType getProcessPath(int pid) throws DeviceNotFoundException {
621 OSProcess process = getProcess(pid);
622 String path = process.getPath();
623 return new StringType(path);
630 public @Nullable DecimalType getProcessThreads(int pid) throws DeviceNotFoundException {
632 OSProcess process = getProcess(pid);
633 int threadCount = process.getThreadCount();
634 return new DecimalType(threadCount);