Initial commit

This commit is contained in:
2025-08-17 18:51:29 +10:00
parent 214d7f8228
commit 02380cd0d9
12 changed files with 1877 additions and 1 deletions

190
binary_sensor.py Normal file
View File

@@ -0,0 +1,190 @@
"""Binary sensor platform for MedMate integration."""
from __future__ import annotations
import logging
from datetime import datetime, date
from typing import Any
from homeassistant.components.binary_sensor import (
BinarySensorEntity,
BinarySensorEntityDescription,
BinarySensorDeviceClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import (
DOMAIN,
CONF_MEDICINE_NAME,
CONF_PRESCRIPTION,
CONF_EXPIRY_DATE,
CONF_REPEATS_LEFT,
)
from .coordinator import MedMateDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up MedMate binary sensors."""
coordinator = hass.data[DOMAIN][config_entry.entry_id]
binary_sensors = [
MedMateDoseDueSensor(coordinator, config_entry),
MedMatePrescriptionActiveSensor(coordinator, config_entry),
MedMateLowInventorySensor(coordinator, config_entry),
]
async_add_entities(binary_sensors)
class MedMateBaseBinarySensor(CoordinatorEntity, BinarySensorEntity):
"""Base binary sensor for MedMate."""
def __init__(
self,
coordinator: MedMateDataUpdateCoordinator,
config_entry: ConfigEntry,
description: BinarySensorEntityDescription,
) -> None:
"""Initialize the binary sensor."""
super().__init__(coordinator)
self.entity_description = description
self.config_entry = config_entry
self._medicine_id = config_entry.data.get("medicine_id")
# Set unique_id
self._attr_unique_id = f"{self._medicine_id}_{description.key}"
@property
def device_info(self) -> dict[str, Any]:
"""Return device information."""
medicine_name = self.coordinator.data.get(CONF_MEDICINE_NAME, "Unknown Medicine")
return {
"identifiers": {(DOMAIN, self._medicine_id)},
"name": f"MedMate - {medicine_name}",
"manufacturer": "MedMate",
"model": "Medicine Tracker",
"sw_version": "1.0.0",
}
@property
def available(self) -> bool:
"""Return if entity is available."""
return self.coordinator.last_update_success and bool(self.coordinator.data)
class MedMateDoseDueSensor(MedMateBaseBinarySensor):
"""Binary sensor for dose due status."""
def __init__(
self,
coordinator: MedMateDataUpdateCoordinator,
config_entry: ConfigEntry,
) -> None:
"""Initialize the dose due sensor."""
description = BinarySensorEntityDescription(
key="dose_due",
name="Dose Due",
icon="mdi:alarm",
)
super().__init__(coordinator, config_entry, description)
@property
def is_on(self) -> bool:
"""Return true if dose is due."""
return self.coordinator.data.get("dose_due", False)
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return additional state attributes."""
data = self.coordinator.data
next_dose = data.get("next_dose")
attrs = {}
if next_dose:
if isinstance(next_dose, str):
try:
next_dose = datetime.fromisoformat(next_dose)
except ValueError:
next_dose = None
attrs["next_dose"] = next_dose
return attrs
class MedMatePrescriptionActiveSensor(MedMateBaseBinarySensor):
"""Binary sensor for prescription active status."""
def __init__(
self,
coordinator: MedMateDataUpdateCoordinator,
config_entry: ConfigEntry,
) -> None:
"""Initialize the prescription active sensor."""
description = BinarySensorEntityDescription(
key="prescription_active",
name="Prescription Active",
icon="mdi:file-document",
)
super().__init__(coordinator, config_entry, description)
@property
def is_on(self) -> bool:
"""Return true if prescription is active."""
return self.coordinator.data.get("prescription_active", False)
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return additional state attributes."""
data = self.coordinator.data
prescription = data.get(CONF_PRESCRIPTION, {})
return {
"expiry_date": prescription.get(CONF_EXPIRY_DATE),
"repeats_left": prescription.get(CONF_REPEATS_LEFT, 0),
}
class MedMateLowInventorySensor(MedMateBaseBinarySensor):
"""Binary sensor for low inventory warning."""
def __init__(
self,
coordinator: MedMateDataUpdateCoordinator,
config_entry: ConfigEntry,
) -> None:
"""Initialize the low inventory sensor."""
description = BinarySensorEntityDescription(
key="low_inventory",
name="Low Inventory",
icon="mdi:alert-circle",
device_class=BinarySensorDeviceClass.PROBLEM,
)
super().__init__(coordinator, config_entry, description)
@property
def is_on(self) -> bool:
"""Return true if inventory is low."""
data = self.coordinator.data
inventory = data.get("inventory", 0)
# Consider inventory low if less than 7 doses remain
# This assumes daily medication - could be made configurable
return inventory < 7
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return additional state attributes."""
data = self.coordinator.data
return {
"current_inventory": data.get("inventory", 0),
"low_threshold": 7,
}