Cloudflare GraphQL - REST sensor value always unknown

Hi all

I have a REST integration which calls Cloudflare’s GraphQL endpoint to get the number of requests to a specific zone (website) and the number of requests blocked.
My sensors however always return unknown.

Here are the tests I performed:

  • Replaced all the !secret values with their actual hard coded values
  • Replaced all Jinja templating stuff like the zoneTag or datetime_geq with actual hard coded values
  • Tested this in Postman and Altair and both got a response
  • Verified the JSON response in the Template section under Development tools to ensure it is correct, and all good.
rest:  
  - resource: !secret graphql_cloudflare_url
    scan_interval: 300
    method: POST
    headers:
      Content-Type: "application/json"
      Authorization: !secret graphql_cloudflare_bearer
    payload: >
      {
        "query": "{
          viewer {
            zones(filter: { zoneTag: \"{{ states('sensor.zone_tag') }}\" }) {
              httpRequestsAdaptiveGroups(
                limit: 10
                filter: { datetime_geq: \"{{ (now() - timedelta(hours=23)).isoformat() }}\" }
              ) {
                count
                dimensions {
                  securityAction
                }
              }
            }
          }
        }"
      }
    sensor:
      - name: "Normal Requests Count"
        value_template: >
          {{
            value_json['data']['viewer']['zones'][0]['httpRequestsAdaptiveGroups']
            | selectattr('dimensions.securityAction', 'equalto', 'unknown')
            | map(attribute='count') | sum
          }}
        unit_of_measurement: "requests"
      - name: "Blocked Requests Count"
        value_template: >
          {{
            value_json['data']['viewer']['zones'][0]['httpRequestsAdaptiveGroups']
            | selectattr('dimensions.securityAction', 'equalto', 'block')
            | map(attribute='count') | sum
          }}
        unit_of_measurement: "requests"

This is what the response looks like in Postman and Altair:
(The “unknown” securityAction in the response just means it was not blocked by the Cloudflare WAF)

{
  "data": {
    "viewer": {
      "zones": [
        {
          "httpRequestsAdaptiveGroups": [
            {
              "count": 1452,
              "dimensions": {
                "securityAction": "unknown"
              }
            },
            {
              "count": 44,
              "dimensions": {
                "securityAction": "block"
              }
            }
          ]
        }
      ]
    }
  },
  "errors": null
}

I have absolutely no idea why the values would always return “unknown”… Any assistance will be greatly appreciated.

I figured it out and will leave it here for anyone else to find.

Because my query had to use templates, I had to change things around a bit:

Step 1:Create 2 new template sensors

template:
  - sensor:
    - name: "Zone Tag"
      state: !secret graphql_cloudflare_zone_mysite
    - name: "Timestamp 23 Hours Ago"
      state: "{{ (now() - timedelta(hours=23)) | as_timestamp | timestamp_custom('%Y-%m-%dT%H:%M:%SZ') }}"

Step 2: Modify the rest sensor
I ended up using “payload_template”

rest:
  - resource: !secret graphql_cloudflare_url
      scan_interval: 300
      method: POST
      headers:
        Content-Type: "application/json"
        Authorization: !secret graphql_cloudflare_bearer
      payload_template: >
        {
          "query": "{ viewer { zones(filter: { zoneTag: \"{{ states('sensor.zone_tag') }}\" }) { httpRequestsAdaptiveGroups( limit: 10 filter: { datetime_geq: \"{{ states('sensor.timestamp_23_hours_ago') }}\" } ) { count dimensions { securityAction } } } } }"
        }
      sensor:
        - name: "Normal Requests Count"
          value_template: >
            {{
              value_json
            }}

Step 3: sensor value
By just putting the raw JSON response inside my sensor, I was able to see the errors coming back from the Cloudflare API and adjust my code accordingly. (The sensor value was a long string with the error which is perfect).

It seems that when HomeAssistant starts up after a restart, the template sensors are not populated yet, so the value for “Normal Requests Count” will be some error essentially saying that the “datetime_geq” is not formatted correctly. But, after “scan_interval” time has passed, everything works as it should.