106 lines
4.1 KiB
Python
106 lines
4.1 KiB
Python
from sqlalchemy import Column, Integer, String, Text, ForeignKey
|
|
from sqlalchemy.orm import declarative_base, relationship
|
|
|
|
Base = declarative_base()
|
|
|
|
|
|
class Device(Base):
|
|
__tablename__ = "device"
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
name = Column(String(100), nullable=False, unique=True)
|
|
battery_slots = Column(Integer, nullable=False, default=1)
|
|
device_type = Column(String(50), nullable=True)
|
|
notes = Column(Text, nullable=True)
|
|
ha_entity_id = Column(String(100), nullable=True) # e.g. "sensor.tv_remote_battery"
|
|
|
|
batteries = relationship("Battery", back_populates="device")
|
|
|
|
def installed_count(self):
|
|
return sum(1 for b in self.batteries if b.status == "installed")
|
|
|
|
def installed_brands(self):
|
|
return set(b.brand for b in self.batteries if b.status == "installed")
|
|
|
|
def has_mixed_brands(self):
|
|
return len(self.installed_brands()) > 1
|
|
|
|
def __repr__(self):
|
|
return f"<Device {self.name}>"
|
|
|
|
|
|
class Battery(Base):
|
|
__tablename__ = "battery"
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
label = Column(String(50), nullable=False)
|
|
brand = Column(String(100), nullable=False)
|
|
status = Column(String(20), nullable=False, default="available")
|
|
device_id = Column(Integer, ForeignKey("device.id", ondelete="SET NULL"), nullable=True)
|
|
notes = Column(Text, nullable=True)
|
|
|
|
# Optional metadata
|
|
size = Column(String(20), nullable=True) # AA, AAA, 18650, CR2032 …
|
|
chemistry = Column(String(20), nullable=True) # NiMH, Alkaline, Li-ion …
|
|
capacity_mah = Column(Integer, nullable=True) # nominal capacity in mAh
|
|
tested_capacity_mah = Column(Integer, nullable=True) # last measured capacity in mAh
|
|
tested_date = Column(String(10), nullable=True) # YYYY-MM-DD of last test
|
|
charge_cycles = Column(Integer, nullable=True) # number of charge cycles
|
|
purchase_date = Column(String(10), nullable=True) # YYYY-MM-DD when purchased
|
|
storage_location = Column(String(100), nullable=True) # where stored when not installed
|
|
battery_percentage = Column(Integer, nullable=True) # current charge %, set by HA poller or manually
|
|
|
|
device = relationship("Device", back_populates="batteries")
|
|
capacity_tests = relationship(
|
|
"CapacityTest", back_populates="battery",
|
|
order_by="CapacityTest.tested_date",
|
|
cascade="all, delete-orphan",
|
|
)
|
|
charge_logs = relationship(
|
|
"ChargeLog", back_populates="battery",
|
|
order_by="ChargeLog.charged_date",
|
|
cascade="all, delete-orphan",
|
|
)
|
|
|
|
def is_available(self):
|
|
return self.status == "available"
|
|
|
|
def is_retired(self):
|
|
return self.status == "retired"
|
|
|
|
def is_installed(self):
|
|
return self.status == "installed"
|
|
|
|
def __repr__(self):
|
|
return f"<Battery {self.label}>"
|
|
|
|
|
|
class CapacityTest(Base):
|
|
__tablename__ = "capacity_test"
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
battery_id = Column(Integer, ForeignKey("battery.id", ondelete="CASCADE"), nullable=False)
|
|
tested_capacity_mah = Column(Integer, nullable=False)
|
|
tested_date = Column(String(10), nullable=False) # YYYY-MM-DD
|
|
notes = Column(Text, nullable=True)
|
|
|
|
battery = relationship("Battery", back_populates="capacity_tests")
|
|
|
|
def __repr__(self):
|
|
return f"<CapacityTest {self.battery_id} {self.tested_date} {self.tested_capacity_mah}mAh>"
|
|
|
|
|
|
class ChargeLog(Base):
|
|
__tablename__ = "charge_log"
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
battery_id = Column(Integer, ForeignKey("battery.id", ondelete="CASCADE"), nullable=False)
|
|
charged_date = Column(String(10), nullable=False) # YYYY-MM-DD
|
|
increment_cycles = Column(Integer, nullable=False, default=0) # 0 or 1
|
|
notes = Column(Text, nullable=True)
|
|
|
|
battery = relationship("Battery", back_populates="charge_logs")
|
|
|
|
def __repr__(self):
|
|
return f"<ChargeLog {self.battery_id} {self.charged_date}>"
|