printer_ha_sensor.py 4.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. from datetime import datetime
  2. from sqlalchemy import Boolean, DateTime, Float, ForeignKey, Integer, String, func
  3. from sqlalchemy.orm import Mapped, mapped_column, relationship
  4. from backend.app.core.database import Base
  5. # Width of the last_state column. The poller truncates what it persists to
  6. # this, because a numeric entity can start reporting free text (an enum, an
  7. # error string) longer than the column -- SQLite stores it anyway, but
  8. # PostgreSQL rejects the row and takes the whole poll batch's commit with it.
  9. # Its sibling in models/location_ha_sensor.py says the same for that table.
  10. LAST_STATE_MAX_LENGTH = 64
  11. class PrinterHASensor(Base):
  12. """A read-only Home Assistant entity bound to a printer (#1148, #448).
  13. Deliberately *not* a ``SmartPlug`` row with a wider entity pattern. A plug
  14. carries auto-on/auto-off, schedules, power alerts, energy snapshots and
  15. ``controls_printer_power``; none of that means anything for a door contact,
  16. and ``get_smart_plug_by_printer`` would hand the card's power button a
  17. sensor to switch. Sensors get their own table and their own read-only
  18. routes instead.
  19. Not to be confused with ``PrinterSensorHistory``, which stores the
  20. printer's *own* heater readings.
  21. """
  22. __tablename__ = "printer_ha_sensors"
  23. id: Mapped[int] = mapped_column(primary_key=True)
  24. printer_id: Mapped[int] = mapped_column(ForeignKey("printers.id", ondelete="CASCADE"), index=True)
  25. name: Mapped[str] = mapped_column(String(100))
  26. entity_id: Mapped[str] = mapped_column(String(255))
  27. # "binary" for binary_sensor.*, "numeric" for sensor.*. Decides how the
  28. # state is rendered and which alert fields apply.
  29. kind: Mapped[str] = mapped_column(String(16), default="binary")
  30. # HA's own device_class, snapshotted when the entity is bound. Drives the
  31. # on/off wording (door -> Open/Closed, motion -> Detected/Clear) and the
  32. # icon, so the card doesn't have to say "On" for an open door.
  33. device_class: Mapped[str | None] = mapped_column(String(32), nullable=True)
  34. # Numeric only: "°C", "%", "ppm", ... shown next to the value.
  35. unit: Mapped[str | None] = mapped_column(String(16), nullable=True)
  36. # What counts as needing attention. One notion, three consumers: the pill
  37. # colour on the card, the notification, and the print interlock.
  38. # Binary sensors use alert_state ("on"/"off"/None), numeric ones the
  39. # thresholds. All None means "just show the value".
  40. alert_state: Mapped[str | None] = mapped_column(String(8), nullable=True)
  41. alert_above: Mapped[float | None] = mapped_column(Float, nullable=True)
  42. alert_below: Mapped[float | None] = mapped_column(Float, nullable=True)
  43. # Hold queued prints for this printer while the sensor is in its alert
  44. # state — the enclosure-door case this feature was asked for. Opt-in, and
  45. # only ever a *hold*: the item stays pending with a waiting_reason and
  46. # dispatches by itself once the door closes.
  47. block_print: Mapped[bool] = mapped_column(Boolean, default=False, server_default="0")
  48. notify_on_alert: Mapped[bool] = mapped_column(Boolean, default=False, server_default="0")
  49. show_on_printer_card: Mapped[bool] = mapped_column(Boolean, default=True, server_default="1")
  50. sort_order: Mapped[int] = mapped_column(Integer, default=0, server_default="0")
  51. # Last poll result. Persisted so a restart doesn't blank the card until the
  52. # first poll lands, and so notifications only fire on a real transition.
  53. last_state: Mapped[str | None] = mapped_column(String(LAST_STATE_MAX_LENGTH), nullable=True)
  54. last_changed: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
  55. last_checked: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
  56. created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now())
  57. updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now())
  58. printer: Mapped["Printer"] = relationship(back_populates="ha_sensors")
  59. from backend.app.models.printer import Printer # noqa: E402