2 * Copyright (c) 2010-2021 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;
16 import java.math.RoundingMode;
18 import org.apache.commons.lang3.ArrayUtils;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.core.library.types.DecimalType;
22 import org.openhab.core.library.types.StringType;
23 import org.osgi.service.component.annotations.Component;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
27 import oshi.SystemInfo;
28 import oshi.hardware.CentralProcessor;
29 import oshi.hardware.ComputerSystem;
30 import oshi.hardware.Display;
31 import oshi.hardware.GlobalMemory;
32 import oshi.hardware.HWDiskStore;
33 import oshi.hardware.HardwareAbstractionLayer;
34 import oshi.hardware.NetworkIF;
35 import oshi.hardware.PowerSource;
36 import oshi.hardware.Sensors;
37 import oshi.software.os.OSFileStore;
38 import oshi.software.os.OSProcess;
39 import oshi.software.os.OperatingSystem;
40 import oshi.util.EdidUtil;
43 * This implementation of {@link SysteminfoInterface} is using the open source library OSHI to provide system
44 * information. OSHI is a free JNA-based (native) Operating System and Hardware Information library for Java.
46 * @author Svilen Valkanov - Initial contribution
47 * @author Lyubomir Papazov - Move the initialization logic that could potentially take long time to the
48 * initializeSysteminfo method
49 * @author Christoph Weitkamp - Update to OSHI 3.13.0 - Replaced deprecated method
50 * CentralProcessor#getSystemSerialNumber()
51 * @author Wouter Born - Update to OSHI 4.0.0 and add null annotations
53 * @see <a href="https://github.com/oshi/oshi">OSHI GitHub repository</a>
56 @Component(service = SysteminfoInterface.class)
57 public class OSHISysteminfo implements SysteminfoInterface {
59 private final Logger logger = LoggerFactory.getLogger(OSHISysteminfo.class);
61 private @NonNullByDefault({}) HardwareAbstractionLayer hal;
63 // Dynamic objects (may be queried repeatedly)
64 private @NonNullByDefault({}) GlobalMemory memory;
65 private @NonNullByDefault({}) CentralProcessor cpu;
66 private @NonNullByDefault({}) Sensors sensors;
68 // Static objects, should be recreated on each request
69 private @NonNullByDefault({}) ComputerSystem computerSystem;
70 private @NonNullByDefault({}) OperatingSystem operatingSystem;
71 private @NonNullByDefault({}) NetworkIF[] networks;
72 private @NonNullByDefault({}) Display[] displays;
73 private @NonNullByDefault({}) OSFileStore[] fileStores;
74 private @NonNullByDefault({}) PowerSource[] powerSources;
75 private @NonNullByDefault({}) HWDiskStore[] drives;
77 public static final int PRECISION_AFTER_DECIMAL_SIGN = 1;
80 * Some of the methods used in this constructor execute native code and require execute permissions
83 public OSHISysteminfo() {
84 logger.debug("OSHISysteminfo service is created");
88 public void initializeSysteminfo() {
89 logger.debug("OSHISysteminfo service starts initializing");
91 SystemInfo systemInfo = new SystemInfo();
92 hal = systemInfo.getHardware();
94 // Doesn't need regular update, they may be queried repeatedly
95 memory = hal.getMemory();
96 cpu = hal.getProcessor();
97 sensors = hal.getSensors();
99 computerSystem = hal.getComputerSystem();
100 operatingSystem = systemInfo.getOperatingSystem();
101 networks = hal.getNetworkIFs();
102 displays = hal.getDisplays();
103 fileStores = operatingSystem.getFileSystem().getFileStores();
104 powerSources = hal.getPowerSources();
105 drives = hal.getDiskStores();
108 private Object getDevice(Object @Nullable [] devices, int index) throws DeviceNotFoundException {
109 if ((devices == null) || (devices.length <= index)) {
110 throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
112 return devices[index];
115 private OSProcess getProcess(int pid) throws DeviceNotFoundException {
116 OSProcess process = operatingSystem.getProcess(pid);
117 if (process == null) {
118 throw new DeviceNotFoundException("Error while getting information for process with PID " + pid);
124 public StringType getOsFamily() {
125 String osFamily = operatingSystem.getFamily();
126 return new StringType(osFamily);
130 public StringType getOsManufacturer() {
131 String osManufacturer = operatingSystem.getManufacturer();
132 return new StringType(osManufacturer);
136 public StringType getOsVersion() {
137 String osVersion = operatingSystem.getVersionInfo().toString();
138 return new StringType(osVersion);
142 public StringType getCpuName() {
143 String name = cpu.getProcessorIdentifier().getName();
144 return new StringType(name);
148 public StringType getCpuDescription() {
149 String model = cpu.getProcessorIdentifier().getModel();
150 String family = cpu.getProcessorIdentifier().getFamily();
151 String serialNumber = computerSystem.getSerialNumber();
152 String identifier = cpu.getProcessorIdentifier().getIdentifier();
153 String vendor = cpu.getProcessorIdentifier().getVendor();
154 String architecture = cpu.getProcessorIdentifier().isCpu64bit() ? "64 bit" : "32 bit";
155 String descriptionFormatString = "Model: %s %s,family: %s, vendor: %s, sn: %s, identifier: %s ";
156 String description = String.format(descriptionFormatString, model, architecture, family, vendor, serialNumber,
159 return new StringType(description);
163 public DecimalType getCpuLogicalCores() {
164 int logicalProcessorCount = cpu.getLogicalProcessorCount();
165 return new DecimalType(logicalProcessorCount);
169 public DecimalType getCpuPhysicalCores() {
170 int physicalProcessorCount = cpu.getPhysicalProcessorCount();
171 return new DecimalType(physicalProcessorCount);
175 public DecimalType getMemoryTotal() {
176 long totalMemory = memory.getTotal();
177 totalMemory = getSizeInMB(totalMemory);
178 return new DecimalType(totalMemory);
182 public DecimalType getMemoryAvailable() {
183 long availableMemory = memory.getAvailable();
184 availableMemory = getSizeInMB(availableMemory);
185 return new DecimalType(availableMemory);
189 public DecimalType getMemoryUsed() {
190 long totalMemory = memory.getTotal();
191 long availableMemory = memory.getAvailable();
192 long usedMemory = totalMemory - availableMemory;
193 usedMemory = getSizeInMB(usedMemory);
194 return new DecimalType(usedMemory);
198 public DecimalType getStorageTotal(int index) throws DeviceNotFoundException {
199 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, index);
200 fileStore.updateAtrributes();
201 long totalSpace = fileStore.getTotalSpace();
202 totalSpace = getSizeInMB(totalSpace);
203 return new DecimalType(totalSpace);
207 public DecimalType getStorageAvailable(int index) throws DeviceNotFoundException {
208 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, index);
209 fileStore.updateAtrributes();
210 long freeSpace = fileStore.getUsableSpace();
211 freeSpace = getSizeInMB(freeSpace);
212 return new DecimalType(freeSpace);
216 public DecimalType getStorageUsed(int index) throws DeviceNotFoundException {
217 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, index);
218 fileStore.updateAtrributes();
219 long totalSpace = fileStore.getTotalSpace();
220 long freeSpace = fileStore.getUsableSpace();
221 long usedSpace = totalSpace - freeSpace;
222 usedSpace = getSizeInMB(usedSpace);
223 return new DecimalType(usedSpace);
227 public @Nullable DecimalType getStorageAvailablePercent(int deviceIndex) throws DeviceNotFoundException {
228 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, deviceIndex);
229 fileStore.updateAtrributes();
230 long totalSpace = fileStore.getTotalSpace();
231 long freeSpace = fileStore.getUsableSpace();
232 if (totalSpace > 0) {
233 double freePercentDecimal = (double) freeSpace / (double) totalSpace;
234 BigDecimal freePercent = getPercentsValue(freePercentDecimal);
235 return new DecimalType(freePercent);
242 public @Nullable DecimalType getStorageUsedPercent(int deviceIndex) throws DeviceNotFoundException {
243 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, deviceIndex);
244 fileStore.updateAtrributes();
245 long totalSpace = fileStore.getTotalSpace();
246 long freeSpace = fileStore.getUsableSpace();
247 long usedSpace = totalSpace - freeSpace;
248 if (totalSpace > 0) {
249 double usedPercentDecimal = (double) usedSpace / (double) totalSpace;
250 BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
251 return new DecimalType(usedPercent);
258 public StringType getStorageName(int index) throws DeviceNotFoundException {
259 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, index);
260 String name = fileStore.getName();
261 return new StringType(name);
265 public StringType getStorageType(int deviceIndex) throws DeviceNotFoundException {
266 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, deviceIndex);
267 String type = fileStore.getType();
268 return new StringType(type);
272 public StringType getStorageDescription(int index) throws DeviceNotFoundException {
273 OSFileStore fileStore = (OSFileStore) getDevice(fileStores, index);
274 String description = fileStore.getDescription();
275 return new StringType(description);
279 public StringType getNetworkIp(int index) throws DeviceNotFoundException {
280 NetworkIF netInterface = (NetworkIF) getDevice(networks, index);
281 netInterface.updateAttributes();
282 String[] ipAddresses = netInterface.getIPv4addr();
283 String ipv4 = (String) getDevice(ipAddresses, 0);
284 return new StringType(ipv4);
288 public StringType getNetworkName(int index) throws DeviceNotFoundException {
289 NetworkIF netInterface = (NetworkIF) getDevice(networks, index);
290 String name = netInterface.getName();
291 return new StringType(name);
295 public StringType getNetworkDisplayName(int index) throws DeviceNotFoundException {
296 NetworkIF netInterface = (NetworkIF) getDevice(networks, index);
297 String adapterName = netInterface.getDisplayName();
298 return new StringType(adapterName);
302 public StringType getDisplayInformation(int index) throws DeviceNotFoundException {
303 Display display = (Display) getDevice(displays, index);
305 byte[] edid = display.getEdid();
306 String manufacturer = EdidUtil.getManufacturerID(edid);
307 String product = EdidUtil.getProductID(edid);
308 String serialNumber = EdidUtil.getSerialNo(edid);
309 int width = EdidUtil.getHcm(edid);
310 int height = EdidUtil.getVcm(edid);
312 String edidFormatString = "Product %s, manufacturer %s, SN: %s, Width: %d, Height: %d";
313 String edidInfo = String.format(edidFormatString, product, manufacturer, serialNumber, width, height);
314 return new StringType(edidInfo);
318 public @Nullable DecimalType getSensorsCpuTemperature() {
319 BigDecimal cpuTemp = new BigDecimal(sensors.getCpuTemperature());
320 cpuTemp = cpuTemp.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
321 return cpuTemp.signum() == 1 ? new DecimalType(cpuTemp) : null;
325 public @Nullable DecimalType getSensorsCpuVoltage() {
326 BigDecimal cpuVoltage = new BigDecimal(sensors.getCpuVoltage());
327 cpuVoltage = cpuVoltage.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
328 return cpuVoltage.signum() == 1 ? new DecimalType(cpuVoltage) : null;
332 public @Nullable DecimalType getSensorsFanSpeed(int index) throws DeviceNotFoundException {
333 int[] fanSpeeds = sensors.getFanSpeeds();
334 int speed = (int) getDevice(ArrayUtils.toObject(fanSpeeds), index);
335 return speed > 0 ? new DecimalType(speed) : null;
339 public @Nullable DecimalType getBatteryRemainingTime(int index) throws DeviceNotFoundException {
340 PowerSource powerSource = (PowerSource) getDevice(powerSources, index);
341 powerSource.updateAttributes();
342 double remainingTimeInSeconds = powerSource.getTimeRemainingEstimated();
343 // The getTimeRemaining() method returns (-1.0) if is calculating or (-2.0) if the time is unlimited.
344 BigDecimal remainingTime = getTimeInMinutes(remainingTimeInSeconds);
345 return remainingTime.signum() == 1 ? new DecimalType(remainingTime) : null;
349 public DecimalType getBatteryRemainingCapacity(int index) throws DeviceNotFoundException {
350 PowerSource powerSource = (PowerSource) getDevice(powerSources, index);
351 powerSource.updateAttributes();
352 double remainingCapacity = powerSource.getRemainingCapacityPercent();
353 BigDecimal remainingCapacityPercents = getPercentsValue(remainingCapacity);
354 return new DecimalType(remainingCapacityPercents);
358 public StringType getBatteryName(int index) throws DeviceNotFoundException {
359 PowerSource powerSource = (PowerSource) getDevice(powerSources, index);
360 String name = powerSource.getName();
361 return new StringType(name);
365 public @Nullable DecimalType getMemoryAvailablePercent() {
366 long availableMemory = memory.getAvailable();
367 long totalMemory = memory.getTotal();
368 if (totalMemory > 0) {
369 double freePercentDecimal = (double) availableMemory / (double) totalMemory;
370 BigDecimal freePercent = getPercentsValue(freePercentDecimal);
371 return new DecimalType(freePercent);
378 public @Nullable DecimalType getMemoryUsedPercent() {
379 long availableMemory = memory.getAvailable();
380 long totalMemory = memory.getTotal();
381 long usedMemory = totalMemory - availableMemory;
382 if (totalMemory > 0) {
383 double usedPercentDecimal = (double) usedMemory / (double) totalMemory;
384 BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
385 return new DecimalType(usedPercent);
392 public StringType getDriveName(int deviceIndex) throws DeviceNotFoundException {
393 HWDiskStore drive = (HWDiskStore) getDevice(drives, deviceIndex);
394 String name = drive.getName();
395 return new StringType(name);
399 public StringType getDriveModel(int deviceIndex) throws DeviceNotFoundException {
400 HWDiskStore drive = (HWDiskStore) getDevice(drives, deviceIndex);
401 String model = drive.getModel();
402 return new StringType(model);
406 public StringType getDriveSerialNumber(int deviceIndex) throws DeviceNotFoundException {
407 HWDiskStore drive = (HWDiskStore) getDevice(drives, deviceIndex);
408 String serialNumber = drive.getSerial();
409 return new StringType(serialNumber);
413 public @Nullable DecimalType getSwapTotal() {
414 long swapTotal = memory.getVirtualMemory().getSwapTotal();
415 swapTotal = getSizeInMB(swapTotal);
416 return new DecimalType(swapTotal);
420 public @Nullable DecimalType getSwapAvailable() {
421 long swapTotal = memory.getVirtualMemory().getSwapTotal();
422 long swapUsed = memory.getVirtualMemory().getSwapUsed();
423 long swapAvailable = swapTotal - swapUsed;
424 swapAvailable = getSizeInMB(swapAvailable);
425 return new DecimalType(swapAvailable);
429 public @Nullable DecimalType getSwapUsed() {
430 long swapUsed = memory.getVirtualMemory().getSwapUsed();
431 swapUsed = getSizeInMB(swapUsed);
432 return new DecimalType(swapUsed);
436 public @Nullable DecimalType getSwapAvailablePercent() {
437 long swapTotal = memory.getVirtualMemory().getSwapTotal();
438 long swapUsed = memory.getVirtualMemory().getSwapUsed();
439 long swapAvailable = swapTotal - swapUsed;
441 double swapAvailablePercentDecimal = (double) swapAvailable / (double) swapTotal;
442 BigDecimal swapAvailablePercent = getPercentsValue(swapAvailablePercentDecimal);
443 return new DecimalType(swapAvailablePercent);
450 public @Nullable DecimalType getSwapUsedPercent() {
451 long swapTotal = memory.getVirtualMemory().getSwapTotal();
452 long swapUsed = memory.getVirtualMemory().getSwapUsed();
454 double swapUsedPercentDecimal = (double) swapUsed / (double) swapTotal;
455 BigDecimal swapUsedPercent = getPercentsValue(swapUsedPercentDecimal);
456 return new DecimalType(swapUsedPercent);
462 private long getSizeInMB(long sizeInBytes) {
463 return Math.round(sizeInBytes / (1024D * 1024));
466 private BigDecimal getPercentsValue(double decimalFraction) {
467 BigDecimal result = new BigDecimal(decimalFraction * 100);
468 result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
472 private BigDecimal getTimeInMinutes(double timeInSeconds) {
473 BigDecimal timeInMinutes = new BigDecimal(timeInSeconds / 60);
474 timeInMinutes = timeInMinutes.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.UP);
475 return timeInMinutes;
481 * This information is available only on Mac and Linux OS.
484 public @Nullable DecimalType getCpuLoad1() {
485 BigDecimal avarageCpuLoad = getAvarageCpuLoad(1);
486 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
492 * This information is available only on Mac and Linux OS.
495 public @Nullable DecimalType getCpuLoad5() {
496 BigDecimal avarageCpuLoad = getAvarageCpuLoad(5);
497 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
503 * This information is available only on Mac and Linux OS.
506 public @Nullable DecimalType getCpuLoad15() {
507 BigDecimal avarageCpuLoad = getAvarageCpuLoad(15);
508 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
511 private BigDecimal getAvarageCpuLoad(int timeInMunutes) {
512 // This parameter is specified in OSHI Javadoc
514 switch (timeInMunutes) {
527 double processorLoads[] = cpu.getSystemLoadAverage(index + 1);
528 BigDecimal result = new BigDecimal(processorLoads[index]);
529 result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
534 public DecimalType getCpuUptime() {
535 long seconds = operatingSystem.getSystemUptime();
536 return new DecimalType(getTimeInMinutes(seconds));
540 public DecimalType getCpuThreads() {
541 int threadCount = operatingSystem.getThreadCount();
542 return new DecimalType(threadCount);
546 public StringType getNetworkMac(int networkIndex) throws DeviceNotFoundException {
547 NetworkIF network = (NetworkIF) getDevice(networks, networkIndex);
548 String mac = network.getMacaddr();
549 return new StringType(mac);
553 public DecimalType getNetworkPacketsReceived(int networkIndex) throws DeviceNotFoundException {
554 NetworkIF network = (NetworkIF) getDevice(networks, networkIndex);
555 network.updateAttributes();
556 long packRecv = network.getPacketsRecv();
557 return new DecimalType(packRecv);
561 public DecimalType getNetworkPacketsSent(int networkIndex) throws DeviceNotFoundException {
562 NetworkIF network = (NetworkIF) getDevice(networks, networkIndex);
563 network.updateAttributes();
564 long packSent = network.getPacketsSent();
565 return new DecimalType(packSent);
569 public DecimalType getNetworkDataSent(int networkIndex) throws DeviceNotFoundException {
570 NetworkIF network = (NetworkIF) getDevice(networks, networkIndex);
571 network.updateAttributes();
572 long bytesSent = network.getBytesSent();
573 return new DecimalType(getSizeInMB(bytesSent));
577 public DecimalType getNetworkDataReceived(int networkIndex) throws DeviceNotFoundException {
578 NetworkIF network = (NetworkIF) getDevice(networks, networkIndex);
579 network.updateAttributes();
580 long bytesRecv = network.getBytesRecv();
581 return new DecimalType(getSizeInMB(bytesRecv));
585 public @Nullable StringType getProcessName(int pid) throws DeviceNotFoundException {
587 OSProcess process = getProcess(pid);
588 String name = process.getName();
589 return new StringType(name);
596 public @Nullable DecimalType getProcessCpuUsage(int pid) throws DeviceNotFoundException {
598 OSProcess process = getProcess(pid);
599 double cpuUsageRaw = (process.getKernelTime() + process.getUserTime()) / process.getUpTime();
600 BigDecimal cpuUsage = getPercentsValue(cpuUsageRaw);
601 return new DecimalType(cpuUsage);
608 public @Nullable DecimalType getProcessMemoryUsage(int pid) throws DeviceNotFoundException {
610 OSProcess process = getProcess(pid);
611 long memortInBytes = process.getResidentSetSize();
612 long memoryInMB = getSizeInMB(memortInBytes);
613 return new DecimalType(memoryInMB);
620 public @Nullable StringType getProcessPath(int pid) throws DeviceNotFoundException {
622 OSProcess process = getProcess(pid);
623 String path = process.getPath();
624 return new StringType(path);
631 public @Nullable DecimalType getProcessThreads(int pid) throws DeviceNotFoundException {
633 OSProcess process = getProcess(pid);
634 int threadCount = process.getThreadCount();
635 return new DecimalType(threadCount);