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;
17 import java.util.List;
19 import org.apache.commons.lang3.ArrayUtils;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.core.library.types.DecimalType;
23 import org.openhab.core.library.types.StringType;
24 import org.osgi.service.component.annotations.Component;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
28 import oshi.SystemInfo;
29 import oshi.hardware.CentralProcessor;
30 import oshi.hardware.ComputerSystem;
31 import oshi.hardware.Display;
32 import oshi.hardware.GlobalMemory;
33 import oshi.hardware.HWDiskStore;
34 import oshi.hardware.HardwareAbstractionLayer;
35 import oshi.hardware.NetworkIF;
36 import oshi.hardware.PowerSource;
37 import oshi.hardware.Sensors;
38 import oshi.software.os.OSFileStore;
39 import oshi.software.os.OSProcess;
40 import oshi.software.os.OperatingSystem;
41 import oshi.util.EdidUtil;
44 * This implementation of {@link SysteminfoInterface} is using the open source library OSHI to provide system
45 * information. OSHI is a free JNA-based (native) Operating System and Hardware Information library for Java.
47 * @author Svilen Valkanov - Initial contribution
48 * @author Lyubomir Papazov - Move the initialization logic that could potentially take long time to the
49 * initializeSysteminfo method
50 * @author Christoph Weitkamp - Update to OSHI 3.13.0 - Replaced deprecated method
51 * CentralProcessor#getSystemSerialNumber()
52 * @author Wouter Born - Update to OSHI 4.0.0 and add null annotations
54 * @see <a href="https://github.com/oshi/oshi">OSHI GitHub repository</a>
57 @Component(service = SysteminfoInterface.class)
58 public class OSHISysteminfo implements SysteminfoInterface {
60 private final Logger logger = LoggerFactory.getLogger(OSHISysteminfo.class);
62 private @NonNullByDefault({}) HardwareAbstractionLayer hal;
64 // Dynamic objects (may be queried repeatedly)
65 private @NonNullByDefault({}) GlobalMemory memory;
66 private @NonNullByDefault({}) CentralProcessor cpu;
67 private @NonNullByDefault({}) Sensors sensors;
69 // Static objects, should be recreated on each request
70 private @NonNullByDefault({}) ComputerSystem computerSystem;
71 private @NonNullByDefault({}) OperatingSystem operatingSystem;
72 private @NonNullByDefault({}) List<NetworkIF> networks;
73 private @NonNullByDefault({}) List<Display> displays;
74 private @NonNullByDefault({}) List<OSFileStore> fileStores;
75 private @NonNullByDefault({}) List<PowerSource> powerSources;
76 private @NonNullByDefault({}) List<HWDiskStore> drives;
78 public static final int PRECISION_AFTER_DECIMAL_SIGN = 1;
81 * Some of the methods used in this constructor execute native code and require execute permissions
84 public OSHISysteminfo() {
85 logger.debug("OSHISysteminfo service is created");
89 public void initializeSysteminfo() {
90 logger.debug("OSHISysteminfo service starts initializing");
92 SystemInfo systemInfo = new SystemInfo();
93 hal = systemInfo.getHardware();
95 // Doesn't need regular update, they may be queried repeatedly
96 memory = hal.getMemory();
97 cpu = hal.getProcessor();
98 sensors = hal.getSensors();
100 computerSystem = hal.getComputerSystem();
101 operatingSystem = systemInfo.getOperatingSystem();
102 networks = hal.getNetworkIFs();
103 displays = hal.getDisplays();
104 fileStores = operatingSystem.getFileSystem().getFileStores();
105 powerSources = hal.getPowerSources();
106 drives = hal.getDiskStores();
109 private <T> T getDevice(List<@Nullable T> devices, int index) throws DeviceNotFoundException {
110 if (devices.size() <= index) {
111 throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
113 return (T) devices.get(index);
116 private <T> T getDevice(T @Nullable [] devices, int index) throws DeviceNotFoundException {
117 if (devices == null || devices.length <= index) {
118 throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
120 return devices[index];
123 private OSProcess getProcess(int pid) throws DeviceNotFoundException {
124 OSProcess process = operatingSystem.getProcess(pid);
125 if (process == null) {
126 throw new DeviceNotFoundException("Error while getting information for process with PID " + pid);
132 public StringType getOsFamily() {
133 String osFamily = operatingSystem.getFamily();
134 return new StringType(osFamily);
138 public StringType getOsManufacturer() {
139 String osManufacturer = operatingSystem.getManufacturer();
140 return new StringType(osManufacturer);
144 public StringType getOsVersion() {
145 String osVersion = operatingSystem.getVersionInfo().toString();
146 return new StringType(osVersion);
150 public StringType getCpuName() {
151 String name = cpu.getProcessorIdentifier().getName();
152 return new StringType(name);
156 public StringType getCpuDescription() {
157 String model = cpu.getProcessorIdentifier().getModel();
158 String family = cpu.getProcessorIdentifier().getFamily();
159 String serialNumber = computerSystem.getSerialNumber();
160 String identifier = cpu.getProcessorIdentifier().getIdentifier();
161 String vendor = cpu.getProcessorIdentifier().getVendor();
162 String architecture = cpu.getProcessorIdentifier().isCpu64bit() ? "64 bit" : "32 bit";
163 String descriptionFormatString = "Model: %s %s,family: %s, vendor: %s, sn: %s, identifier: %s ";
164 String description = String.format(descriptionFormatString, model, architecture, family, vendor, serialNumber,
167 return new StringType(description);
171 public DecimalType getCpuLogicalCores() {
172 int logicalProcessorCount = cpu.getLogicalProcessorCount();
173 return new DecimalType(logicalProcessorCount);
177 public DecimalType getCpuPhysicalCores() {
178 int physicalProcessorCount = cpu.getPhysicalProcessorCount();
179 return new DecimalType(physicalProcessorCount);
183 public DecimalType getMemoryTotal() {
184 long totalMemory = memory.getTotal();
185 totalMemory = getSizeInMB(totalMemory);
186 return new DecimalType(totalMemory);
190 public DecimalType getMemoryAvailable() {
191 long availableMemory = memory.getAvailable();
192 availableMemory = getSizeInMB(availableMemory);
193 return new DecimalType(availableMemory);
197 public DecimalType getMemoryUsed() {
198 long totalMemory = memory.getTotal();
199 long availableMemory = memory.getAvailable();
200 long usedMemory = totalMemory - availableMemory;
201 usedMemory = getSizeInMB(usedMemory);
202 return new DecimalType(usedMemory);
206 public DecimalType getStorageTotal(int index) throws DeviceNotFoundException {
207 OSFileStore fileStore = getDevice(fileStores, index);
208 fileStore.updateAttributes();
209 long totalSpace = fileStore.getTotalSpace();
210 totalSpace = getSizeInMB(totalSpace);
211 return new DecimalType(totalSpace);
215 public DecimalType getStorageAvailable(int index) throws DeviceNotFoundException {
216 OSFileStore fileStore = getDevice(fileStores, index);
217 fileStore.updateAttributes();
218 long freeSpace = fileStore.getUsableSpace();
219 freeSpace = getSizeInMB(freeSpace);
220 return new DecimalType(freeSpace);
224 public DecimalType getStorageUsed(int index) throws DeviceNotFoundException {
225 OSFileStore fileStore = getDevice(fileStores, index);
226 fileStore.updateAttributes();
227 long totalSpace = fileStore.getTotalSpace();
228 long freeSpace = fileStore.getUsableSpace();
229 long usedSpace = totalSpace - freeSpace;
230 usedSpace = getSizeInMB(usedSpace);
231 return new DecimalType(usedSpace);
235 public @Nullable DecimalType getStorageAvailablePercent(int deviceIndex) throws DeviceNotFoundException {
236 OSFileStore fileStore = getDevice(fileStores, deviceIndex);
237 fileStore.updateAttributes();
238 long totalSpace = fileStore.getTotalSpace();
239 long freeSpace = fileStore.getUsableSpace();
240 if (totalSpace > 0) {
241 double freePercentDecimal = (double) freeSpace / (double) totalSpace;
242 BigDecimal freePercent = getPercentsValue(freePercentDecimal);
243 return new DecimalType(freePercent);
250 public @Nullable DecimalType getStorageUsedPercent(int deviceIndex) throws DeviceNotFoundException {
251 OSFileStore fileStore = getDevice(fileStores, deviceIndex);
252 fileStore.updateAttributes();
253 long totalSpace = fileStore.getTotalSpace();
254 long freeSpace = fileStore.getUsableSpace();
255 long usedSpace = totalSpace - freeSpace;
256 if (totalSpace > 0) {
257 double usedPercentDecimal = (double) usedSpace / (double) totalSpace;
258 BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
259 return new DecimalType(usedPercent);
266 public StringType getStorageName(int index) throws DeviceNotFoundException {
267 OSFileStore fileStore = getDevice(fileStores, index);
268 String name = fileStore.getName();
269 return new StringType(name);
273 public StringType getStorageType(int deviceIndex) throws DeviceNotFoundException {
274 OSFileStore fileStore = getDevice(fileStores, deviceIndex);
275 String type = fileStore.getType();
276 return new StringType(type);
280 public StringType getStorageDescription(int index) throws DeviceNotFoundException {
281 OSFileStore fileStore = getDevice(fileStores, index);
282 String description = fileStore.getDescription();
283 return new StringType(description);
287 public StringType getNetworkIp(int index) throws DeviceNotFoundException {
288 NetworkIF netInterface = getDevice(networks, index);
289 netInterface.updateAttributes();
290 String[] ipAddresses = netInterface.getIPv4addr();
291 String ipv4 = getDevice(ipAddresses, 0);
292 return new StringType(ipv4);
296 public StringType getNetworkName(int index) throws DeviceNotFoundException {
297 NetworkIF netInterface = getDevice(networks, index);
298 String name = netInterface.getName();
299 return new StringType(name);
303 public StringType getNetworkDisplayName(int index) throws DeviceNotFoundException {
304 NetworkIF netInterface = getDevice(networks, index);
305 String adapterName = netInterface.getDisplayName();
306 return new StringType(adapterName);
310 public StringType getDisplayInformation(int index) throws DeviceNotFoundException {
311 Display display = getDevice(displays, index);
313 byte[] edid = display.getEdid();
314 String manufacturer = EdidUtil.getManufacturerID(edid);
315 String product = EdidUtil.getProductID(edid);
316 String serialNumber = EdidUtil.getSerialNo(edid);
317 int width = EdidUtil.getHcm(edid);
318 int height = EdidUtil.getVcm(edid);
320 String edidFormatString = "Product %s, manufacturer %s, SN: %s, Width: %d, Height: %d";
321 String edidInfo = String.format(edidFormatString, product, manufacturer, serialNumber, width, height);
322 return new StringType(edidInfo);
326 public @Nullable DecimalType getSensorsCpuTemperature() {
327 BigDecimal cpuTemp = new BigDecimal(sensors.getCpuTemperature());
328 cpuTemp = cpuTemp.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
329 return cpuTemp.signum() == 1 ? new DecimalType(cpuTemp) : null;
333 public @Nullable DecimalType getSensorsCpuVoltage() {
334 BigDecimal cpuVoltage = new BigDecimal(sensors.getCpuVoltage());
335 cpuVoltage = cpuVoltage.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
336 return cpuVoltage.signum() == 1 ? new DecimalType(cpuVoltage) : null;
340 public @Nullable DecimalType getSensorsFanSpeed(int index) throws DeviceNotFoundException {
341 int[] fanSpeeds = sensors.getFanSpeeds();
342 int speed = getDevice(ArrayUtils.toObject(fanSpeeds), index);
343 return speed > 0 ? new DecimalType(speed) : null;
347 public @Nullable DecimalType getBatteryRemainingTime(int index) throws DeviceNotFoundException {
348 PowerSource powerSource = getDevice(powerSources, index);
349 powerSource.updateAttributes();
350 double remainingTimeInSeconds = powerSource.getTimeRemainingEstimated();
351 // The getTimeRemaining() method returns (-1.0) if is calculating or (-2.0) if the time is unlimited.
352 BigDecimal remainingTime = getTimeInMinutes(remainingTimeInSeconds);
353 return remainingTime.signum() == 1 ? new DecimalType(remainingTime) : null;
357 public DecimalType getBatteryRemainingCapacity(int index) throws DeviceNotFoundException {
358 PowerSource powerSource = getDevice(powerSources, index);
359 powerSource.updateAttributes();
360 double remainingCapacity = powerSource.getRemainingCapacityPercent();
361 BigDecimal remainingCapacityPercents = getPercentsValue(remainingCapacity);
362 return new DecimalType(remainingCapacityPercents);
366 public StringType getBatteryName(int index) throws DeviceNotFoundException {
367 PowerSource powerSource = getDevice(powerSources, index);
368 String name = powerSource.getName();
369 return new StringType(name);
373 public @Nullable DecimalType getMemoryAvailablePercent() {
374 long availableMemory = memory.getAvailable();
375 long totalMemory = memory.getTotal();
376 if (totalMemory > 0) {
377 double freePercentDecimal = (double) availableMemory / (double) totalMemory;
378 BigDecimal freePercent = getPercentsValue(freePercentDecimal);
379 return new DecimalType(freePercent);
386 public @Nullable DecimalType getMemoryUsedPercent() {
387 long availableMemory = memory.getAvailable();
388 long totalMemory = memory.getTotal();
389 long usedMemory = totalMemory - availableMemory;
390 if (totalMemory > 0) {
391 double usedPercentDecimal = (double) usedMemory / (double) totalMemory;
392 BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
393 return new DecimalType(usedPercent);
400 public StringType getDriveName(int deviceIndex) throws DeviceNotFoundException {
401 HWDiskStore drive = getDevice(drives, deviceIndex);
402 String name = drive.getName();
403 return new StringType(name);
407 public StringType getDriveModel(int deviceIndex) throws DeviceNotFoundException {
408 HWDiskStore drive = getDevice(drives, deviceIndex);
409 String model = drive.getModel();
410 return new StringType(model);
414 public StringType getDriveSerialNumber(int deviceIndex) throws DeviceNotFoundException {
415 HWDiskStore drive = getDevice(drives, deviceIndex);
416 String serialNumber = drive.getSerial();
417 return new StringType(serialNumber);
421 public @Nullable DecimalType getSwapTotal() {
422 long swapTotal = memory.getVirtualMemory().getSwapTotal();
423 swapTotal = getSizeInMB(swapTotal);
424 return new DecimalType(swapTotal);
428 public @Nullable DecimalType getSwapAvailable() {
429 long swapTotal = memory.getVirtualMemory().getSwapTotal();
430 long swapUsed = memory.getVirtualMemory().getSwapUsed();
431 long swapAvailable = swapTotal - swapUsed;
432 swapAvailable = getSizeInMB(swapAvailable);
433 return new DecimalType(swapAvailable);
437 public @Nullable DecimalType getSwapUsed() {
438 long swapUsed = memory.getVirtualMemory().getSwapUsed();
439 swapUsed = getSizeInMB(swapUsed);
440 return new DecimalType(swapUsed);
444 public @Nullable DecimalType getSwapAvailablePercent() {
445 long swapTotal = memory.getVirtualMemory().getSwapTotal();
446 long swapUsed = memory.getVirtualMemory().getSwapUsed();
447 long swapAvailable = swapTotal - swapUsed;
449 double swapAvailablePercentDecimal = (double) swapAvailable / (double) swapTotal;
450 BigDecimal swapAvailablePercent = getPercentsValue(swapAvailablePercentDecimal);
451 return new DecimalType(swapAvailablePercent);
458 public @Nullable DecimalType getSwapUsedPercent() {
459 long swapTotal = memory.getVirtualMemory().getSwapTotal();
460 long swapUsed = memory.getVirtualMemory().getSwapUsed();
462 double swapUsedPercentDecimal = (double) swapUsed / (double) swapTotal;
463 BigDecimal swapUsedPercent = getPercentsValue(swapUsedPercentDecimal);
464 return new DecimalType(swapUsedPercent);
470 private long getSizeInMB(long sizeInBytes) {
471 return Math.round(sizeInBytes / (1024D * 1024));
474 private BigDecimal getPercentsValue(double decimalFraction) {
475 BigDecimal result = new BigDecimal(decimalFraction * 100);
476 result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
480 private BigDecimal getTimeInMinutes(double timeInSeconds) {
481 BigDecimal timeInMinutes = new BigDecimal(timeInSeconds / 60);
482 timeInMinutes = timeInMinutes.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.UP);
483 return timeInMinutes;
489 * This information is available only on Mac and Linux OS.
492 public @Nullable DecimalType getCpuLoad1() {
493 BigDecimal avarageCpuLoad = getAvarageCpuLoad(1);
494 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
500 * This information is available only on Mac and Linux OS.
503 public @Nullable DecimalType getCpuLoad5() {
504 BigDecimal avarageCpuLoad = getAvarageCpuLoad(5);
505 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
511 * This information is available only on Mac and Linux OS.
514 public @Nullable DecimalType getCpuLoad15() {
515 BigDecimal avarageCpuLoad = getAvarageCpuLoad(15);
516 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
519 private BigDecimal getAvarageCpuLoad(int timeInMunutes) {
520 // This parameter is specified in OSHI Javadoc
522 switch (timeInMunutes) {
535 double processorLoads[] = cpu.getSystemLoadAverage(index + 1);
536 BigDecimal result = new BigDecimal(processorLoads[index]);
537 result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
542 public DecimalType getCpuUptime() {
543 long seconds = operatingSystem.getSystemUptime();
544 return new DecimalType(getTimeInMinutes(seconds));
548 public DecimalType getCpuThreads() {
549 int threadCount = operatingSystem.getThreadCount();
550 return new DecimalType(threadCount);
554 public StringType getNetworkMac(int networkIndex) throws DeviceNotFoundException {
555 NetworkIF network = getDevice(networks, networkIndex);
556 String mac = network.getMacaddr();
557 return new StringType(mac);
561 public DecimalType getNetworkPacketsReceived(int networkIndex) throws DeviceNotFoundException {
562 NetworkIF network = getDevice(networks, networkIndex);
563 network.updateAttributes();
564 long packRecv = network.getPacketsRecv();
565 return new DecimalType(packRecv);
569 public DecimalType getNetworkPacketsSent(int networkIndex) throws DeviceNotFoundException {
570 NetworkIF network = getDevice(networks, networkIndex);
571 network.updateAttributes();
572 long packSent = network.getPacketsSent();
573 return new DecimalType(packSent);
577 public DecimalType getNetworkDataSent(int networkIndex) throws DeviceNotFoundException {
578 NetworkIF network = getDevice(networks, networkIndex);
579 network.updateAttributes();
580 long bytesSent = network.getBytesSent();
581 return new DecimalType(getSizeInMB(bytesSent));
585 public DecimalType getNetworkDataReceived(int networkIndex) throws DeviceNotFoundException {
586 NetworkIF network = getDevice(networks, networkIndex);
587 network.updateAttributes();
588 long bytesRecv = network.getBytesRecv();
589 return new DecimalType(getSizeInMB(bytesRecv));
593 public @Nullable StringType getProcessName(int pid) throws DeviceNotFoundException {
595 OSProcess process = getProcess(pid);
596 String name = process.getName();
597 return new StringType(name);
604 public @Nullable DecimalType getProcessCpuUsage(int pid) throws DeviceNotFoundException {
606 OSProcess process = getProcess(pid);
607 double cpuUsageRaw = (process.getKernelTime() + process.getUserTime()) / process.getUpTime();
608 BigDecimal cpuUsage = getPercentsValue(cpuUsageRaw);
609 return new DecimalType(cpuUsage);
616 public @Nullable DecimalType getProcessMemoryUsage(int pid) throws DeviceNotFoundException {
618 OSProcess process = getProcess(pid);
619 long memortInBytes = process.getResidentSetSize();
620 long memoryInMB = getSizeInMB(memortInBytes);
621 return new DecimalType(memoryInMB);
628 public @Nullable StringType getProcessPath(int pid) throws DeviceNotFoundException {
630 OSProcess process = getProcess(pid);
631 String path = process.getPath();
632 return new StringType(path);
639 public @Nullable DecimalType getProcessThreads(int pid) throws DeviceNotFoundException {
641 OSProcess process = getProcess(pid);
642 int threadCount = process.getThreadCount();
643 return new DecimalType(threadCount);