Skip to content

Commit 1e327a5

Browse files
author
Franz646
committed
feat: ignore platforms — v2.0.0
- UI: ctrl-bar ora include campo 'Ignore platforms' con tag pill rimovibili - Le righe ignorate restano visibili in tabella ma semitrasparenti con '— ignored' - Backend: parametro ignore_platforms nella query string del /scan endpoint - Detector: detect_orphans() accetta ignore_platforms=set() e salta le entità corrispondenti - Bump versione 1.5.6 → 2.0.0 (breaking change: nuovo parametro API)
1 parent 2d8e39c commit 1e327a5

5 files changed

Lines changed: 87 additions & 20 deletions

File tree

custom_components/orphan_cleaner/const.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,4 @@
4747
PANEL_ICON = "mdi:broom"
4848

4949
# Versione corrente (usata per cache-busting)
50-
VERSION = "1.5.6"
50+
VERSION = "2.0.0"

custom_components/orphan_cleaner/frontend/panel.html

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,18 @@
180180
border-radius:var(--radius-sm); padding:3px 7px; font-size:13px;
181181
background:var(--surface); color:var(--text); text-align:center; }
182182

183+
.ignore-tag { display:inline-flex; align-items:center; gap:4px;
184+
background:var(--surface2); border:0.5px solid var(--border2);
185+
border-radius:99px; padding:2px 9px; font-size:11px; color:var(--text2); }
186+
.ignore-tag-x { cursor:pointer; opacity:0.5; font-size:13px; line-height:1; }
187+
.ignore-tag-x:hover { opacity:1; }
188+
.ctrl-bar input[type=text] { border:0.5px solid var(--border2);
189+
border-radius:var(--radius-sm); padding:3px 8px; font-size:12px;
190+
background:var(--surface); color:var(--text); width:120px; }
191+
.ctrl-bar button.btn-add { font-size:11px; padding:3px 9px; }
192+
tr.ignored { opacity:0.35; }
193+
tr.ignored td { color:var(--text3); }
194+
183195
@media (max-width: 600px) {
184196
.stat-row { grid-template-columns: 1fr 1fr; }
185197
.topbar-sub { display: none; }
@@ -196,7 +208,7 @@
196208
<svg viewBox="0 0 24 24"><path d="M19.36 2.72 20.78 4.14l-1.4 1.41 1.41 1.41-4.24 4.24-1.41-1.41-6.89 6.9a4 4 0 0 1-1.2 4.71l-2.83 2.12-1.41-1.41 2.12-2.83a2 2 0 0 0 .27-2.1l-.27-.42L3 15.36l1.41-1.41 1.42 1.42 5.65-5.66-1.41-1.41 4.24-4.24 1.41 1.41z"/></svg>
197209
</div>
198210
<div>
199-
<div class="topbar-title">Orphan Entity Cleaner <span style="font-size:11px;font-weight:400;color:var(--text2);margin-left:6px">v1.5.6</span></div>
211+
<div class="topbar-title">Orphan Entity Cleaner <span style="font-size:11px;font-weight:400;color:var(--text2);margin-left:6px">v2.0.0</span></div>
200212
<div class="topbar-sub">Home Assistant Integration — orphan entity cleanup</div>
201213
</div>
202214
<div class="topbar-spacer"></div>
@@ -218,6 +230,11 @@
218230
<div class="ctrl-sep"></div>
219231
<span style="color:var(--text2)">Min age (h)</span>
220232
<input type="number" id="min-age" value="24" min="1" max="720">
233+
<div class="ctrl-sep"></div>
234+
<span style="color:var(--text2)">Ignore platforms</span>
235+
<input type="text" id="ignore-input" placeholder="es. tuya, zha…">
236+
<button class="btn-add" onclick="ignoreAdd()">+ Add</button>
237+
<div id="ignore-tags" style="display:flex;gap:5px;flex-wrap:wrap;align-items:center;"></div>
221238
</div>
222239

223240
<!-- Toolbar -->
@@ -282,8 +299,31 @@ <h2>Confirm deletion</h2>
282299
</div>
283300

284301
<script>
285-
let allOrphans = [];
286-
let selected = new Set();
302+
let allOrphans = [];
303+
let selected = new Set();
304+
let ignorePlatforms = new Set();
305+
306+
function ignoreAdd() {
307+
const inp = document.getElementById('ignore-input');
308+
const val = inp.value.trim().toLowerCase().replace(/[^a-z0-9_]/g, '');
309+
if (!val) return;
310+
if (ignorePlatforms.has(val)) { inp.value = ''; return; }
311+
ignorePlatforms.add(val);
312+
inp.value = '';
313+
renderIgnoreTags();
314+
renderTable(filteredOrphans());
315+
}
316+
function ignoreRemove(val) {
317+
ignorePlatforms.delete(val);
318+
renderIgnoreTags();
319+
renderTable(filteredOrphans());
320+
}
321+
function renderIgnoreTags() {
322+
const c = document.getElementById('ignore-tags');
323+
c.innerHTML = [...ignorePlatforms].map(v =>
324+
`<span class="ignore-tag">${v} <span class="ignore-tag-x" onclick="ignoreRemove('${v}')">×</span></span>`
325+
).join('');
326+
}
287327

288328
function addLog(msg, type='info') {
289329
const box = document.getElementById('log-box');
@@ -304,22 +344,27 @@ <h2>Confirm deletion</h2>
304344
function filteredOrphans() {
305345
const q = (document.getElementById('filter').value || '').toLowerCase();
306346
const heur = document.getElementById('heur-on')?.checked;
307-
return allOrphans.filter(e =>
308-
(heur || e.method !== 'heuristic') &&
309-
(!q || e.entity_id.includes(q) || e.platform.includes(q))
310-
);
347+
return allOrphans
348+
.filter(e => (heur || e.method !== 'heuristic') &&
349+
(!q || e.entity_id.includes(q) || e.platform.includes(q)))
350+
.map(e => ({ ...e, _ignored: ignorePlatforms.has(e.platform) }));
351+
}
352+
function activeOrphans() {
353+
return filteredOrphans().filter(e => !e._ignored);
311354
}
312-
function selAll() { filteredOrphans().forEach(e => selected.add(e.entity_id)); renderTable(filteredOrphans()); updateSelCount(); }
355+
function selAll() { activeOrphans().forEach(e => selected.add(e.entity_id)); renderTable(filteredOrphans()); updateSelCount(); }
313356
function deselAll() { selected.clear(); renderTable(filteredOrphans()); updateSelCount(); }
314357
function toggleRow(id, chk) { chk ? selected.add(id) : selected.delete(id); updateSelCount(); }
315358
function applyFilter() {
316359
const items = filteredOrphans();
317360
renderTable(items);
318361
const q = document.getElementById('filter').value;
319-
document.getElementById('tbl-meta').textContent = q ? items.length+' of '+allOrphans.length : '';
362+
const active = items.filter(e => !e._ignored).length;
363+
document.getElementById('tbl-meta').textContent = q ? active+' of '+allOrphans.length : '';
320364
}
321365
function renderTable(items) {
322366
const wrap = document.getElementById('tbl-wrap');
367+
const activeItems = items.filter(e => !e._ignored);
323368
if (!items.length) {
324369
wrap.innerHTML = `<div class="empty">
325370
<svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>
@@ -328,6 +373,17 @@ <h2>Confirm deletion</h2>
328373
return;
329374
}
330375
const rows = items.map(e => {
376+
if (e._ignored) {
377+
const dis = e.disabled_by ? '<span class="badge badge-disabled">disabled</span>' : '';
378+
const age = e.age_hours != null ? e.age_hours+'h' : '—';
379+
return `<tr class="ignored">
380+
<td class="td-chk"></td>
381+
<td><span class="eid">${e.entity_id}</span>${dis}</td>
382+
<td><span class="platform-badge">${e.platform}</span></td>
383+
<td class="td-method"><span style="color:var(--text3)">— ignored</span></td>
384+
<td class="td-age"><span class="age-text">—</span></td>
385+
</tr>`;
386+
}
331387
const chk = selected.has(e.entity_id) ? 'checked' : '';
332388
const badge = e.method === 'timestamp'
333389
? '<span class="badge badge-ts">orphaned_timestamp</span>'
@@ -436,6 +492,7 @@ <h2>Confirm deletion</h2>
436492
}
437493
}
438494
document.addEventListener('keydown', e => { if (e.key==='Escape') closeModal(); });
495+
document.getElementById('ignore-input').addEventListener('keydown', e => { if (e.key==='Enter') ignoreAdd(); });
439496
</script>
440497
</body>
441498
</html>

custom_components/orphan_cleaner/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"domain": "orphan_cleaner",
33
"name": "Orphan Entity Cleaner",
4-
"version": "1.5.6",
4+
"version": "2.0.0",
55
"documentation": "https://github.com/Franz646/orphan-cleaner",
66
"issue_tracker": "https://github.com/Franz646/orphan-cleaner/issues",
77
"codeowners": [

custom_components/orphan_cleaner/orphan_detector.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,23 @@ def detect_orphans(
5151
hass: HomeAssistant,
5252
min_age_hours: int = 24,
5353
aggressive: bool = False,
54+
ignore_platforms: set[str] | None = None,
5455
) -> list[OrphanInfo]:
5556

56-
entity_registry = er.async_get(hass)
57-
now = time.time()
57+
entity_registry = er.async_get(hass)
58+
now = time.time()
5859
orphans: list[OrphanInfo] = []
59-
seen: set[str] = set()
60+
seen: set[str] = set()
61+
_ignore = ignore_platforms or set()
6062

6163
for entry in entity_registry.entities.values():
6264
platform = entry.platform or ""
6365
cfg_entry_id = entry.config_entry_id
6466

67+
# Skip platforms the user wants to ignore
68+
if platform and platform in _ignore:
69+
continue
70+
6571
# Method 1: orphaned_timestamp
6672
ts = getattr(entry, "orphaned_timestamp", None)
6773
if ts is not None:

custom_components/orphan_cleaner/panel_api.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,25 @@ async def get(self, request: web.Request) -> web.Response:
5353

5454
min_age = int(request.rel_url.query.get("min_age", config.get(CONF_MIN_AGE_HOURS, DEFAULT_MIN_AGE_HOURS)))
5555
aggressive = request.rel_url.query.get("heuristic", "0") == "1"
56+
raw_ignore = request.rel_url.query.get("ignore_platforms", "")
57+
ignore_platforms = {p.strip() for p in raw_ignore.split(",") if p.strip()} if raw_ignore else set()
5658

5759
try:
5860
from homeassistant.helpers import entity_registry as er
5961
registry = er.async_get(hass)
6062
total = len(registry.entities)
61-
orphans = detect_orphans(hass, min_age_hours=min_age, aggressive=aggressive)
63+
orphans = detect_orphans(hass, min_age_hours=min_age, aggressive=aggressive,
64+
ignore_platforms=ignore_platforms)
6265

6366
return web.Response(
6467
content_type="application/json",
6568
text=json.dumps({
66-
"total": total,
67-
"orphans": [o.as_dict() for o in orphans],
68-
"min_age": min_age,
69-
"aggressive": aggressive,
70-
"error": None,
69+
"total": total,
70+
"orphans": [o.as_dict() for o in orphans],
71+
"min_age": min_age,
72+
"aggressive": aggressive,
73+
"ignore_platforms": list(ignore_platforms),
74+
"error": None,
7175
}),
7276
)
7377
except Exception as exc:

0 commit comments

Comments
 (0)