PrinterHASensorRow.tsx 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. import { useQuery } from '@tanstack/react-query';
  2. import { Gauge } from 'lucide-react';
  3. import { useTranslation } from 'react-i18next';
  4. import { api } from '../api/client';
  5. import type { PrinterHASensorReading } from '../api/client';
  6. import { describeHASensorReading, iconForHASensor } from '../utils/haSensorDisplay';
  7. /**
  8. * The Home Assistant sensors bound to a printer, on its card (#1148, #448).
  9. *
  10. * Read-only by design — these are contacts and thermometers, not switches, so
  11. * nothing here is clickable. The sibling "HA:" row above handles the entities
  12. * you can actually operate.
  13. */
  14. interface Props {
  15. printerId: number;
  16. }
  17. export function PrinterHASensorRow({ printerId }: Props) {
  18. const { t } = useTranslation();
  19. const { data: readings } = useQuery({
  20. queryKey: ['haSensorReadings', printerId],
  21. queryFn: () => api.getHASensorReadings(printerId),
  22. // Served from the backend poller's cache, so this costs a local request
  23. // and never a Home Assistant round trip. Matched to the poller's own
  24. // cadence — refetching faster would only re-read the same reading.
  25. refetchInterval: 15000,
  26. });
  27. if (!readings?.length) return null;
  28. const describe = (reading: PrinterHASensorReading): string => describeHASensorReading(reading, t);
  29. return (
  30. <div className="flex items-center gap-2 mt-2">
  31. <Gauge className="w-[var(--pc-i35,0.875rem)] h-[var(--pc-i35,0.875rem)] text-blue-600 dark:text-blue-400 flex-shrink-0" />
  32. <span className="text-xs text-bambu-gray">{t('haSensors.label')}</span>
  33. <div className="h-[2px] w-5 bg-bambu-dark-tertiary/50" />
  34. <div className="flex flex-wrap gap-1">
  35. {readings.map((reading) => {
  36. const Icon = iconForHASensor(reading);
  37. const unreachable = !reading.reachable || reading.state === null;
  38. return (
  39. <span
  40. key={reading.id}
  41. title={
  42. reading.block_print
  43. ? t('haSensors.blocksPrints', { entity: reading.entity_id })
  44. : reading.entity_id
  45. }
  46. className={`px-2 py-0.5 text-xs rounded flex items-center gap-1 ${
  47. unreachable
  48. ? 'bg-bambu-dark-tertiary/50 text-bambu-gray'
  49. : reading.alerting
  50. ? 'bg-red-100 dark:bg-red-500/20 text-red-700 dark:text-red-400'
  51. : 'bg-bambu-dark-tertiary text-bambu-gray'
  52. }`}
  53. >
  54. <Icon className="w-[var(--pc-i25,0.625rem)] h-[var(--pc-i25,0.625rem)]" />
  55. <span>{reading.name}</span>
  56. <span className="font-medium">{describe(reading)}</span>
  57. </span>
  58. );
  59. })}
  60. </div>
  61. </div>
  62. );
  63. }