API v2.0 - CPU Temp & Pool Status

HungrySkeltal

Dabbler
Joined
Apr 17, 2020
Messages
11
Hi all - I've been rolling with my FreeNAS for about a year now and am loving it.

Since upgrading to 11.3, I've been going through and upgrading my HomeAssistant sensors to start reading from the new v2.0 API instead of the old v1.0 API.
I've managed to navigate the new REST API for everything I need (disk temps, alerts, etc) with the exception of two points:

1 - the CPU temp. Is anyone able to point me what API call I should be making to find this?
2 - the health of a pool. API 1.0 returns a status of 'HEALTHY'/'DEGRADED' etc. However it appears that the new API has switched to reporting the status as 'ONLINE', (and there is an extra field showing a flag of "healthy = true"). I'm concerned that reading the 'ONLINE' status won't change even if a pool becomes degraded. If a pool does become degraded, is the only warning of this the "healthy = false" flag? or am I missing another field in there somewhere which specifies healthy/degraded? I am using this command: http://192.168.XXX.XXX/api/v2.0/pool

Thanks in advance!
 

sretalla

Powered by Neutrality
Moderator
Joined
Jan 1, 2016
Messages
9,703
I confirm with a pool containing an offline disk (and in pool state of DEGRADED), the API reports the pool, VDEV and disk correctly (DEGRADED, DEGRADED and OFFLINE). (TrueNAS Core 12 Nightly).
 

sretalla

Powered by Neutrality
Moderator
Joined
Jan 1, 2016
Messages
9,703
If I do a GET with: /api/v2.0/stats/get_sources (no data)

I see a nice list of things that might be what you want...

Code:
{
"cputemp-7": [
  "temperature"
],
"df-mnt-vol3-backup-backup": [
  "df_complex-reserved",
  "df_complex-free",
  "df_complex-used"
],
"df-root": [
  "df_complex-reserved",
  "df_complex-free",
  "df_complex-used"
],
"df-mnt-vol1-tempBackup-iocage-images": [
  "df_complex-reserved",
  "df_complex-free",
  "df_complex-used"
],
"df-mnt-vol2-iocage-jails": [
  "df_complex-free",
  "df_complex-reserved",
  "df_complex-used"
],
"interface-vnet0:6": [
  "if_octets",
  "if_packets",
  "if_errors"
],
"df-mnt-vol5-backup-.bhyve_containers": [
  "df_complex-used",
  "df_complex-free",
  "df_complex-reserved"
],
"disktemp-da0": [
  "temperature"
],
"df-mnt-vol1-backup-jails": [
  "df_complex-reserved",
  "df_complex-used",
  "df_complex-free"
],
"df-mnt-vol5-backup-backup-jails-owncloud_1": [
  "df_complex-used",
  "df_complex-reserved",
  "df_complex-free"
],
"df-var-tmp-tmpelbbzv57": [
  "df_complex-reserved",
  "df_complex-used",
  "df_complex-free"
],
"df-mnt-vol1-tempBackup-jails-owncloud_1": [
  "df_complex-free",
  "df_complex-used",
  "df_complex-reserved"
],
"interface-tap0": [
  "if_errors",
  "if_packets",
  "if_octets"
],
"df-mnt-vol1-tempBackup-iocage-releases-11.1-RELEASE": [
  "df_complex-used",
  "df_complex-free",
  "df_complex-reserved"
],
"df-mnt-vol1-tempBackup-jails": [
  "df_complex-reserved",
  "df_complex-used",
  "df_complex-free"
],
"disk-da7": [
  "disk_time",
  "disk_ops",
  "disk_octets",
  "disk_io_time"
],
"df-mnt-vol1-tempBackup-iocage-templates": [
  "df_complex-reserved",
  "df_complex-free",
  "df_complex-used"
],
"df-mnt-vol2-iocage-jails-plexmediaserver-plexpass": [
  "df_complex-free",
  "df_complex-used",
  "df_complex-reserved"
],
"disktemp-ada1": [
  "temperature"
],
"cpu-7": [
  "cpu-user",
  "cpu-interrupt",
  "cpu-system",
  "cpu-nice",
  "cpu-idle"
],
"processes": [
  "ps_state-sleeping",
  "ps_state-running",
  "ps_state-stopped",
  "ps_state-zombies",
  "ps_state-wait",
  "ps_state-idle",
  "ps_state-blocked"
],
... (truncated, but you get the point... especially looking at the first one)

Now the thing to do is a POST to : /api/v2.0/stats/get_dataset_info (this time with data, since it's a POST)
Code:
{
  "source": "cputemp-1",
  "type": "temperature"
}


Which gets back:
Code:
{
"source": "cputemp-1",
"type": "temperature",
"datasets": {
  "value": {
   "type": "GAUGE"
  }
},
"step": 10,
"last_update": 1587286114
}


OK; great... we can see that there's something there... now to get it:

Which is where I'm stumped...

Doing a POST to /api/v2.0/stats/get_data (with the following data...)

Code:
{
  "stats_list": [
    {
      "GAUGE": "cputemp-1"
    },
    {
      "GAUGE": "cputemp-2"
    }
  ]
}


returns:
"Item#0 is not valid per list types: [GAUGE] Field was not expected"


Based on these specs for the Arguments, I can't tell what the names and values of the array members needs to be to get a valid response.

Code:
First Argument:
{
"type": "array",
"title": "stats_list",
"items": [
  {
   "type": "object"
  }
]
}

Second Argument (?):
{
"type": "object",
"properties": {
  "step": {
   "type": "integer"
  },
  "start": {
   "type": "string"
  },
  "end": {
   "type": "string"
  }
},
"additionalProperties": false,
"title": "stats-filter",
"default": {}
}
 
Last edited:

HungrySkeltal

Dabbler
Joined
Apr 17, 2020
Messages
11
looks like you got to the exact same stage that I did! :p
I've been fiddling around with it but haven't been able to crack it just yet...
 

sretalla

Powered by Neutrality
Moderator
Joined
Jan 1, 2016
Messages
9,703
So I guess we could modify the request slightly to say does anyone know how or have an example of the arguments needed to POST to the get_data function for any counter/measure?
 

tprelog

Patron
Joined
Mar 2, 2016
Messages
297
Hey guys - I think I'm making some progress for the cpu temp

Both examples use specific moment in time (epoch)
you need to change password in --user 'root:password'

Notice a few things in example 1 output
  1. this reports the temperature in kelvin
  2. the decimal must be moved one place to the left before converting to Celsius
Example 1:
Code:
curl -X POST "http://localhost/api/v2.0/stats/get_data" -H "accept: */*" -H "Content-Type: application/json" -d '{"stats_list":[{"source":"cputemp-0", "type":"temperature", "dataset":"value"}],"stats-filter":{"start":"1596384276","end":"1596384276"}}' --user 'root:password'



Example 1 output:

Code:
{
  "about": "Data for cputemp-0/temperature",
  "meta": {
    "start": 1596384280,
    "end": 1596384280,
    "step": 10,
    "legend": [
      "cputemp-0/temperature"
    ]
  },
  "data": [
    [
      2937.848284
    ]
  ]
}



Example 2:
Code:
curl -X POST "http://localhost/api/v2.0/reporting/get_data" -H "accept: */*" -H "Content-Type: application/json" -d '{"graphs":[{"name":"cputemp"}],"reporting_query":{"start":"1596384276","end":"1596384276","aggregate":true}}' --user 'root:password'


Example 2 output:
Code:
[
  {
    "name": "cputemp",
    "identifier": null,
    "data": [
      [
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284
      ]
    ],
    "start": 1596384280,
    "end": 1596384280,
    "step": 10,
    "legend": [
      "cputemp0",
      "cputemp1",
      "cputemp2",
      "cputemp3",
      "cputemp4",
      "cputemp5",
      "cputemp6",
      "cputemp7"
    ],
    "aggregations": {
      "min": [
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284
      ],
      "mean": [
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284
      ],
      "max": [
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284,
        20.6348284
      ]
    }
  }
]
 

sretalla

Powered by Neutrality
Moderator
Joined
Jan 1, 2016
Messages
9,703
That is indeed progress of a sort... what I was hoping to see was the current value of a counter/value, not a graph (although I guess your result could be parsed for it... but that seems wasteful to me).
 

HungrySkeltal

Dabbler
Joined
Apr 17, 2020
Messages
11
Thanks tprelog. It's a nice step in solving the puzzle - but as sretalla mentioned, it would be ideal to just pull the current value... (if that's even possible!)
 

tprelog

Patron
Joined
Mar 2, 2016
Messages
297
I have not found a good way to get a current value -- Besides using an epoch time, you can also use
Code:
curl -X POST "http://localhost/api/v2.0/reporting/get_data" -H "accept: */*" -H "Content-Type: application/json" -d '{"graphs":[{"name":"cputemp"}],"reporting_query":{"unit":"HOUR","page":0,"aggregate":true}}' --user 'root:password'


While this will retrieve an almost real-time result with out specifing a point in time - the unit cannot be set for less than HOUR so the resulting output in my case is over 3600 lines. (there are 360 data blocks reported per hour -- The last two are always null)
https://paste.ubuntu.com/p/b6jN5VC2Zm/

I'm parsing this HOUR output with a rest sensor in Home Assistant to report the current CPU temperature, although I do share similar opinion with @sretalla -- This seems excessive to me -- I realize this a limited use case, please disregard the remaining post if this does not apply to you

Code:
sensor:
  - platform: rest
    name: truenas_cpu
    method: POST
    authentication: basic
    username: !secret tn_user
    password: !secret tn_passwd
    headers:
      Content-Type: application/json
    resource: http://truenas.local/api/v2.0/reporting/get_data
    payload: >-
      {
        "graphs":[{"name":"cputemp"}],
        "reporting_query":{"unit":"HOUR","page":0,"aggregate":true}
      }
    device_class: temperature
    unit_of_measurement: '°'
    json_attributes_path: "$.[0]"
    json_attributes:
      - aggregations
    value_template: '{{ "%.1f"% ((value_json[0].data[358][0] + value_json[0].data[358][1] + value_json[0].data[358][2] + value_json[0].data[358][3] + value_json[0].data[358][4] + value_json[0].data[358][5] + value_json[0].data[358][6] + value_json[0].data[358][7]) / 8) }}'


This is the basic result -- The sensor state, 21.6 is the almost current temperature (average all 8 cores from data block 358). It updates every 30 seconds so I assume the temperature shown to be no more than 30 - 60 seconds old. The aggregations are the min, max temp and average temp reported for each of the 8 CPU cores over the last hour. -- This is far from perfect but I'm making do with it for now
1596576806880.png
 

tprelog

Patron
Joined
Mar 2, 2016
Messages
297
@tprelog nice one! I'll be using this approach!
Just a slight edit to account for my 6 core CPU and it works like a dream

I slightly improved the value template for the temperature sensor
- To set number of cores just change cores=8

I also figured out / switched to using an API key instead of the username and password.

Code:
  - platform: rest
    name: nas_cpu_report
    resource: http://truenas.local/api/v2.0/reporting/get_data
    headers:
      Authorization: !secret tn_api_key
      Content-Type: application/json
      User-Agent: Home Assistant
    device_class: temperature
    unit_of_measurement: '°'
    method: POST
    payload: >-
      {
        "graphs":[{"name":"cputemp"}],
        "reporting_query":{"unit":"HOUR","page":0,"aggregate":true}
      }
    json_attributes_path: "$.[0]"
    json_attributes:
      - aggregations
    value_template: >-
      {% set tn = namespace(temp=0, cores=8) %}
      {% for core in range(0, tn.cores) %}
        {% set tn.temp = tn.temp + value_json[0].data[358][core] %}
      {% endfor %}
      {{ "%.1f"% (tn.temp / tn.cores) }}
 

mgretz

Cadet
Joined
Dec 17, 2023
Messages
1
Is this still working with TrueNAS-SCALE-23.10.0.1?


If I do a GET with: /api/v2.0/stats/get_sources (no data)

if get an empty response?

1702806616153.png
 
Top