Skip to content

Instantly share code, notes, and snippets.

@zubir2k
Last active April 14, 2025 07:50
Show Gist options
  • Select an option

  • Save zubir2k/7a205c4131a8bed372a3923d077e91f8 to your computer and use it in GitHub Desktop.

Select an option

Save zubir2k/7a205c4131a8bed372a3923d077e91f8 to your computer and use it in GitHub Desktop.

⚡TNB NEM Calculator

This template will create the necessary sensor that calculates your monthly TNB bill with NEM (Net Energy Metering).

📦 Installation

  1. Set path /HAMY as package directory in your configuration.yaml (if you have not done so)
homeassistant:
  packages: !include_dir_named HAMY/
  1. Just copy the template file tnb_nem.yaml into that folder
  2. Restart HA
  3. Add new Entity Card into your dashboard

📑 How to use

  1. Just enter your total import and export (in kWh)
  2. The sensor will do the rest

image

🎨 Entity Cards

type: entities
entities:
  - entity: input_number.tnb_import_energy
  - entity: input_number.tnb_export_energy
  - type: section
  - entity: sensor.tnb_import_cost
    name: Grid Import (Buy)
  - entity: sensor.tnb_export_earnings
    name: Grid Export (Sell)
  - entity: sensor.tnb_tariff_tier
  - type: section
  - entity: sensor.tnb_icpt_charges
    name: ICPT
  - entity: sensor.tnb_kwtbb
    name: KWTBB (1.6%)
  - entity: sensor.tnb_sst
    name: SST (8%)
  - type: section
  - entity: sensor.tnb_final_bill

📃 Template File (tnb_nem.yaml)

## NEM Calculator
input_number:
  tnb_import_energy:
    name: "Total Import (kWh)"
    min: 0
    max: 8000
    step: 1
    unit_of_measurement: "kWh"
    mode: box

  tnb_export_energy:
    name: "Total Export (kWh)"
    min: 0
    max: 8000
    step: 1
    unit_of_measurement: "kWh"
    mode: box

template:
  - sensor:
    ## Sensor 1: Current Tariff Tier ##
    - name: "TNB Tariff Tier"
      unique_id: tnb_tariff_tier
      unit_of_measurement: "MYR/kWh"
      state: >-
        {% set import_energy = states('input_number.tnb_import_energy') | float(0) %}
        {% if import_energy > 900 %}
          0.571
        {% elif import_energy > 600 %}
          0.546
        {% elif import_energy > 300 %}
          0.516
        {% elif import_energy > 200 %}
          0.334
        {% else %}
          0.218
        {% endif %}
      attributes:
        blocktariff: >-
          {% set import_energy = states('input_number.tnb_import_energy') | float(0) %}
          {% if import_energy > 900 %}
            Tier 5
          {% elif import_energy > 600 %}
            Tier 4
          {% elif import_energy > 300 %}
            Tier 3
          {% elif import_energy > 200 %}
            Tier 2
          {% else %}
            Tier 1
          {% endif %}

    ## Sensor 2: Import Charges Calculation ##
    - name: "TNB Import Cost"
      unique_id: tnb_import_cost
      unit_of_measurement: "MYR"
      device_class: monetary
      state: >-
        {% set import_energy = states('input_number.tnb_import_energy') | float %}
        {% set above900 = [import_energy - 900, 0] | max %}
        {% set above600 = [import_energy - above900 - 600, 0] | max %}
        {% set above300 = [import_energy - above900 - above600 - 300, 0] | max %}
        {% set above200 = [import_energy - above900 - above600 - above300 - 200, 0] | max %}
        {% set below200 = import_energy - above900 - above600 - above300 - above200 %}
        {{ (above900 * 0.571 + above600 * 0.546 + above300 * 0.516 + above200 * 0.334 + below200 * 0.218) | round(2) }}

    ## Sensor 3: Export Earnings Calculation ##
    - name: "TNB Export Earnings"
      unique_id: tnb_export_earnings
      unit_of_measurement: "MYR"
      device_class: monetary
      state: >-
        {% set export_energy = states('input_number.tnb_export_energy') | float(0) %}
        {% set current_tariff = states('sensor.tnb_tariff_tier') | float(0) %}
        {% if current_tariff >= 0.571 %}
          {% set above900 = min(export_energy, max(0, export_energy - 900)) %}
          {% set above600 = min(export_energy - above900, 300) %}
          {% set above300 = min(export_energy - above900 - above600, 300) %}
          {% set above200 = min(export_energy - above900 - above600 - above300, 100) %}
          {% set below200 = max(export_energy - above900 - above600 - above300 - above200, 0) %}
        {% elif current_tariff >= 0.546 %}
          {% set above900 = 0 %}
          {% set above600 = min(export_energy, 300) %}
          {% set above300 = min(export_energy - above600, 300) %}
          {% set above200 = min(export_energy - above600 - above300, 100) %}
          {% set below200 = max(export_energy - above600 - above300 - above200, 0) %}
        {% elif current_tariff >= 0.516 %}
          {% set above900 = 0 %}
          {% set above600 = 0 %}
          {% set above300 = min(export_energy, 300) %}
          {% set above200 = min(export_energy - above300, 100) %}
          {% set below200 = max(export_energy - above300 - above200, 0) %}
        {% elif current_tariff >= 0.334 %}
          {% set above900 = 0 %}
          {% set above600 = 0 %}
          {% set above300 = 0 %}
          {% set above200 = min(export_energy, 100) %}
          {% set below200 = max(export_energy - above200, 0) %}
        {% else %}
          {% set above900 = 0 %}
          {% set above600 = 0 %}
          {% set above300 = 0 %}
          {% set above200 = 0 %}
          {% set below200 = export_energy %}
        {% endif %}
        {{ (above900 * 0.571 + above600 * 0.546 + above300 * 0.516 + above200 * 0.334 + below200 * 0.218) | round(2) }}

    ## Sensor 4: ICPT Charges ##
    - name: "TNB ICPT Charges"
      unique_id: tnb_icpt_charges
      unit_of_measurement: "MYR"
      device_class: monetary
      state: >-
        {% set import_energy = states('input_number.tnb_import_energy') | float(0) %}
        {% if import_energy <= 600 %}
          {{ (import_energy * -0.02) | round(2) }}
        {% elif import_energy > 1500 %}
          {{ (import_energy * 0.10) | round(2) }}
        {% else %}
          0
        {% endif %}

    ## Sensor 5: SST (8% Tax) ##
    - name: "TNB SST"
      unique_id: tnb_sst
      unit_of_measurement: "MYR"
      device_class: monetary
      state: >-
        {% set import_cost = states('sensor.tnb_import_cost') | float(0) %}
        {% set icpt = states('sensor.tnb_icpt_charges') | float(0) %}
        {% set import_energy = states('input_number.tnb_import_energy') | float(0) %}
        {% if import_energy > 600 %}
          {{ ((import_cost + icpt) * 0.08) | round(2) }}
        {% else %}
          0
        {% endif %}

    ## Sensor 6: KWTBB (1.6%) ##
    - name: "TNB KWTBB"
      unique_id: tnb_kwtbb
      unit_of_measurement: "MYR"
      device_class: monetary
      state: >-
        {% set import_cost = states('sensor.tnb_import_cost') | float(0) %}
        {% set icpt = states('sensor.tnb_icpt_charges') | float(0) %}
        {% if import_cost > 77 %}
          {{ ((import_cost + icpt) * 0.016) | round(2) }}
        {% else %}
          0
        {% endif %}

    ## Sensor 7: Final Import Cost (After Additional Charges) ##
    - name: "TNB Import Cost (Final)"
      unique_id: tnb_import_cost_final
      unit_of_measurement: "MYR"
      device_class: monetary
      state: >-
        {% set import_cost = states('sensor.tnb_import_cost') | float(0) %}
        {% set icpt = states('sensor.tnb_icpt_charges') | float(0) %}
        {% set sst = states('sensor.tnb_sst') | float(0) %}
        {% set kwtbb = states('sensor.tnb_kwtbb') | float(0) %}
        {{ (import_cost + icpt + sst + kwtbb) | round(2) }}

    ## Sensor 8: Final Bill After NEM Offset ##
    - name: "TNB Final Bill"
      unique_id: tnb_final_bill
      unit_of_measurement: "MYR"
      device_class: monetary
      state: >-
        {% set import_cost = states('sensor.tnb_import_cost_final') | float(0) %}
        {% set export_earnings = states('sensor.tnb_export_earnings') | float(0) %}
        {{ (import_cost - export_earnings) | round(2) }}

⚠️ DISCLAIMER

This template serves as simulation and education purposes.

## NEM Calculator
input_number:
tnb_import_energy:
name: "Total Import (kWh)"
min: 0
max: 8000
step: 1
unit_of_measurement: "kWh"
mode: box
tnb_export_energy:
name: "Total Export (kWh)"
min: 0
max: 8000
step: 1
unit_of_measurement: "kWh"
mode: box
template:
- sensor:
## Sensor 1: Current Tariff Tier ##
- name: "TNB Tariff Tier"
unique_id: tnb_tariff_tier
unit_of_measurement: "MYR/kWh"
state: >-
{% set import_energy = states('input_number.tnb_import_energy') | float(0) %}
{% if import_energy > 900 %}
0.571
{% elif import_energy > 600 %}
0.546
{% elif import_energy > 300 %}
0.516
{% elif import_energy > 200 %}
0.334
{% else %}
0.218
{% endif %}
attributes:
blocktariff: >-
{% set import_energy = states('input_number.tnb_import_energy') | float(0) %}
{% if import_energy > 900 %}
Tier 5
{% elif import_energy > 600 %}
Tier 4
{% elif import_energy > 300 %}
Tier 3
{% elif import_energy > 200 %}
Tier 2
{% else %}
Tier 1
{% endif %}
## Sensor 2: Import Charges Calculation ##
- name: "TNB Import Cost"
unique_id: tnb_import_cost
unit_of_measurement: "MYR"
device_class: monetary
state: >-
{% set import_energy = states('input_number.tnb_import_energy') | float %}
{% set above900 = [import_energy - 900, 0] | max %}
{% set above600 = [import_energy - above900 - 600, 0] | max %}
{% set above300 = [import_energy - above900 - above600 - 300, 0] | max %}
{% set above200 = [import_energy - above900 - above600 - above300 - 200, 0] | max %}
{% set below200 = import_energy - above900 - above600 - above300 - above200 %}
{{ (above900 * 0.571 + above600 * 0.546 + above300 * 0.516 + above200 * 0.334 + below200 * 0.218) | round(2) }}
## Sensor 3: Export Earnings Calculation ##
- name: "TNB Export Earnings"
unique_id: tnb_export_earnings
unit_of_measurement: "MYR"
device_class: monetary
state: >-
{% set export_energy = states('input_number.tnb_export_energy') | float(0) %}
{% set current_tariff = states('sensor.tnb_tariff_tier') | float(0) %}
{% if current_tariff >= 0.571 %}
{% set above900 = min(export_energy, max(0, export_energy - 900)) %}
{% set above600 = min(export_energy - above900, 300) %}
{% set above300 = min(export_energy - above900 - above600, 300) %}
{% set above200 = min(export_energy - above900 - above600 - above300, 100) %}
{% set below200 = max(export_energy - above900 - above600 - above300 - above200, 0) %}
{% elif current_tariff >= 0.546 %}
{% set above900 = 0 %}
{% set above600 = min(export_energy, 300) %}
{% set above300 = min(export_energy - above600, 300) %}
{% set above200 = min(export_energy - above600 - above300, 100) %}
{% set below200 = max(export_energy - above600 - above300 - above200, 0) %}
{% elif current_tariff >= 0.516 %}
{% set above900 = 0 %}
{% set above600 = 0 %}
{% set above300 = min(export_energy, 300) %}
{% set above200 = min(export_energy - above300, 100) %}
{% set below200 = max(export_energy - above300 - above200, 0) %}
{% elif current_tariff >= 0.334 %}
{% set above900 = 0 %}
{% set above600 = 0 %}
{% set above300 = 0 %}
{% set above200 = min(export_energy, 100) %}
{% set below200 = max(export_energy - above200, 0) %}
{% else %}
{% set above900 = 0 %}
{% set above600 = 0 %}
{% set above300 = 0 %}
{% set above200 = 0 %}
{% set below200 = export_energy %}
{% endif %}
{{ (above900 * 0.571 + above600 * 0.546 + above300 * 0.516 + above200 * 0.334 + below200 * 0.218) | round(2) }}
## Sensor 4: ICPT Charges ##
- name: "TNB ICPT Charges"
unique_id: tnb_icpt_charges
unit_of_measurement: "MYR"
device_class: monetary
state: >-
{% set import_energy = states('input_number.tnb_import_energy') | float(0) %}
{% if import_energy <= 600 %}
{{ (import_energy * -0.02) | round(2) }}
{% elif import_energy > 1500 %}
{{ (import_energy * 0.10) | round(2) }}
{% else %}
0
{% endif %}
## Sensor 5: SST (8% Tax) ##
- name: "TNB SST"
unique_id: tnb_sst
unit_of_measurement: "MYR"
device_class: monetary
state: >-
{% set import_cost = states('sensor.tnb_import_cost') | float(0) %}
{% set icpt = states('sensor.tnb_icpt_charges') | float(0) %}
{% set import_energy = states('input_number.tnb_import_energy') | float(0) %}
{% if import_energy > 600 %}
{{ ((import_cost + icpt) * 0.08) | round(2) }}
{% else %}
0
{% endif %}
## Sensor 6: KWTBB (1.6%) ##
- name: "TNB KWTBB"
unique_id: tnb_kwtbb
unit_of_measurement: "MYR"
device_class: monetary
state: >-
{% set import_cost = states('sensor.tnb_import_cost') | float(0) %}
{% set icpt = states('sensor.tnb_icpt_charges') | float(0) %}
{% if import_cost > 77 %}
{{ ((import_cost + icpt) * 0.016) | round(2) }}
{% else %}
0
{% endif %}
## Sensor 7: Final Import Cost (After Additional Charges) ##
- name: "TNB Import Cost (Final)"
unique_id: tnb_import_cost_final
unit_of_measurement: "MYR"
device_class: monetary
state: >-
{% set import_cost = states('sensor.tnb_import_cost') | float(0) %}
{% set icpt = states('sensor.tnb_icpt_charges') | float(0) %}
{% set sst = states('sensor.tnb_sst') | float(0) %}
{% set kwtbb = states('sensor.tnb_kwtbb') | float(0) %}
{{ (import_cost + icpt + sst + kwtbb) | round(2) }}
## Sensor 8: Final Bill After NEM Offset ##
- name: "TNB Final Bill"
unique_id: tnb_final_bill
unit_of_measurement: "MYR"
device_class: monetary
state: >-
{% set import_cost = states('sensor.tnb_import_cost_final') | float(0) %}
{% set export_earnings = states('sensor.tnb_export_earnings') | float(0) %}
{{ (import_cost - export_earnings) | round(2) }}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment