插座量得到功率,家電就會自己告訴你它在做什麼:電鍋煮好了、洗衣機洗完了、開著忘了關。這篇講我家電量監測實際在用的事,分兩層:插在插座上的功率感測,和裝在電箱裡的逐迴路電表。最後附兩段可以直接抄的 YAML。

2023 年 11 月,我在智慧家庭的筆記裡把「全家電量監測」劃掉,旁邊寫:「監測這個好像不能幹嘛,我們家的電費已經不高了。」同一頁還寫了一句「直接用插座監測更好」。三年後回頭看,後面那句是對的,前面那句只對了一半:電費確實不是重點,但「能幹嘛」比我想的多很多。

插座層:讓家電告訴你它在做什麼

原理很簡單。家電在工作和待機時的耗電差很多,所以只要知道它現在吃多少瓦,就知道它在哪個階段。智慧插座或功率感測延長線,大多會把即時功率回報給 Home Assistant,剩下的就是寫規則。

電鍋:煮好通知、忘了按就斷電

  • 煮好了:功率掉到 50 W 以下,代表從加熱跳到保溫,通知「煮好了」。
  • 防空燒:插頭插上、開關打開,但三分鐘都沒有動作,就自己斷電。防的是那個一年一次、忘了按下去的空燒。

洗衣機:洗好會廣播,沒人收會催

  • 功率低於 3.5 W,從家裡的喇叭廣播「洗好了」。
  • 一小時沒人去收,再催一次。
  • 洗衣次數累積超過 30 次,提醒洗槽。

洗衣機的功率曲線很亂:注水、轉、停、脫水,中間會有幾段幾乎零功率的空檔。所以門檻之外,一定要加一個持續時間,例如「低於 3.5 W 持續 3 分鐘」,不然洗到一半就會被當成洗好了。

一樣的做法還能用在

只要「工作」和「沒在工作」的功率分得開,就能這樣做:烘衣機、洗碗機、熱水壺、除濕機(水箱滿了會停機,功率掉下來就知道該倒水了)。我家廚房家電這組有 8 條自動化,洗衣 4 條,從 2023 年底建好到現在幾乎沒動過。

電箱層:每一條迴路用了多少電

插座只看得到插在上面的那一台。想看整個家,就要往電箱走。

我家電箱裡裝了一台 Emporia Vue,8 條分路各夾一顆電流鉤,每一路的用電都進到 Home Assistant。看得到的東西變成:

  • 哪一條迴路在吃電。 冷氣、廚房、某個房間,分開看。
  • 全家這期用了多少。 從計費週期開始那天累積起來。
  • 預估電費。 把本期用電外推成兩個月,套台電的累進級距表,就有預估電價和預估電費。

說實話,我後來發現自己對電費的興趣沒那麼大 🤣 這組數字最常被我拿來看的,是 AI 每天開著跑、燒 GPU 之後,那條迴路的用電真的上去了。

預估數字,一定要看分母

電費預估讓我踩了三次假警報,三次是同一件事。值得單獨講,因為任何「用目前資料外推」的數字都會遇到。

預估的公式是:

本期累積用電 ÷ 週期已過天數 × 60 天 = 預估兩個月用電

一、計費週期重置的那一刻。 2026 年 8 月 13 日 00:00 週期重置,累積用電歸零,天數回到 1。00:01 分母只有一分鐘,這一分鐘的用電被乘以 60 天,預估電價直接碰到台電累進級距的最高那一級,那天沒有人多用一度電。之後幾分鐘在第 4、5 級之間換,到中午分母累積到半天,才回到前一晚的第 3 級。分母小的時候,分子的任何一點雜訊都會被放大 60 倍。

計費週期重置那一天預估電價級距怎麼跳:前一晚第 3 級,00:00 除以零變 unavailable,00:01 到 00:10 在第 4 到 6 級之間換,之後停在第 4 級整個上午,12:18 回到第 3 級

二、兩條歸零線。 2026 年 5 月,累積用電每到月初就歸零,但帳單週期是 60 天。原來有兩個地方都在負責歸零:Home Assistant 內建的 utility meter 照日曆走,我自己寫的計數器照 60 天走,兩條不同步。一個數字只能有一個地方負責歸零。

三、整批數字消失。 2026 年 8 月 23 日主機升級重開後,電表的整合沒有再載入,54 個電力數字一起消失,兩天後才發現。從 Emporia 的雲端回填之前,先拿前一天正常的資料對帳,8 條分路誤差都在 0.6% 以內才寫進去;補不回來的 34 分鐘就留白。

還有一個不會發警報的:級距表本身會過期。台電 2025 年 10 月 1 日調過價,2026 年 3 月的電價審議會決議不調;寫死在公式裡的舊電價不會自己知道,只會安靜地每天錯一點。

查清楚之後,我把「預估電費每多 100 元就通知」的提醒關了,公式沒有修。理由很簡單:我本來就不想管電費,而加特例會讓下一次除錯更難。如果你要做電費預估,下面的 YAML 是照這三件事重寫的版本。

2026 年要裝,先知道的事

逐迴路電表:Emporia Vue 出廠是純雲端的,資料先上它的伺服器再回到你家,斷網就沒有數字。社群有把 Vue 2 和 Vue 3 刷成 ESPHome 的專案(emporia-vue-local),刷完完全本地。代價是要開殼接序列線,Vue 3 得焊到板子背面的焊點或用治具,刷之前一定先把原廠韌體備份下來,刷完原廠 app 就不能用了。電流鉤那一端是市電,裝機找電工。

台電智慧電表:Home Assistant 目前沒有成熟的官方整合。社群的幾個方案,有的拿台電 app 帳密去撈後端、好幾年沒更新;有的要你從瀏覽器開發者工具抓 cookie 和 token,幾小時就過期。如果你要的只是「本期用了多少」,台電 app 的用電查詢已經有 15 分鐘粒度。

插座層:最划算。先挑你最常忘記的那一台家電,一顆插座就能開始。

可以直接抄的部分

洗衣機洗好通知(示意版)

先建一個 input_boolean.washer_running helper 記「正在洗」,把 sensor.washer_power 換成你插座的功率感測器,notify.notify 換成你的通知服務。門檻和持續時間依你家洗衣機的功率曲線調。

alias: 洗衣機洗好通知
mode: single
triggers:
  - trigger: numeric_state          # 開始洗:功率高於 10 W 持續 1 分鐘
    id: start
    entity_id: sensor.washer_power
    above: 10
    for: "00:01:00"
  - trigger: numeric_state          # 洗好:功率低於 3.5 W 持續 3 分鐘
    id: done
    entity_id: sensor.washer_power
    below: 3.5
    for: "00:03:00"
actions:
  - choose:
      - conditions:
          - condition: trigger
            id: start
        sequence:
          - action: input_boolean.turn_on
            target:
              entity_id: input_boolean.washer_running
      - conditions:
          - condition: trigger
            id: done
          - condition: state          # 剛剛真的在洗,才算洗好
            entity_id: input_boolean.washer_running
            state: "on"
        sequence:
          - action: input_boolean.turn_off
            target:
              entity_id: input_boolean.washer_running
          - action: notify.notify
            data:
              message: "洗衣機洗好了"

電費預估:分母看得到的累進級距

這份跟我家跑的那版不一樣,是照上面三件事重寫的:只有一個地方負責歸零(utility meter 的 cron),分母是一個獨立的 sensor,分母不到 3 天預估就是 unavailable,級距表是 2025 年 10 月 1 日起實施的台電表燈非時間電價的兩個月版本。把 sensor.your_total_energy_kwh 換成你家電表的總累積度數,cron 改成你的抄表日。

# 一、累積用電:整套系統裡唯一會歸零的地方
utility_meter:
  power_cycle_energy:
    source: sensor.your_total_energy_kwh     # 電表總累積度數(kWh,total_increasing)
    name: Power cycle energy
    cron: "0 0 13 1,3,5,7,9,11 *"            # 例:奇數月 13 日 00:00 歸零,改成你的抄表日

template:
  - sensor:
      # 二、分母:週期已過天數,做成看得到的 sensor
      - name: Power cycle days
        unique_id: power_cycle_days
        unit_of_measurement: d
        state: >-
          {% set reset = state_attr('sensor.power_cycle_energy', 'last_reset') %}
          {% if reset is none %}unknown{% else %}
          {{ ((now() - (reset | as_datetime)).total_seconds() / 86400) | round(2) }}
          {% endif %}

      # 三、外推:分母不到 3 天就不給數字,附可信度
      - name: Power cycle estimate
        unique_id: power_cycle_estimate
        unit_of_measurement: kWh
        availability: "{{ states('sensor.power_cycle_days') | float(0) >= 3 }}"
        state: >-
          {% set kwh = states('sensor.power_cycle_energy') | float(0) %}
          {% set days = states('sensor.power_cycle_days') | float(1) %}
          {{ (kwh / days * 60) | round(0) }}
        attributes:
          confidence: >-
            {% set d = states('sensor.power_cycle_days') | float(0) %}
            {{ 'low' if d < 7 else 'medium' if d < 21 else 'high' }}

      # 四、級距電價:表燈非時間電價,兩個月級距=月級距 ×2,2025-10-01 版
      - name: Power tier price
        unique_id: power_tier_price
        unit_of_measurement: "TWD/kWh"
        availability: "{{ has_value('sensor.power_cycle_estimate') }}"
        state: >-
          {% set kwh = states('sensor.power_cycle_estimate') | float(0) %}
          {% set reset = state_attr('sensor.power_cycle_energy', 'last_reset') | as_datetime %}
          {% set ns = namespace(summer=0, price=none) %}
          {% for i in range(60) %}
            {% if (reset + timedelta(days=i)).month in [6, 7, 8, 9] %}
              {% set ns.summer = ns.summer + 1 %}
            {% endif %}
          {% endfor %}
          {% set r = ns.summer / 60 %}
          {% set tiers = [(240, 1.78, 1.78), (660, 2.55, 2.26), (1000, 3.80, 3.13),
                          (1400, 5.14, 4.24), (2000, 6.44, 5.27), (999999, 8.86, 7.03)] %}
          {% for t in tiers if ns.price is none %}
            {% if kwh <= t[0] %}{% set ns.price = t[1] * r + t[2] * (1 - r) %}{% endif %}
          {% endfor %}
          {{ ns.price | round(2) }}

      # 五、預估電費:逐級累進加總
      - name: Power cycle bill
        unique_id: power_cycle_bill
        unit_of_measurement: TWD
        availability: "{{ has_value('sensor.power_cycle_estimate') }}"
        state: >-
          {% set kwh = states('sensor.power_cycle_estimate') | float(0) %}
          {% set reset = state_attr('sensor.power_cycle_energy', 'last_reset') | as_datetime %}
          {% set ns = namespace(summer=0, total=0.0, left=kwh, prev=0) %}
          {% for i in range(60) %}
            {% if (reset + timedelta(days=i)).month in [6, 7, 8, 9] %}
              {% set ns.summer = ns.summer + 1 %}
            {% endif %}
          {% endfor %}
          {% set r = ns.summer / 60 %}
          {% set tiers = [(240, 1.78, 1.78), (660, 2.55, 2.26), (1000, 3.80, 3.13),
                          (1400, 5.14, 4.24), (2000, 6.44, 5.27), (999999, 8.86, 7.03)] %}
          {% for t in tiers if ns.left > 0 %}
            {% set use = [ns.left, t[0] - ns.prev] | min %}
            {% set ns.total = ns.total + use * (t[1] * r + t[2] * (1 - r)) %}
            {% set ns.left = ns.left - use %}
            {% set ns.prev = t[0] %}
          {% endfor %}
          {{ ns.total | round(0) }}

三個提醒。第一,60 要跟你的 cron 週期一致。第二,級距表是常數,把「每年 4 月和 10 月看一次電價表」寫進你的維運清單。第三,如果要在上面掛通知,觸發條件加一個 confidence == 'high',讓提醒只在分母夠大的時候說話。

這份後來整理成一個可以直接裝的 package:ha-taipower-estimate(https://github.com/drpwchen/ha-taipower-estimate)。比上面多了週期進度和夏月占比兩個 sensor、住宅以外的表燈電價和單月抄表,費率集中在一個附官方電價表連結的檔,還有一支帶入電表 sensor 和抄表日就產出 YAML 的產生器,以及 38 個對照台電官方算例的測試。它還沒在真的 Home Assistant 上跑過,第一次裝請對照你的電費單。

最後

有了插座層,家裡最常被忘記的那幾台家電會自己開口;有了電箱層,你會知道錢花在哪一條迴路。兩層都裝的話,先從插座開始,一顆就能開始用。