Skip to content

Commit

Permalink
add cpu frequency
Browse files Browse the repository at this point in the history
Signed-off-by: Mark Herwege <[email protected]>
  • Loading branch information
mherwege committed Dec 6, 2023
1 parent a850df1 commit 982b0c8
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 9 deletions.
25 changes: 16 additions & 9 deletions bundles/org.openhab.binding.systeminfo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
The system information binding provides operating system and hardware information including:

- Operating system name, version and manufacturer;
- CPU average load for last 1, 5, 15 minutes, name, description, number of physical and logical cores, running threads number, system uptime;
- CPU average load for last 1, 5, 15 minutes, name, description, number of physical and logical cores, running threads number, system uptime, max frequency and frequency by logical core;
- Free, total and available memory;
- Free, total and available swap memory;
- Hard drive name, model and serial number;
Expand Down Expand Up @@ -76,9 +76,9 @@ In the list below, you can find, how are channel group and channels id`s related
- **group** `battery` (deviceIndex)
- **channel** `name, remainingCapacity, remainingTime`
- **group** `cpu`
- **channel** `name, description, load, load1, load5, load15, uptime, threads`
- **channel** `name, description, maxfreq, freq `(deviceIndex)`, load, load1, load5, load15, uptime, threads`
- **group** `sensors`
- **channel** `cpuTemp, cpuVoltage, fanSpeed`
- **channel** `cpuTemp, cpuVoltage, fanSpeed `(deviceIndex)
- **group** `network` (deviceIndex)
- **channel** `ip, mac, networkDisplayName, networkName, packetsSent, packetsReceived, dataSent, dataReceived`
- **group** `currentProcess`
Expand All @@ -92,7 +92,7 @@ The groups marked with "(deviceIndex)" may have device index attached to the Cha
- deviceIndex ::= number >= 0
- (e.g. _storage1#available_)

The `fanSpeed` channel in the `sensors` group may have a device index attached to the Channel.
The channels marked with "(deviceIndex)" may have a device index attached to the Channel.

- channel ::= channel_group & # channel_id & (deviceIndex)
- deviceIndex ::= number >= 0
Expand All @@ -119,6 +119,8 @@ The binding introduces the following channels:
| load5 | Load for the last 5 minutes | Number | Medium | True |
| load15 | Load for the last 15 minutes | Number | Medium | True |
| threads | Number of threads currently running or for the process | Number | Medium | True |
| maxfreq | CPU maximum frequency | Number:Frequency | Low | True |
| freq | Logical processor frequency | Number:Frequency | High | True |
| path | The full path of the process | String | Low | False |
| uptime | System uptime (time after start) in minutes | Number:Time | Medium | True |
| name | Name of the device or process | String | Low | False |
Expand Down Expand Up @@ -172,16 +174,17 @@ Parameter PID has a default value 0 - this is the PID of the System Idle process
## Known issues and workarounds

- Temperature readings are not well supported on standard Windows systems, run [OpenHardwareMonitor.exe](https://openhardwaremonitor.org) for the binding to get more reliable readings.
- CPU frequency readings are not available on some OS versions.

## Reporting issues

As already mentioned this binding depends heavily on the [OSHI](https://github.com/oshi/oshi) API to provide the operating system and hardware information.

Take a look at the console for an ERROR log message.

If you find an issue with a support for a specific hardware or software architecture please take a look at the [OSHI issues](https://github.com/oshi/oshi/issues).
If you find an issue with support for a specific hardware or software architecture please take a look at the [OSHI issues](https://github.com/oshi/oshi/issues).
Your problem might have be already reported and solved!
Feel free to open a new issue there with the log message and the and information about your software or hardware configuration.
Feel free to open a new issue there with the log message and the information about your software or hardware configuration.

For a general problem with the binding report the issue directly to openHAB.

Expand Down Expand Up @@ -209,6 +212,8 @@ Number Network_PacketsReceived "Packets received" <returnpipe> { chann
/* CPU information*/
String CPU_Name "Name" <none> { channel="systeminfo:computer:work:cpu#name" }
String CPU_Description "Description" <none> { channel="systeminfo:computer:work:cpu#description" }
Number:Frequency CPU_MaxFreq "CPU Max Frequency" <none> { channel="systeminfo:computer:work:cpu#maxfreq" }
Number:Frequency CPU_Freq "CPU Frequency" <none> { channel="systeminfo:computer:work:cpu#freq" }
Number:Dimensionless CPU_Load "CPU Load" <none> { channel="systeminfo:computer:work:cpu#load" }
Number CPU_Load1 "Load (1 min)" <none> { channel="systeminfo:computer:work:cpu#load1" }
Number CPU_Load5 "Load (5 min)" <none> { channel="systeminfo:computer:work:cpu#load5" }
Expand All @@ -232,7 +237,7 @@ Number:Dimensionless Storage_Available_Percent "Available (%)" <none> { chann
Number:Dimensionless Storage_Used_Percent "Used (%)" <none> { channel="systeminfo:computer:work:storage#usedPercent" }

/* Memory information*/
Number Memory_Available "Available" <none> { channel="systeminfo:computer:work:memory#available" }
Number:DataAmount Memory_Available "Available" <none> { channel="systeminfo:computer:work:memory#available" }
Number:DataAmount Memory_Used "Used" <none> { channel="systeminfo:computer:work:memory#used" }
Number:DataAmount Memory_Total "Total" <none> { channel="systeminfo:computer:work:memory#total" }
Number:Dimensionless Memory_Available_Percent "Available (%)" <none> { channel="systeminfo:computer:work:memory#availablePercent" }
Expand Down Expand Up @@ -260,14 +265,14 @@ Number Sensor_FanSpeed "Fan speed" <fan> { chann

/* Current process information*/
Number:Dimensionless Current_process_load "Load" <none> { channel="systeminfo:computer:work:currentProcess#load" }
Number:Dimensionless Current_process_used "Used" <none> { channel="systeminfo:computer:work:currentProcess#used" }
Number:DataAmount Current_process_used "Used" <none> { channel="systeminfo:computer:work:currentProcess#used" }
String Current_process_name "Name" <none> { channel="systeminfo:computer:work:currentProcess#name" }
Number Current_process_threads "Threads" <none> { channel="systeminfo:computer:work:currentProcess#threads" }
String Current_process_path "Path" <none> { channel="systeminfo:computer:work:currentProcess#path" }

/* Process information*/
Number:Dimensionless Process_load "Load" <none> { channel="systeminfo:computer:work:process#load" }
Number:Dimensionless Process_used "Used" <none> { channel="systeminfo:computer:work:process#used" }
Number:DataAmount Process_used "Used" <none> { channel="systeminfo:computer:work:process#used" }
String Process_name "Name" <none> { channel="systeminfo:computer:work:process#name" }
Number Process_threads "Threads" <none> { channel="systeminfo:computer:work:process#threads" }
String Process_path "Path" <none> { channel="systeminfo:computer:work:process#path" }
Expand All @@ -290,6 +295,8 @@ sitemap systeminfo label="Systeminfo" {
Frame label="CPU Information" {
Default item=CPU_Name
Default item=CPU_Description
Default item=CPU_MaxFreq
Default item=CPU_Freq
Default item=CPU_Load1
Default item=CPU_Load5
Default item=CPU_Load15
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*
* @author Svilen Valkanov - Initial contribution
* @author Mark Herwege - Add dynamic creation of extra channels
* @author Mark Herwege - Processor frequency channels
*/
@NonNullByDefault
public class SysteminfoBindingConstants {
Expand Down Expand Up @@ -278,6 +279,16 @@ public class SysteminfoBindingConstants {
*/
public static final String CHANNEL_CPU_DESCRIPTION = "cpu#description";

/**
* Maximum frequency of the CPU
*/
public static final String CHANNEL_CPU_MAXFREQ = "cpu#maxfreq";

/**
* Maximum frequency of the CPU
*/
public static final String CHANNEL_CPU_FREQ = "cpu#freq";

/**
* Average recent CPU load
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,15 @@ public List<ChannelGroupDefinition> getChannelGroupDefinitions(ThingTypeUID type
ChannelBuilder builder = ChannelBuilder.create(channelUID).withType(channelTypeUID)
.withConfiguration(baseChannel.getConfiguration());
builder.withLabel(channelType.getLabel() + " " + index);
builder.withDefaultTags(channelType.getTags());
String description = channelType.getDescription();
if (description != null) {
builder.withDescription(description);
}
String itemType = channelType.getItemType();
if (itemType != null) {
builder.withAcceptedItemType(itemType);
}
return builder.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
* @author Lyubomir Papzov - Separate the creation of the systeminfo object and its initialization
* @author Wouter Born - Add null annotations
* @author Mark Herwege - Add dynamic creation of extra channels
* @author Mark Herwege - Processor frequency channels
*/
@NonNullByDefault
public class SysteminfoHandler extends BaseThingHandler {
Expand Down Expand Up @@ -258,6 +259,7 @@ private boolean addDynamicChannels() {

List<Channel> newChannels = new ArrayList<>();
newChannels.addAll(createChannels(thingUID, CHANNEL_SENSORS_FAN_SPEED, systeminfo.getFanCount()));
newChannels.addAll(createChannels(thingUID, CHANNEL_CPU_FREQ, systeminfo.getLogicalProcessorCount()));
if (!newChannels.isEmpty()) {
logger.debug("Creating additional channels");
newChannels.addAll(0, thing.getChannels());
Expand Down Expand Up @@ -482,6 +484,12 @@ private State getInfoForChannel(ChannelUID channelUID) {
case CHANNEL_SENSORS_FAN_SPEED:
state = systeminfo.getSensorsFanSpeed(deviceIndex);
break;
case CHANNEL_CPU_MAXFREQ:
state = systeminfo.getCpuMaxFreq();
break;
case CHANNEL_CPU_FREQ:
state = systeminfo.getCpuFreq(deviceIndex);
break;
case CHANNEL_CPU_LOAD:
PercentType cpuLoad = cpuLoadCache.getValue();
state = (cpuLoad != null) ? new QuantityType<>(cpuLoad, Units.PERCENT) : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.Map;

import javax.measure.quantity.ElectricPotential;
import javax.measure.quantity.Frequency;
import javax.measure.quantity.Temperature;
import javax.measure.quantity.Time;

Expand Down Expand Up @@ -197,6 +198,18 @@ public DecimalType getCpuPhysicalCores() {
return new DecimalType(physicalProcessorCount);
}

@Override
public @Nullable QuantityType<Frequency> getCpuMaxFreq() {
long maxFreq = cpu.getMaxFreq();
return maxFreq >= 0 ? new QuantityType<>(maxFreq, Units.HERTZ) : null;
}

@Override
public @Nullable QuantityType<Frequency> getCpuFreq(int logicalProcessorIndex) {
long freq = cpu.getCurrentFreq()[logicalProcessorIndex];
return freq >= 0 ? new QuantityType<>(freq, Units.HERTZ) : null;
}

@Override
public QuantityType<DataAmount> getMemoryTotal() {
long totalMemory = memory.getTotal();
Expand Down Expand Up @@ -713,4 +726,9 @@ public int getDriveCount() {
public int getFanCount() {
return sensors.getFanSpeeds().length;
}

@Override
public int getLogicalProcessorCount() {
return cpu.getLogicalProcessorCount();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package org.openhab.binding.systeminfo.internal.model;

import javax.measure.quantity.ElectricPotential;
import javax.measure.quantity.Frequency;
import javax.measure.quantity.Temperature;
import javax.measure.quantity.Time;

Expand All @@ -31,6 +32,7 @@
* @author Wouter Born - Add null annotations
* @author Mark Herwege - Add dynamic creation of extra channels
* @author Mark Herwege - Use units of measure
* @author Mark Herwege - Processor frequency channels
*/
@NonNullByDefault
public interface SysteminfoInterface {
Expand Down Expand Up @@ -80,6 +82,18 @@ public interface SysteminfoInterface {
*/
DecimalType getCpuPhysicalCores();

/**
* Get the maximum CPU frequency of the processor.
*/
@Nullable
QuantityType<Frequency> getCpuMaxFreq();

/**
* Get the current CPU frequency of a logical processor.
*/
@Nullable
QuantityType<Frequency> getCpuFreq(int logicalProcessorIndex);

/**
* Returns the system cpu load.
*
Expand Down Expand Up @@ -521,4 +535,11 @@ public interface SysteminfoInterface {
* @return fan count
*/
int getFanCount();

/**
* Returns the number of logical processors.
*
* @return logical processor count
*/
int getLogicalProcessorCount();
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@
<channels>
<channel id="name" typeId="name"/>
<channel id="description" typeId="description"/>
<channel id="maxfreq" typeId="maxfreq"/>
<channel id="freq" typeId="freq"/>
<channel id="load" typeId="load"/>
<channel id="load1" typeId="loadAverage"/>
<channel id="load5" typeId="loadAverage"/>
Expand Down Expand Up @@ -308,6 +310,22 @@
<config-description-ref uri="channel-type:systeminfo:mediumpriority"/>
</channel-type>

<channel-type id="maxfreq" advanced="true">
<item-type>Number:Frequency</item-type>
<label>Maximum Frequency</label>
<description>CPU maximum frequency</description>
<state readOnly="true" pattern="%.0f MHz"/>
<config-description-ref uri="channel-type:systeminfo:lowpriority"/>
</channel-type>

<channel-type id="freq" advanced="true">
<item-type>Number:Frequency</item-type>
<label>Current Frequency</label>
<description>Logical processor frequency</description>
<state readOnly="true" pattern="%.0f MHz"/>
<config-description-ref uri="channel-type:systeminfo:highpriority"/>
</channel-type>

<channel-type id="load">
<item-type>Number:Dimensionless</item-type>
<label>Load</label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
</channel-groups>

<properties>
<property name="thingTypeVersion">1</property>

<property name="CPU Logical Cores">Not available</property>
<property name="CPU Physical Cores">Not available</property>
<property name="OS Manufacturer">Not available</property>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<update:update-descriptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:update="https://openhab.org/schemas/update-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/update-description/v1.0.0 https://openhab.org/schemas/update-description-1.0.0.xsd">

<thing-type uid="systeminfo:computer">
<instruction-set targetVersion="1">
<add-channel id="maxFreq" groupIds="cpu">
<type>systeminfo:maxfreq</type>
</add-channel>
<add-channel id="freq" groupIds="cpu">
<type>systeminfo:freq</type>
</add-channel>
</instruction-set>
</thing-type>

</update:update-descriptions>
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.List;

import javax.measure.quantity.ElectricPotential;
import javax.measure.quantity.Frequency;
import javax.measure.quantity.Temperature;
import javax.measure.quantity.Time;

Expand Down Expand Up @@ -399,6 +400,30 @@ public void assertStateOfSecondDeviceIsUpdated() {
assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, UnDefType.UNDEF);
}

@Test
public void assertChannelCpuMaxFreq() {
String channnelID = SysteminfoBindingConstants.CHANNEL_CPU_MAXFREQ;
String acceptedItemType = "Number:Frequency";

QuantityType<Frequency> mockedCpuMaxFreqValue = new QuantityType<>(2500, Units.HERTZ);
when(mockedSystemInfo.getCpuMaxFreq()).thenReturn(mockedCpuMaxFreqValue);

initializeThingWithChannel(channnelID, acceptedItemType);
assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuMaxFreqValue);
}

@Test
public void assertChannelCpuFreq() {
String channnelID = SysteminfoBindingConstants.CHANNEL_CPU_FREQ;
String acceptedItemType = "Number:Frequency";

QuantityType<Frequency> mockedCpuFreqValue = new QuantityType<>(2500, Units.HERTZ);
when(mockedSystemInfo.getCpuFreq(0)).thenReturn(mockedCpuFreqValue);

initializeThingWithChannel(channnelID, acceptedItemType);
assertItemState(acceptedItemType, DEFAULT_TEST_ITEM_NAME, DEFAULT_CHANNEL_TEST_PRIORITY, mockedCpuFreqValue);
}

@Test
public void assertChannelCpuLoadIsUpdated() {
String channnelID = SysteminfoBindingConstants.CHANNEL_CPU_LOAD;
Expand Down

0 comments on commit 982b0c8

Please sign in to comment.