None of the WMI classes seemed to be available to me, and I was getting frustrated. I resorted to something messy, probably error prone, and definitely not memory efficient. I don’t care. It works. Read on if you’re interested.
You will need
- HWInfo
- A share exposing a directory readable by HA to the rest of the network
- A Windows host running Powershell version 5+
Docker
If you’re running a container, and you didn’t bind the /config directory to the host filesystem when you first built it, you’ll need to rebuild. I just tarred the whole config directory, copied it out of the container, built anew with
docker run –v /home/cooldude/.homeassistant/config:/config etc etc etc
then untarred into ~/.homeassistant/config
.
Samba
Create a share for the Windows hosts to write their sensor logs. You’d obviously need to enter your username or @groupname in valid users
[homeassistant]
path = /home/cooldude/.homeassistant
valid users = @smbgrp
browsable = yes
writable = yes
read only = no
HWInfo
On the Windows hosts whose temps we want to monitor, we need to install HWInfo, set it to auto-run, set the sensors we want to log and start logging.
First, the HWInfo config. I reduced the monitored sensors to just what I wanted: core average and max temperatures.
General : Set temperature units and polling rate.
Layout : Use the Monitoring checkbox to disable the unwanted sensors. You can hold ctrl to select multiple rows to uncheck.
So I ended up with a pretty restricted list of things to monitor. Glances takes care of everything else I’m interested in.
Use this icon to start logging to a CSV.
Set this to a local directory. We’ll massage this data before dumping it on the share.
Logging cannot be started automatically. If the computer reboots, you’ll have to go in and click this button again. The developers have intentionally left this need unfulfilled, as is explained in this thread.
Fully automated monitoring/reporting is reserved for the HWiNFO SDK, which is a commercial product.
I haven’t found recent evidence of anyone implementing the SDK in a way that would help us here, so this is glaring flaw #1.
Powershell
Things we need to achieve here.
- Get the headers from the CSV, remove characters that homeassistant won’t like, and rename duplicate headers where two sensors return the same data, like Core 0 Avg Temp C for each CPU.
- Ensure that temperatures are expressed as integers
- Mount our homeassistant share as a PSDrive
- Construct a JSON object of our desired parameters and write it out to a single-line text file at the directory homeassistant is configured to check (HALogging:\config\filesensors)
The single line part of this is crucial. A file sensor only reads the last line of a file, so all our values need to be there. If you’re getting value_json.whatever is not defined in your HA log, you probably have some junk data on your final line. Or you have a typo, I guess.
If your share is secured, you’ll want to create your own $credential object to import. If not, you’ll want to remove -Credential $credential
.
Plug your values into $sharePath ('\\<host>\<share>'
), $logFile (the log coming from HWInfo) and $outFile (the file to be read by homeassistant).
Add the attributes you want to extract to $values. Use the names that appear in the CSV.
Create a schedule task to run Powershell on startup with -WindowStyle Hidden -File C:\Scripts\LogTemps.ps1
.
#HomeAssistant Share
$sharePath = '\\10.1.1.52\homeassistant'
#Set up file share access
$credential = Import-Clixml -Path "$PSScriptRoot\Cred_HALogging.xml"
New-PSDrive -Name HALogging -PSProvider FileSystem -Credential $credential -Root $sharePath -ErrorAction Stop
$logFile = "$env:ProgramData\Logs\HWInfo\sensors.CSV"
$outFile = "HALogging:\config\filesensors\deathknell.txt"
#Set trap to unmount PSDrive if breaking error is encountered
trap {
Remove-PSDrive -Name HALogging
}
##################################
### SELECT PARAMETERS TO QUERY ###
##################################
$values = @(
#Add value headers here as they appear in the CSV
'Date',
'Time',
'Core Temperatures (avg) [°C]',
'Core Max [°C]'
) | ForEach-Object {
$_ -replace '[^a-zA-Z0-9 ]',''
}
$valueList = [System.Collections.ArrayList]::new()
########################
### ASSEMBLE HEADERS ###
########################
#Get headers from CSV. Clean unfriendly characters and whitespace.
$headersRaw = Get-Content $logFile | Select-Object -First 1
$headersRaw = @($headersRaw -split ',').Where({![string]::IsNullOrWhiteSpace($_)}).Foreach({$_ -replace '\s{3,}.*$','' -replace '[^a-zA-Z0-9 ]',''})
#Find duplicate headers and construct a table of replacements
Remove-Variable replace -ErrorAction Ignore
[object[]]$replace = @()
$headersRaw | Group-Object | ForEach-Object {
$string = $_.Name
$idxs = 0..$headersRaw.Count | Where-Object {$headersRaw[$_] -eq $string}
Write-Debug "$string appears at indexes $($idxs -join ',')"
for ($i=0;$i-lt$_.Count;$i++) {
$prepend = ''
if ($_.Count -gt 1) {
$prepend = "CPU$i "
}
$replaceObj = New-Object psobject -Property @{
Index = $idxs[$i]
OldVal = $string
NewVal = "$prepend$string".Replace(' ','_')
}
[object[]]$replace += $replaceObj
}
}
#Construct new headers from the replacement table
$headers = @()
$headers = $replace | Sort-Object -Property Index | Select-Object -ExpandProperty NewVal
#################
### MAIN LOOP ###
#################
while ($true) {
#Import last line of the CSV using the new headers, and selecting the values from $values.
$tempData = Import-Csv -Header $headers -Path C:\ProgramData\logs\HWInfo\sensors.CSV | Select -Last 1 | Select-Object $valueList
#Express all numeric values as Int32
$Ints = $tempData.psobject.properties | Where-Object {$_.Value -match '^\d+$'}
foreach ($prop in $Ints) {
$Name = $prop.Name
$Val = $prop.Value
$tempData.$Name = $Val.ToInt32($null)
}
#Write to terminal
$tempData | Out-Host
#Output as JSON
$tempData | ConvertTo-Json -Compress | Out-File $outFile -Encoding ascii -Force
#Delay and reset
Start-Sleep -Seconds 15
Clear
}
HomeAssistant config
I created a directory just for the text files, 'cause it’s probably gonna get messy in there. From the container’s perspective, this is /config/filesensors.
This requires us to whitelist the directory with the following change to configuration.yaml.
homeassistant:
allowlist_external_dirs:
- "/config/filesensors"
Then we create out sensors. I’ll just show the one here. I created one of these for each value I’m extracting from the JSON.
sensor:
- platform: file
name: 'Deathknell CPU0 Avg Temp C'
file_path: /config/filesensors/deathknell.txt
value_template: '{{ value_json.CPU0_Core_Temperatures_avg_C }}'
unit_of_measurement: "°C"
If the config passes validation, restart HA.
We have sensors!
Yaaay! (Values on the left are from Glances)
So yeah. It works, and can theoretically work for anything that HWInfo can report on.
Glaring flaw #2 is the fact that we’re creating an additional entity for each sensor, instead of one object with attributes. If this were a REST API, I would define my endpoint and declare all the attributes I want to pull from it. Here, we’re telling HA to read the same file over and over again, targeting a new attribute each time. json_attributes is not a valid parameter for the file sensor. I tried.