Propane Tank monitoring

Google Icons for Android app: https://fonts.google.com/icons
SF Symbols for iOS app: SF Symbols - Apple Developer

Glad it got working!

Soooo Close but…

I have everything ID wise correct, I can see the RESTful integration in devices and services, but when I select one of the items I receive the following.

This entity (‘sensor.centriconnect_sensor_tank_level’) does not have a unique ID, therefore its settings cannot be managed from the UI. See the documentation for more detail.

everything in the config.yaml looks identical to the yaml above, obviously with my own id information.

I am not sure what I am missing.

Thanks for the information on the icons.

See my reply to @philgrocks …still having issues.

Ah yes, I ran into that problem as well. I neglected to update when I fixed that problem… I’m soooo sorry!

So what is needed is to add unique id for each item. Here is the update code. See unique_id under each name.

Configuration for “mypropane” from “CentriConnect”
rest:
resource: “https://api.centriconnect.com/centriconnect/**user id goes here**/device/device id goes here/all-data”
scan_interval: 21600 #Every 6 hours as limited by CentriConnect
method: GET
params:
device_auth: “device auth goes here
sensor:
- name: “MyPropane Level”
unique_id: “MyPropane_Level”
value_template: “{{ value_json [‘bf1b1b89-bba9-44f3-a337-2f2efe02a665’] [‘TankLevel’] | round(0) }}”
unit_of_measurement: “%”
- name: “MyPropane Next Time”
value_template: “{{ value_json [‘bf1b1b89-bba9-44f3-a337-2f2efe02a665’] [‘NextPostTimeIso’] }}”
- name: “MyPropane Last Time”
value_template: “{{ value_json [‘bf1b1b89-bba9-44f3-a337-2f2efe02a665’] [‘LastPostTimeIso’] }}”
- name: “MyPropane Battery Volts”
value_template: “{{ value_json [‘bf1b1b89-bba9-44f3-a337-2f2efe02a665’] [‘BatteryVolts’] | round(2) }}”
unit_of_measurement: “V”
- name: “MyPropane Solar Volts”
value_template: “{{ value_json [‘bf1b1b89-bba9-44f3-a337-2f2efe02a665’] [‘SolarVolts’] | round(2)}}”
unit_of_measurement: “V”
- name: “MyPropane Alert Status”
value_template: “{{ value_json [‘bf1b1b89-bba9-44f3-a337-2f2efe02a665’] [‘AlertStatus’] }}”
- name: “MyPropane Signal Quality”
value_template: “{{ value_json [‘bf1b1b89-bba9-44f3-a337-2f2efe02a665’] [‘SignalQualLTE’] | round(2) }}”
unit_of_measurement: “dB”
- name: “MyPropane Tank Size”
value_template: “{{ value_json [‘bf1b1b89-bba9-44f3-a337-2f2efe02a665’] [‘TankSize’] }}”
unit_of_measurement: “Gallons”
- name: “MyPropane Name”
value_template: “{{ value_json [‘bf1b1b89-bba9-44f3-a337-2f2efe02a665’] [‘DeviceName’] }}”
- name: “MyPropane Altitude”
value_template: “{{ value_json [‘bf1b1b89-bba9-44f3-a337-2f2efe02a665’] [‘Altitude’] | round(2) }}”
unit_of_measurement: “Meters”

ETA: OK , made some progress. with the help of an online editor I was able to get the formatting correct and after restarting everything I now have entities in the RESTful integration that are manageable!! of course I also have duplicate unmanageable entries too…not sure how to get rid of those. But a step in the right direction.

When creating a gauge card I am receiving the following
image

Am I correct in assuming this is because the data hasn’t been pulled from the unit yet? :crossed_fingers:


I need to walk away from this for a little bit. if you code above works, then something is getting lost in the copy and paste.

Here is what I am seeing in config.yaml, and every time I try to fix something another thing goes red. I try to add the unique ids and get more errors

My lack of understanding of with yaml is probably a contributor

I really appreciate the assist, but I think I am going to curl up in the corner now.

I have run into odd characters getting into the code when I would cut and paste. I suspect this has happened to you.

What I had to do (and it is a pain) is to retype each line and delete the code that was pasted.

Hey LiQuid COOled can you provide a list of prerequisites? I have Custom Button Card and Custom Mushroom Card, but cannot get your code to show me anything :rofl:

I am on vacation, but will assist you next week when I get back if you still need assistance.

hey @LiQuid_cOOled! If the info is here, I’ve missed it. How do you get the info from the Mopeka bridge into HA? Are you scraping a website or something more glamorous?

I think I missed something the script run but I am not getting data any time I add a Gauge I am getting “Entity is non-numeric: sensor.my_propane_level”

Fixed the errors…Fixed Used Chat GPT there was a typo with quotes

Could you share your icon somewhere?

Here is my card and code.

type: custom:mushroom-legacy-template-card
primary: >-
  {{ [0, [((states('sensor.propane_tank_tank_weight') | float - 17) / 21 * 100),
  100] | min] | max | round(0) }} %
secondary: ""
icon: mdi:propane-tank
entity: sensor.propane_tank_tank_weight
icon_color: |-
  {% set bl = states('sensor.propane_tank_tank_weight') | int %}
   
  {% if bl < 20 %} orange
  {% elif bl < 27 %} blue
  {% elif bl < 38 %} green
  {% elif bl == 38 %} green
  {% endif %}
card_mod:
  style:
    mushroom-shape-icon$: |
      .shape {
        --shape-color: none !important;
      }
    .: |
      ha-state-icon {
        animation: pulse 2s ease-in-out infinite;
        stroke: white;
            stroke-width: .4px;
      }
      ha-card {
      background: none !important;
      border: none !important;
      }  
visibility:
  - condition: numeric_state
    entity: sensor.propane_tank_tank_weight
    below: 20

I think @LiQuid_cOOled helped me with animation. Thank you

Could you please share the icon image? I haven’t been able to do it here at all.

Super happy with this product so far. The base of the unit is magnetic but my tank is underground so I just attached some magnetic discs to the green plastic lid and it looks great. Here is my HA display. Simple but informative.

Configuration was super easy and Nick at Centri was very responsive.

A couple new sensors were recently added for tank temp in C and F (at bottom):

# configuration for "<ASSET_NAME>" from "CentriConnect"
rest:
  scan_interval: 21600  # Every 6 hours (CentriConnect limit)
  resource: "https://api.centriconnect.com/centriconnect/<ACCOUNT_UUID>/device/<DEVICE_UUID>/all-data"
  method: GET
  params:
    device_auth: "<DEVICE_AUTH_CODE>"
  sensor:
    - name: "<ASSET_NAME> Level"
      value_template: "{{ value_json['<DEVICE_UUID>']['TankLevel'] | round(0) }}"
      unit_of_measurement: "%"
      state_class: measurement

    - name: "<ASSET_NAME> Next Time"
      value_template: "{{ value_json['<DEVICE_UUID>']['NextPostTimeIso'] }}"
      device_class: timestamp

    - name: "<ASSET_NAME> Last Time"
      value_template: "{{ value_json['<DEVICE_UUID>']['LastPostTimeIso'] }}"
      device_class: timestamp

    - name: "<ASSET_NAME> Battery Volts"
      value_template: "{{ value_json['<DEVICE_UUID>']['BatteryVolts'] | round(2) }}"
      unit_of_measurement: "V"
      device_class: voltage
      state_class: measurement

    - name: "<ASSET_NAME> Solar Volts"
      value_template: "{{ value_json['<DEVICE_UUID>']['SolarVolts'] | round(2) }}"
      unit_of_measurement: "V"
      device_class: voltage
      state_class: measurement

    - name: "<ASSET_NAME> Alert Status"
      value_template: "{{ value_json['<DEVICE_UUID>']['AlertStatus'] }}"

    - name: "<ASSET_NAME> Signal Quality"
      value_template: "{{ value_json['<DEVICE_UUID>']['SignalQualLTE'] | round(2) }}"
      unit_of_measurement: "dBm"
      device_class: signal_strength
      state_class: measurement

    - name: "<ASSET_NAME> Tank Size"
      value_template: "{{ value_json['<DEVICE_UUID>']['TankSize'] }}"
      unit_of_measurement: "gal"
      state_class: measurement

    - name: "<ASSET_NAME> Name"
      value_template: "{{ value_json['<DEVICE_UUID>']['DeviceName'] }}"

    - name: "<ASSET_NAME> Altitude"
      value_template: "{{ value_json['<DEVICE_UUID>']['Altitude'] | round(2) }}"
      unit_of_measurement: "m"
      device_class: distance
      state_class: measurement

    - name: "<ASSET_NAME> Temperature (°C)"
      value_template: "{{ value_json['<DEVICE_UUID>']['DeviceTempCelsius'] | float | round(1) }}"
      unit_of_measurement: "°C"
      device_class: temperature
      state_class: measurement

    - name: "<ASSET_NAME> Temperature (°F)"
      value_template: "{{ value_json['<DEVICE_UUID>']['DeviceTempFahrenheit'] | float | round(1) }}"
      unit_of_measurement: "°F"
      device_class: temperature
      state_class: measurement

It’s a SVG so I’d have to email it to you. DM me if that is an option for you.

Here is the file as a PNG. You can use an online PNG to SVG converter like this

I got mine all up and working within an hour or so. Very pleased so far

Has anyone using the Centri device worked out how to calculate consumption and store it in a way where it could be used on the energy dashboard?

I've been running a Centri MyPropane tank monitor on my 1,000-gallon tank and got it fully integrated into Home Assistant, including the Energy dashboard. Here's the complete setup in case it helps anyone else.


What you get

  • Live tank level (%)
  • Remaining gallons
  • Last/next update timestamps (in local time)
  • Lifetime gallons consumed (running total, survives restarts)
  • Energy dashboard integration via a CCF sensor
  • Deduplication so repeated API responses never double-count consumption
  • Startup automation that fetches fresh data immediately after an HA restart

Prerequisites

You need three credentials:

  • User ID (account-level UUID) - this is found in the My Propane app (Settings -> User Profile)
  • Device ID (printed on the monitor - standard UUID format, e.g. 473fc198-1adf-4b9f-afde-4f3af9f2644e)
  • Device Auth (numeric code)

The last two can be found on your device activation card that came with your device (the one that says "SAVE THIS CARD" at the top. Centri support ([email protected]) may be able to help if you've lost this card, but I had mine. They can also send you the API documentation if you need it - they're very helpful and responsive!


Package file

Create config/packages/propane.yaml (requires packages: !include_dir_named packages in your configuration.yaml). Replace the three placeholder values (YOUR_USER_ID, YOUR_DEVICE_ID, YOUR_DEVICE_AUTH) with your credentials and set tank_size to match your tank.

# CentriConnect Propane Tank Monitor
# API rate limit: 4 requests/device/day — scan_interval: 21600 matches exactly.

sensor:
  - platform: rest
    name: "Centri Tank Level"
    unique_id: centri_tank_level
    resource: "https://api.centriconnect.com/centriconnect/YOUR_USER_ID/device/YOUR_DEVICE_ID/all-data"
    method: GET
    params:
      device_auth: "YOUR_DEVICE_AUTH"
    scan_interval: 21600
    unit_of_measurement: "%"
    state_class: measurement
    value_template: "{{ value_json['YOUR_DEVICE_ID']['TankLevel'] }}"
    json_attributes_path: "$['YOUR_DEVICE_ID']"
    json_attributes:
      - AlertStatus
      - Altitude
      - BatteryVolts
      - DeviceID
      - DeviceName
      - LastPostTimeIso
      - Latitude
      - Longitude
      - NextPostTimeIso
      - SignalQualLTE
      - SolarVolts
      - TankSize
      - TankSizeUnit
      - VersionHW
      - VersionLTE

input_number:
  propane_total_consumed_gal:
    name: "Propane Total Consumed"
    min: 0
    max: 999999
    step: 0.1
    unit_of_measurement: "gal"
    mode: box
    icon: mdi:propane-tank-outline

  propane_last_level_pct:
    name: "Propane Last Known Level"
    min: 0
    max: 100
    step: 0.1
    unit_of_measurement: "%"
    mode: box
    icon: mdi:gauge

input_text:
  propane_last_post_time:
    name: "Propane Last Processed Post Time"
    max: 50
    icon: mdi:clock-check

template:
  - sensor:
      - name: "Propane Tank Remaining Gallons"
        unique_id: propane_tank_remaining_gallons
        unit_of_measurement: "gal"
        state_class: measurement
        icon: mdi:propane-tank
        availability: "{{ has_value('sensor.centri_tank_level') }}"
        state: >
          {% set tank_size = state_attr('sensor.centri_tank_level', 'TankSize') | float(0) %}
          {% set tank_level = states('sensor.centri_tank_level') | float(0) %}
          {{ (tank_size * tank_level / 100) | round(1) }}

      - name: "Propane Tank Last Updated"
        unique_id: propane_tank_last_updated
        icon: mdi:clock-outline
        availability: "{{ state_attr('sensor.centri_tank_level', 'LastPostTimeIso') is not none }}"
        state: >
          {% set ts = state_attr('sensor.centri_tank_level', 'LastPostTimeIso') %}
          {% if ts %}
            {{ (ts | as_datetime).astimezone() | as_timestamp | timestamp_custom('%Y-%m-%d %H:%M') }}
          {% else %}
            Unknown
          {% endif %}

      - name: "Propane Tank Next Update"
        unique_id: propane_tank_next_update
        icon: mdi:clock-check-outline
        availability: "{{ state_attr('sensor.centri_tank_level', 'NextPostTimeIso') is not none }}"
        state: >
          {% set ts = state_attr('sensor.centri_tank_level', 'NextPostTimeIso') %}
          {% if ts %}
            {{ (ts | as_datetime).astimezone() | as_timestamp | timestamp_custom('%Y-%m-%d %H:%M') }}
          {% else %}
            Unknown
          {% endif %}

      - name: "Propane Total Consumed Gallons"
        unique_id: propane_total_consumed_gallons
        unit_of_measurement: "gal"
        state_class: total_increasing
        icon: mdi:propane-tank-outline
        state: "{{ states('input_number.propane_total_consumed_gal') | float(0) | round(1) }}"

      # For Energy dashboard → Gas Consumption section.
      # 1 US gallon liquid propane = 0.3649 CCF (industry standard at 60°F).
      - name: "Propane Total Consumed CCF"
        unique_id: propane_total_consumed_ccf
        unit_of_measurement: "CCF"
        device_class: gas
        state_class: total_increasing
        icon: mdi:fire
        state: >
          {{ (states('input_number.propane_total_consumed_gal') | float(0) * 0.3649) | round(3) }}

automation:
  - id: centri_refresh_on_start
    alias: "Refresh Centri Propane Sensor on Startup"
    trigger:
      - platform: homeassistant
        event: start
    action:
      - delay:
          seconds: 30
      - service: homeassistant.update_entity
        target:
          entity_id: sensor.centri_tank_level
    mode: single

  - id: centri_track_consumption
    alias: "Track Propane Consumption"
    description: >
      Fires on every new sensor reading. Deduplicates via LastPostTimeIso so
      hourly polls or repeated restarts never double-count consumption.
    trigger:
      - platform: state
        entity_id: sensor.centri_tank_level
        not_to:
          - unknown
          - unavailable
    action:
      - variables:
          new_level: "{{ trigger.to_state.state | float(0) }}"
          new_post_time: "{{ state_attr('sensor.centri_tank_level', 'LastPostTimeIso') | string }}"
          last_post_time: "{{ states('input_text.propane_last_post_time') }}"
          prev_level: >
            {% if trigger.from_state is not none
               and trigger.from_state.state not in ['unknown', 'unavailable'] %}
              {{ trigger.from_state.state | float(0) }}
            {% else %}
              {{ states('input_number.propane_last_level_pct') | float(0) }}
            {% endif %}
      - choose:
          - conditions:
              - condition: template
                value_template: "{{ new_post_time != last_post_time and new_post_time not in ['None', 'unknown', ''] }}"
            sequence:
              - variables:
                  drop: "{{ [prev_level - new_level, 0] | max }}"
                  tank_size: "{{ state_attr('sensor.centri_tank_level', 'TankSize') | float(0) }}"
              - choose:
                  - conditions:
                      - condition: template
                        value_template: "{{ drop > 0 and drop <= 30 }}"
                    sequence:
                      - service: input_number.set_value
                        target:
                          entity_id: input_number.propane_total_consumed_gal
                        data:
                          value: >
                            {{ (states('input_number.propane_total_consumed_gal') | float(0)
                                + drop / 100 * tank_size) | round(2) }}
              - service: input_text.set_value
                target:
                  entity_id: input_text.propane_last_post_time
                data:
                  value: "{{ new_post_time }}"
              - service: input_number.set_value
                target:
                  entity_id: input_number.propane_last_level_pct
                data:
                  value: "{{ new_level }}"
    mode: single

First-run bootstrap

After restarting HA, go to Developer Tools → States, find sensor.centri_tank_level, and note its LastPostTimeIso attribute and current state. Then set the two helpers via Developer Tools → Services:

# input_number.set_value
entity_id: input_number.propane_last_level_pct
value: 57.2   # ← your current TankLevel %

# input_text.set_value
entity_id: input_text.propane_last_post_time
value: "2026-05-18 17:01:44.000"   # ← your current LastPostTimeIso

This tells the automation "I've already processed this reading" so the first real consumption event is recorded correctly.


Energy dashboard

Go to Settings → Dashboards → Energy → Gas consumption and add sensor.propane_total_consumed_ccf.

HA's gas section only accepts m³, ft³, or CCF - not gallons directly. The conversion used here (0.3649 CCF/gallon) is the industry-standard figure for US liquid propane vaporised at 60°F.

A note on temperature correction

The API returns TankLevel as a raw percentage with no mention of temperature correction in the documentation. Propane expands roughly 1% per 10°F, so across a large seasonal temperature swing you may see a few percentage points of variance that doesn't represent actual consumption. The gallon figures should be treated as estimates rather than billing-precise measurements.


How deduplication works

The Centri device posts data ~4 times a day. Each posting has a unique LastPostTimeIso timestamp that doesn't change no matter how many times you poll the API afterward. The input_text.propane_last_post_time helper stores the timestamp of the last reading that was actually processed. When the automation fires, it compares the incoming timestamp against the stored one — if they match, the response is a duplicate and everything is skipped. This means you can safely lower scan_interval without risking double-counted consumption.


Dashboard card

@royf007 published a HACS custom card that displays a nice animated horizontal tank with color-coded fill level. Install via HACS → Custom repositories, then:

yaml

type: custom:ha-centri-propane-tank-card
entity: sensor.centri_tank_level
tank_size: 1000   # your tank capacity in gallons