Youād think, wouldnāt you? Except that WD have thought of that, and - in a frenzy of XML standard breaking - have crippled configuration of locked down the cron as well.
However I have come up with an alternative trick, which avoids the need for ssh, but does need some minor manual intervention at each reboot.
- Logon as the ssh user.
- mkdir /mnt/HD/HD_a2/Nas_Prog/stats
- ln -s /mnt/HD/HD_a2/Nas_Prog/stats /var/www/stats
So
- Thatās the āstatsā folder now available at http://url_of_nas/stats.
- The āstatsā folder and itās contents are persistent, but you have to redo step 3 at every reboot. A pain, but substantially easier than dealing with the intracacies of ssh.
Next we need some content, preferrably serving JSON. The following are a version of the ssh files that came before:
.htaccess:
php_flag apc.cache_by_default Off
Disabling the apc cache is essential, unless you want the same stats each time you query.
UPDATE: The .htaccess file is not required for OS5.
disk.php
<?php
$rtn = array();
$cmd = "df | sed -n '/HD_a2/s/ \+/ /gp' | cut -d' ' -f 2";
// Execute the command, and get the result from the returned array
exec($cmd, $disktotres, $int);
$rtn["disk"]["kB"]["total"] = $disktotres[0];
$cmd = "df | sed -n '/HD_a2/s/ \+/ /gp' | cut -d' ' -f 3";
// Execute the command, and get the result from the returned array
exec($cmd, $diskusedres, $int);
$rtn["disk"]["kB"]["used"] = $diskusedres[0];
$cmd = "df | sed -n '/HD_a2/s/ \+/ /gp' | cut -d' ' -f 4";
// Execute the command, and get the result from the returned array
exec($cmd, $diskfreeres, $int);
$rtn["disk"]["kB"]["free"] = $diskfreeres[0];
$cmd = "df | sed -n '/HD_a2/s/ \+/ /gp' | cut -d' ' -f 5";
// Execute the command, and get the result from the returned array
exec($cmd, $diskpusedres, $int);
$pctused = str_replace('%','',$diskpusedres[0]);
$rtn["disk"]["percent"]["used"] = str_replace('%','',$pctused);
$rtn["disk"]["percent"]["free"] = 100 - $pctused;
// Return to the client
echo json_encode($rtn, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK);
// {"disk":{"kB":{"total":3837326424,"used":744981880,"free":3053341972},"percent":{"used":20,"free":80}}}
?>
memory.php
<?php
$rtn = array();
$cmd = "cat /proc/meminfo | sed -n '/MemTotal:/s/ \+/ /gp' | cut -d' ' -f 2";
// Execute the command, and get the result from the returned array
exec($cmd, $memtotres, $int);
$rtn["memory"]["total"] = $memtotres[0];
$cmd = "cat /proc/meminfo | sed -n '/MemFree:/s/ \+/ /gp' | cut -d' ' -f 2";
// Execute the command, and get the result from the returned array
exec($cmd, $memfreeres, $int);
$rtn["memory"]["free"] = $memfreeres[0];
$cmd = "cat /proc/meminfo | sed -n '/Buffers:/s/ \+/ /gp' | cut -d' ' -f 2";
// Execute the command, and get the result from the returned array
exec($cmd, $membufres, $int);
$rtn["memory"]["buffered"] = $membufres[0];
$cmd = "cat /proc/meminfo | sed -n '/Cached:/s/ \+/ /gp' | head -n 1 | cut -d' ' -f 2";
// Execute the command, and get the result from the returned array
exec($cmd, $memcachres, $int);
$rtn["memory"]["cached"] = $memcachres[0];
$cmd = "free -m | sed -n '/Swap/s/ \+/ /gp' | cut -d' ' -f 2";
// Execute the command, and get the result from the returned array
exec($cmd, $swaptotres, $int);
$rtn["swap"]["total"] = $swaptotres[0];
$cmd = "free -m | sed -n '/Swap/s/ \+/ /gp' | cut -d' ' -f 4";
// Execute the command, and get the result from the returned array
exec($cmd, $swapfreeres, $int);
$rtn["swap"]["free"] = $swapfreeres[0];
$cmd = "free -m | sed -n '/Swap/s/ \+/ /gp' | cut -d' ' -f 3";
// Execute the command, and get the result from the returned array
exec($cmd, $swapusedres, $int);
$rtn["swap"]["used"] = $swapusedres[0];
$cmd = "cat /proc/meminfo | sed -n '/SwapCached/s/ \+/ /gp' | cut -d' ' -f 2";
// Execute the command, and get the result from the returned array
exec($cmd, $swapcachres, $int);
$rtn["swap"]["cached"] = $swapcachres[0];
// Return to the client
echo json_encode($rtn, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK);
// {"memory":{"total":2084352,"free":907360,"buffered":165088,"cached":554944},"swap":{"total":2097056,"free":2097056,"used":0,"cached":0}}
?>
nas.php
<?php
$rtn = array();
$cmd = "/usr/sbin/fan_control -g 0";
// Execute the command, and get the result from the returned array
exec($cmd, $fanres, $int);
//$rtn["res"] = $fanres;
$rtn["temp"]["board"] = explode(" ", $fanres[0])[3];
$rtn["temp"]["hd0"] = explode("=", $fanres[1])[1];
$rtn["temp"]["hd1"] = explode("=", $fanres[2])[1];
$rtn["temp"]["hd2"] = explode("=", $fanres[3])[1];
$rtn["temp"]["hd3"] = explode("=", $fanres[4])[1];
$rtn["temp"]["cpu"] = explode("=", $fanres[5])[1];
$cmd = "/usr/local/sbin/getSmartStatus.sh";
// Execute the command, and get the result from the returned array
exec($cmd, $smtres, $int);
$rtn["smart"] = $smtres[0];
// Get the old software version
$cmd = "cat /etc/NAS_CFG/config.xml | tr -d '\n' | tr -d '\t'";
exec($cmd, $confxmlread, $int);
$confxmlfile = '<?xml version="1.0" encoding="utf-8" standalone="yes" ?>'.$confxmlread[0];
// Replace invalid XML tags <1> through to <5>
for ($cnt = 1; $cnt < 6; $cnt++){
$confxmlfile = str_replace("<".$cnt.">", "<cron_".$cnt.">", $confxmlfile);
$confxmlfile = str_replace("</".$cnt.">", "</cron_".$cnt.">", $confxmlfile);
}
$confxmlres = simplexml_load_string($confxmlfile);
$confxmlarr = get_object_vars($confxmlres);
// Get the current firmware version
$rtn["fw"]["current"] = $confxmlarr["sw_ver_1"];
// Get the new software version
$cmd = "cat /tmp/upgrade_count";
exec($cmd, $newfw, $int);
$newfw = $newfw[0];
// Set a default
$rtn["fw"]["new"] = $newfw;
// Work out if it's the latest version
if ($rtn["fw"]["current"] <> $rtn["fw"]["new"]) {
$rtn["fw"]["status"] = "Upgrade to ".$rtn["fw"]["new"];
}
// CPU usage
$cmd = "/usr/sbin/mpstat | sed -n '/all/s/ \+/ /gp' | cut -d' ' -f 12 | sed 's/%//g'";
// Execute the command, and get the result from the returned array
exec($cmd, $mpres, $int);
$rtn["cpu"]["idle"] = $mpres[0];
$rtn["cpu"]["used"] = 100 - $mpres[0];
// $cmd = "fan_control -g 4 | cut -d' ' -f 4";
$cmd = "cat /tmp/fan_rpm";
// Execute the command, and get the result from the returned array
exec($cmd, $fanrpmres, $int);
$rtn["fan"]["rpm"] = $fanrpmres[0];
// Return to the client
echo json_encode($rtn, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK);
?>
storage.php
<?php
$rtn = array();
$cmd = "cat /tmp/storage_usage.xml";
exec($cmd, $storagexml, $int);
$storageres = simplexml_load_string($storagexml[0]);
$storagearr = get_object_vars($storageres);
$rtn["storage"]["unit"] = 'kB';
$rtn["storage"]["size"] = $storagearr['size'] / 1024;
$rtn["storage"]["usage"] = $storagearr['usage'] / 1024;
$rtn["storage"]["video"] = $storagearr['video'] / 1024;
$rtn["storage"]["photos"] = $storagearr['photos'] / 1024;
$rtn["storage"]["music"] = $storagearr['music'] / 1024;
$rtn["storage"]["other"] = $storagearr['other'] / 1024;
// Return to the client
echo json_encode($rtn, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK);
?>
Now thatās out of the way, itās over to the Home Assistant box. Here, Home Assistant is installed on top of the Raspberry Pi OS, so I regret that, if this does not work for you, youāll have to find your own way using the tools that are available in HASS OS, or whatever other OS you have chosen to run.
Here crontab is used to call a script to call curl to get a set of JSON files to the Home Assistant box. This gets around the ridiculously small 255 character limit of the REST Sensor tool in Home Assistant.
Crontab of homeassistant user: Every two minutes, reach out to the NAS for stats.
*/2 * * * * /home/homeassistant/bin/nas_stats.sh
The files are copied to .tmp, then renamed, to avoid timing errors (copying a completed file is instantaneous, compared to the time to arrive over http, and the sensors in Home Assistant really donāt like incomplete json!).
/home/homeassistant/bin/nas_stats.sh
#!/bin/bash
mkdir -p /home/homeassistant/.homeassistant/files
cd /home/homeassistant/.homeassistant/files
# Get the disk usage of my_nas
curl http://url_of_nas/stats/disk.php > my_nas_disk.json.tmp
rm my_nas_disk.json
mv my_nas_disk.json.tmp my_nas_disk.json
# Get the memory & swap usage of my_nas
curl http://url_of_nas/stats/memory.php > my_nas_memory.json.tmp
rm my_nas_memory.json
mv my_nas_memory.json.tmp my_nas_memory.json
# Get the board sensors of my_nas
curl http://url_of_nas/stats/nas.php > my_nas_nas.json.tmp
rm my_nas_nas.json
mv my_nas_nas.json.tmp my_nas_nas.json
# Get the storage usage of my_nas
curl http://url_of_nas/stats/storage.php > my_nas_storage.json.tmp
rm my_nas_storage.json
mv my_nas_storage.json.tmp my_nas_storage.json
Finally, place this file in the packages area of your Home Assistant:
nas.yaml
# /config/packages/nas.yaml
###################################
############ NAS STATS ############
###################################
# A cron job runs curl, which gets a file from http://url_of_nas/stats/<something>.php
#
sensor:
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_nas.json
name: nas_cpu_temp
value_template: "{{ value_json.temp.cpu }}"
unit_of_measurement: 'Ā°C'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_nas.json
name: nas_board_temp
value_template: "{{ value_json.temp.board }}"
unit_of_measurement: 'Ā°C'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_nas.json
name: nas_hd0_temp
value_template: "{{ value_json.temp.hd0 }}"
unit_of_measurement: 'Ā°C'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_nas.json
name: nas_hd1_temp
value_template: "{{ value_json.temp.hd1 }}"
unit_of_measurement: 'Ā°C'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_nas.json
name: nas_hd2_temp
value_template: "{{ value_json.temp.hd2 }}"
unit_of_measurement: 'Ā°C'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_nas.json
name: nas_hd3_temp
value_template: "{{ value_json.temp.hd3 }}"
unit_of_measurement: 'Ā°C'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_nas.json
name: nas_smart_status
value_template: "{{ value_json.smart }}"
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_nas.json
name: nas_firmware_current
value_template: "{{ value_json.fw.current }}"
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_nas.json
name: nas_firmware_new
value_template: "{{ value_json.fw.new }}"
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_nas.json
name: nas_firmware_status
value_template: "{{ value_json.fw.status }}"
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_nas.json
name: nas_fan_rpm
value_template: "{{ value_json.fan.rpm }}"
unit_of_measurement: 'rpm'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_disk.json
name: nas_disk_space_total
value_template: "{{ value_json.disk.kB.total }}"
unit_of_measurement: 'kB'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_disk.json
name: nas_disk_space_used
value_template: "{{ value_json.disk.kB.used }}"
unit_of_measurement: 'kB'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_disk.json
name: nas_disk_space_free
value_template: "{{ value_json.disk.kB.free }}"
unit_of_measurement: 'kB'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_disk.json
name: nas_disk_perc_used
value_template: "{{ value_json.disk.percent.used }}"
unit_of_measurement: '%'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_disk.json
name: nas_disk_perc_free
value_template: "{{ value_json.disk.percent.free }}"
unit_of_measurement: '%'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_nas.json
name: nas_cpu_idle
value_template: "{{ value_json.cpu.idle }}"
unit_of_measurement: '%'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_nas.json
name: nas_cpu_used
value_template: "{{ value_json.cpu.used }}"
unit_of_measurement: '%'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_storage.json
name: nas_storage_size
value_template: "{{ value_json.storage.size }}"
unit_of_measurement: 'kB'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_storage.json
name: nas_storage_usage
value_template: "{{ value_json.storage.usage }}"
unit_of_measurement: 'kB'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_storage.json
name: nas_storage_video
value_template: "{{ value_json.storage.video }}"
unit_of_measurement: 'kB'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_storage.json
name: nas_storage_photos
value_template: "{{ value_json.storage.photos }}"
unit_of_measurement: 'kB'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_storage.json
name: nas_storage_music
value_template: "{{ value_json.storage.music }}"
unit_of_measurement: 'kB'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_storage.json
name: nas_storage_other
value_template: "{{ value_json.storage.other }}"
unit_of_measurement: 'kB'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_memory.json
name: nas_mem_tot
value_template: "{{ value_json.memory.total }}"
unit_of_measurement: 'bytes'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_memory.json
name: nas_mem_free
value_template: "{{ value_json.memory.free }}"
unit_of_measurement: 'bytes'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_memory.json
name: nas_mem_buffered
value_template: "{{ value_json.memory.buffered }}"
unit_of_measurement: 'bytes'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_memory.json
name: nas_mem_cached
value_template: "{{ value_json.memory.cached }}"
unit_of_measurement: 'bytes'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_memory.json
name: nas_swap_tot
value_template: "{{ value_json.swap.total }}"
unit_of_measurement: 'bytes'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_memory.json
name: nas_swap_free
value_template: "{{ value_json.swap.free }}"
unit_of_measurement: 'bytes'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_memory.json
name: nas_swap_used
value_template: "{{ value_json.swap.used }}"
unit_of_measurement: 'bytes'
- platform: file
file_path: /home/homeassistant/.homeassistant/files/my_nas_memory.json
name: nas_swap_cached
value_template: "{{ value_json.swap.cached }}"
unit_of_measurement: 'bytes'
###################################
############ NAS STATS ############
###################################
- platform: template
sensors:
nas_ram_perc_used:
friendly_name: NAS RAM Usage
value_template: "{{ ( 100 * (states('sensor.nas_mem_tot')|int - states('sensor.nas_mem_free')|int - states('sensor.nas_mem_buffered')|int - states('sensor.nas_mem_cached')|int ) / states('sensor.nas_mem_tot')|int) | round(1) }}"
unit_of_measurement: '%'
nas_swap_perc_used:
friendly_name: NAS Swap Usage
value_template: "{{ ((states('sensor.nas_swap_used')|int / states('sensor.nas_swap_tot')|int) * 100) | round(1) }}"
unit_of_measurement: '%'
# Average Fan RPM
- platform: filter
name: "nas_fan_rpm_smooth"
entity_id: sensor.nas_fan_rpm
filters:
- filter: lowpass
time_constant: 10
precision: 2
Ok, I think thatās everything I have. Iām not claiming the stats are all correct - in fact at least some of them wonāt be, my knowledge of the WD innards and Bash is enough to be dangerous, but certainly not in-depth - so Iām happy to stand corrected.