Updated apps

This commit is contained in:
2026-07-20 22:52:35 -04:00
parent 28a8cb98f6
commit a0c3271743
1164 changed files with 94781 additions and 6892 deletions
@@ -53,6 +53,7 @@ SUMMARY_METRICS: list[tuple[str, str]] = [
if TYPE_CHECKING:
from . import MaintenanceSupporterConfigEntry
from .helpers.battery_fleet import BatteryOverview
from .helpers.documents import DocumentStore
_LOGGER = logging.getLogger(__name__)
@@ -80,6 +81,9 @@ async def async_setup_entry(
entities.append(DocumentStorageSensor(hass, doc_store))
# Spare parts: how many parts across all objects need reordering.
entities.append(PartsToReorderSensor(hass))
# Battery fleet: how many batteries (across all Battery Notes devices)
# need replacing now — the aggregate that backs the single fleet task.
entities.append(BatteryFleetLowSensor(hass))
async_add_entities(entities)
return
@@ -573,9 +577,7 @@ class PartStockSensor(MaintenanceEntity, SensorEntity):
await super().async_added_to_hass()
from .parts_runtime import SIGNAL_PARTS_UPDATED
self.async_on_remove(
async_dispatcher_connect(self.hass, SIGNAL_PARTS_UPDATED, self._handle_parts_update)
)
self.async_on_remove(async_dispatcher_connect(self.hass, SIGNAL_PARTS_UPDATED, self._handle_parts_update))
@callback
def _handle_parts_update(self, entry_id: str) -> None:
@@ -647,9 +649,7 @@ class PartsToReorderSensor(SensorEntity):
await super().async_added_to_hass()
from .parts_runtime import SIGNAL_PARTS_UPDATED
self.async_on_remove(
async_dispatcher_connect(self.hass, SIGNAL_PARTS_UPDATED, self._handle_parts_update)
)
self.async_on_remove(async_dispatcher_connect(self.hass, SIGNAL_PARTS_UPDATED, self._handle_parts_update))
@callback
def _handle_parts_update(self, _entry_id: str) -> None:
@@ -674,6 +674,67 @@ class PartsToReorderSensor(SensorEntity):
return count
class BatteryFleetLowSensor(SensorEntity):
"""How many batteries across all Battery Notes devices are low right now.
Aggregate over the fleet (not one sensor per battery). Its state is the
threshold source for the single "Replace low batteries" fleet task; the
grouped shopping needs + forecast ride along as attributes. Lives on the
global hub device; refreshes off the Battery Notes lifecycle events.
"""
_attr_has_entity_name = True
_attr_translation_key = "batteries_to_replace"
_attr_state_class = SensorStateClass.MEASUREMENT
_attr_icon = "mdi:battery-alert"
_attr_native_unit_of_measurement = "batteries"
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the global battery-fleet low-count sensor."""
self.hass = hass
self._attr_unique_id = f"{GLOBAL_UNIQUE_ID}_batteries_to_replace"
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, GLOBAL_UNIQUE_ID)},
name="Maintenance Supporter",
entry_type=DeviceEntryType.SERVICE,
)
self.entity_id = async_generate_entity_id(ENTITY_ID_FORMAT, "maintenance_supporter_batteries_to_replace", hass=hass)
async def async_added_to_hass(self) -> None:
"""Refresh on Battery Notes threshold/replaced/increased events."""
await super().async_added_to_hass()
for event in (
"battery_notes_battery_threshold",
"battery_notes_battery_replaced",
"battery_notes_battery_increased",
):
self.async_on_remove(self.hass.bus.async_listen(event, self._handle_event))
@callback
def _handle_event(self, _event: Any) -> None:
self.async_write_ha_state()
def _overview(self) -> BatteryOverview:
from .helpers.battery_fleet import compute_overview
return compute_overview(self.hass)
@property
def native_value(self) -> int:
return self._overview().low_count
@property
def extra_state_attributes(self) -> dict[str, Any]:
ov = self._overview()
return {
"total_batteries": ov.total,
"soon_count": len(ov.soon),
"needs_now": dict(ov.needs_now),
"needs_soon": dict(ov.needs_soon),
"battery_types": ov.types,
}
class DocumentStorageSensor(SensorEntity):
"""Total on-disk size of stored document blobs — the real per-backup cost.