Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
46046eb
Really just copy/paste from feature request
MitchellThompkins Dec 20, 2025
d8b5389
modified these files a little bit to check for install
MitchellThompkins Dec 22, 2025
89356bc
copy/paste some more functions and do some minor clean-up
MitchellThompkins Dec 23, 2025
a878c47
add temperature module
MitchellThompkins Dec 23, 2025
b288994
okay, I'm new to typescript and don't know what I'm doing yet but get…
MitchellThompkins Dec 23, 2025
3ac98bc
starting to actually build this out
MitchellThompkins Dec 26, 2025
b867834
fix typescript typing issues
MitchellThompkins Dec 26, 2025
45b3816
I don't expect the tests to pass but pnpm exec tsc --noEmit is happy …
MitchellThompkins Dec 27, 2025
702ae9c
apparently this needs to be injectable for Nest?
MitchellThompkins Dec 28, 2025
5a5d3d9
deconflict graphql name for now
MitchellThompkins Dec 28, 2025
694cf68
Deconflict graphql names
MitchellThompkins Dec 28, 2025
644b56c
hardcode this file for now
MitchellThompkins Dec 28, 2025
0063a8a
try a new parsing method
MitchellThompkins Dec 28, 2025
a299d66
hey, this actually returns data to the graphql query
MitchellThompkins Dec 29, 2025
88719fd
Parsing json is a lot more reliable than stupid regex
MitchellThompkins Dec 29, 2025
0a2d6be
Add some simple cacheing
MitchellThompkins Dec 31, 2025
8fc82c3
update this to use lm-sensors class
MitchellThompkins Jan 1, 2026
fe82be0
Need to pass LMSensorsObject For NodeJS
MitchellThompkins Jan 3, 2026
35b5e24
Need to pass LMSensorsObject For NodeJS
MitchellThompkins Jan 3, 2026
db915a1
oops I forgot to check this in
MitchellThompkins Jan 3, 2026
9b350ca
idk what I was doing before but this actually passes in the LmService…
MitchellThompkins Jan 3, 2026
33150ec
Make this a bit more extensible
MitchellThompkins Jan 3, 2026
7c9717f
rename this file b/c I don't like underscores
MitchellThompkins Jan 3, 2026
f576460
Add a disk sensor service as a wrapper
MitchellThompkins Jan 3, 2026
8736b0d
claude tells me this needs to be an export as well
MitchellThompkins Jan 4, 2026
69493bd
First pass at adding history
MitchellThompkins Jan 8, 2026
ab7da0c
Forgot to add this to the service
MitchellThompkins Jan 8, 2026
61a054d
try to add basic config service
MitchellThompkins Jan 10, 2026
efd0e1f
add this config service control
MitchellThompkins Jan 11, 2026
dcefb98
adding unit tests
MitchellThompkins Jan 11, 2026
2901d2d
update tests
MitchellThompkins Jan 14, 2026
19441e4
update unit tests
MitchellThompkins Jan 19, 2026
6345e75
fixing unit tests
MitchellThompkins Jan 19, 2026
71ab7e2
more unit test fixes
MitchellThompkins Jan 19, 2026
7c4f662
okay these tests actually pass now!
MitchellThompkins Jan 19, 2026
281dcb1
Add config to check if temperature feature is enabled
MitchellThompkins Jan 19, 2026
9071522
Remove duplicate import lol
MitchellThompkins Jan 19, 2026
686cc81
trying to get api to accept config file
MitchellThompkins Jan 20, 2026
097f768
add some debug info
MitchellThompkins Jan 20, 2026
02f6718
hey the config actually loads now!
MitchellThompkins Jan 20, 2026
731e8f8
fix tests after I figured out how to read config file
MitchellThompkins Jan 20, 2026
f1a1e24
update api-config
MitchellThompkins Jan 24, 2026
d9b80a4
update with new providers
MitchellThompkins Jan 24, 2026
8f9dd51
comments
MitchellThompkins Jan 24, 2026
63722cd
fix unit tests for lm_sensors
MitchellThompkins Jan 25, 2026
8861a82
implement remaining todos
MitchellThompkins Feb 1, 2026
fa1354b
forgot to check this in
MitchellThompkins Feb 1, 2026
9196778
parse json instead of strings, because some drives return an unexpect…
MitchellThompkins Feb 1, 2026
f21e1aa
fixed this diskservice unit test
MitchellThompkins Feb 1, 2026
a21c31c
add ipmi config
MitchellThompkins Feb 1, 2026
2f7a26a
slight update
MitchellThompkins Feb 1, 2026
7b49f8b
remove AI lies
MitchellThompkins Feb 1, 2026
01644ce
handle more temperature types
MitchellThompkins Feb 1, 2026
6fbad30
add more silly temperatures
MitchellThompkins Feb 1, 2026
d855946
I forgot to check in this file!
MitchellThompkins Feb 1, 2026
5c94746
add default api.json for development
MitchellThompkins Feb 1, 2026
5e18bfb
allow thresholds to use target units
MitchellThompkins Feb 1, 2026
52e5b32
need to include the original dev api content
MitchellThompkins Feb 1, 2026
fd81810
remove incorrect comment
MitchellThompkins Feb 1, 2026
f9cad0d
Handle user defined temperature thresholds and typo clean-up
MitchellThompkins Feb 1, 2026
9e2abc1
update docs
MitchellThompkins Feb 1, 2026
5856142
revert the default api sandbox
MitchellThompkins Feb 1, 2026
f735e9b
revert this change
MitchellThompkins Feb 1, 2026
bc6c1c5
add lm_sensor timeout
MitchellThompkins Feb 2, 2026
60776ac
make threshold configuration more robust
MitchellThompkins Feb 2, 2026
57529ea
do some slightly safer handling of Record objects
MitchellThompkins Feb 2, 2026
1b74793
filter out NaN, Inf, and -Inf. Add tests for it.
MitchellThompkins Feb 2, 2026
3a65f63
remove unused variable
MitchellThompkins Feb 2, 2026
00820c0
remove unused variable
MitchellThompkins Feb 2, 2026
cb6a1a9
documentation update
MitchellThompkins Feb 2, 2026
41b6432
Add checks for 0 deg C and update unit tests to reflect that. Also ad…
MitchellThompkins Feb 2, 2026
341754a
do not use any
MitchellThompkins Feb 2, 2026
4c3de3d
do not use any in lm_sensors.service.spec.ts
MitchellThompkins Feb 2, 2026
7cae963
I let AI try to correct other useages of any, but need to test it first
MitchellThompkins Feb 2, 2026
7ac9641
The great any purge from tests
MitchellThompkins Feb 2, 2026
1ad71c7
Remove usage of any in temperature.service.spec.ts
MitchellThompkins Feb 2, 2026
af73fa5
rip out more usage of any
MitchellThompkins Feb 2, 2026
9cfd14d
do not use non-null operator in this way
MitchellThompkins Feb 2, 2026
805020d
get rid of more usages of any
MitchellThompkins Feb 2, 2026
0fe051d
copy timeout pattern for ipmi (again, I have not been able to actuall…
MitchellThompkins Feb 2, 2026
27a04af
I let gemini churn on the type errors in the unit tests and it did up…
MitchellThompkins Feb 2, 2026
749d9c8
add unit tests for impi sensors
MitchellThompkins Feb 2, 2026
ab13466
Handle null-able payload and update unit tests
MitchellThompkins Feb 2, 2026
6feddff
removed misleading comment
MitchellThompkins Feb 2, 2026
ce9d0cb
remove extra comments
MitchellThompkins Feb 2, 2026
f8ecc23
fix some minor typing issues. I think non-null is fine here
MitchellThompkins Feb 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ For detailed information about specific features:
- [Feature Flags](docs/developer/feature-flags.md) - Conditionally enabling functionality
- [Repository Organization](docs/developer/repo-organization.md) - Codebase structure
- [Development Workflows](docs/developer/workflows.md) - Development processes
- [Temperature Monitoring](docs/developer/temperature.md) - Configuration and API details for temperature sensors
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure where to put these docs, so I put it here for now. Let me know if that should change.


## License

Expand Down
30 changes: 28 additions & 2 deletions api/dev/configs/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,31 @@
"ssoSubIds": [],
"plugins": [
"unraid-api-plugin-connect"
]
}
],
"temperature": {
"enabled": true,
"polling_interval": 5000,
"default_unit": "celsius",
"history": {
"max_readings": 100,
"retention_ms": 86400000
},
"thresholds": {
"cpu_warning": 70,
"cpu_critical": 85,
"disk_warning": 50,
"disk_critical": 60
},
"sensors": {
"lm_sensors": {
"enabled": true
},
"smartctl": {
"enabled": true
},
"ipmi": {
"enabled": true
}
}
}
}
141 changes: 141 additions & 0 deletions api/docs/developer/temperature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Temperature Monitoring

The Temperature Monitoring feature allows the Unraid API to collect and expose temperature metrics from various sensors (CPU, Disks, Motherboard, etc.).

## Configuration

You can configure the temperature monitoring behavior in your `api.json`.
Nominally the `api.json` file is found at
`/boot/config/plugins/dynamix.my.servers/configs/`.

### `api.temperature` Object

| Key | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `enabled` | `boolean` | `true` | Globally enable or disable temperature monitoring. |
| `default_unit` | `string` | `"celsius"` | The unit to return values in. Options: `"celsius"`, `"fahrenheit"`, `"kelvin"`, `"rankine"`. |
| `polling_interval` | `number` | `5000` | Polling interval in milliseconds for the subscription. |
| `history.max_readings` | `number` | `1000` | (Internal) Number of historical data points to keep in memory per sensor. |
| `history.retention_ms` | `number` | `86400000` | (Internal) Retention period for historical data in milliseconds. |

### `api.temperature.sensors` Object

Enable or disable specific sensor providers.

| Key | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `lm_sensors.enabled` | `boolean` | `true` | Enable `lm-sensors` provider (requires `sensors` binary). |
| `lm_sensors.config_path` | `string` | `null` | Optional path to a specific sensors config file (passed as `-c` to `sensors`). |
| `smartctl.enabled` | `boolean` | `true` | Enable disk temperature monitoring via `smartctl` (via DiskService). |
| `ipmi.enabled` | `boolean` | `true` | Enable IPMI sensor provider (requires `ipmitool`). |

### `api.temperature.thresholds` Object

Customize warning and critical thresholds.

| Key | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `cpu_warning` | `number` | `70` | Warning threshold for CPU. |
| `cpu_critical` | `number` | `85` | Critical threshold for CPU. |
| `disk_warning` | `number` | `50` | Warning threshold for Disks. |
| `disk_critical` | `number` | `60` | Critical threshold for Disks. |

### Sample Configuration

Example of an `api.json` configuration file:

```json
{
"version": "4.28.2+9196778e",
"extraOrigins": [],
"sandbox": true,
"ssoSubIds": [],
"plugins": [
"unraid-api-plugin-connect"
],
"temperature": {
"enabled": true,
"polling_interval": 10000,
"default_unit": "celsius",
"history": {
"max_readings": 144,
"retention_ms": 86400000
},
"thresholds": {
"cpu_warning": 75,
"cpu_critical": 90,
"disk_warning": 50,
"disk_critical": 60
},
"sensors": {
"lm_sensors": {
"enabled": true,
"config_path": "/etc/sensors3.conf"
},
"smartctl": {
"enabled": true
},
"ipmi": {
"enabled": false
}
}
}
}
```

## GraphQL API

### Query: `metrics` -> `temperature`

Returns a snapshot of the current temperature metrics.

```graphql
query {
metrics {
temperature {
id
summary {
average
hottest {
name
current { value unit }
}
}
sensors {
id
name
type
current {
value
unit
status
}
history {
value
timestamp
}
}
}
}
}
```

### Subscription: `systemMetricsTemperature`

Subscribes to temperature updates (pushed at `polling_interval`).

```graphql
subscription {
systemMetricsTemperature {
summary {
average
}
sensors {
name
current {
value
}
}
}
}
```
29 changes: 29 additions & 0 deletions api/src/unraid-api/cli/generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,17 @@ export type CpuLoad = {
percentUser: Scalars['Float']['output'];
};

export type CpuPackages = Node & {
__typename?: 'CpuPackages';
id: Scalars['PrefixedID']['output'];
/** Power draw per package (W) */
power: Array<Scalars['Float']['output']>;
/** Temperature per package (°C) */
temp: Array<Scalars['Float']['output']>;
/** Total CPU package power draw (W) */
totalPower: Scalars['Float']['output'];
};

export type CpuUtilization = Node & {
__typename?: 'CpuUtilization';
/** CPU load for each core */
Expand Down Expand Up @@ -591,6 +602,19 @@ export type Customization = {
theme: Theme;
};

/** Customization related mutations */
export type CustomizationMutations = {
__typename?: 'CustomizationMutations';
/** Update the UI theme (writes dynamix.cfg) */
setTheme: Theme;
};


/** Customization related mutations */
export type CustomizationMutationsSetThemeArgs = {
theme: ThemeName;
};

export type DeleteApiKeyInput = {
ids: Array<Scalars['PrefixedID']['input']>;
};
Expand Down Expand Up @@ -1065,6 +1089,7 @@ export type InfoCpu = Node & {
manufacturer?: Maybe<Scalars['String']['output']>;
/** CPU model */
model?: Maybe<Scalars['String']['output']>;
packages: CpuPackages;
/** Number of physical processors */
processors?: Maybe<Scalars['Int']['output']>;
/** CPU revision */
Expand All @@ -1081,6 +1106,8 @@ export type InfoCpu = Node & {
stepping?: Maybe<Scalars['Int']['output']>;
/** Number of CPU threads */
threads?: Maybe<Scalars['Int']['output']>;
/** Per-package array of core/thread pairs, e.g. [[[0,1],[2,3]], [[4,5],[6,7]]] */
topology: Array<Array<Array<Scalars['Int']['output']>>>;
/** CPU vendor */
vendor?: Maybe<Scalars['String']['output']>;
/** CPU voltage */
Expand Down Expand Up @@ -1422,6 +1449,7 @@ export type Mutation = {
createDockerFolderWithItems: ResolvedOrganizerV1;
/** Creates a new notification record */
createNotification: Notification;
customization: CustomizationMutations;
/** Deletes all archived notifications on server. */
deleteArchivedNotifications: NotificationOverview;
deleteDockerEntries: ResolvedOrganizerV1;
Expand Down Expand Up @@ -2269,6 +2297,7 @@ export type Subscription = {
parityHistorySubscription: ParityCheck;
serversSubscription: Server;
systemMetricsCpu: CpuUtilization;
systemMetricsCpuTelemetry: CpuPackages;
systemMetricsMemory: MemoryUtilization;
upsUpdates: UpsDevice;
};
Expand Down
2 changes: 1 addition & 1 deletion api/src/unraid-api/graph/resolvers/disks/disks.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ import { DisksService } from '@app/unraid-api/graph/resolvers/disks/disks.servic

@Module({
providers: [DisksResolver, DisksService],
exports: [DisksResolver],
exports: [DisksResolver, DisksService],
})
export class DisksModule {}
Loading