2 * Copyright (c) 2010-2022 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.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({}) List<NetworkIF> networks;
72 private @NonNullByDefault({}) List<Display> displays;
73 private @NonNullByDefault({}) List<OSFileStore> fileStores;
74 private @NonNullByDefault({}) List<PowerSource> powerSources;
75 private @NonNullByDefault({}) List<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 <T> T getDevice(List<@Nullable T> devices, int index) throws DeviceNotFoundException {
109 if (devices.size() <= index) {
110 throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
112 return (T) devices.get(index);
115 private <T> T getDevice(T @Nullable [] devices, int index) throws DeviceNotFoundException {
116 if (devices == null || devices.length <= index) {
117 throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
119 return devices[index];
122 private OSProcess getProcess(int pid) throws DeviceNotFoundException {
123 OSProcess process = operatingSystem.getProcess(pid);
124 if (process == null) {
125 throw new DeviceNotFoundException("Error while getting information for process with PID " + pid);
131 public StringType getOsFamily() {
132 String osFamily = operatingSystem.getFamily();
133 return new StringType(osFamily);
137 public StringType getOsManufacturer() {
138 String osManufacturer = operatingSystem.getManufacturer();
139 return new StringType(osManufacturer);
143 public StringType getOsVersion() {
144 String osVersion = operatingSystem.getVersionInfo().toString();
145 return new StringType(osVersion);
149 public StringType getCpuName() {
150 String name = cpu.getProcessorIdentifier().getName();
151 return new StringType(name);
155 public StringType getCpuDescription() {
156 String model = cpu.getProcessorIdentifier().getModel();
157 String family = cpu.getProcessorIdentifier().getFamily();
158 String serialNumber = computerSystem.getSerialNumber();
159 String identifier = cpu.getProcessorIdentifier().getIdentifier();
160 String vendor = cpu.getProcessorIdentifier().getVendor();
161 String architecture = cpu.getProcessorIdentifier().isCpu64bit() ? "64 bit" : "32 bit";
162 String descriptionFormatString = "Model: %s %s,family: %s, vendor: %s, sn: %s, identifier: %s ";
163 String description = String.format(descriptionFormatString, model, architecture, family, vendor, serialNumber,
166 return new StringType(description);
170 public DecimalType getCpuLogicalCores() {
171 int logicalProcessorCount = cpu.getLogicalProcessorCount();
172 return new DecimalType(logicalProcessorCount);
176 public DecimalType getCpuPhysicalCores() {
177 int physicalProcessorCount = cpu.getPhysicalProcessorCount();
178 return new DecimalType(physicalProcessorCount);
182 public DecimalType getMemoryTotal() {
183 long totalMemory = memory.getTotal();
184 totalMemory = getSizeInMB(totalMemory);
185 return new DecimalType(totalMemory);
189 public DecimalType getMemoryAvailable() {
190 long availableMemory = memory.getAvailable();
191 availableMemory = getSizeInMB(availableMemory);
192 return new DecimalType(availableMemory);
196 public DecimalType getMemoryUsed() {
197 long totalMemory = memory.getTotal();
198 long availableMemory = memory.getAvailable();
199 long usedMemory = totalMemory - availableMemory;
200 usedMemory = getSizeInMB(usedMemory);
201 return new DecimalType(usedMemory);
205 public DecimalType getStorageTotal(int index) throws DeviceNotFoundException {
206 OSFileStore fileStore = getDevice(fileStores, index);
207 fileStore.updateAttributes();
208 long totalSpace = fileStore.getTotalSpace();
209 totalSpace = getSizeInMB(totalSpace);
210 return new DecimalType(totalSpace);
214 public DecimalType getStorageAvailable(int index) throws DeviceNotFoundException {
215 OSFileStore fileStore = getDevice(fileStores, index);
216 fileStore.updateAttributes();
217 long freeSpace = fileStore.getUsableSpace();
218 freeSpace = getSizeInMB(freeSpace);
219 return new DecimalType(freeSpace);
223 public DecimalType getStorageUsed(int index) throws DeviceNotFoundException {
224 OSFileStore fileStore = getDevice(fileStores, index);
225 fileStore.updateAttributes();
226 long totalSpace = fileStore.getTotalSpace();
227 long freeSpace = fileStore.getUsableSpace();
228 long usedSpace = totalSpace - freeSpace;
229 usedSpace = getSizeInMB(usedSpace);
230 return new DecimalType(usedSpace);
234 public @Nullable DecimalType getStorageAvailablePercent(int deviceIndex) throws DeviceNotFoundException {
235 OSFileStore fileStore = getDevice(fileStores, deviceIndex);
236 fileStore.updateAttributes();
237 long totalSpace = fileStore.getTotalSpace();
238 long freeSpace = fileStore.getUsableSpace();
239 if (totalSpace > 0) {
240 double freePercentDecimal = (double) freeSpace / (double) totalSpace;
241 BigDecimal freePercent = getPercentsValue(freePercentDecimal);
242 return new DecimalType(freePercent);
249 public @Nullable DecimalType getStorageUsedPercent(int deviceIndex) throws DeviceNotFoundException {
250 OSFileStore fileStore = getDevice(fileStores, deviceIndex);
251 fileStore.updateAttributes();
252 long totalSpace = fileStore.getTotalSpace();
253 long freeSpace = fileStore.getUsableSpace();
254 long usedSpace = totalSpace - freeSpace;
255 if (totalSpace > 0) {
256 double usedPercentDecimal = (double) usedSpace / (double) totalSpace;
257 BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
258 return new DecimalType(usedPercent);
265 public StringType getStorageName(int index) throws DeviceNotFoundException {
266 OSFileStore fileStore = getDevice(fileStores, index);
267 String name = fileStore.getName();
268 return new StringType(name);
272 public StringType getStorageType(int deviceIndex) throws DeviceNotFoundException {
273 OSFileStore fileStore = getDevice(fileStores, deviceIndex);
274 String type = fileStore.getType();
275 return new StringType(type);
279 public StringType getStorageDescription(int index) throws DeviceNotFoundException {
280 OSFileStore fileStore = getDevice(fileStores, index);
281 String description = fileStore.getDescription();
282 return new StringType(description);
286 public StringType getNetworkIp(int index) throws DeviceNotFoundException {
287 NetworkIF netInterface = getDevice(networks, index);
288 netInterface.updateAttributes();
289 String[] ipAddresses = netInterface.getIPv4addr();
290 String ipv4 = getDevice(ipAddresses, 0);
291 return new StringType(ipv4);
295 public StringType getNetworkName(int index) throws DeviceNotFoundException {
296 NetworkIF netInterface = getDevice(networks, index);
297 String name = netInterface.getName();
298 return new StringType(name);
302 public StringType getNetworkDisplayName(int index) throws DeviceNotFoundException {
303 NetworkIF netInterface = getDevice(networks, index);
304 String adapterName = netInterface.getDisplayName();
305 return new StringType(adapterName);
309 public StringType getDisplayInformation(int index) throws DeviceNotFoundException {
310 Display display = getDevice(displays, index);
312 byte[] edid = display.getEdid();
313 String manufacturer = EdidUtil.getManufacturerID(edid);
314 String product = EdidUtil.getProductID(edid);
315 String serialNumber = EdidUtil.getSerialNo(edid);
316 int width = EdidUtil.getHcm(edid);
317 int height = EdidUtil.getVcm(edid);
319 String edidFormatString = "Product %s, manufacturer %s, SN: %s, Width: %d, Height: %d";
320 String edidInfo = String.format(edidFormatString, product, manufacturer, serialNumber, width, height);
321 return new StringType(edidInfo);
325 public @Nullable DecimalType getSensorsCpuTemperature() {
326 BigDecimal cpuTemp = new BigDecimal(sensors.getCpuTemperature());
327 cpuTemp = cpuTemp.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
328 return cpuTemp.signum() == 1 ? new DecimalType(cpuTemp) : null;
332 public @Nullable DecimalType getSensorsCpuVoltage() {
333 BigDecimal cpuVoltage = new BigDecimal(sensors.getCpuVoltage());
334 cpuVoltage = cpuVoltage.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
335 return cpuVoltage.signum() == 1 ? new DecimalType(cpuVoltage) : null;
339 public @Nullable DecimalType getSensorsFanSpeed(int index) throws DeviceNotFoundException {
340 int[] fanSpeeds = sensors.getFanSpeeds();
341 int speed = 0;// 0 means unable to measure speed
342 if (index < fanSpeeds.length) {
343 speed = fanSpeeds[index];
345 return speed > 0 ? new DecimalType(speed) : null;
349 public @Nullable DecimalType getBatteryRemainingTime(int index) throws DeviceNotFoundException {
350 PowerSource powerSource = getDevice(powerSources, index);
351 powerSource.updateAttributes();
352 double remainingTimeInSeconds = powerSource.getTimeRemainingEstimated();
353 // The getTimeRemaining() method returns (-1.0) if is calculating or (-2.0) if the time is unlimited.
354 BigDecimal remainingTime = getTimeInMinutes(remainingTimeInSeconds);
355 return remainingTime.signum() == 1 ? new DecimalType(remainingTime) : null;
359 public DecimalType getBatteryRemainingCapacity(int index) throws DeviceNotFoundException {
360 PowerSource powerSource = getDevice(powerSources, index);
361 powerSource.updateAttributes();
362 double remainingCapacity = powerSource.getRemainingCapacityPercent();
363 BigDecimal remainingCapacityPercents = getPercentsValue(remainingCapacity);
364 return new DecimalType(remainingCapacityPercents);
368 public StringType getBatteryName(int index) throws DeviceNotFoundException {
369 PowerSource powerSource = getDevice(powerSources, index);
370 String name = powerSource.getName();
371 return new StringType(name);
375 public @Nullable DecimalType getMemoryAvailablePercent() {
376 long availableMemory = memory.getAvailable();
377 long totalMemory = memory.getTotal();
378 if (totalMemory > 0) {
379 double freePercentDecimal = (double) availableMemory / (double) totalMemory;
380 BigDecimal freePercent = getPercentsValue(freePercentDecimal);
381 return new DecimalType(freePercent);
388 public @Nullable DecimalType getMemoryUsedPercent() {
389 long availableMemory = memory.getAvailable();
390 long totalMemory = memory.getTotal();
391 long usedMemory = totalMemory - availableMemory;
392 if (totalMemory > 0) {
393 double usedPercentDecimal = (double) usedMemory / (double) totalMemory;
394 BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
395 return new DecimalType(usedPercent);
402 public StringType getDriveName(int deviceIndex) throws DeviceNotFoundException {
403 HWDiskStore drive = getDevice(drives, deviceIndex);
404 String name = drive.getName();
405 return new StringType(name);
409 public StringType getDriveModel(int deviceIndex) throws DeviceNotFoundException {
410 HWDiskStore drive = getDevice(drives, deviceIndex);
411 String model = drive.getModel();
412 return new StringType(model);
416 public StringType getDriveSerialNumber(int deviceIndex) throws DeviceNotFoundException {
417 HWDiskStore drive = getDevice(drives, deviceIndex);
418 String serialNumber = drive.getSerial();
419 return new StringType(serialNumber);
423 public @Nullable DecimalType getSwapTotal() {
424 long swapTotal = memory.getVirtualMemory().getSwapTotal();
425 swapTotal = getSizeInMB(swapTotal);
426 return new DecimalType(swapTotal);
430 public @Nullable DecimalType getSwapAvailable() {
431 long swapTotal = memory.getVirtualMemory().getSwapTotal();
432 long swapUsed = memory.getVirtualMemory().getSwapUsed();
433 long swapAvailable = swapTotal - swapUsed;
434 swapAvailable = getSizeInMB(swapAvailable);
435 return new DecimalType(swapAvailable);
439 public @Nullable DecimalType getSwapUsed() {
440 long swapUsed = memory.getVirtualMemory().getSwapUsed();
441 swapUsed = getSizeInMB(swapUsed);
442 return new DecimalType(swapUsed);
446 public @Nullable DecimalType getSwapAvailablePercent() {
447 long swapTotal = memory.getVirtualMemory().getSwapTotal();
448 long swapUsed = memory.getVirtualMemory().getSwapUsed();
449 long swapAvailable = swapTotal - swapUsed;
451 double swapAvailablePercentDecimal = (double) swapAvailable / (double) swapTotal;
452 BigDecimal swapAvailablePercent = getPercentsValue(swapAvailablePercentDecimal);
453 return new DecimalType(swapAvailablePercent);
460 public @Nullable DecimalType getSwapUsedPercent() {
461 long swapTotal = memory.getVirtualMemory().getSwapTotal();
462 long swapUsed = memory.getVirtualMemory().getSwapUsed();
464 double swapUsedPercentDecimal = (double) swapUsed / (double) swapTotal;
465 BigDecimal swapUsedPercent = getPercentsValue(swapUsedPercentDecimal);
466 return new DecimalType(swapUsedPercent);
472 private long getSizeInMB(long sizeInBytes) {
473 return Math.round(sizeInBytes / (1024D * 1024));
476 private BigDecimal getPercentsValue(double decimalFraction) {
477 BigDecimal result = new BigDecimal(decimalFraction * 100);
478 result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
482 private BigDecimal getTimeInMinutes(double timeInSeconds) {
483 BigDecimal timeInMinutes = new BigDecimal(timeInSeconds / 60);
484 timeInMinutes = timeInMinutes.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.UP);
485 return timeInMinutes;
491 * This information is available only on Mac and Linux OS.
494 public @Nullable DecimalType getCpuLoad1() {
495 BigDecimal avarageCpuLoad = getAvarageCpuLoad(1);
496 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
502 * This information is available only on Mac and Linux OS.
505 public @Nullable DecimalType getCpuLoad5() {
506 BigDecimal avarageCpuLoad = getAvarageCpuLoad(5);
507 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
513 * This information is available only on Mac and Linux OS.
516 public @Nullable DecimalType getCpuLoad15() {
517 BigDecimal avarageCpuLoad = getAvarageCpuLoad(15);
518 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
521 private BigDecimal getAvarageCpuLoad(int timeInMunutes) {
522 // This parameter is specified in OSHI Javadoc
524 switch (timeInMunutes) {
537 double processorLoads[] = cpu.getSystemLoadAverage(index + 1);
538 BigDecimal result = new BigDecimal(processorLoads[index]);
539 result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
544 public DecimalType getCpuUptime() {
545 long seconds = operatingSystem.getSystemUptime();
546 return new DecimalType(getTimeInMinutes(seconds));
550 public DecimalType getCpuThreads() {
551 int threadCount = operatingSystem.getThreadCount();
552 return new DecimalType(threadCount);
556 public StringType getNetworkMac(int networkIndex) throws DeviceNotFoundException {
557 NetworkIF network = getDevice(networks, networkIndex);
558 String mac = network.getMacaddr();
559 return new StringType(mac);
563 public DecimalType getNetworkPacketsReceived(int networkIndex) throws DeviceNotFoundException {
564 NetworkIF network = getDevice(networks, networkIndex);
565 network.updateAttributes();
566 long packRecv = network.getPacketsRecv();
567 return new DecimalType(packRecv);
571 public DecimalType getNetworkPacketsSent(int networkIndex) throws DeviceNotFoundException {
572 NetworkIF network = getDevice(networks, networkIndex);
573 network.updateAttributes();
574 long packSent = network.getPacketsSent();
575 return new DecimalType(packSent);
579 public DecimalType getNetworkDataSent(int networkIndex) throws DeviceNotFoundException {
580 NetworkIF network = getDevice(networks, networkIndex);
581 network.updateAttributes();
582 long bytesSent = network.getBytesSent();
583 return new DecimalType(getSizeInMB(bytesSent));
587 public DecimalType getNetworkDataReceived(int networkIndex) throws DeviceNotFoundException {
588 NetworkIF network = getDevice(networks, networkIndex);
589 network.updateAttributes();
590 long bytesRecv = network.getBytesRecv();
591 return new DecimalType(getSizeInMB(bytesRecv));
595 public @Nullable StringType getProcessName(int pid) throws DeviceNotFoundException {
597 OSProcess process = getProcess(pid);
598 String name = process.getName();
599 return new StringType(name);
606 public @Nullable DecimalType getProcessCpuUsage(int pid) throws DeviceNotFoundException {
608 OSProcess process = getProcess(pid);
609 double cpuUsageRaw = (process.getKernelTime() + process.getUserTime()) / process.getUpTime();
610 BigDecimal cpuUsage = getPercentsValue(cpuUsageRaw);
611 return new DecimalType(cpuUsage);
618 public @Nullable DecimalType getProcessMemoryUsage(int pid) throws DeviceNotFoundException {
620 OSProcess process = getProcess(pid);
621 long memortInBytes = process.getResidentSetSize();
622 long memoryInMB = getSizeInMB(memortInBytes);
623 return new DecimalType(memoryInMB);
630 public @Nullable StringType getProcessPath(int pid) throws DeviceNotFoundException {
632 OSProcess process = getProcess(pid);
633 String path = process.getPath();
634 return new StringType(path);
641 public @Nullable DecimalType getProcessThreads(int pid) throws DeviceNotFoundException {
643 OSProcess process = getProcess(pid);
644 int threadCount = process.getThreadCount();
645 return new DecimalType(threadCount);