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
56 * @see <a href="https://github.com/oshi/oshi">OSHI GitHub repository</a>
59 @Component(service = SysteminfoInterface.class)
60 public class OSHISysteminfo implements SysteminfoInterface {
62 private final Logger logger = LoggerFactory.getLogger(OSHISysteminfo.class);
64 private @NonNullByDefault({}) HardwareAbstractionLayer hal;
66 // Dynamic objects (may be queried repeatedly)
67 private @NonNullByDefault({}) GlobalMemory memory;
68 private @NonNullByDefault({}) CentralProcessor cpu;
69 private @NonNullByDefault({}) Sensors sensors;
71 // Static objects, should be recreated on each request
72 private @NonNullByDefault({}) ComputerSystem computerSystem;
73 private @NonNullByDefault({}) OperatingSystem operatingSystem;
74 private @NonNullByDefault({}) List<NetworkIF> networks;
75 private @NonNullByDefault({}) List<Display> displays;
76 private @NonNullByDefault({}) List<OSFileStore> fileStores;
77 private @NonNullByDefault({}) List<PowerSource> powerSources;
78 private @NonNullByDefault({}) List<HWDiskStore> drives;
80 // Array containing cpu tick info to calculate CPU load, according to oshi doc:
81 // 8 long values representing time spent in User, Nice, System, Idle, IOwait, IRQ, SoftIRQ, and Steal states
82 private long[] ticks = new long[8];
83 // Map containing previous process state to calculate load by process
84 private Map<Integer, OSProcess> processTicks = new HashMap<>();
86 public static final int PRECISION_AFTER_DECIMAL_SIGN = 1;
89 * Some of the methods used in this constructor execute native code and require execute permissions
92 public OSHISysteminfo() {
93 logger.debug("OSHISysteminfo service is created");
97 public void initializeSysteminfo() {
98 logger.debug("OSHISysteminfo service starts initializing");
100 SystemInfo systemInfo = new SystemInfo();
101 hal = systemInfo.getHardware();
103 // Doesn't need regular update, they may be queried repeatedly
104 memory = hal.getMemory();
105 cpu = hal.getProcessor();
106 sensors = hal.getSensors();
108 computerSystem = hal.getComputerSystem();
109 operatingSystem = systemInfo.getOperatingSystem();
110 networks = hal.getNetworkIFs();
111 displays = hal.getDisplays();
112 fileStores = operatingSystem.getFileSystem().getFileStores();
113 powerSources = hal.getPowerSources();
114 drives = hal.getDiskStores();
117 private <T> T getDevice(List<@Nullable T> devices, int index) throws DeviceNotFoundException {
118 if (devices.size() <= index) {
119 throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
121 return (T) devices.get(index);
124 private <T> T getDevice(T @Nullable [] devices, int index) throws DeviceNotFoundException {
125 if (devices == null || devices.length <= index) {
126 throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
128 return devices[index];
131 private OSProcess getProcess(int pid) throws DeviceNotFoundException {
132 OSProcess process = operatingSystem.getProcess(pid);
133 if (process == null) {
134 throw new DeviceNotFoundException("Error while getting information for process with PID " + pid);
140 public StringType getOsFamily() {
141 String osFamily = operatingSystem.getFamily();
142 return new StringType(osFamily);
146 public StringType getOsManufacturer() {
147 String osManufacturer = operatingSystem.getManufacturer();
148 return new StringType(osManufacturer);
152 public StringType getOsVersion() {
153 String osVersion = operatingSystem.getVersionInfo().toString();
154 return new StringType(osVersion);
158 public StringType getCpuName() {
159 String name = cpu.getProcessorIdentifier().getName();
160 return new StringType(name);
164 public StringType getCpuDescription() {
165 String model = cpu.getProcessorIdentifier().getModel();
166 String family = cpu.getProcessorIdentifier().getFamily();
167 String serialNumber = computerSystem.getSerialNumber();
168 String identifier = cpu.getProcessorIdentifier().getIdentifier();
169 String vendor = cpu.getProcessorIdentifier().getVendor();
170 String architecture = cpu.getProcessorIdentifier().isCpu64bit() ? "64 bit" : "32 bit";
171 String descriptionFormatString = "Model: %s %s,family: %s, vendor: %s, sn: %s, identifier: %s ";
172 String description = String.format(descriptionFormatString, model, architecture, family, vendor, serialNumber,
175 return new StringType(description);
179 public DecimalType getCpuLogicalCores() {
180 int logicalProcessorCount = cpu.getLogicalProcessorCount();
181 return new DecimalType(logicalProcessorCount);
185 public DecimalType getCpuPhysicalCores() {
186 int physicalProcessorCount = cpu.getPhysicalProcessorCount();
187 return new DecimalType(physicalProcessorCount);
191 public DecimalType getMemoryTotal() {
192 long totalMemory = memory.getTotal();
193 totalMemory = getSizeInMB(totalMemory);
194 return new DecimalType(totalMemory);
198 public DecimalType getMemoryAvailable() {
199 long availableMemory = memory.getAvailable();
200 availableMemory = getSizeInMB(availableMemory);
201 return new DecimalType(availableMemory);
205 public DecimalType getMemoryUsed() {
206 long totalMemory = memory.getTotal();
207 long availableMemory = memory.getAvailable();
208 long usedMemory = totalMemory - availableMemory;
209 usedMemory = getSizeInMB(usedMemory);
210 return new DecimalType(usedMemory);
214 public DecimalType getStorageTotal(int index) throws DeviceNotFoundException {
215 OSFileStore fileStore = getDevice(fileStores, index);
216 fileStore.updateAttributes();
217 long totalSpace = fileStore.getTotalSpace();
218 totalSpace = getSizeInMB(totalSpace);
219 return new DecimalType(totalSpace);
223 public DecimalType getStorageAvailable(int index) throws DeviceNotFoundException {
224 OSFileStore fileStore = getDevice(fileStores, index);
225 fileStore.updateAttributes();
226 long freeSpace = fileStore.getUsableSpace();
227 freeSpace = getSizeInMB(freeSpace);
228 return new DecimalType(freeSpace);
232 public DecimalType getStorageUsed(int index) throws DeviceNotFoundException {
233 OSFileStore fileStore = getDevice(fileStores, index);
234 fileStore.updateAttributes();
235 long totalSpace = fileStore.getTotalSpace();
236 long freeSpace = fileStore.getUsableSpace();
237 long usedSpace = totalSpace - freeSpace;
238 usedSpace = getSizeInMB(usedSpace);
239 return new DecimalType(usedSpace);
243 public @Nullable DecimalType getStorageAvailablePercent(int deviceIndex) throws DeviceNotFoundException {
244 OSFileStore fileStore = getDevice(fileStores, deviceIndex);
245 fileStore.updateAttributes();
246 long totalSpace = fileStore.getTotalSpace();
247 long freeSpace = fileStore.getUsableSpace();
248 if (totalSpace > 0) {
249 double freePercentDecimal = (double) freeSpace / (double) totalSpace;
250 BigDecimal freePercent = getPercentsValue(freePercentDecimal);
251 return new DecimalType(freePercent);
258 public @Nullable DecimalType getStorageUsedPercent(int deviceIndex) throws DeviceNotFoundException {
259 OSFileStore fileStore = getDevice(fileStores, deviceIndex);
260 fileStore.updateAttributes();
261 long totalSpace = fileStore.getTotalSpace();
262 long freeSpace = fileStore.getUsableSpace();
263 long usedSpace = totalSpace - freeSpace;
264 if (totalSpace > 0) {
265 double usedPercentDecimal = (double) usedSpace / (double) totalSpace;
266 BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
267 return new DecimalType(usedPercent);
274 public StringType getStorageName(int index) throws DeviceNotFoundException {
275 OSFileStore fileStore = getDevice(fileStores, index);
276 String name = fileStore.getName();
277 return new StringType(name);
281 public StringType getStorageType(int deviceIndex) throws DeviceNotFoundException {
282 OSFileStore fileStore = getDevice(fileStores, deviceIndex);
283 String type = fileStore.getType();
284 return new StringType(type);
288 public StringType getStorageDescription(int index) throws DeviceNotFoundException {
289 OSFileStore fileStore = getDevice(fileStores, index);
290 String description = fileStore.getDescription();
291 return new StringType(description);
295 public StringType getNetworkIp(int index) throws DeviceNotFoundException {
296 NetworkIF netInterface = getDevice(networks, index);
297 netInterface.updateAttributes();
298 String[] ipAddresses = netInterface.getIPv4addr();
299 String ipv4 = getDevice(ipAddresses, 0);
300 return new StringType(ipv4);
304 public StringType getNetworkName(int index) throws DeviceNotFoundException {
305 NetworkIF netInterface = getDevice(networks, index);
306 String name = netInterface.getName();
307 return new StringType(name);
311 public StringType getNetworkDisplayName(int index) throws DeviceNotFoundException {
312 NetworkIF netInterface = getDevice(networks, index);
313 String adapterName = netInterface.getDisplayName();
314 return new StringType(adapterName);
318 public StringType getDisplayInformation(int index) throws DeviceNotFoundException {
319 Display display = getDevice(displays, index);
321 byte[] edid = display.getEdid();
322 String manufacturer = EdidUtil.getManufacturerID(edid);
323 String product = EdidUtil.getProductID(edid);
324 String serialNumber = EdidUtil.getSerialNo(edid);
325 int width = EdidUtil.getHcm(edid);
326 int height = EdidUtil.getVcm(edid);
328 String edidFormatString = "Product %s, manufacturer %s, SN: %s, Width: %d, Height: %d";
329 String edidInfo = String.format(edidFormatString, product, manufacturer, serialNumber, width, height);
330 return new StringType(edidInfo);
334 public @Nullable DecimalType getSensorsCpuTemperature() {
335 BigDecimal cpuTemp = new BigDecimal(sensors.getCpuTemperature());
336 cpuTemp = cpuTemp.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
337 return cpuTemp.signum() == 1 ? new DecimalType(cpuTemp) : null;
341 public @Nullable DecimalType getSensorsCpuVoltage() {
342 BigDecimal cpuVoltage = new BigDecimal(sensors.getCpuVoltage());
343 cpuVoltage = cpuVoltage.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
344 return cpuVoltage.signum() == 1 ? new DecimalType(cpuVoltage) : null;
348 public @Nullable DecimalType getSensorsFanSpeed(int index) throws DeviceNotFoundException {
349 int[] fanSpeeds = sensors.getFanSpeeds();
350 int speed = 0; // 0 means unable to measure speed
351 if (index < fanSpeeds.length) {
352 speed = fanSpeeds[index];
354 return speed > 0 ? new DecimalType(speed) : null;
358 public @Nullable DecimalType getBatteryRemainingTime(int index) throws DeviceNotFoundException {
359 PowerSource powerSource = getDevice(powerSources, index);
360 powerSource.updateAttributes();
361 double remainingTimeInSeconds = powerSource.getTimeRemainingEstimated();
362 // The getTimeRemaining() method returns (-1.0) if is calculating or (-2.0) if the time is unlimited.
363 BigDecimal remainingTime = getTimeInMinutes(remainingTimeInSeconds);
364 return remainingTime.signum() == 1 ? new DecimalType(remainingTime) : null;
368 public DecimalType getBatteryRemainingCapacity(int index) throws DeviceNotFoundException {
369 PowerSource powerSource = getDevice(powerSources, index);
370 powerSource.updateAttributes();
371 double remainingCapacity = powerSource.getRemainingCapacityPercent();
372 BigDecimal remainingCapacityPercents = getPercentsValue(remainingCapacity);
373 return new DecimalType(remainingCapacityPercents);
377 public StringType getBatteryName(int index) throws DeviceNotFoundException {
378 PowerSource powerSource = getDevice(powerSources, index);
379 String name = powerSource.getName();
380 return new StringType(name);
384 public @Nullable DecimalType getMemoryAvailablePercent() {
385 long availableMemory = memory.getAvailable();
386 long totalMemory = memory.getTotal();
387 if (totalMemory > 0) {
388 double freePercentDecimal = (double) availableMemory / (double) totalMemory;
389 BigDecimal freePercent = getPercentsValue(freePercentDecimal);
390 return new DecimalType(freePercent);
397 public @Nullable DecimalType getMemoryUsedPercent() {
398 long availableMemory = memory.getAvailable();
399 long totalMemory = memory.getTotal();
400 long usedMemory = totalMemory - availableMemory;
401 if (totalMemory > 0) {
402 double usedPercentDecimal = (double) usedMemory / (double) totalMemory;
403 BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
404 return new DecimalType(usedPercent);
411 public StringType getDriveName(int deviceIndex) throws DeviceNotFoundException {
412 HWDiskStore drive = getDevice(drives, deviceIndex);
413 String name = drive.getName();
414 return new StringType(name);
418 public StringType getDriveModel(int deviceIndex) throws DeviceNotFoundException {
419 HWDiskStore drive = getDevice(drives, deviceIndex);
420 String model = drive.getModel();
421 return new StringType(model);
425 public StringType getDriveSerialNumber(int deviceIndex) throws DeviceNotFoundException {
426 HWDiskStore drive = getDevice(drives, deviceIndex);
427 String serialNumber = drive.getSerial();
428 return new StringType(serialNumber);
432 public @Nullable DecimalType getSwapTotal() {
433 long swapTotal = memory.getVirtualMemory().getSwapTotal();
434 swapTotal = getSizeInMB(swapTotal);
435 return new DecimalType(swapTotal);
439 public @Nullable DecimalType getSwapAvailable() {
440 long swapTotal = memory.getVirtualMemory().getSwapTotal();
441 long swapUsed = memory.getVirtualMemory().getSwapUsed();
442 long swapAvailable = swapTotal - swapUsed;
443 swapAvailable = getSizeInMB(swapAvailable);
444 return new DecimalType(swapAvailable);
448 public @Nullable DecimalType getSwapUsed() {
449 long swapUsed = memory.getVirtualMemory().getSwapUsed();
450 swapUsed = getSizeInMB(swapUsed);
451 return new DecimalType(swapUsed);
455 public @Nullable DecimalType getSwapAvailablePercent() {
456 long swapTotal = memory.getVirtualMemory().getSwapTotal();
457 long swapUsed = memory.getVirtualMemory().getSwapUsed();
458 long swapAvailable = swapTotal - swapUsed;
460 double swapAvailablePercentDecimal = (double) swapAvailable / (double) swapTotal;
461 BigDecimal swapAvailablePercent = getPercentsValue(swapAvailablePercentDecimal);
462 return new DecimalType(swapAvailablePercent);
469 public @Nullable DecimalType getSwapUsedPercent() {
470 long swapTotal = memory.getVirtualMemory().getSwapTotal();
471 long swapUsed = memory.getVirtualMemory().getSwapUsed();
473 double swapUsedPercentDecimal = (double) swapUsed / (double) swapTotal;
474 BigDecimal swapUsedPercent = getPercentsValue(swapUsedPercentDecimal);
475 return new DecimalType(swapUsedPercent);
481 private long getSizeInMB(long sizeInBytes) {
482 return Math.round(sizeInBytes / (1024D * 1024));
485 private BigDecimal getPercentsValue(double decimalFraction) {
486 BigDecimal result = new BigDecimal(decimalFraction * 100);
487 result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
491 private BigDecimal getTimeInMinutes(double timeInSeconds) {
492 BigDecimal timeInMinutes = new BigDecimal(timeInSeconds / 60);
493 timeInMinutes = timeInMinutes.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.UP);
494 return timeInMinutes;
498 public @Nullable PercentType getSystemCpuLoad() {
499 PercentType load = (ticks[0] > 0) ? new PercentType(getPercentsValue(cpu.getSystemCpuLoadBetweenTicks(ticks)))
501 ticks = cpu.getSystemCpuLoadTicks();
508 * This information is available only on Mac and Linux OS.
511 public @Nullable DecimalType getCpuLoad1() {
512 BigDecimal avarageCpuLoad = getAvarageCpuLoad(1);
513 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
519 * This information is available only on Mac and Linux OS.
522 public @Nullable DecimalType getCpuLoad5() {
523 BigDecimal avarageCpuLoad = getAvarageCpuLoad(5);
524 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
530 * This information is available only on Mac and Linux OS.
533 public @Nullable DecimalType getCpuLoad15() {
534 BigDecimal avarageCpuLoad = getAvarageCpuLoad(15);
535 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
538 private BigDecimal getAvarageCpuLoad(int timeInMinutes) {
539 // This parameter is specified in OSHI Javadoc
541 switch (timeInMinutes) {
554 double processorLoads[] = cpu.getSystemLoadAverage(index + 1);
555 BigDecimal result = new BigDecimal(processorLoads[index]);
556 result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
561 public DecimalType getCpuUptime() {
562 long seconds = operatingSystem.getSystemUptime();
563 return new DecimalType(getTimeInMinutes(seconds));
567 public DecimalType getCpuThreads() {
568 int threadCount = operatingSystem.getThreadCount();
569 return new DecimalType(threadCount);
573 public StringType getNetworkMac(int networkIndex) throws DeviceNotFoundException {
574 NetworkIF network = getDevice(networks, networkIndex);
575 String mac = network.getMacaddr();
576 return new StringType(mac);
580 public DecimalType getNetworkPacketsReceived(int networkIndex) throws DeviceNotFoundException {
581 NetworkIF network = getDevice(networks, networkIndex);
582 network.updateAttributes();
583 long packRecv = network.getPacketsRecv();
584 return new DecimalType(packRecv);
588 public DecimalType getNetworkPacketsSent(int networkIndex) throws DeviceNotFoundException {
589 NetworkIF network = getDevice(networks, networkIndex);
590 network.updateAttributes();
591 long packSent = network.getPacketsSent();
592 return new DecimalType(packSent);
596 public DecimalType getNetworkDataSent(int networkIndex) throws DeviceNotFoundException {
597 NetworkIF network = getDevice(networks, networkIndex);
598 network.updateAttributes();
599 long bytesSent = network.getBytesSent();
600 return new DecimalType(getSizeInMB(bytesSent));
604 public DecimalType getNetworkDataReceived(int networkIndex) throws DeviceNotFoundException {
605 NetworkIF network = getDevice(networks, networkIndex);
606 network.updateAttributes();
607 long bytesRecv = network.getBytesRecv();
608 return new DecimalType(getSizeInMB(bytesRecv));
612 public @Nullable StringType getProcessName(int pid) throws DeviceNotFoundException {
614 OSProcess process = getProcess(pid);
615 String name = process.getName();
616 return new StringType(name);
623 public @Nullable PercentType getProcessCpuUsage(int pid) throws DeviceNotFoundException {
625 OSProcess process = getProcess(pid);
626 PercentType load = (processTicks.containsKey(pid))
627 ? new PercentType(getPercentsValue(process.getProcessCpuLoadBetweenTicks(processTicks.get(pid))))
629 processTicks.put(pid, process);
637 public @Nullable DecimalType getProcessMemoryUsage(int pid) throws DeviceNotFoundException {
639 OSProcess process = getProcess(pid);
640 long memortInBytes = process.getResidentSetSize();
641 long memoryInMB = getSizeInMB(memortInBytes);
642 return new DecimalType(memoryInMB);
649 public @Nullable StringType getProcessPath(int pid) throws DeviceNotFoundException {
651 OSProcess process = getProcess(pid);
652 String path = process.getPath();
653 return new StringType(path);
660 public @Nullable DecimalType getProcessThreads(int pid) throws DeviceNotFoundException {
662 OSProcess process = getProcess(pid);
663 int threadCount = process.getThreadCount();
664 return new DecimalType(threadCount);