找回密碼
 立即註冊
惟家LINE群QRCODE
    查看: 3|回覆: 0

    房間顏色隨溫度連續變化(Picture Elements 做不到的效果)

    [複製鏈接]

    !lvup!   100%

    363

    主題

    27

    回帖

    2200萬

    積分

    管理員

    積分
    22005752
    發表於 3 天前 | 顯示全部樓層 |閱讀模式
    這篇做一個效果:每個房間的顏色反映它的溫度

    20 度是藍的、25 度是綠的、30 度是紅的,而且是連續漸變。



    為什麼 Picture Elements 做不到

    Picture Elements 的房間顏色是一張圖片

    要做「連續變化」的話,你得準備幾十張不同顏色的 PNG ——
    不切實際。

    SVG 的房間是一個可以隨時改顏色的圖形
    所以只要算出「這個溫度該是什麼顏色」就好。

    做法:用 style_set 算顏色
    1.   rules:
    2.     - entities:
    3.         - sensor.living_room_temperature
    4.       elements:
    5.         - room.living_room          # SVG 裡房間的 id
    6.       state_action:
    7.         action: call-service
    8.         service: floorplan.style_set
    9.         service_data: >
    10.           ${(() => {
    11.             const t = parseFloat(entity.state);
    12.             if (isNaN(t)) return 'fill: #E0E0E0; transition: fill 1.5s;';
    13.             // 把 18~32 度對應到色相 210(藍) ~ 0(紅)
    14.             const lo = 18, hi = 32;
    15.             const p = Math.max(0, Math.min(1, (t - lo) / (hi - lo)));
    16.             const hue = 210 - p * 210;
    17.             return 'fill: hsl(' + hue.toFixed(0) + ', 65%, 72%);'
    18.                  + ' transition: fill 1.5s ease-in-out;';
    19.           })()}
    複製代碼

    這段在做什麼



    關鍵是用 HSL 而不是 RGB。

    HSL = 色相(Hue)、飽和度(Saturation)、亮度(Lightness)


    • 色相是一個 0-360 的圓環
        0 = 紅、120 = 綠、210 = 藍、240 = 深藍
    • 飽和度:0% 是灰的、100% 是最鮮豔
    • 亮度:0% 是黑、50% 是標準、100% 是白


    為什麼 HSL 比 RGB 好用

    你只要改一個數字(色相),顏色就從藍變綠變紅

    用 RGB 的話要同時算三個數字,而且中間會經過很醜的顏色。

    那段算式拆開來看
    1. const p = (t - 18) / (32 - 18)
    2. // 18 度 → p = 0
    3. // 25 度 → p = 0.5
    4. // 32 度 → p = 1
    5. const hue = 210 - p * 210
    6. // p = 0   → hue = 210(藍)
    7. // p = 0.5 → hue = 105(綠)
    8. // p = 1   → hue = 0  (紅)
    複製代碼

    Math.max(0, Math.min(1, ...)) 是把 p 限制在 0-1 之間 ——
    溫度超出範圍的話不會算出奇怪的顏色。

    transition: fill 1.5s

    這一行讓顏色是「慢慢變」的

    溫度感測器可能一分鐘才回報一次,
    沒有 transition 的話顏色會一格一格跳,很不自然。

    調整參數的建議



    範圍(lo 和 hi)


    • 台灣室內常見範圍:22-30 度
    • 範圍太寬 → 顏色變化不明顯
    • 範圍太窄 → 整年都是紅的或藍的


    我的建議:夏天用 24-32、冬天用 16-26
    或者乾脆全年用 20-30。

    飽和度和亮度
    1. hsl(hue, 65%, 72%)
    複製代碼


    • 飽和度 65% —— 再高會太刺眼
    • 亮度 72% —— 淺一點,讓上面的文字和圖示看得清楚


    如果房間文字看不清楚,把亮度調到 80%。

    同樣的技巧可以用在

    濕度
    1.             // 40~80% 對應 淺黃 ~ 深藍
    2.             const h = parseFloat(entity.state);
    3.             const p = Math.max(0, Math.min(1, (h - 40) / 40));
    4.             const hue = 50 + p * 160;   // 50(黃) → 210(藍)
    5.             return 'fill: hsl(' + hue + ', 60%, 75%); transition: fill 1.5s;';
    複製代碼

    空氣品質(CO2)
    1.             // 400~1500 ppm 對應 綠 ~ 紅
    2.             const c = parseFloat(entity.state);
    3.             const p = Math.max(0, Math.min(1, (c - 400) / 1100));
    4.             const hue = 120 - p * 120;   // 120(綠) → 0(紅)
    5.             return 'fill: hsl(' + hue + ', 60%, 72%); transition: fill 2s;';
    複製代碼

    用電量
    1.             // 0~2000W 對應 淺灰 ~ 橘
    2.             const w = parseFloat(entity.state);
    3.             const p = Math.max(0, Math.min(1, w / 2000));
    4.             return 'fill: hsl(30, ' + (p * 70).toFixed(0) + '%, '
    5.                  + (92 - p * 25).toFixed(0) + '%); transition: fill 1s;';
    複製代碼

    這個是改飽和度和亮度,色相固定在橘色 ——
    用電越多,橘色越濃。



    進階:用一個開關切換顯示模式

    做一個 input_select 讓使用者選「現在要看什麼」:
    1. input_select:
    2.   floorplan_mode:
    3.     name: 平面圖顯示模式
    4.     options:
    5.       - 燈光
    6.       - 溫度
    7.       - 濕度
    8.       - 空氣品質
    9.     initial: 燈光
    複製代碼

    然後規則裡加判斷:
    1.   rules:
    2.     - entities:
    3.         - sensor.living_room_temperature
    4.         - input_select.floorplan_mode
    5.       elements:
    6.         - room.living_room
    7.       state_action:
    8.         action: call-service
    9.         service: floorplan.style_set
    10.         service_data: >
    11.           ${(() => {
    12.             const mode = entities['input_select.floorplan_mode'].state;
    13.             if (mode !== '溫度') return '';    // 不是溫度模式就不動
    14.             const t = parseFloat(entities['sensor.living_room_temperature'].state);
    15.             if (isNaN(t)) return 'fill: #E0E0E0;';
    16.             const p = Math.max(0, Math.min(1, (t - 20) / 12));
    17.             return 'fill: hsl(' + (210 - p * 210).toFixed(0)
    18.                  + ', 65%, 72%); transition: fill 1.5s;';
    19.           })()}
    複製代碼

    注意


    • entities 要把 input_select 也列進去
        否則切換模式時規則不會被觸發
    • 用 entities['實體ID'] 存取其他實體
        不是只能用 entity(那個是「觸發這條規則的實體」)


    一個很實用的加分項:把數字也顯示出來

    光看顏色不知道是幾度。在房間裡放一個文字元素:
    1.     - entities:
    2.         - sensor.living_room_temperature
    3.       elements:
    4.         - text.living_room_temp       # SVG 裡文字元素的 id
    5.       state_action:
    6.         action: call-service
    7.         service: floorplan.text_set
    8.         service_data: '${parseFloat(entity.state).toFixed(1) + "°"}'
    複製代碼

    顏色告訴你「大概怎樣」,數字告訴你「確切多少」。

    效能提醒

    這類規則會在每次感測器回報時執行。

    如果你有十個溫度感測器、每分鐘回報一次,
    那就是一分鐘十次 JavaScript 執行 + DOM 更新

    實際上完全沒問題(瀏覽器處理這種量綽綽有餘),
    但如果你的感測器設定成每秒回報,那就該去改回報間隔了。

    在 Z2M 裡可以設定回報門檻(變化超過 0.5 度才回報),
    這對電池壽命和效能都有幫助。

    ---

    下一篇:SVG 平面圖的完整除錯指南

    把前面提到的所有問題整理成一份速查。
    您需要登入後纔可以回帖 登入 | 立即註冊

    本版積分規則

    Archiver|手機版|惟家的智能論壇

    GMT+8, 2026-9-24 12:18 , Processed in 0.076155 second(s), 24 queries .

    快速回覆 返回頂部 返回列表