mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 16:38:31 +00:00
v1.44.6: room climate counts only air temperature
After v1.44.5 read the area registry instead of visible icons, every hidden temperature entity in the area became a candidate, including ones measuring something other than room air. Verified against a live 60-area install: a NAS processor temperature, kettle water, a 90 C sauna heater and a virtual better_thermostat all leaked into room averages. - areaClimate(): skip entity_category (diagnostic/config), skip EXCLUDED_DOMAINS platforms, skip entity ids naming a non-air medium (water/coolant/flow_temp/ return_temp/target/setpoint/chip/cpu/processor/board/device_temp/batter/ freezer/fridge/oven/kettle/boiler). - rules.ts: kettle/thermopot -> mdi:kettle, sauna/harvia -> mdi:hot-tub, so they no longer fall through to the generic thermometer rule. - test: all four real false positives asserted out, one real sensor left. - docs: CHANGELOG.md + CHANGELOG.ru.md + STATUS.md snapshot.
This commit is contained in:
@@ -13,7 +13,7 @@ FILES_URL = "/houseplan_files/files"
|
|||||||
CONTENT_URL = "/api/houseplan/content"
|
CONTENT_URL = "/api/houseplan/content"
|
||||||
FILES_DIR = "houseplan/files"
|
FILES_DIR = "houseplan/files"
|
||||||
CONF_ADMIN_ONLY = "admin_only"
|
CONF_ADMIN_ONLY = "admin_only"
|
||||||
VERSION = "1.44.5"
|
VERSION = "1.44.6"
|
||||||
|
|
||||||
DEFAULT_CONFIG: dict = {
|
DEFAULT_CONFIG: dict = {
|
||||||
"spaces": [],
|
"spaces": [],
|
||||||
|
|||||||
@@ -16,5 +16,5 @@
|
|||||||
"issue_tracker": "https://github.com/Matysh/houseplan-card/issues",
|
"issue_tracker": "https://github.com/Matysh/houseplan-card/issues",
|
||||||
"requirements": [],
|
"requirements": [],
|
||||||
"single_config_entry": true,
|
"single_config_entry": true,
|
||||||
"version": "1.44.5"
|
"version": "1.44.6"
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Vendored
+13
-13
File diff suppressed because one or more lines are too long
@@ -1,5 +1,23 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v1.44.6 — 2026-07-27
|
||||||
|
- **Only room *air* counts as room climate.** After v1.44.5 started reading the
|
||||||
|
area registry instead of the visible icons, every hidden temperature entity in
|
||||||
|
the area became a candidate — including ones that measure something other than
|
||||||
|
the air. Three guards now run before averaging: entities marked
|
||||||
|
diagnostic/config are skipped, entities from curated-out integrations are
|
||||||
|
skipped, and entity ids naming a non-air medium are skipped
|
||||||
|
(`water`, `coolant`, `flow_temp`, `return_temp`, `target`, `setpoint`, `chip`,
|
||||||
|
`cpu`, `processor`, `board`, `device_temp`, `batter`, `freezer`, `fridge`,
|
||||||
|
`oven`, `kettle`, `boiler`).
|
||||||
|
On a live 60-area install this removed four real false positives: a NAS
|
||||||
|
processor temperature, the water in a smart kettle, a 90 °C sauna heater and a
|
||||||
|
virtual `better_thermostat` duplicating the real sensor.
|
||||||
|
- **New icon rules:** kettles/thermopots get `mdi:kettle`, saunas
|
||||||
|
(`sauna`, `harvia`, `парная`) get `mdi:hot-tub` — previously both fell through
|
||||||
|
to the generic thermometer rule, which is also what made them count as room
|
||||||
|
climate.
|
||||||
|
|
||||||
## v1.44.5 — 2026-07-27
|
## v1.44.5 — 2026-07-27
|
||||||
- **Room climate now counts every sensor in the area**, including devices that
|
- **Room climate now counts every sensor in the area**, including devices that
|
||||||
are not placed on the plan (hidden by curation or by you). Previously the
|
are not placed on the plan (hidden by curation or by you). Previously the
|
||||||
|
|||||||
@@ -6,6 +6,23 @@
|
|||||||
> **Правило проекта:** оба файла пополняются в одном коммите с самим
|
> **Правило проекта:** оба файла пополняются в одном коммите с самим
|
||||||
> изменением — как и остальная документация (см. docs/STATUS.md).
|
> изменением — как и остальная документация (см. docs/STATUS.md).
|
||||||
|
|
||||||
|
## v1.44.6 — 2026-07-27
|
||||||
|
- **Климатом комнаты считается только температура *воздуха*.** После v1.44.5,
|
||||||
|
когда данные стали браться из реестра зон, а не с видимых значков,
|
||||||
|
кандидатами стали все скрытые датчики температуры в зоне — в том числе те,
|
||||||
|
что меряют вовсе не воздух. Перед усреднением теперь работают три фильтра:
|
||||||
|
пропускаются сущности с категорией диагностика/настройка, сущности
|
||||||
|
исключённых интеграций и сущности, в id которых назван не-воздушный носитель
|
||||||
|
(`water`, `coolant`, `flow_temp`, `return_temp`, `target`, `setpoint`, `chip`,
|
||||||
|
`cpu`, `processor`, `board`, `device_temp`, `batter`, `freezer`, `fridge`,
|
||||||
|
`oven`, `kettle`, `boiler`).
|
||||||
|
На живой установке с 60 зонами это убрало четыре реальных ложных
|
||||||
|
срабатывания: температуру процессора NAS, воду в умном чайнике, сауну с 90 °C
|
||||||
|
и виртуальный `better_thermostat`, дублирующий настоящий датчик.
|
||||||
|
- **Новые правила иконок:** чайники и термопоты получают `mdi:kettle`, сауны
|
||||||
|
(`sauna`, `harvia`, `парная`) — `mdi:hot-tub`. Раньше и те и другие попадали
|
||||||
|
под общее правило термометра, из-за чего и учитывались в климате комнаты.
|
||||||
|
|
||||||
## v1.44.5 — 2026-07-27
|
## v1.44.5 — 2026-07-27
|
||||||
- **Климат комнаты считается по всем датчикам зоны**, включая устройства,
|
- **Климат комнаты считается по всем датчикам зоны**, включая устройства,
|
||||||
которых нет на плане (скрыты курированием или вами). Раньше среднее бралось
|
которых нет на плане (скрыты курированием или вами). Раньше среднее бралось
|
||||||
|
|||||||
+4
-4
@@ -11,18 +11,18 @@
|
|||||||
> (versions, publication, infrastructure), DEVELOPMENT.md for new gotchas,
|
> (versions, publication, infrastructure), DEVELOPMENT.md for new gotchas,
|
||||||
> ARCHITECTURE.md for design changes, ROADMAP.md when plans move.
|
> ARCHITECTURE.md for design changes, ROADMAP.md when plans move.
|
||||||
|
|
||||||
## Snapshot (2026-07-24)
|
## Snapshot (2026-07-27)
|
||||||
|
|
||||||
| Item | State |
|
| Item | State |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Version | **v1.41.0** everywhere (manifest, const.py, package.json, CARD_VERSION); deployed to the home instance |
|
| Version | **v1.44.6** everywhere (manifest, const.py, package.json, CARD_VERSION); deployed to the home instance |
|
||||||
| Workflow | Since 2026-07-22: minor changes go to branch **`dev`** (build + smokes → deploy home → commit → push, NO release); releases are batched on the owner's command (merge dev→main, one tag, one release with a summary changelog, CI checked on dev beforehand) |
|
| Workflow | Since 2026-07-22: minor changes go to branch **`dev`** (build + smokes → deploy home → commit → push, NO release); releases are batched on the owner's command (merge dev→main, one tag, one release with a summary changelog, CI checked on dev beforehand) |
|
||||||
| GitHub | https://github.com/Matysh/houseplan-card — `main` = releases up to **v1.40.1**; `dev` ahead with v1.40.2+ (speaker icons, kiosk). Push via SSH key `ha_jb` (remote git@github.com:…); API releases via the fine-grained PAT in `~/.git-credentials` (Contents R/W, issued 2026-07-23) |
|
| GitHub | https://github.com/Matysh/houseplan-card — `main` = releases up to **v1.40.1**; `dev` ahead with v1.40.2+ (speaker icons, kiosk). Push via SSH key `ha_jb` (remote git@github.com:…); API releases via the fine-grained PAT in `~/.git-credentials` (Contents R/W, issued 2026-07-23) |
|
||||||
| CI | validate.yml (hacs + hassfest + frontend + backend) green; release.yml attaches the bundle on release publish |
|
| CI | validate.yml (hacs + hassfest + frontend + backend) green; release.yml attaches the bundle on release publish |
|
||||||
| HACS | Custom repository works. **Inclusion PR: hacs/default#9004** — open, valid, labeled; ~864 older open PRs but merge rate ≈180/mo; realistic ETA 1–3 months (checked 2026-07-24) |
|
| HACS | Custom repository works. **Inclusion PR: hacs/default#9004** — open, valid, labeled; ~864 older open PRs but merge rate ≈180/mo; realistic ETA 1–3 months (checked 2026-07-24) |
|
||||||
| Home instance | ha.jbstudio.pro (SSH port 323, key `ha_jb`), deployed **v1.41.0** via direct copy (HACS custom repo also installed) |
|
| Home instance | ha.jbstudio.pro (SSH port 323, key `ha_jb`), deployed **v1.44.6** via direct copy (HACS custom repo also installed) |
|
||||||
| Localization | UI en/ru (src/i18n/*.json), everything user-visible localized incl. kiosk popover |
|
| Localization | UI en/ru (src/i18n/*.json), everything user-visible localized incl. kiosk popover |
|
||||||
| Tests | 111 frontend (node:test) + 12 pure backend + 12 HA-harness (CI, py3.13); ~30 demo smoke suites (headless chromium) |
|
| Tests | 121 frontend (node:test) + 12 pure backend + 12 HA-harness (CI, py3.13); ~30 demo smoke suites (headless chromium) |
|
||||||
| Community | **Telegram chat: https://t.me/ha_houseplan** (created 2026-07-27) — the primary user-facing support channel; GitHub issues stay for bugs/features. Link it from any new release notes and posts |
|
| Community | **Telegram chat: https://t.me/ha_houseplan** (created 2026-07-27) — the primary user-facing support channel; GitHub issues stay for bugs/features. Link it from any new release notes and posts |
|
||||||
| Product scope | docs/SCOPE.md (2026-07-22) is the feature guard rail — check before accepting any feature |
|
| Product scope | docs/SCOPE.md (2026-07-22) is the feature guard rail — check before accepting any feature |
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "houseplan-card",
|
"name": "houseplan-card",
|
||||||
"version": "1.44.5",
|
"version": "1.44.6",
|
||||||
"description": "Interactive house plan Lovelace card for Home Assistant",
|
"description": "Interactive house plan Lovelace card for Home Assistant",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
+22
-1
@@ -2,7 +2,7 @@
|
|||||||
* Building the device list from HA registries: curation, light groups,
|
* Building the device list from HA registries: curation, light groups,
|
||||||
* markers (overrides/virtual). No Lit/DOM — only the hass object.
|
* markers (overrides/virtual). No Lit/DOM — only the hass object.
|
||||||
*/
|
*/
|
||||||
import { iconFor, iconFromDeviceClasses, DOMAIN_PRIORITY, FALLBACK_ICON, type CompiledIconRule } from './rules';
|
import { iconFor, iconFromDeviceClasses, DOMAIN_PRIORITY, FALLBACK_ICON, type CompiledIconRule, EXCLUDED_DOMAINS } from './rules';
|
||||||
import { averageLqi } from './logic';
|
import { averageLqi } from './logic';
|
||||||
import type { DevItem, Marker, ServerConfig } from './types';
|
import type { DevItem, Marker, ServerConfig } from './types';
|
||||||
|
|
||||||
@@ -400,6 +400,20 @@ export function areaHum(
|
|||||||
return Math.round(vals.reduce((a, b) => a + b, 0) / vals.length);
|
return Math.round(vals.reduce((a, b) => a + b, 0) / vals.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity ids that measure something other than room air, however honest their
|
||||||
|
* device_class is: water and coolant loops, chip/CPU/board temperatures,
|
||||||
|
* battery temperature, setpoints (target/external) and the like.
|
||||||
|
*/
|
||||||
|
const NON_AIR_RE = new RegExp(
|
||||||
|
[
|
||||||
|
'water', 'voda', 'coolant', 'flow_?temp', 'return_?temp', 'target', 'setpoint',
|
||||||
|
'chip', 'cpu', 'processor', 'board', 'core_temp', 'device_temp',
|
||||||
|
'batter', 'akkum', 'freezer', 'fridge', 'oven', 'kettle', 'boiler',
|
||||||
|
].join('|'),
|
||||||
|
'i',
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Room climate from EVERY sensor of the area — including devices that are not
|
* Room climate from EVERY sensor of the area — including devices that are not
|
||||||
* placed on the plan (hidden by curation or by the user). The old helpers read
|
* placed on the plan (hidden by curation or by the user). The old helpers read
|
||||||
@@ -420,6 +434,13 @@ export function areaClimate(
|
|||||||
const dev = reg.device_id ? hass.devices?.[reg.device_id] : null;
|
const dev = reg.device_id ? hass.devices?.[reg.device_id] : null;
|
||||||
const entArea = reg.area_id || dev?.area_id || null;
|
const entArea = reg.area_id || dev?.area_id || null;
|
||||||
if (entArea !== area) continue;
|
if (entArea !== area) continue;
|
||||||
|
// Not every "temperature" is room air. Real finds on a live install: the
|
||||||
|
// NAS processor temperature, the water in a smart kettle, a sauna heater at
|
||||||
|
// 90 C and a virtual better_thermostat duplicating the real sensor (field
|
||||||
|
// question, 2026-07-27). Three guards, cheapest first:
|
||||||
|
if (reg.entity_category) continue; // diagnostic/config readings
|
||||||
|
if (EXCLUDED_DOMAINS.has(reg.platform)) continue; // curated-out integrations
|
||||||
|
if (NON_AIR_RE.test(eid)) continue; // water/chip/flow/target/...
|
||||||
const key = reg.device_id || eid;
|
const key = reg.device_id || eid;
|
||||||
if (!groups.has(key)) {
|
if (!groups.has(key)) {
|
||||||
const st = hass.states?.[eid];
|
const st = hass.states?.[eid];
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import './space-card';
|
|||||||
import { cardStyles } from './styles';
|
import { cardStyles } from './styles';
|
||||||
import { langOf, t, type I18nKey } from './i18n';
|
import { langOf, t, type I18nKey } from './i18n';
|
||||||
|
|
||||||
const CARD_VERSION = '1.44.5';
|
const CARD_VERSION = '1.44.6';
|
||||||
const LS_KEY = 'houseplan_card_layout_v1';
|
const LS_KEY = 'houseplan_card_layout_v1';
|
||||||
const LS_CFG = 'houseplan_card_cfg_v1'; // cache of the server config+layout for instant rendering
|
const LS_CFG = 'houseplan_card_cfg_v1'; // cache of the server config+layout for instant rendering
|
||||||
const LS_ZOOM = 'houseplan_card_zoom_v1';
|
const LS_ZOOM = 'houseplan_card_zoom_v1';
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ export const DEFAULT_ICON_RULES: IconRule[] = [
|
|||||||
{ pattern: 'клапан|valve', icon: 'mdi:pipe-valve' },
|
{ pattern: 'клапан|valve', icon: 'mdi:pipe-valve' },
|
||||||
{ pattern: 'дым|smoke', icon: 'mdi:smoke-detector' },
|
{ pattern: 'дым|smoke', icon: 'mdi:smoke-detector' },
|
||||||
{ pattern: 'термоголов|trv|radiator', icon: 'mdi:radiator' },
|
{ pattern: 'термоголов|trv|radiator', icon: 'mdi:radiator' },
|
||||||
|
{ pattern: 'чайник|kettle|термопот', icon: 'mdi:kettle' },
|
||||||
|
{ pattern: 'сауна|sauna|harvia|парная|парилк', icon: 'mdi:hot-tub' },
|
||||||
{ pattern: 'температ|temperature|climate sensor', icon: 'mdi:thermometer' },
|
{ pattern: 'температ|temperature|climate sensor', icon: 'mdi:thermometer' },
|
||||||
{ pattern: 'qingping|air monitor|молекул|air quality', icon: 'mdi:air-filter' },
|
{ pattern: 'qingping|air monitor|молекул|air quality', icon: 'mdi:air-filter' },
|
||||||
{ pattern: 'штор|curtain|blind|shade', icon: 'mdi:roller-shade' },
|
{ pattern: 'штор|curtain|blind|shade', icon: 'mdi:roller-shade' },
|
||||||
|
|||||||
@@ -400,3 +400,39 @@ test('areaClimate: counts sensors that are NOT on the plan (field report)', () =
|
|||||||
};
|
};
|
||||||
assert.equal(areaClimate(hass2, 'kitchen', 'temp'), null);
|
assert.equal(areaClimate(hass2, 'kitchen', 'temp'), null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('areaClimate: only ROOM AIR counts (field question, 2026-07-27)', () => {
|
||||||
|
const hass = {
|
||||||
|
devices: {
|
||||||
|
good: { id: 'good', name: 'Датчик температуры спальня', area_id: 'bed' },
|
||||||
|
kettle: { id: 'kettle', name: 'Polaris PWK-1725CGLD', model: 'Kettle', area_id: 'bed' },
|
||||||
|
nas: { id: 'nas', name: 'System Monitor', area_id: 'bed' },
|
||||||
|
sauna: { id: 'sauna', name: 'Сауна Harvia', area_id: 'bed' },
|
||||||
|
trv: { id: 'trv', name: 'Термоголовка в спальне', area_id: 'bed' },
|
||||||
|
bt: { id: 'bt', name: 'Спальня better thermostat', area_id: 'bed' },
|
||||||
|
zb: { id: 'zb', name: 'SLZB-06MU', area_id: 'bed' },
|
||||||
|
},
|
||||||
|
entities: {
|
||||||
|
'sensor.good_t': { device_id: 'good', platform: 'mqtt' },
|
||||||
|
// вода в чайнике
|
||||||
|
'sensor.kettle_current_temperature': { device_id: 'kettle', platform: 'syncleo_kettle' },
|
||||||
|
// температура процессора: и diagnostic, и исключённая интеграция
|
||||||
|
'sensor.nas_processor_temperature': { device_id: 'nas', platform: 'systemmonitor', entity_category: 'diagnostic' },
|
||||||
|
'sensor.sauna_temperature': { device_id: 'sauna', platform: 'harvia_sauna' },
|
||||||
|
'sensor.trv_local_temperature': { device_id: 'trv', platform: 'mqtt' },
|
||||||
|
'sensor.bt_temperature': { device_id: 'bt', platform: 'better_thermostat' },
|
||||||
|
'sensor.zb_core_chip_temp': { device_id: 'zb', platform: 'smlight', entity_category: 'diagnostic' },
|
||||||
|
},
|
||||||
|
states: {
|
||||||
|
'sensor.good_t': { state: '22.0', attributes: { device_class: 'temperature', unit_of_measurement: '°C' } },
|
||||||
|
'sensor.kettle_current_temperature': { state: '95', attributes: { device_class: 'temperature', unit_of_measurement: '°C' } },
|
||||||
|
'sensor.nas_processor_temperature': { state: '61', attributes: { device_class: 'temperature', unit_of_measurement: '°C' } },
|
||||||
|
'sensor.sauna_temperature': { state: '90', attributes: { device_class: 'temperature', unit_of_measurement: '°C' } },
|
||||||
|
'sensor.trv_local_temperature': { state: '24', attributes: { device_class: 'temperature', unit_of_measurement: '°C' } },
|
||||||
|
'sensor.bt_temperature': { state: '22.0', attributes: { device_class: 'temperature', unit_of_measurement: '°C' } },
|
||||||
|
'sensor.zb_core_chip_temp': { state: '48', attributes: { device_class: 'temperature', unit_of_measurement: '°C' } },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// остаётся ровно один настоящий датчик воздуха
|
||||||
|
assert.equal(areaClimate(hass, 'bed', 'temp'), 22);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user