2 * Copyright (c) 2010-2024 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 javax.measure.quantity.ElectricPotential;
22 import javax.measure.quantity.Temperature;
23 import javax.measure.quantity.Time;
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.openhab.core.library.dimension.DataAmount;
28 import org.openhab.core.library.types.DecimalType;
29 import org.openhab.core.library.types.PercentType;
30 import org.openhab.core.library.types.QuantityType;
31 import org.openhab.core.library.types.StringType;
32 import org.openhab.core.library.unit.SIUnits;
33 import org.openhab.core.library.unit.Units;
34 import org.osgi.service.component.annotations.Component;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 import oshi.SystemInfo;
39 import oshi.hardware.CentralProcessor;
40 import oshi.hardware.ComputerSystem;
41 import oshi.hardware.Display;
42 import oshi.hardware.GlobalMemory;
43 import oshi.hardware.HWDiskStore;
44 import oshi.hardware.HardwareAbstractionLayer;
45 import oshi.hardware.NetworkIF;
46 import oshi.hardware.PowerSource;
47 import oshi.hardware.Sensors;
48 import oshi.software.os.OSFileStore;
49 import oshi.software.os.OSProcess;
50 import oshi.software.os.OperatingSystem;
51 import oshi.util.EdidUtil;
54 * This implementation of {@link SysteminfoInterface} is using the open source library OSHI to provide system
55 * information. OSHI is a free JNA-based (native) Operating System and Hardware Information library for Java.
57 * @author Svilen Valkanov - Initial contribution
58 * @author Lyubomir Papazov - Move the initialization logic that could potentially take long time to the
59 * initializeSysteminfo method
60 * @author Christoph Weitkamp - Update to OSHI 3.13.0 - Replaced deprecated method
61 * CentralProcessor#getSystemSerialNumber()
62 * @author Wouter Born - Update to OSHI 4.0.0 and add null annotations
63 * @author Mark Herwege - Add dynamic creation of extra channels
64 * @author Mark Herwege - Use units of measure
66 * @see <a href="https://github.com/oshi/oshi">OSHI GitHub repository</a>
69 @Component(service = SysteminfoInterface.class)
70 public class OSHISysteminfo implements SysteminfoInterface {
72 private final Logger logger = LoggerFactory.getLogger(OSHISysteminfo.class);
74 private @NonNullByDefault({}) HardwareAbstractionLayer hal;
76 // Dynamic objects (may be queried repeatedly)
77 private @NonNullByDefault({}) GlobalMemory memory;
78 private @NonNullByDefault({}) CentralProcessor cpu;
79 private @NonNullByDefault({}) Sensors sensors;
81 // Static objects, should be recreated on each request
82 private @NonNullByDefault({}) ComputerSystem computerSystem;
83 private @NonNullByDefault({}) OperatingSystem operatingSystem;
84 private @NonNullByDefault({}) List<NetworkIF> networks;
85 private @NonNullByDefault({}) List<Display> displays;
86 private @NonNullByDefault({}) List<OSFileStore> fileStores;
87 private @NonNullByDefault({}) List<PowerSource> powerSources;
88 private @NonNullByDefault({}) List<HWDiskStore> drives;
90 // Array containing cpu tick info to calculate CPU load, according to oshi doc:
91 // 8 long values representing time spent in User, Nice, System, Idle, IOwait, IRQ, SoftIRQ, and Steal states
92 private long[] ticks = new long[8];
93 // Map containing previous process state to calculate load by process
94 private Map<Integer, OSProcess> processTicks = new HashMap<>();
96 public static final int PRECISION_AFTER_DECIMAL_SIGN = 1;
99 * Some of the methods used in this constructor execute native code and require execute permissions
102 public OSHISysteminfo() {
103 logger.debug("OSHISysteminfo service is created");
107 public void initializeSysteminfo() {
108 logger.debug("OSHISysteminfo service starts initializing");
110 SystemInfo systemInfo = new SystemInfo();
111 hal = systemInfo.getHardware();
113 // Doesn't need regular update, they may be queried repeatedly
114 memory = hal.getMemory();
115 cpu = hal.getProcessor();
116 sensors = hal.getSensors();
118 computerSystem = hal.getComputerSystem();
119 operatingSystem = systemInfo.getOperatingSystem();
120 networks = hal.getNetworkIFs();
121 displays = hal.getDisplays();
122 fileStores = operatingSystem.getFileSystem().getFileStores();
123 powerSources = hal.getPowerSources();
124 drives = hal.getDiskStores();
127 private <T> T getDevice(List<@Nullable T> devices, int index) throws DeviceNotFoundException {
128 if (devices.size() <= index) {
129 throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
131 return (T) devices.get(index);
134 private <T> T getDevice(T @Nullable [] devices, int index) throws DeviceNotFoundException {
135 if (devices == null || devices.length <= index) {
136 throw new DeviceNotFoundException("Device with index: " + index + " can not be found!");
138 return devices[index];
141 private OSProcess getProcess(int pid) throws DeviceNotFoundException {
142 OSProcess process = operatingSystem.getProcess(pid);
143 if (process == null) {
144 throw new DeviceNotFoundException("Error while getting information for process with PID " + pid);
150 public StringType getOsFamily() {
151 String osFamily = operatingSystem.getFamily();
152 return new StringType(osFamily);
156 public StringType getOsManufacturer() {
157 String osManufacturer = operatingSystem.getManufacturer();
158 return new StringType(osManufacturer);
162 public StringType getOsVersion() {
163 String osVersion = operatingSystem.getVersionInfo().toString();
164 return new StringType(osVersion);
168 public StringType getCpuName() {
169 String name = cpu.getProcessorIdentifier().getName();
170 return new StringType(name);
174 public StringType getCpuDescription() {
175 String model = cpu.getProcessorIdentifier().getModel();
176 String family = cpu.getProcessorIdentifier().getFamily();
177 String serialNumber = computerSystem.getSerialNumber();
178 String identifier = cpu.getProcessorIdentifier().getIdentifier();
179 String vendor = cpu.getProcessorIdentifier().getVendor();
180 String architecture = cpu.getProcessorIdentifier().isCpu64bit() ? "64 bit" : "32 bit";
181 String descriptionFormatString = "Model: %s %s,family: %s, vendor: %s, sn: %s, identifier: %s ";
182 String description = String.format(descriptionFormatString, model, architecture, family, vendor, serialNumber,
185 return new StringType(description);
189 public DecimalType getCpuLogicalCores() {
190 int logicalProcessorCount = cpu.getLogicalProcessorCount();
191 return new DecimalType(logicalProcessorCount);
195 public DecimalType getCpuPhysicalCores() {
196 int physicalProcessorCount = cpu.getPhysicalProcessorCount();
197 return new DecimalType(physicalProcessorCount);
201 public QuantityType<DataAmount> getMemoryTotal() {
202 long totalMemory = memory.getTotal();
203 totalMemory = getSizeInMB(totalMemory);
204 return new QuantityType<>(totalMemory, Units.MEBIBYTE);
208 public QuantityType<DataAmount> getMemoryAvailable() {
209 long availableMemory = memory.getAvailable();
210 availableMemory = getSizeInMB(availableMemory);
211 return new QuantityType<>(availableMemory, Units.MEBIBYTE);
215 public QuantityType<DataAmount> getMemoryUsed() {
216 long totalMemory = memory.getTotal();
217 long availableMemory = memory.getAvailable();
218 long usedMemory = totalMemory - availableMemory;
219 usedMemory = getSizeInMB(usedMemory);
220 return new QuantityType<>(usedMemory, Units.MEBIBYTE);
224 public QuantityType<DataAmount> getStorageTotal(int index) throws DeviceNotFoundException {
225 OSFileStore fileStore = getDevice(fileStores, index);
226 fileStore.updateAttributes();
227 long totalSpace = fileStore.getTotalSpace();
228 totalSpace = getSizeInMB(totalSpace);
229 return new QuantityType<>(totalSpace, Units.MEBIBYTE);
233 public QuantityType<DataAmount> getStorageAvailable(int index) throws DeviceNotFoundException {
234 OSFileStore fileStore = getDevice(fileStores, index);
235 fileStore.updateAttributes();
236 long freeSpace = fileStore.getUsableSpace();
237 freeSpace = getSizeInMB(freeSpace);
238 return new QuantityType<>(freeSpace, Units.MEBIBYTE);
242 public QuantityType<DataAmount> getStorageUsed(int index) throws DeviceNotFoundException {
243 OSFileStore fileStore = getDevice(fileStores, index);
244 fileStore.updateAttributes();
245 long totalSpace = fileStore.getTotalSpace();
246 long freeSpace = fileStore.getUsableSpace();
247 long usedSpace = totalSpace - freeSpace;
248 usedSpace = getSizeInMB(usedSpace);
249 return new QuantityType<>(usedSpace, Units.MEBIBYTE);
253 public @Nullable PercentType getStorageAvailablePercent(int deviceIndex) throws DeviceNotFoundException {
254 OSFileStore fileStore = getDevice(fileStores, deviceIndex);
255 fileStore.updateAttributes();
256 long totalSpace = fileStore.getTotalSpace();
257 long freeSpace = fileStore.getUsableSpace();
258 if (totalSpace > 0) {
259 double freePercentDecimal = (double) freeSpace / (double) totalSpace;
260 BigDecimal freePercent = getPercentsValue(freePercentDecimal);
261 return new PercentType(freePercent);
268 public @Nullable PercentType getStorageUsedPercent(int deviceIndex) throws DeviceNotFoundException {
269 OSFileStore fileStore = getDevice(fileStores, deviceIndex);
270 fileStore.updateAttributes();
271 long totalSpace = fileStore.getTotalSpace();
272 long freeSpace = fileStore.getUsableSpace();
273 long usedSpace = totalSpace - freeSpace;
274 if (totalSpace > 0) {
275 double usedPercentDecimal = (double) usedSpace / (double) totalSpace;
276 BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
277 return new PercentType(usedPercent);
284 public StringType getStorageName(int index) throws DeviceNotFoundException {
285 OSFileStore fileStore = getDevice(fileStores, index);
286 String name = fileStore.getName();
287 return new StringType(name);
291 public StringType getStorageType(int deviceIndex) throws DeviceNotFoundException {
292 OSFileStore fileStore = getDevice(fileStores, deviceIndex);
293 String type = fileStore.getType();
294 return new StringType(type);
298 public StringType getStorageDescription(int index) throws DeviceNotFoundException {
299 OSFileStore fileStore = getDevice(fileStores, index);
300 String description = fileStore.getDescription();
301 return new StringType(description);
305 public StringType getNetworkIp(int index) throws DeviceNotFoundException {
306 NetworkIF netInterface = getDevice(networks, index);
307 netInterface.updateAttributes();
308 String[] ipAddresses = netInterface.getIPv4addr();
309 String ipv4 = getDevice(ipAddresses, 0);
310 return new StringType(ipv4);
314 public StringType getNetworkName(int index) throws DeviceNotFoundException {
315 NetworkIF netInterface = getDevice(networks, index);
316 String name = netInterface.getName();
317 return new StringType(name);
321 public StringType getNetworkDisplayName(int index) throws DeviceNotFoundException {
322 NetworkIF netInterface = getDevice(networks, index);
323 String adapterName = netInterface.getDisplayName();
324 return new StringType(adapterName);
328 public StringType getDisplayInformation(int index) throws DeviceNotFoundException {
329 Display display = getDevice(displays, index);
331 byte[] edid = display.getEdid();
332 String manufacturer = EdidUtil.getManufacturerID(edid);
333 String product = EdidUtil.getProductID(edid);
334 String serialNumber = EdidUtil.getSerialNo(edid);
335 int width = EdidUtil.getHcm(edid);
336 int height = EdidUtil.getVcm(edid);
338 String edidFormatString = "Product %s, manufacturer %s, SN: %s, Width: %d, Height: %d";
339 String edidInfo = String.format(edidFormatString, product, manufacturer, serialNumber, width, height);
340 return new StringType(edidInfo);
344 public @Nullable QuantityType<Temperature> getSensorsCpuTemperature() {
345 BigDecimal cpuTemp = new BigDecimal(sensors.getCpuTemperature());
346 cpuTemp = cpuTemp.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
347 return cpuTemp.signum() == 1 ? new QuantityType<>(cpuTemp, SIUnits.CELSIUS) : null;
351 public @Nullable QuantityType<ElectricPotential> getSensorsCpuVoltage() {
352 BigDecimal cpuVoltage = new BigDecimal(sensors.getCpuVoltage());
353 cpuVoltage = cpuVoltage.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
354 return cpuVoltage.signum() == 1 ? new QuantityType<>(cpuVoltage, Units.VOLT) : null;
358 public @Nullable DecimalType getSensorsFanSpeed(int index) throws DeviceNotFoundException {
359 int[] fanSpeeds = sensors.getFanSpeeds();
360 int speed = 0; // 0 means unable to measure speed
361 if (index < fanSpeeds.length) {
362 speed = fanSpeeds[index];
364 throw new DeviceNotFoundException();
366 return speed > 0 ? new DecimalType(speed) : null;
370 public @Nullable QuantityType<Time> getBatteryRemainingTime(int index) throws DeviceNotFoundException {
371 PowerSource powerSource = getDevice(powerSources, index);
372 powerSource.updateAttributes();
373 double remainingTimeInSeconds = powerSource.getTimeRemainingEstimated();
374 // The getTimeRemaining() method returns (-1.0) if is calculating or (-2.0) if the time is unlimited.
375 BigDecimal remainingTime = getTimeInMinutes(remainingTimeInSeconds);
376 return remainingTime.signum() == 1 ? new QuantityType<>(remainingTime, Units.MINUTE) : null;
380 public PercentType getBatteryRemainingCapacity(int index) throws DeviceNotFoundException {
381 PowerSource powerSource = getDevice(powerSources, index);
382 powerSource.updateAttributes();
383 double remainingCapacity = powerSource.getRemainingCapacityPercent();
384 BigDecimal remainingCapacityPercents = getPercentsValue(remainingCapacity);
385 return new PercentType(remainingCapacityPercents);
389 public StringType getBatteryName(int index) throws DeviceNotFoundException {
390 PowerSource powerSource = getDevice(powerSources, index);
391 String name = powerSource.getName();
392 return new StringType(name);
396 public @Nullable PercentType getMemoryAvailablePercent() {
397 long availableMemory = memory.getAvailable();
398 long totalMemory = memory.getTotal();
399 if (totalMemory > 0) {
400 double freePercentDecimal = (double) availableMemory / (double) totalMemory;
401 BigDecimal freePercent = getPercentsValue(freePercentDecimal);
402 return new PercentType(freePercent);
409 public @Nullable PercentType getMemoryUsedPercent() {
410 long availableMemory = memory.getAvailable();
411 long totalMemory = memory.getTotal();
412 long usedMemory = totalMemory - availableMemory;
413 if (totalMemory > 0) {
414 double usedPercentDecimal = (double) usedMemory / (double) totalMemory;
415 BigDecimal usedPercent = getPercentsValue(usedPercentDecimal);
416 return new PercentType(usedPercent);
423 public StringType getDriveName(int deviceIndex) throws DeviceNotFoundException {
424 HWDiskStore drive = getDevice(drives, deviceIndex);
425 String name = drive.getName();
426 return new StringType(name);
430 public StringType getDriveModel(int deviceIndex) throws DeviceNotFoundException {
431 HWDiskStore drive = getDevice(drives, deviceIndex);
432 String model = drive.getModel();
433 return new StringType(model);
437 public StringType getDriveSerialNumber(int deviceIndex) throws DeviceNotFoundException {
438 HWDiskStore drive = getDevice(drives, deviceIndex);
439 String serialNumber = drive.getSerial();
440 return new StringType(serialNumber);
444 public QuantityType<DataAmount> getSwapTotal() {
445 long swapTotal = memory.getVirtualMemory().getSwapTotal();
446 swapTotal = getSizeInMB(swapTotal);
447 return new QuantityType<>(swapTotal, Units.MEBIBYTE);
451 public QuantityType<DataAmount> getSwapAvailable() {
452 long swapTotal = memory.getVirtualMemory().getSwapTotal();
453 long swapUsed = memory.getVirtualMemory().getSwapUsed();
454 long swapAvailable = swapTotal - swapUsed;
455 swapAvailable = getSizeInMB(swapAvailable);
456 return new QuantityType<>(swapAvailable, Units.MEBIBYTE);
460 public QuantityType<DataAmount> getSwapUsed() {
461 long swapUsed = memory.getVirtualMemory().getSwapUsed();
462 swapUsed = getSizeInMB(swapUsed);
463 return new QuantityType<>(swapUsed, Units.MEBIBYTE);
467 public @Nullable PercentType getSwapAvailablePercent() {
468 long swapTotal = memory.getVirtualMemory().getSwapTotal();
469 long swapUsed = memory.getVirtualMemory().getSwapUsed();
470 long swapAvailable = swapTotal - swapUsed;
472 double swapAvailablePercentDecimal = (double) swapAvailable / (double) swapTotal;
473 BigDecimal swapAvailablePercent = getPercentsValue(swapAvailablePercentDecimal);
474 return new PercentType(swapAvailablePercent);
481 public @Nullable PercentType getSwapUsedPercent() {
482 long swapTotal = memory.getVirtualMemory().getSwapTotal();
483 long swapUsed = memory.getVirtualMemory().getSwapUsed();
485 double swapUsedPercentDecimal = (double) swapUsed / (double) swapTotal;
486 BigDecimal swapUsedPercent = getPercentsValue(swapUsedPercentDecimal);
487 return new PercentType(swapUsedPercent);
493 private long getSizeInMB(long sizeInBytes) {
494 return Math.round(sizeInBytes / (1024D * 1024));
497 private BigDecimal getPercentsValue(double decimalFraction) {
498 BigDecimal result = new BigDecimal(decimalFraction * 100);
499 result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
503 private BigDecimal getTimeInMinutes(double timeInSeconds) {
504 BigDecimal timeInMinutes = new BigDecimal(timeInSeconds / 60);
505 timeInMinutes = timeInMinutes.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.UP);
506 return timeInMinutes;
510 public @Nullable PercentType getSystemCpuLoad() {
511 PercentType load = (ticks[0] > 0) ? new PercentType(getPercentsValue(cpu.getSystemCpuLoadBetweenTicks(ticks)))
513 ticks = cpu.getSystemCpuLoadTicks();
520 * This information is available only on Mac and Linux OS.
523 public @Nullable DecimalType getCpuLoad1() {
524 BigDecimal avarageCpuLoad = getAvarageCpuLoad(1);
525 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
531 * This information is available only on Mac and Linux OS.
534 public @Nullable DecimalType getCpuLoad5() {
535 BigDecimal avarageCpuLoad = getAvarageCpuLoad(5);
536 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
542 * This information is available only on Mac and Linux OS.
545 public @Nullable DecimalType getCpuLoad15() {
546 BigDecimal avarageCpuLoad = getAvarageCpuLoad(15);
547 return avarageCpuLoad.signum() == -1 ? null : new DecimalType(avarageCpuLoad);
550 private BigDecimal getAvarageCpuLoad(int timeInMinutes) {
551 // This parameter is specified in OSHI Javadoc
553 switch (timeInMinutes) {
566 double processorLoads[] = cpu.getSystemLoadAverage(index + 1);
567 BigDecimal result = new BigDecimal(processorLoads[index]);
568 result = result.setScale(PRECISION_AFTER_DECIMAL_SIGN, RoundingMode.HALF_UP);
573 public QuantityType<Time> getCpuUptime() {
574 long seconds = operatingSystem.getSystemUptime();
575 return new QuantityType<>(getTimeInMinutes(seconds), Units.MINUTE);
579 public DecimalType getCpuThreads() {
580 int threadCount = operatingSystem.getThreadCount();
581 return new DecimalType(threadCount);
585 public StringType getNetworkMac(int networkIndex) throws DeviceNotFoundException {
586 NetworkIF network = getDevice(networks, networkIndex);
587 String mac = network.getMacaddr();
588 return new StringType(mac);
592 public DecimalType getNetworkPacketsReceived(int networkIndex) throws DeviceNotFoundException {
593 NetworkIF network = getDevice(networks, networkIndex);
594 network.updateAttributes();
595 long packRecv = network.getPacketsRecv();
596 return new DecimalType(packRecv);
600 public DecimalType getNetworkPacketsSent(int networkIndex) throws DeviceNotFoundException {
601 NetworkIF network = getDevice(networks, networkIndex);
602 network.updateAttributes();
603 long packSent = network.getPacketsSent();
604 return new DecimalType(packSent);
608 public QuantityType<DataAmount> getNetworkDataSent(int networkIndex) throws DeviceNotFoundException {
609 NetworkIF network = getDevice(networks, networkIndex);
610 network.updateAttributes();
611 long bytesSent = network.getBytesSent();
612 return new QuantityType<>(getSizeInMB(bytesSent), Units.MEBIBYTE);
616 public QuantityType<DataAmount> getNetworkDataReceived(int networkIndex) throws DeviceNotFoundException {
617 NetworkIF network = getDevice(networks, networkIndex);
618 network.updateAttributes();
619 long bytesRecv = network.getBytesRecv();
620 return new QuantityType<>(getSizeInMB(bytesRecv), Units.MEBIBYTE);
624 public int getCurrentProcessID() {
625 return operatingSystem.getProcessId();
629 public @Nullable StringType getProcessName(int pid) throws DeviceNotFoundException {
631 OSProcess process = getProcess(pid);
632 String name = process.getName();
633 return new StringType(name);
640 public @Nullable DecimalType getProcessCpuUsage(int pid) throws DeviceNotFoundException {
642 OSProcess process = getProcess(pid);
643 DecimalType load = (processTicks.containsKey(pid))
644 ? new DecimalType(getPercentsValue(process.getProcessCpuLoadBetweenTicks(processTicks.get(pid))))
646 processTicks.put(pid, process);
654 public @Nullable QuantityType<DataAmount> getProcessMemoryUsage(int pid) throws DeviceNotFoundException {
656 OSProcess process = getProcess(pid);
657 long memortInBytes = process.getResidentSetSize();
658 long memoryInMB = getSizeInMB(memortInBytes);
659 return new QuantityType<>(memoryInMB, Units.MEBIBYTE);
666 public @Nullable StringType getProcessPath(int pid) throws DeviceNotFoundException {
668 OSProcess process = getProcess(pid);
669 String path = process.getPath();
670 return new StringType(path);
677 public @Nullable DecimalType getProcessThreads(int pid) throws DeviceNotFoundException {
679 OSProcess process = getProcess(pid);
680 int threadCount = process.getThreadCount();
681 return new DecimalType(threadCount);
688 public int getNetworkIFCount() {
689 return networks.size();
693 public int getDisplayCount() {
694 return displays.size();
698 public int getFileOSStoreCount() {
699 return fileStores.size();
703 public int getPowerSourceCount() {
704 return powerSources.size();
708 public int getDriveCount() {
709 return drives.size();
713 public int getFanCount() {
714 return sensors.getFanSpeeds().length;