Skip to content

Commit 89648d7

Browse files
committed
v0.5.4: add Drive Standby + Agent OS sensors, rename Scan Interval to HA Poll Interval
- Add DriveStandbySensor (per-drive binary sensor with data_as_of attribute) - Add AgentOSSensor (agent diagnostic reporting linux/darwin/windows) - Rename AgentScanIntervalSensor to AgentPollIntervalSensor; preserve unique_id - Deprecate in_standby and data_as_of attributes on the Health sensor (kept for backward compatibility; tracked for future removal) - Bump manifest to 0.5.4 - README: document v0.5.3 agent diagnostic entities + v0.5.4 additions - README: disambiguate agent scan_interval vs HA Poll Interval - README: add v0.5.4 roadmap entries - build-journal: tick 'Update README entity table' item
1 parent 5f2d1cb commit 89648d7

7 files changed

Lines changed: 172 additions & 22 deletions

File tree

CHANGELOG.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,23 @@
22

33
All notable changes to SMART Sniffer are documented here.
44

5+
## v0.5.4 -- 2026-04-21
6+
7+
Integration-only release. No agent changes required.
8+
9+
### Added
10+
- **Drive Standby binary sensor** -- each drive now has a dedicated Standby entity that reports on when the drive is spun down and off when it is active. When on, the sensor exposes a `data_as_of` attribute showing when the cached SMART readings were last refreshed. Makes it straightforward to automate on standby state directly rather than reading an attribute off the Health sensor.
11+
- **Agent OS diagnostic sensor** -- the agent device now exposes an OS entity reporting the agent host as linux, darwin, or windows. Visible by default; useful for at-a-glance inventory across mixed deployments.
12+
13+
### Changed
14+
- **Scan Interval renamed to HA Poll Interval** -- the existing agent diagnostic entity has been renamed to clarify that it represents how often Home Assistant polls the agent, not how often the agent itself reads SMART data from drives. The underlying entity is preserved, so existing automations and templates referencing the entity ID continue to work.
15+
16+
### Deprecated
17+
- **`binary_sensor.*_health` attributes `in_standby` and `data_as_of`** -- superseded by the new `binary_sensor.*_standby` entity (state and `data_as_of` attribute). The attributes remain on the Health sensor in this release for backward compatibility with v0.5.3 automations and will be removed in a future release. Tracked in `docs/internal/process/deprecations.md`.
18+
19+
### Upgrade Notes
20+
- **Integration update only.** Update via HACS and reload. No agent update, installer re-run, or configuration change is required.
21+
522
## v0.5.3 -- 2026-04-19
623

724
### Fixed
@@ -14,7 +31,7 @@ All notable changes to SMART Sniffer are documented here.
1431
- **Agent diagnostic entities** -- version, last seen, IP, port, scan interval, and auth status are now available as entities under each agent device. Version is enabled by default; the rest are hidden by default and can be enabled in entity settings.
1532
- **Minimum smartctl version check** -- the agent now verifies smartctl 7.0+ is installed before starting, with clear upgrade instructions if not. Older versions lack the JSON output the agent depends on, and previously failed silently.
1633
- **OS and uptime in health endpoint** -- the agent's `/api/health` response now includes host OS and uptime, used by the new diagnostic entities.
17-
- **Standby indicators on drive sensors** -- when a drive is sleeping and being served from cache, its sensors gain `in_standby` and `data_as_of` attributes so you can tell the data is stale and how old it is.
34+
- **Standby indicators on drive sensors** -- when a drive is sleeping and being served from cache, its sensors gain `in_standby` and `data_as_of` attributes so you can tell the data is stale and how old it is. *(Deprecated in v0.5.4 -- replaced by a dedicated Standby binary sensor. The v0.5.3 attributes remain for backward compatibility.)*
1835

1936
### Upgrade Notes
2037
- **Agent update required.** Re-run the installer or replace the binary to get the new features. Existing configs work without changes -- all new options default to current behavior.

README.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Attributes: `mountpoint`, `device`, `fstype`, `total_gb`, `used_gb`, `available_
6767
|--------|-------------|
6868
| Attention Needed | Proactive health alert — `NO` / `MAYBE` / `YES` / `UNSUPPORTED` |
6969
| Health | SMART pass/fail — OK, Problem, or Unknown |
70+
| Standby | Whether the drive is currently spun down. When On, exposes a `data_as_of` attribute showing when the cached SMART readings were last refreshed. |
7071
| Temperature | Current drive temp (°C) |
7172
| Power-On Hours | Total hours powered on |
7273
| SMART Status | Raw SMART verdict (PASSED / FAILED) |
@@ -88,6 +89,33 @@ Attributes: `mountpoint`, `device`, `fstype`, `total_gb`, `used_gb`, `available_
8889

8990
</details>
9091

92+
<details>
93+
<summary><strong>Entities per agent</strong></summary>
94+
95+
<br>
96+
97+
Each agent host groups under its own HA device alongside the drive devices. These entities describe the agent itself rather than any one drive. Enabled-by-default entities show up as soon as you reload the integration; the rest are visible from the device page's "disabled" section and can be turned on from entity settings.
98+
99+
**Primary (enabled by default):**
100+
101+
| Entity | Type | Description |
102+
|--------|------|-------------|
103+
| Agent Status | binary_sensor | Connectivity. On when reachable, Off when disconnected. Stays available across outages so automations can trigger on it. |
104+
| Agent Version | sensor | Semantic version reported by the agent. Drives the "Agent version outdated" HA repair notification. |
105+
| OS | sensor | Agent host OS: `linux`, `darwin`, or `windows`. Reports `unknown` for agents older than v0.5.3. |
106+
107+
**Diagnostic (disabled by default):**
108+
109+
| Entity | Type | Description |
110+
|--------|------|-------------|
111+
| Agent Last Seen | sensor | Timestamp of the most recent successful poll. |
112+
| Agent IP | sensor | IP address recorded for the agent in the HA config entry. |
113+
| Agent Port | sensor | Port the agent is serving on (default 9099). |
114+
| HA Poll Interval | sensor | How often Home Assistant polls the agent, in seconds. Distinct from the agent's own `scan_interval`. |
115+
| Auth Active | binary_sensor | On when a bearer token is configured for this agent. |
116+
117+
</details>
118+
91119
<details>
92120
<summary><strong>Attention Needed — how it classifies drives</strong></summary>
93121

@@ -131,7 +159,7 @@ filesystems: # optional — set by installer's disk usage pick
131159
132160
All options can also be set via CLI flags: `--port`, `--token`, `--scan-interval`, `--interface`, `--config`.
133161

134-
**Scan interval:** Uses Go duration syntax — `30s`, `5m`, `1h`, `24h` are all valid. Each poll reads SMART data via `smartctl`, which wakes any drive that is spun down or in standby. If you have drives that sleep between accesses, a longer interval like `12h` or `24h` keeps them from waking unnecessarily.
162+
**Scan interval:** Uses Go duration syntax — `30s`, `5m`, `1h`, `24h` are all valid. Each poll reads SMART data via `smartctl`, which wakes any drive that is spun down or in standby. If you have drives that sleep between accesses, a longer interval like `12h` or `24h` keeps them from waking unnecessarily. This is the *agent-side* read cadence and is separate from the HA Poll Interval entity, which reflects how often Home Assistant pulls fresh data from the agent itself.
135163

136164
**Network interface:** The `advertise_interface` setting restricts mDNS to a single interface. The installer sets this during setup if you pick a specific interface. When not set, the agent auto-filters known virtual interfaces (Docker, ZeroTier, Tailscale, WireGuard, etc.) and advertises on all remaining physical interfaces. To change the interface after install, edit `config.yaml` and restart the service — no reinstall needed.
137165

@@ -348,6 +376,8 @@ This is the most common "why isn't my drive showing data?" scenario. It's a hard
348376
- [ ] SAS/SCSI drive support
349377
- [x] Integration: agent connectivity sensor + diagnostic entities (version, last seen, IP, port, auth) -- shipped v0.5.3
350378
- [x] Agent: smartctl minimum version check (fail early with clear message if < 7.0) -- shipped v0.5.3
379+
- [x] Integration: dedicated Drive Standby binary sensor with `data_as_of` attribute -- shipped v0.5.4
380+
- [x] Integration: Agent OS diagnostic sensor (linux / darwin / windows) -- shipped v0.5.4
351381
- [ ] Agent: container-aware filesystem reporting (MNT_PREFIX path mapping for Docker deployments)
352382

353383
## Community Deployments

custom_components/smart_sniffer/binary_sensor.py

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
"""Binary sensor for SMART Sniffer — drive health.
1+
"""Binary sensors for SMART Sniffer — drive health and standby.
22
3-
One binary sensor per drive:
3+
Per-drive binary sensors:
44
5-
health — SMART's official pass/fail verdict + NVMe critical_warning.
6-
This is the lagging indicator: drives can report PASSED right up
7-
until catastrophic failure.
5+
health — SMART's official pass/fail verdict + NVMe critical_warning.
6+
This is the lagging indicator: drives can report PASSED right up
7+
until catastrophic failure.
88
9-
device_class PROBLEM: on = SMART FAILED, off = SMART PASSED.
10-
Returns None (HA renders "Unknown") when the drive provides no
11-
usable SMART data (e.g., USB enclosures blocking passthrough).
9+
device_class PROBLEM: on = SMART FAILED, off = SMART PASSED.
10+
Returns None (HA renders "Unknown") when the drive provides no
11+
usable SMART data (e.g., USB enclosures blocking passthrough).
12+
13+
standby — Whether the drive is currently spun down. When on, the SMART
14+
readings for this drive are being served from cache; the
15+
sensor exposes a data_as_of attribute so consumers can see
16+
how stale those readings are. Introduced in v0.5.4.
1217
1318
The early-warning "Attention Needed" sensor lives in sensor.py as an enum
1419
sensor (NO / MAYBE / YES / UNSUPPORTED). See attention.py for the logic.
@@ -103,11 +108,12 @@ async def async_setup_entry(
103108

104109
entities: list[BinarySensorEntity] = []
105110

106-
# Per-drive health sensors.
111+
# Per-drive health + standby sensors.
107112
for drive_id, drive_data in coordinator.data.items():
108113
if drive_id.startswith("_"):
109114
continue # skip internal keys like _filesystems
110115
entities.append(SmartSnifferHealthSensor(coordinator, drive_id, drive_data))
116+
entities.append(DriveStandbySensor(coordinator, drive_id, drive_data))
111117

112118
# Agent-level connectivity and auth sensors.
113119
entities.append(AgentStatusBinarySensor(health_coordinator, entry))
@@ -174,13 +180,75 @@ def extra_state_attributes(self) -> dict[str, Any]:
174180
status = smart_data.get("smart_status", {})
175181
if isinstance(status, dict):
176182
attrs["smart_passed"] = status.get("passed")
177-
# Standby attributes when drive is sleeping.
183+
# DEPRECATED (v0.5.4): the in_standby and data_as_of attributes on
184+
# this sensor are superseded by the dedicated DriveStandbySensor
185+
# (binary_sensor.*_standby). Kept here for backward compatibility
186+
# with v0.5.3 automations/templates. Planned removal in a future
187+
# release. See docs/internal/process/deprecations.md.
178188
if drive_data.get("in_standby"):
179189
attrs["in_standby"] = True
180190
attrs["data_as_of"] = drive_data.get("last_updated", "unknown")
181191
return attrs
182192

183193

194+
class DriveStandbySensor(
195+
CoordinatorEntity[SmartSnifferCoordinator], BinarySensorEntity
196+
):
197+
"""Binary sensor showing whether the drive is currently in standby.
198+
199+
on = drive is spun down / sleeping; SMART data served from cache
200+
off = drive is active; SMART data is fresh
201+
202+
When on, exposes `data_as_of` as an attribute so consumers can see how
203+
stale the cached readings are. Introduced in v0.5.4 as the canonical
204+
replacement for the in_standby/data_as_of attributes previously attached
205+
to the Health sensor.
206+
"""
207+
208+
_attr_has_entity_name = True
209+
_attr_name = "Standby"
210+
_attr_entity_category = EntityCategory.DIAGNOSTIC
211+
_attr_entity_registry_enabled_default = True
212+
213+
def __init__(
214+
self,
215+
coordinator: SmartSnifferCoordinator,
216+
drive_id: str,
217+
drive_data: dict[str, Any],
218+
) -> None:
219+
super().__init__(coordinator)
220+
self._drive_id = drive_id
221+
model = drive_data.get("model", "Unknown Drive")
222+
serial = drive_data.get("serial", drive_id)
223+
self._attr_unique_id = (
224+
f"{coordinator.config_entry.entry_id}_{drive_id}_standby"
225+
)
226+
self._attr_device_info = {
227+
"identifiers": {(DOMAIN, drive_id)},
228+
"name": f"{model} ({serial})",
229+
"manufacturer": model.split()[0] if model else "Unknown",
230+
"model": model,
231+
"serial_number": serial,
232+
}
233+
234+
@property
235+
def is_on(self) -> bool:
236+
"""Return True when the drive is in standby."""
237+
drive_data = self.coordinator.data.get(self._drive_id) or {}
238+
return bool(drive_data.get("in_standby", False))
239+
240+
@property
241+
def icon(self) -> str:
242+
return "mdi:sleep" if self.is_on else "mdi:power"
243+
244+
@property
245+
def extra_state_attributes(self) -> dict[str, Any]:
246+
if not self.is_on:
247+
return {}
248+
drive_data = self.coordinator.data.get(self._drive_id) or {}
249+
return {"data_as_of": drive_data.get("last_updated", "unknown")}
250+
251+
184252
# ---------------------------------------------------------------------------
185253
# Agent connectivity binary sensor
186254
# ---------------------------------------------------------------------------

custom_components/smart_sniffer/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99
"iot_class": "local_polling",
1010
"issue_tracker": "https://github.com/DAB-LABS/smart-sniffer/issues",
1111
"requirements": [],
12-
"version": "0.5.0",
12+
"version": "0.5.4",
1313
"zeroconf": [{"type": "_smartha._tcp.local."}]
1414
}

custom_components/smart_sniffer/sensor.py

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,8 @@ async def async_setup_entry(
434434
entities.append(AgentLastSeenSensor(health_coordinator, entry))
435435
entities.append(AgentIPSensor(health_coordinator, entry))
436436
entities.append(AgentPortSensor(health_coordinator, entry))
437-
entities.append(AgentScanIntervalSensor(health_coordinator, entry))
437+
entities.append(AgentOSSensor(health_coordinator, entry))
438+
entities.append(AgentPollIntervalSensor(health_coordinator, entry))
438439

439440
async_add_entities(entities, update_before_add=False)
440441

@@ -922,13 +923,44 @@ def native_value(self) -> int:
922923
return self._entry.data.get(CONF_PORT, 9099)
923924

924925

925-
class AgentScanIntervalSensor(
926+
class AgentOSSensor(
926927
CoordinatorEntity[AgentHealthCoordinator], SensorEntity
927928
):
928-
"""Agent scan interval from config entry (read-only diagnostic)."""
929+
"""Agent host OS (linux, darwin, windows) from the health endpoint."""
929930

930931
_attr_has_entity_name = True
931-
_attr_name = "Scan Interval"
932+
_attr_name = "OS"
933+
_attr_icon = "mdi:monitor"
934+
_attr_entity_category = EntityCategory.DIAGNOSTIC
935+
_attr_entity_registry_enabled_default = True
936+
937+
def __init__(self, coordinator: AgentHealthCoordinator, entry: ConfigEntry) -> None:
938+
super().__init__(coordinator)
939+
self._entry = entry
940+
self._attr_unique_id = f"{entry.entry_id}_agent_os"
941+
self._attr_device_info = _agent_device_info(entry)
942+
943+
@property
944+
def native_value(self) -> str | None:
945+
value = self.coordinator.data.get("os")
946+
return value or None
947+
948+
949+
class AgentPollIntervalSensor(
950+
CoordinatorEntity[AgentHealthCoordinator], SensorEntity
951+
):
952+
"""HA-side poll interval from config entry (read-only diagnostic).
953+
954+
Reports how often Home Assistant pulls fresh data from the agent.
955+
Distinct from the agent's own scan_interval (how often the agent
956+
reads SMART data from drives).
957+
958+
Unique ID retains the legacy "_agent_scan_interval" suffix so existing
959+
entity registry entries are preserved across the v0.5.4 rename.
960+
"""
961+
962+
_attr_has_entity_name = True
963+
_attr_name = "HA Poll Interval"
932964
_attr_device_class = SensorDeviceClass.DURATION
933965
_attr_native_unit_of_measurement = UnitOfTime.SECONDS
934966
_attr_icon = "mdi:timer-outline"

docs/build-journal.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ Immediate:
352352

353353
- [ ] Fix `--config` flag in Go agent (or fix Windows installer to use working directory)
354354
- [ ] Clean committed build artifacts from git history
355-
- [ ] Update README entity table with all current sensors
355+
- [x] Update README entity table with all current sensors -- ✅ shipped v0.5.4 (added per-agent entity table covering v0.5.3 diagnostics + v0.5.4 Drive Standby and Agent OS)
356356
- [x] Update `early-warning-attributes.md` YAML examples (binary → enum) — ✅ fixed in v0.4.22
357357
- [x] Validate release workflow (push `v0.1.0` tag, watch Actions) — ✅ working
358358
- [x] Test `install.sh` on Linux — ✅ tested on Kali VM (QEMU). Install, reinstall, uninstall all working. Auth token verified.

llms.txt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@ Key behaviors:
2424
percentage sensor with total/used/available attributes under a "Disk Usage" device.
2525
Served via /api/filesystems on the agent. Requires agent v0.5.0+.
2626
- Standby-aware polling: optional standby_mode config prevents the agent from waking
27-
sleeping HDDs during scans. When a drive is in standby, cached data is served and
28-
drive sensors gain in_standby and data_as_of attributes. Useful for NAS users.
27+
sleeping HDDs during scans. When a drive is in standby, cached data is served. Each
28+
drive has a dedicated Standby binary sensor (v0.5.4+) that reports whether the drive
29+
is currently spun down, with a data_as_of attribute showing how stale the cached
30+
SMART readings are. Useful for NAS users with sleeping drives.
2931
- Agent connectivity sensor: a binary sensor per agent shows Connected or Disconnected
3032
in real time. Stays available even when the agent is offline.
31-
- Agent diagnostic entities: version, last seen, IP, port, scan interval, and auth
32-
status are available as HA entities under each agent device (v0.5.3+).
33+
- Agent diagnostic entities: version, last seen, IP, port, HA Poll Interval, OS, and
34+
auth status are available as HA entities under each agent device (v0.5.3+, OS added
35+
in v0.5.4).
3336
- Agent version check: the integration detects outdated agents and shows a repair card
3437
in HA Settings with the current vs. required version and an upgrade command.
3538
- Zero-config agent discovery via mDNS -- no manual IP or port configuration required

0 commit comments

Comments
 (0)