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.HashMap;
18 import java.util.List;
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;
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;
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.
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
57 * @see <a href="https://github.com/oshi/oshi">OSHI GitHub repository</a>
60 @Component(service = SysteminfoInterface.class)
61 public class OSHISysteminfo implements SysteminfoInterface {
63 private final Logger logger = LoggerFactory.getLogger(OSHISysteminfo.class);
65 private @NonNullByDefault({}) HardwareAbstractionLayer hal;
67 // Dynamic objects (may be queried repeatedly)
68 private @NonNullByDefault({}) GlobalMemory memory;
69 private @NonNullByDefault({}) CentralProcessor cpu;
70 private @NonNullByDefault({}) Sensors sensors;
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;
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<>();
87 public static final int PRECISION_AFTER_DECIMAL_SIGN = 1;
90 * Some of the methods used in this constructor execute native code and require execute permissions
93 public OSHISysteminfo() {
94 logger.debug("OSHISysteminfo service is created");
98 public void initializeSysteminfo() {
99 logger.debug("OSHISysteminfo service starts initializing");
101 SystemInfo systemInfo = new SystemInfo();
102 hal = systemInfo.getHardware();
104 // Doesn't need regular update, they may be queried repeatedly
105 memory = hal.getMemory();
106 cpu = hal.getProcessor();
107 sensors = hal.getSensors();
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();
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!");
122 return (T) devices.get(index);
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!");
129 return devices[index];
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);
141 public StringType getOsFamily() {
142 String osFamily = operatingSystem.getFamily();
143 return new StringType(osFamily);
147 public StringType getOsManufacturer() {
148 String osManufacturer = operatingSystem.getManufacturer();
149 return new StringType(osManufacturer);
153 public StringType getOsVersion() {
154 String osVersion = operatingSystem.getVersionInfo().toString();
155 return new StringType(osVersion);
159 public StringType getCpuName() {
160 String name = cpu.getProcessorIdentifier().getName();
161 return new StringType(name);
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,
176 return new StringType(description);
180 public DecimalType getCpuLogicalCores() {
181 int logicalProcessorCount = cpu.getLogicalProcessorCount();
182 return new DecimalType(logicalProcessorCount);
186 public DecimalType getCpuPhysicalCores() {
187 int physicalProcessorCount = cpu.getPhysicalProcessorCount();
188 return new DecimalType(physicalProcessorCount);
192 public DecimalType getMemoryTotal() {
193 long totalMemory = memory.getTotal();
194 totalMemory = getSizeInMB(totalMemory);
195 return new DecimalType(totalMemory);
199 public DecimalType getMemoryAvailable() {
200 long availableMemory = memory.getAvailable();
201 availableMemory = getSizeInMB(availableMemory);
202 return new DecimalType(availableMemory);
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);
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);
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);
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);
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);
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);
275 public StringType getStorageName(int index) throws DeviceNotFoundException {
276 OSFileStore fileStore = getDevice(fileStores, index);
277 String name = fileStore.getName();
278 return new StringType(name);
282 public StringType getStorageType(int deviceIndex) throws DeviceNotFoundException {
283 OSFileStore fileStore = getDevice(fileStores, deviceIndex);
284 String type = fileStore.getType();
285 return new StringType(type);
289 public StringType getStorageDescription(int index) throws DeviceNotFoundException {
290 OSFileStore fileStore = getDevice(fileStores, index);
291 String description = fileStore.getDescription();
292 return new StringType(description);
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);
305 public StringType getNetworkName(int index) throws DeviceNotFoundException {
306 NetworkIF netInterface = getDevice(networks, index);
307 String name = netInterface.getName();
308 return new StringType(name);
312 public StringType getNetworkDisplayName(int index) throws DeviceNotFoundException {
313 NetworkIF netInterface = getDevice(networks, index);
314 String adapterName = netInterface.getDisplayName();
315 return new StringType(adapterName);
319 public StringType getDisplayInformation(int index) throws DeviceNotFoundException {
320 Display display = getDevice(displays, index);
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);
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);
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;
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;
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];
355 throw new DeviceNotFoundException();
357 return speed > 0 ? new DecimalType(speed) : null;
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;
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);
380 public StringType getBatteryName(int index) throws DeviceNotFoundException {
381 PowerSource powerSource = getDevice(powerSources, index);
382 String name = powerSource.getName();
383 return new StringType(name);
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);
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);
414 public StringType getDriveName(int deviceIndex) throws DeviceNotFoundException {
415 HWDiskStore drive = getDevice(drives, deviceIndex);
416 String name = drive.getName();
417 return new StringType(name);
421 public StringType getDriveModel(int deviceIndex) throws DeviceNotFoundException {
422 HWDiskStore drive = getDevice(drives, deviceIndex);
423 String model = drive.getModel();
424 return new StringType(model);
428 public StringType getDriveSerialNumber(int deviceIndex) throws DeviceNotFoundException {
429 HWDiskStore drive = getDevice(drives, deviceIndex);
430 String serialNumber = drive.getSerial();
431 return new StringType(serialNumber);
435 public @Nullable DecimalType getSwapTotal() {
436 long swapTotal = memory.getVirtualMemory().getSwapTotal();
437 swapTotal = getSizeInMB(swapTotal);
438 return new DecimalType(swapTotal);
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);
451 public @Nullable DecimalType getSwapUsed() {
452 long swapUsed = memory.getVirtualMemory().getSwapUsed();
453 swapUsed = getSizeInMB(swapUsed);
454 return new DecimalType(swapUsed);
458 public @Nullable DecimalType getSwapAvailablePercent() {
459 long swapTotal = memory.getVirtualMemory().getSwapTotal();
460 long swapUsed = memory.getVirtualMemory().getSwapUsed();
461 long swapAvailable = swapTotal - swapUsed;
463 double swapAvailablePercentDecimal = (double) swapAvailable / (double) swapTotal;
464 BigDecimal swapAvailablePercent = getPercentsValue(swapAvailablePercentDecimal);
465 return new DecimalType(swapAvailablePercent);
472 public @Nullable DecimalType getSwapUsedPercent() {
473 long swapTotal = memory.getVirtualMemory().getSwapTotal();
474 long swapUsed = memory.getVirtualMemory().getSwapUsed();
476 double swapUsedPercentDecimal = (double) swapUsed / (double) swapTotal;
477 BigDecimal swapUsedPercent = getPercentsValue(swapUsedPercentDecimal);
478 return new DecimalType(swapUsedPercent);
484 private long getSizeInMB(long sizeInBytes) {
485 return Math.round(sizeInBytes / (1024D * 1024));
488 private BigDecimal getPercentsValue(double decimalFraction) {
489 BigDecimal result = new BigDecimal(decimalFraction * 100);
490 result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
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;
501 public @Nullable PercentType getSystemCpuLoad() {
502 PercentType load = (ticks[0] > 0) ? new PercentType(getPercentsValue(cpu.getSystemCpuLoadBetweenTicks(ticks)))
504 ticks = cpu.getSystemCpuLoadTicks();
511 * This information is available only on Mac and Linux OS.
514 public @Nullable DecimalType getCpuLoad1() {
515 BigDecimal avarageCpuLoad = getAvarageCpuLoad(1);
516 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
522 * This information is available only on Mac and Linux OS.
525 public @Nullable DecimalType getCpuLoad5() {
526 BigDecimal avarageCpuLoad = getAvarageCpuLoad(5);
527 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
533 * This information is available only on Mac and Linux OS.
536 public @Nullable DecimalType getCpuLoad15() {
537 BigDecimal avarageCpuLoad = getAvarageCpuLoad(15);
538 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
541 private BigDecimal getAvarageCpuLoad(int timeInMinutes) {
542 // This parameter is specified in OSHI Javadoc
544 switch (timeInMinutes) {
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);
564 public DecimalType getCpuUptime() {
565 long seconds = operatingSystem.getSystemUptime();
566 return new DecimalType(getTimeInMinutes(seconds));
570 public DecimalType getCpuThreads() {
571 int threadCount = operatingSystem.getThreadCount();
572 return new DecimalType(threadCount);
576 public StringType getNetworkMac(int networkIndex) throws DeviceNotFoundException {
577 NetworkIF network = getDevice(networks, networkIndex);
578 String mac = network.getMacaddr();
579 return new StringType(mac);
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);
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);
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));
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));
615 public int getCurrentProcessID() {
616 return operatingSystem.getProcessId();
620 public @Nullable StringType getProcessName(int pid) throws DeviceNotFoundException {
622 OSProcess process = getProcess(pid);
623 String name = process.getName();
624 return new StringType(name);
631 public @Nullable DecimalType getProcessCpuUsage(int pid) throws DeviceNotFoundException {
633 OSProcess process = getProcess(pid);
634 DecimalType load = (processTicks.containsKey(pid))
635 ? new DecimalType(getPercentsValue(process.getProcessCpuLoadBetweenTicks(processTicks.get(pid))))
637 processTicks.put(pid, process);
645 public @Nullable DecimalType getProcessMemoryUsage(int pid) throws DeviceNotFoundException {
647 OSProcess process = getProcess(pid);
648 long memortInBytes = process.getResidentSetSize();
649 long memoryInMB = getSizeInMB(memortInBytes);
650 return new DecimalType(memoryInMB);
657 public @Nullable StringType getProcessPath(int pid) throws DeviceNotFoundException {
659 OSProcess process = getProcess(pid);
660 String path = process.getPath();
661 return new StringType(path);
668 public @Nullable DecimalType getProcessThreads(int pid) throws DeviceNotFoundException {
670 OSProcess process = getProcess(pid);
671 int threadCount = process.getThreadCount();
672 return new DecimalType(threadCount);
679 public int getNetworkIFCount() {
680 return networks.size();
684 public int getDisplayCount() {
685 return displays.size();
689 public int getFileOSStoreCount() {
690 return fileStores.size();
694 public int getPowerSourceCount() {
695 return powerSources.size();
699 public int getDriveCount() {
700 return drives.size();
704 public int getFanCount() {
705 return sensors.getFanSpeeds().length;