Chapter 9

Gas bottle (Mopeka)

The Mopeka sensor is the most demanding of them all — it does not send a level, only raw values. The percentage has to be calculated. We use exactly the same formulas the CampMatic app uses, so that app and Home Assistant show the same number.

12 minutes Last verified: 22.09.2026 DE EN

What the sensor actually sends

The Mopeka measures upwards through the bottom of the bottle using ultrasound. What reaches you is the time of flight of that pulse — not the fill height, and certainly not a percentage:

The Mopeka topics text
campmatic/<GERAETE_ID>/mopeka/bottle_<SLOT_MOPEKA>/raw_tof        time of flight, microseconds
campmatic/<GERAETE_ID>/mopeka/bottle_<SLOT_MOPEKA>/raw_t          temperature byte (0–127)
campmatic/<GERAETE_ID>/mopeka/bottle_<SLOT_MOPEKA>/battery_raw    battery as a raw byte
campmatic/<GERAETE_ID>/mopeka/bottle_<SLOT_MOPEKA>/quality        measurement quality 0–3
campmatic/<GERAETE_ID>/mopeka/bottle_<SLOT_MOPEKA>/rssi           signal strength in dBm
Why not percent straight away?
A time of flight only becomes a height once temperature is taken into account — sound travels slower when it is cold. And a height only becomes a level once you know how tall the bottle is. Neither lives in the sensor. The upside of splitting it this way: a new bottle type or a recalibration needs no firmware update.

Creating the raw sensors

Append the following sensors under the existing mqtt: sensor: in your /config/packages/campmatic.yaml. Replace <GERAETE_ID> and <SLOT_MOPEKA> with your values.

/config/packages/campmatic.yaml — under mqtt: sensor: yaml Download
    - name: "Gas Bottle Time Of Flight"
      unique_id: campmatic_<GERAETE_ID>_mopeka_<SLOT_MOPEKA>_tof
      state_topic: "campmatic/<GERAETE_ID>/mopeka/bottle_<SLOT_MOPEKA>/raw_tof"
      state_class: measurement
      entity_category: diagnostic
      icon: mdi:radar
      expire_after: 2700
      availability_topic: "campmatic/<GERAETE_ID>/status"
      payload_available: "online"
      payload_not_available: "offline"

    - name: "Gas Bottle Temperature Byte"
      unique_id: campmatic_<GERAETE_ID>_mopeka_<SLOT_MOPEKA>_rawt
      state_topic: "campmatic/<GERAETE_ID>/mopeka/bottle_<SLOT_MOPEKA>/raw_t"
      state_class: measurement
      entity_category: diagnostic
      expire_after: 2700
      availability_topic: "campmatic/<GERAETE_ID>/status"
      payload_available: "online"
      payload_not_available: "offline"

    - name: "Gas Bottle Temperature"
      unique_id: campmatic_<GERAETE_ID>_mopeka_<SLOT_MOPEKA>_temp
      state_topic: "campmatic/<GERAETE_ID>/mopeka/bottle_<SLOT_MOPEKA>/raw_t"
      value_template: "{{ value | int - 40 }}"
      unit_of_measurement: "°C"
      device_class: temperature
      state_class: measurement
      expire_after: 2700
      availability_topic: "campmatic/<GERAETE_ID>/status"
      payload_available: "online"
      payload_not_available: "offline"

    - name: "Gas Bottle Sensor Battery"
      unique_id: campmatic_<GERAETE_ID>_mopeka_<SLOT_MOPEKA>_batt
      state_topic: "campmatic/<GERAETE_ID>/mopeka/bottle_<SLOT_MOPEKA>/battery_raw"
      value_template: "{{ (value | float(0) / 32.0) | round(2) }}"
      unit_of_measurement: "V"
      device_class: voltage
      state_class: measurement
      entity_category: diagnostic
      expire_after: 2700
      availability_topic: "campmatic/<GERAETE_ID>/status"
      payload_available: "online"
      payload_not_available: "offline"

    - name: "Gas Bottle Quality"
      unique_id: campmatic_<GERAETE_ID>_mopeka_<SLOT_MOPEKA>_quality
      state_topic: "campmatic/<GERAETE_ID>/mopeka/bottle_<SLOT_MOPEKA>/quality"
      state_class: measurement
      entity_category: diagnostic
      icon: mdi:signal-cellular-2
      expire_after: 2700
      availability_topic: "campmatic/<GERAETE_ID>/status"
      payload_available: "online"
      payload_not_available: "offline"
The temperature is easy
The temperature byte is a number from 0 to 127. Subtract forty and you have degrees Celsius — 59 becomes 19 °C. We create both: the byte is needed for the fill height in a moment, the degrees are for you to read.

Calculating height and level

Now the actual maths. It belongs under template: sensor: — if you do not have that section yet, create it once.

/config/packages/campmatic.yaml — under template: yaml Download
template:
  - sensor:
      - name: "Gas Bottle Level Height"
        unique_id: campmatic_<GERAETE_ID>_mopeka_<SLOT_MOPEKA>_mm
        unit_of_measurement: "mm"
        state_class: measurement
        icon: mdi:arrow-expand-vertical
        availability: >-
          {{ states('sensor.gas_bottle_time_of_flight') not in ['unknown','unavailable']
             and states('sensor.gas_bottle_temperature_byte') not in ['unknown','unavailable'] }}
        state: >-
          {% set tof = states('sensor.gas_bottle_time_of_flight') | float(0) %}
          {% set t   = states('sensor.gas_bottle_temperature_byte') | float(0) %}
          {{ (tof * (0.573045 - 0.002822 * t - 0.00000535 * t * t)) | round(1) }}

      - name: "Gas Bottle Level"
        unique_id: campmatic_<GERAETE_ID>_mopeka_<SLOT_MOPEKA>_level
        unit_of_measurement: "%"
        state_class: measurement
        icon: mdi:propane-tank
        availability: >-
          {{ states('sensor.gas_bottle_level_height') not in ['unknown','unavailable'] }}
        state: >-
          {% set empty = 38 %}
          {% set full  = 333 %}
          {% set mm = states('sensor.gas_bottle_level_height') | float(0) %}
          {{ [[((mm - empty) / (full - empty) * 100), 0] | max, 100] | min | round(0) }}
The "t" in the formula is the byte, not the degrees
The fill-height formula deliberately uses sensor.gas_bottle_temperature_byte and not sensor.gas_bottle_temperature. Put the Celsius figure in there and you get values that look plausible — but are wrong.

Entering your bottle

The two numbers empty and full in the level formula depend on your bottle. The empty value is the same for all of them, only the full value differs:

Values per bottle type (mm) text
Bottle type                    empty    full
6 kg steel                        38      333
11 kg steel                       38      358
14 kg steel                       38      408
6 kg aluminium                    38      300
11 kg aluminium                   38      460
CampingGaz 907                    38      130
US 20 lb                          38      248
US 30 lb                          38      381
US 40 lb                          38      508
Which bottle do I have?
You already read the configured type in the previous chapter — it appears in the reply as tank_type, for example EUROPE_6KG for the 6 kg steel bottle.
Why the level stops around 80 percent
A gas bottle is never filled completely, for safety reasons. The full value in the table reflects the usual fill mark, not the top of the bottle. A freshly exchanged bottle therefore reads close to 100 percent even though there is physically still air above it.

Several bottles

With two Mopeka sensors you create the whole block a second time — with the second slot number and different names. Make sure the unique_id and the references inside the formulas move along too:

Second bottle on slot 5 yaml
    - name: "Gas Bottle Right Time Of Flight"   # instead of "Gas Bottle Time Of Flight"
      unique_id: campmatic_<GERAETE_ID>_mopeka_5_tof
      state_topic: "campmatic/<GERAETE_ID>/mopeka/bottle_5/raw_tof"
      ...

      # and in the formula as well:
      {% set tof = states('sensor.gas_bottle_right_time_of_flight') | float(0) %}

Do not forget: after saving, check the configuration and restart Home Assistant.

Before you move on
  • The configuration check reports no error
  • sensor.gas_bottle_level_height shows a value in millimetres
  • sensor.gas_bottle_level shows a plausible percentage
  • The quality reads 2 or 3 — at 0 or 1 the sensor is not seated properly