@homebridge-plugins/homebridge-eufy-security 5.0.0-beta.3 → 5.0.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/config.schema.json +48 -0
  2. package/dist/account/temporary-authentication.js +3 -1
  3. package/dist/account/temporary-authentication.js.map +1 -1
  4. package/dist/configuration.js +44 -2
  5. package/dist/configuration.js.map +1 -1
  6. package/dist/device/member-evidence.js +10 -3
  7. package/dist/device/member-evidence.js.map +1 -1
  8. package/dist/device/snapshot.js +1 -1
  9. package/dist/device/snapshot.js.map +1 -1
  10. package/dist/diagnostics.js +694 -83
  11. package/dist/diagnostics.js.map +1 -1
  12. package/dist/homekit/adapters/camera-streaming.js +56 -27
  13. package/dist/homekit/adapters/camera-streaming.js.map +1 -1
  14. package/dist/homekit/adapters/lock.js +45 -16
  15. package/dist/homekit/adapters/lock.js.map +1 -1
  16. package/dist/homekit/adapters/motion.js +1 -1
  17. package/dist/homekit/adapters/motion.js.map +1 -1
  18. package/dist/homekit/adapters/package-sensor.js +63 -0
  19. package/dist/homekit/adapters/package-sensor.js.map +1 -0
  20. package/dist/homekit/adapters/registry.js +2 -0
  21. package/dist/homekit/adapters/registry.js.map +1 -1
  22. package/dist/homekit/adapters/security-system.js +101 -45
  23. package/dist/homekit/adapters/security-system.js.map +1 -1
  24. package/dist/homekit/coverage-matrix.js +1 -3
  25. package/dist/homekit/coverage-matrix.js.map +1 -1
  26. package/dist/homekit/reconciler.js +21 -5
  27. package/dist/homekit/reconciler.js.map +1 -1
  28. package/dist/media/last-successful-image.js +5 -3
  29. package/dist/media/last-successful-image.js.map +1 -1
  30. package/dist/media/live-stream.js +200 -84
  31. package/dist/media/live-stream.js.map +1 -1
  32. package/dist/media/recording.js +11 -5
  33. package/dist/media/recording.js.map +1 -1
  34. package/dist/media/snapshot.js +21 -8
  35. package/dist/media/snapshot.js.map +1 -1
  36. package/dist/media/station-live-sessions.js +47 -43
  37. package/dist/media/station-live-sessions.js.map +1 -1
  38. package/dist/platform.js +27 -7
  39. package/dist/platform.js.map +1 -1
  40. package/dist/runtime/channel.js +116 -4
  41. package/dist/runtime/channel.js.map +1 -1
  42. package/dist/runtime/device-settings.js +88 -0
  43. package/dist/runtime/device-settings.js.map +1 -0
  44. package/dist/runtime/owner.js +12 -0
  45. package/dist/runtime/owner.js.map +1 -1
  46. package/dist/runtime/sdk-client.js +19 -4
  47. package/dist/runtime/sdk-client.js.map +1 -1
  48. package/dist/ui/dashboard.js +32 -14
  49. package/dist/ui/dashboard.js.map +1 -1
  50. package/dist/ui/runtime-channel-client.js +64 -1
  51. package/dist/ui/runtime-channel-client.js.map +1 -1
  52. package/dist/ui/server.js +64 -5
  53. package/dist/ui/server.js.map +1 -1
  54. package/homebridge-ui/public/app.css +359 -88
  55. package/homebridge-ui/public/assets/icons/battery_0.svg +1 -0
  56. package/homebridge-ui/public/assets/icons/battery_1.svg +1 -0
  57. package/homebridge-ui/public/assets/icons/battery_2.svg +1 -0
  58. package/homebridge-ui/public/assets/icons/battery_3.svg +1 -0
  59. package/homebridge-ui/public/assets/icons/battery_4.svg +1 -0
  60. package/homebridge-ui/public/assets/icons/battery_5.svg +1 -0
  61. package/homebridge-ui/public/assets/icons/battery_6.svg +1 -0
  62. package/homebridge-ui/public/assets/icons/battery_full.svg +1 -0
  63. package/homebridge-ui/public/assets/icons/contact_support.svg +1 -0
  64. package/homebridge-ui/public/assets/icons/delete.svg +1 -0
  65. package/homebridge-ui/public/assets/icons/download.svg +1 -0
  66. package/homebridge-ui/public/assets/icons/extension_off.svg +1 -0
  67. package/homebridge-ui/public/assets/icons/info.svg +1 -0
  68. package/homebridge-ui/public/assets/icons/key_off.svg +1 -0
  69. package/homebridge-ui/public/assets/icons/keyboard_arrow_left.svg +1 -0
  70. package/homebridge-ui/public/assets/icons/keyboard_arrow_right.svg +1 -0
  71. package/homebridge-ui/public/assets/icons/keyboard_double_arrow_left.svg +1 -0
  72. package/homebridge-ui/public/assets/icons/keyboard_double_arrow_right.svg +1 -0
  73. package/homebridge-ui/public/assets/icons/movie_off.svg +1 -0
  74. package/homebridge-ui/public/assets/icons/refresh.svg +1 -0
  75. package/homebridge-ui/public/assets/icons/satellite_alt.svg +1 -0
  76. package/homebridge-ui/public/assets/icons/search_off.svg +1 -0
  77. package/homebridge-ui/public/assets/icons/sensors_off.svg +1 -0
  78. package/homebridge-ui/public/assets/icons/settings_photo_camera.svg +1 -0
  79. package/homebridge-ui/public/assets/icons/solar_power.svg +1 -0
  80. package/homebridge-ui/public/assets/icons/videocam_off.svg +1 -0
  81. package/homebridge-ui/public/i18n/en.json +119 -78
  82. package/homebridge-ui/public/i18n/fr.json +119 -78
  83. package/homebridge-ui/public/index.html +115 -106
  84. package/homebridge-ui/public/js/app.js +350 -358
  85. package/homebridge-ui/public/js/dashboard.js +139 -10
  86. package/homebridge-ui/public/js/profile-wizard.js +17 -54
  87. package/i18n/runtime/en.json +8 -3
  88. package/package.json +9 -6
@@ -274,7 +274,9 @@ h2 {
274
274
  padding: 8px 12px;
275
275
  }
276
276
 
277
- .diagnostics-actions {
277
+ .diagnostics-actions:not([hidden]) {
278
+ display: flex;
279
+ gap: 8px;
278
280
  margin-top: 4px;
279
281
  }
280
282
 
@@ -290,42 +292,25 @@ h2 {
290
292
  padding: clamp(16px, 4vw, 24px);
291
293
  }
292
294
 
293
- .diagnostics-question,
294
- .diagnostics-direct,
295
- .diagnostics-frequency,
296
- .diagnostics-match {
295
+ .diagnostics-question {
297
296
  display: grid;
298
297
  gap: 16px;
299
298
  }
300
299
 
301
- .diagnostics-question > strong,
302
- .diagnostics-frequency > strong,
303
- .diagnostics-match > strong {
304
- font-size: clamp(17px, 4vw, 21px);
305
- line-height: 1.35;
306
- letter-spacing: -0.01em;
307
- text-wrap: balance;
308
- }
309
-
310
- .diagnostics-match p {
311
- margin-bottom: 0;
312
- }
313
-
314
- .diagnostics-choice-actions {
315
- display: grid;
316
- grid-template-columns: repeat(2, minmax(0, 1fr));
317
- gap: 8px;
300
+ .diagnostics-question > h3,
301
+ .diagnostics-guidance > h3 {
302
+ margin: 0;
318
303
  }
319
304
 
320
- .diagnostics-choice-actions button {
321
- width: 100%;
322
- padding-inline: 16px;
305
+ .diagnostics-guidance > h3 {
306
+ font-size: inherit;
323
307
  }
324
308
 
325
- button.diagnostics-secondary-action {
326
- border: 1px solid var(--line-strong);
327
- background: transparent;
328
- color: var(--ink);
309
+ .diagnostics-question > h3 {
310
+ font-size: clamp(17px, 4vw, 21px);
311
+ line-height: 1.35;
312
+ letter-spacing: -0.01em;
313
+ text-wrap: balance;
329
314
  }
330
315
 
331
316
  button.diagnostics-quiet-action {
@@ -366,55 +351,159 @@ button.diagnostics-quiet-action {
366
351
  text-transform: uppercase;
367
352
  }
368
353
 
369
- .diagnostics-actions button {
370
- width: 100%;
354
+ .diagnostics-actions [data-diagnostics-reproduction] {
355
+ flex: 1 1 auto;
371
356
  }
372
357
 
358
+ .diagnostics-result h2,
373
359
  .diagnostics-result p {
374
360
  margin-bottom: 0;
375
361
  }
376
362
 
377
- .diagnostics-privacy {
363
+ .diagnostics-note {
364
+ margin-bottom: 0;
378
365
  color: var(--muted);
379
366
  font-size: 12px;
380
367
  }
381
368
 
382
- .diagnostics-result {
369
+ .diagnostics-result[open] {
383
370
  display: grid;
371
+ max-width: 560px;
384
372
  gap: 8px;
385
373
  }
386
374
 
387
- .diagnostics-result a {
388
- display: inline-block;
389
- margin-top: 8px;
390
- color: var(--accent-ink);
375
+ /*
376
+ * The download is a button because it acts here; the two ways to report are anchors because they navigate to
377
+ * GitHub, side by side because either one finishes the handoff.
378
+ */
379
+ .diagnostics-result [data-diagnostics-export],
380
+ .diagnostics-handoff a {
381
+ display: flex;
382
+ min-height: 44px;
383
+ align-items: center;
384
+ justify-content: center;
385
+ border-radius: var(--radius-control);
386
+ padding: 8px 12px;
391
387
  font-weight: 600;
388
+ text-align: center;
389
+ text-decoration: none;
392
390
  }
393
391
 
394
- .diagnostics-result button {
395
- width: 100%;
392
+ /*
393
+ * The dialog sits in the top layer, which adds nothing to the page height Homebridge sizes its iframe by, so the
394
+ * stage keeps room for it rather than leaving it to scroll inside a sliver.
395
+ */
396
+ .diagnostics-stage:has(> .diagnostics-result[open]) {
397
+ min-height: 320px;
396
398
  }
397
399
 
398
- .diagnostics-manifest {
399
- margin-top: 12px;
400
- border-block: 1px solid var(--line);
401
- padding-block: 8px;
402
- font-size: 12px;
403
- font-variant-numeric: tabular-nums;
400
+ .diagnostics-handoff:not([hidden]) {
401
+ display: grid;
402
+ grid-template-columns: 1fr 1fr;
403
+ gap: 8px;
404
+ margin-top: 8px;
404
405
  }
405
406
 
406
- .diagnostics-manifest ul {
407
- margin: 8px 0;
408
- padding-left: 20px;
407
+ .diagnostics-handoff [data-diagnostics-issue] {
408
+ background: var(--accent);
409
+ color: var(--on-accent);
409
410
  }
410
411
 
411
- .diagnostics-review-confirm {
412
- display: flex;
413
- align-items: flex-start;
412
+ .diagnostics-handoff [data-diagnostics-existing-issue] {
413
+ border: 1px solid var(--line-strong);
414
+ color: var(--ink);
415
+ }
416
+
417
+ .diagnostics-result [data-diagnostics-result-status]:empty {
418
+ display: none;
419
+ }
420
+
421
+ .diagnostics-result [data-diagnostics-download-again],
422
+ .diagnostics-result [data-diagnostics-start-another] {
423
+ justify-self: center;
424
+ }
425
+
426
+ /*
427
+ * The opening screen: every area at once, three to a row, each label over its one-line summary.
428
+ *
429
+ * Each tile carries a Material Symbols outline glyph (Apache-2.0) as a mask rather than an element. A mask lets
430
+ * the glyph take the tile's own colour in every state instead of being baked into an image. The data URI is inline, so
431
+ * the panel renders the same on a host with no route to the internet.
432
+ */
433
+ .diagnostics-tiles {
434
+ display: grid;
435
+ grid-template-columns: repeat(3, minmax(0, 1fr));
414
436
  gap: 8px;
415
437
  margin-top: 12px;
438
+ }
439
+
440
+ .diagnostics-tiles button {
441
+ display: flex;
442
+ min-height: 88px;
443
+ flex-direction: column;
444
+ align-items: center;
445
+ justify-content: center;
446
+ gap: 8px;
447
+ padding: 10px 6px;
448
+ border: 1px solid var(--line-strong);
449
+ background: transparent;
450
+ color: var(--ink);
416
451
  font-size: 12px;
417
- cursor: pointer;
452
+ font-weight: 600;
453
+ line-height: 1.25;
454
+ overflow-wrap: break-word;
455
+ text-align: center;
456
+ }
457
+
458
+ .diagnostics-tiles button::before {
459
+ width: 26px;
460
+ height: 26px;
461
+ flex: none;
462
+ background: currentColor;
463
+ content: '';
464
+ mask-position: center;
465
+ mask-repeat: no-repeat;
466
+ mask-size: contain;
467
+ }
468
+
469
+ .diagnostics-tiles small {
470
+ color: var(--muted);
471
+ font-size: 11px;
472
+ font-weight: 400;
473
+ }
474
+
475
+ .diagnostics-tiles button:hover:not(:disabled) {
476
+ border-color: var(--accent);
477
+ background: var(--accent-soft);
478
+ color: var(--accent-ink);
479
+ }
480
+
481
+ .diagnostics-tiles [data-diagnostics-tile='startup-authentication']::before {
482
+ mask-image: url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%20-960%20960%20960%22%3E%3Cpath%20d=%22M480-120v-80h280v-560H480v-80h280q33%200%2056.5%2023.5T840-760v560q0%2033-23.5%2056.5T760-120H480Zm-80-160-55-58%20102-102H120v-80h327L345-622l55-58%20200%20200-200%20200Z%22/%3E%3C/svg%3E');
483
+ }
484
+
485
+ .diagnostics-tiles [data-diagnostics-tile='device-representation']::before {
486
+ mask-image: url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%20-960%20960%20960%22%3E%3Cpath%20d=%22M400-560ZM160-160q-33%200-56.5-23.5T80-240v-480q0-33%2023.5-56.5T160-800h640q33%200%2056.5%2023.5T880-720H160v480h80v80h-80Zm640-80v-320H640v320h160Zm-180%2080q-25%200-42.5-17.5T560-220v-360q0-25%2017.5-42.5T620-640h200q25%200%2042.5%2017.5T880-580v360q0%2025-17.5%2042.5T820-160H620Zm100-300q13%200%2021.5-9t8.5-21q0-13-8.5-21.5T720-520q-12%200-21%208.5t-9%2021.5q0%2012%209%2021t21%209ZM340-160l-20-70q-19-17-29.5-40T280-320q0-27%2010.5-50t29.5-40l20-70h120l20%2070q19%2017%2029.5%2040t10.5%2050q0%2027-10.5%2050T480-230l-20%2070H340Zm60-100q26%200%2043-17.5t17-42.5q0-25-18-42.5T400-380q-24%200-42%2017t-18%2043q0%2026%2017%2043t43%2017Zm320-140Z%22/%3E%3C/svg%3E');
487
+ }
488
+
489
+ .diagnostics-tiles [data-diagnostics-tile='control-state']::before {
490
+ mask-image: url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%20-960%20960%20960%22%3E%3Cpath%20d=%22M440-120v-240h80v80h320v80H520v80h-80Zm-320-80v-80h240v80H120Zm160-160v-80H120v-80h160v-80h80v240h-80Zm160-80v-80h400v80H440Zm160-160v-240h80v80h160v80H680v80h-80Zm-480-80v-80h400v80H120Z%22/%3E%3C/svg%3E');
491
+ }
492
+
493
+ .diagnostics-tiles [data-diagnostics-tile='live-media']::before {
494
+ mask-image: url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%20-960%20960%20960%22%3E%3Cpath%20d=%22M160-160q-33%200-56.5-23.5T80-240v-480q0-33%2023.5-56.5T160-800h480q33%200%2056.5%2023.5T720-720v180l160-160v440L720-420v180q0%2033-23.5%2056.5T640-160H160Zm0-80h480v-480H160v480Zm0%200v-480%20480Z%22/%3E%3C/svg%3E');
495
+ }
496
+
497
+ .diagnostics-tiles [data-diagnostics-tile='hksv-recording']::before {
498
+ mask-image: url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%20-960%20960%20960%22%3E%3Cpath%20d=%22m160-800%2080%20160h120l-80-160h80l80%20160h120l-80-160h80l80%20160h120l-80-160h120q33%200%2056.5%2023.5T880-720v480q0%2033-23.5%2056.5T800-160H160q-33%200-56.5-23.5T80-240v-480q0-33%2023.5-56.5T160-800Zm0%20240v320h640v-320H160Zm0%200v320-320Z%22/%3E%3C/svg%3E');
499
+ }
500
+
501
+ .diagnostics-tiles [data-diagnostics-tile='dashboard-ui']::before {
502
+ mask-image: url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%20-960%20960%20960%22%3E%3Cpath%20d=%22M520-600v-240h320v240H520ZM120-440v-400h320v400H120Zm400%20320v-400h320v400H520Zm-400%200v-240h320v240H120Zm80-400h160v-240H200v240Zm400%20320h160v-240H600v240Zm0-480h160v-80H600v80ZM200-200h160v-80H200v80Zm160-320Zm240-160Zm0%20240ZM360-280Z%22/%3E%3C/svg%3E');
503
+ }
504
+
505
+ .diagnostics-tiles [data-diagnostics-tile='other']::before {
506
+ mask-image: url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%20-960%20960%20960%22%3E%3Cpath%20d=%22M513.5-254.5Q528-269%20528-290t-14.5-35.5Q499-340%20478-340t-35.5%2014.5Q428-311%20428-290t14.5%2035.5Q457-240%20478-240t35.5-14.5ZM442-394h74q0-33%207.5-52t42.5-52q26-26%2041-49.5t15-56.5q0-56-41-86t-97-30q-57%200-92.5%2030T342-618l66%2026q5-18%2022.5-39t53.5-21q32%200%2048%2017.5t16%2038.5q0%2020-12%2037.5T506-526q-44%2039-54%2059t-10%2073Zm38%20314q-83%200-156-31.5T197-197q-54-54-85.5-127T80-480q0-83%2031.5-156T197-763q54-54%20127-85.5T480-880q83%200%20156%2031.5T763-763q54%2054%2085.5%20127T880-480q0%2083-31.5%20156T763-197q-54%2054-127%2085.5T480-80Zm0-80q134%200%20227-93t93-227q0-134-93-227t-227-93q-134%200-227%2093t-93%20227q0%20134%2093%20227t227%2093Zm0-320Z%22/%3E%3C/svg%3E');
418
507
  }
419
508
 
420
509
  .advanced-fields {
@@ -451,6 +540,12 @@ button.diagnostics-quiet-action {
451
540
  font: inherit;
452
541
  }
453
542
 
543
+ .advanced-fields input[type='checkbox'] {
544
+ justify-self: start;
545
+ width: auto;
546
+ min-height: 0;
547
+ }
548
+
454
549
  .advanced-fields small,
455
550
  .advanced-device-help {
456
551
  color: var(--muted);
@@ -536,6 +631,7 @@ button.diagnostics-quiet-action {
536
631
  border: 1px solid var(--line-strong);
537
632
  border-radius: var(--radius-control);
538
633
  background: var(--panel);
634
+ color: var(--ink);
539
635
  padding: 0;
540
636
  }
541
637
 
@@ -570,38 +666,84 @@ button.diagnostics-quiet-action {
570
666
  transform 150ms var(--ease-out);
571
667
  }
572
668
 
669
+ .dashboard-action[data-collecting],
670
+ .dashboard-action:has(.diagnostics-name) {
671
+ width: auto;
672
+ grid-auto-flow: column;
673
+ gap: 6px;
674
+ padding: 0 10px;
675
+ }
676
+
573
677
  .dashboard-action[data-collecting] {
574
678
  border-color: var(--accent);
575
679
  background: color-mix(in srgb, var(--accent) 16%, var(--panel));
576
680
  }
577
681
 
682
+ /*
683
+ * The dashboard's diagnostics action is named in words at rest, and a capture under way replaces that name
684
+ * beside the icon, so neither rests on the icon or on colour alone.
685
+ */
686
+ .diagnostics-name,
687
+ .diagnostics-capturing {
688
+ font-size: 12px;
689
+ font-weight: 600;
690
+ }
691
+
692
+ .diagnostics-capturing,
693
+ .dashboard-action[data-collecting] .diagnostics-name {
694
+ display: none;
695
+ }
696
+
697
+ .dashboard-action[data-collecting] .diagnostics-capturing {
698
+ display: inline;
699
+ }
700
+
701
+ /* A problem the dashboard shows: an alert ring, and a dot so the state does not rest on colour alone. */
702
+ .dashboard-action[data-attention] {
703
+ border-color: var(--alert);
704
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--alert) 24%, transparent);
705
+ }
706
+
707
+ .dashboard-action[data-attention]::before {
708
+ content: '';
709
+ position: absolute;
710
+ top: -5px;
711
+ right: -5px;
712
+ width: 8px;
713
+ height: 8px;
714
+ border: 2px solid var(--panel);
715
+ border-radius: 50%;
716
+ background: var(--alert);
717
+ }
718
+
578
719
  .dashboard-action:hover::after,
579
720
  .dashboard-action:focus-visible::after {
580
721
  opacity: 1;
581
722
  transform: translateY(0);
582
723
  }
583
724
 
584
- /*
585
- * The way into a diagnostic run, standing where a status pill used to. A pill saying "Connected" was decoration on
586
- * the healthy path and a dead end on every other; this appears only where a run would explain something.
587
- */
588
- .dashboard-diagnose {
589
- flex: 0 0 auto;
590
- /* Buttons are full width by default here, and this one sits inline beside the title. */
591
- width: auto;
592
- border: 1px solid var(--alert);
593
- border-radius: var(--radius-pill);
594
- background: color-mix(in srgb, var(--alert) 16%, var(--panel));
725
+ .blocking-dialog {
726
+ max-width: 420px;
727
+ border: 0;
728
+ border-radius: var(--radius-panel);
729
+ background: var(--panel);
730
+ box-shadow: var(--shadow-overlay);
595
731
  color: var(--ink);
596
- padding: 6px 12px;
597
- font: inherit;
598
- font-size: 12px;
599
- font-weight: 600;
600
- cursor: pointer;
732
+ padding: 24px;
733
+ }
734
+
735
+ .blocking-dialog::backdrop {
736
+ background: rgb(0 0 0 / 30%);
737
+ backdrop-filter: blur(5px);
738
+ }
739
+
740
+ .blocking-dialog h2 {
741
+ margin: 0 0 8px;
601
742
  }
602
743
 
603
- .dashboard-diagnose:hover {
604
- background: color-mix(in srgb, var(--alert) 26%, var(--panel));
744
+ .blocking-dialog p {
745
+ margin: 0 0 20px;
746
+ color: var(--ink-secondary);
605
747
  }
606
748
 
607
749
  .device-group + .device-group {
@@ -1149,7 +1291,128 @@ button.diagnostics-quiet-action {
1149
1291
  font-weight: 700;
1150
1292
  }
1151
1293
 
1152
- .preference-grid .preference-changed .snapshot-description {
1294
+ .preference-grid .preference-changed /*
1295
+ * The guard-mode map: four HomeKit states against nine station modes, one pairing per row.
1296
+ *
1297
+ * A table because the content is one, which also gives each select its own row label without a second element
1298
+ * beside it. Row rules and no box: the panel's own surface encloses it, and the one control in the row is the
1299
+ * select, which carries the only border here.
1300
+ */
1301
+ .arming-setting {
1302
+ color: var(--muted);
1303
+ font-size: 11px;
1304
+ }
1305
+
1306
+ .arming-setting > summary {
1307
+ display: grid;
1308
+ grid-template-columns: minmax(0, 1fr) 12px;
1309
+ align-items: center;
1310
+ gap: 2px 8px;
1311
+ cursor: pointer;
1312
+ }
1313
+
1314
+ /* `display: grid` on a summary drops the native marker, so the affordance is drawn back in as a caret. */
1315
+ .arming-setting > summary::marker,
1316
+ .arming-setting > summary::-webkit-details-marker {
1317
+ content: '';
1318
+ display: none;
1319
+ }
1320
+
1321
+ .arming-setting > summary::after {
1322
+ content: '';
1323
+ grid-row: 1 / span 2;
1324
+ grid-column: 2;
1325
+ width: 6px;
1326
+ height: 6px;
1327
+ border-right: 1.5px solid var(--muted);
1328
+ border-bottom: 1.5px solid var(--muted);
1329
+ transform: rotate(45deg);
1330
+ transition: transform 140ms var(--ease-out);
1331
+ }
1332
+
1333
+ .arming-setting[open] > summary::after {
1334
+ transform: rotate(-135deg);
1335
+ }
1336
+
1337
+ /* The map as one line, which is the answer most households only ever read. */
1338
+ .arming-summary {
1339
+ overflow: hidden;
1340
+ color: var(--muted);
1341
+ font-size: 10px;
1342
+ text-overflow: ellipsis;
1343
+ white-space: nowrap;
1344
+ }
1345
+
1346
+ .arming-setting[open] > summary {
1347
+ margin-bottom: 8px;
1348
+ }
1349
+
1350
+ .arming-table {
1351
+ width: 100%;
1352
+ border-collapse: collapse;
1353
+ }
1354
+
1355
+ .arming-table thead th {
1356
+ padding: 0 0 6px;
1357
+ border-bottom: 1px solid var(--line-strong);
1358
+ color: var(--muted);
1359
+ font-size: 10px;
1360
+ font-weight: 600;
1361
+ letter-spacing: 0.04em;
1362
+ text-align: left;
1363
+ text-transform: uppercase;
1364
+ }
1365
+
1366
+ .arming-table tbody tr + tr th,
1367
+ .arming-table tbody tr + tr td {
1368
+ border-top: 1px solid var(--line);
1369
+ }
1370
+
1371
+ .arming-table th[scope='row'] {
1372
+ width: 38%;
1373
+ padding: 6px 10px 6px 0;
1374
+ color: var(--ink);
1375
+ font-size: 11px;
1376
+ font-weight: 600;
1377
+ text-align: left;
1378
+ }
1379
+
1380
+ .arming-table th[scope='row'] label {
1381
+ cursor: pointer;
1382
+ }
1383
+
1384
+ .arming-table td {
1385
+ padding: 6px 0;
1386
+ }
1387
+
1388
+ .arming-table select {
1389
+ width: 100%;
1390
+ min-height: 28px;
1391
+ border: 1px solid var(--line-strong);
1392
+ border-radius: var(--radius-control);
1393
+ background: var(--panel);
1394
+ color: var(--ink);
1395
+ padding: 2px 6px;
1396
+ font-size: 11px;
1397
+ }
1398
+
1399
+ /* The changed row carries the weight, so a pending map reads as which pairing moved rather than as a heavier table. */
1400
+ .arming-setting .snapshot-help {
1401
+ margin-top: 6px;
1402
+ }
1403
+
1404
+ .arming-table select.preference-control-changed {
1405
+ border-color: var(--accent);
1406
+ font-weight: 700;
1407
+ }
1408
+
1409
+ @media (pointer: coarse) {
1410
+ .arming-table select {
1411
+ min-height: 44px;
1412
+ }
1413
+ }
1414
+
1415
+ .snapshot-description {
1153
1416
  font-weight: 400;
1154
1417
  }
1155
1418
 
@@ -1322,8 +1585,7 @@ button.diagnostics-quiet-action {
1322
1585
  cursor: pointer;
1323
1586
  }
1324
1587
 
1325
- .first-setup-confirmation input,
1326
- .diagnostics-review-confirm input {
1588
+ .first-setup-confirmation input {
1327
1589
  appearance: none;
1328
1590
  display: grid;
1329
1591
  place-content: center;
@@ -1338,8 +1600,7 @@ button.diagnostics-quiet-action {
1338
1600
  padding: 0;
1339
1601
  }
1340
1602
 
1341
- .first-setup-confirmation input::before,
1342
- .diagnostics-review-confirm input::before {
1603
+ .first-setup-confirmation input::before {
1343
1604
  content: '';
1344
1605
  width: 8px;
1345
1606
  height: 4px;
@@ -1348,14 +1609,12 @@ button.diagnostics-quiet-action {
1348
1609
  transform: rotate(-45deg) scale(0);
1349
1610
  }
1350
1611
 
1351
- .first-setup-confirmation input:checked,
1352
- .diagnostics-review-confirm input:checked {
1612
+ .first-setup-confirmation input:checked {
1353
1613
  border-color: var(--accent);
1354
1614
  background: var(--accent);
1355
1615
  }
1356
1616
 
1357
- .first-setup-confirmation input:checked::before,
1358
- .diagnostics-review-confirm input:checked::before {
1617
+ .first-setup-confirmation input:checked::before {
1359
1618
  transform: rotate(-45deg) scale(1);
1360
1619
  }
1361
1620
 
@@ -1655,7 +1914,6 @@ button:disabled {
1655
1914
  cursor: not-allowed;
1656
1915
  }
1657
1916
 
1658
- button.diagnostics-secondary-action:hover:not(:disabled),
1659
1917
  .dashboard-page-back:hover:not(:disabled),
1660
1918
  .warm-up-controls button:hover:not(:disabled),
1661
1919
  .warm-up-column button:hover:not(:disabled),
@@ -1687,8 +1945,7 @@ button.diagnostics-quiet-action:hover:not(:disabled) {
1687
1945
  }
1688
1946
 
1689
1947
  .warm-up-controls button:disabled,
1690
- .warm-up-column button:disabled,
1691
- button.diagnostics-secondary-action:disabled {
1948
+ .warm-up-column button:disabled {
1692
1949
  border-color: var(--line);
1693
1950
  background: var(--panel);
1694
1951
  color: var(--ink-disabled);
@@ -1734,6 +1991,20 @@ input:focus-visible {
1734
1991
  }
1735
1992
 
1736
1993
  @media (max-width: 420px) {
1994
+ /*
1995
+ * Two across rather than three. Seven areas still fit on one screen at this width, and a third column
1996
+ * leaves each label about ninety pixels, where the longest of them runs to four lines.
1997
+ */
1998
+ .diagnostics-tiles {
1999
+ grid-template-columns: repeat(2, minmax(0, 1fr));
2000
+ }
2001
+
2002
+ /* The row keeps its three actions on one line by leaving the diagnostics action to its icon. */
2003
+ .diagnostics-name {
2004
+ display: none;
2005
+ }
2006
+
2007
+
1737
2008
  .status-row {
1738
2009
  display: grid;
1739
2010
  }
@@ -1806,14 +2077,14 @@ input:focus-visible {
1806
2077
  width: 100%;
1807
2078
  }
1808
2079
 
1809
- .diagnostics-choice-actions {
1810
- display: grid;
1811
- grid-template-columns: repeat(2, minmax(0, 1fr));
2080
+ /* Two actions side by side leave each a sliver of a label at this width, so they stack. */
2081
+ .diagnostics-handoff:not([hidden]) {
2082
+ grid-template-columns: 1fr;
1812
2083
  }
1813
2084
 
1814
- .diagnostics-choice-actions button {
1815
- width: 100%;
1816
- min-width: 0;
2085
+ .diagnostics-choice-actions {
2086
+ display: grid;
2087
+ gap: 8px;
1817
2088
  }
1818
2089
 
1819
2090
  .device-grid {
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M320-80q-17 0-28.5-11.5T280-120v-640q0-17 11.5-28.5T320-800h80v-80h160v80h80q17 0 28.5 11.5T680-760v640q0 17-11.5 28.5T640-80H320Zm40-80h240v-560H360v560Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M320-80q-17 0-28.5-11.5T280-120v-640q0-17 11.5-28.5T320-800h80v-80h160v80h80q17 0 28.5 11.5T680-760v640q0 17-11.5 28.5T640-80H320Zm40-160h240v-480H360v480Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M320-80q-17 0-28.5-11.5T280-120v-640q0-17 11.5-28.5T320-800h80v-80h160v80h80q17 0 28.5 11.5T680-760v640q0 17-11.5 28.5T640-80H320Zm40-240h240v-400H360v400Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M320-80q-17 0-28.5-11.5T280-120v-640q0-17 11.5-28.5T320-800h80v-80h160v80h80q17 0 28.5 11.5T680-760v640q0 17-11.5 28.5T640-80H320Zm40-320h240v-320H360v320Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M320-80q-17 0-28.5-11.5T280-120v-640q0-17 11.5-28.5T320-800h80v-80h160v80h80q17 0 28.5 11.5T680-760v640q0 17-11.5 28.5T640-80H320Zm40-400h240v-240H360v240Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M320-80q-17 0-28.5-11.5T280-120v-640q0-17 11.5-28.5T320-800h80v-80h160v80h80q17 0 28.5 11.5T680-760v640q0 17-11.5 28.5T640-80H320Zm40-480h240v-160H360v160Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M320-80q-17 0-28.5-11.5T280-120v-640q0-17 11.5-28.5T320-800h80v-80h160v80h80q17 0 28.5 11.5T680-760v640q0 17-11.5 28.5T640-80H320Zm40-560h240v-80H360v80Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M320-80q-17 0-28.5-11.5T280-120v-640q0-17 11.5-28.5T320-800h80v-80h160v80h80q17 0 28.5 11.5T680-760v640q0 17-11.5 28.5T640-80H320Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="m480-80-10-120h-10q-142 0-241-99t-99-241q0-142 99-241t241-99q71 0 132.5 26.5t108 73q46.5 46.5 73 108T800-540q0 75-24.5 144t-67 128q-42.5 59-101 107T480-80Zm80-146q71-60 115.5-140.5T720-540q0-109-75.5-184.5T460-800q-109 0-184.5 75.5T200-540q0 109 75.5 184.5T460-280h100v54Zm-101-95q17 0 29-12t12-29q0-17-12-29t-29-12q-17 0-29 12t-12 29q0 17 12 29t29 12Zm-29-127h60q0-30 6-42t38-44q18-18 30-39t12-45q0-51-34.5-76.5T460-720q-44 0-74 24.5T344-636l56 22q5-17 19-33.5t41-16.5q27 0 40.5 15t13.5 33q0 17-10 30.5T480-558q-35 30-42.5 47.5T430-448Zm30-65Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M480-320 280-520l56-58 104 104v-326h80v326l104-104 56 58-200 200ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M200-120q-33 0-56.5-23.5T120-200v-152q48 0 84-30.5t36-77.5q0-47-36-77.5T120-568v-152q0-17 6-31.5t17-25.5l57 57v88q54 20 87 67t33 105q0 57-33 104t-87 68v88h88q21-54 68-87t104-33q57 0 104 33t68 87h88l57 57q-11 11-25.5 17t-31.5 6H568q0-48-30.5-84T460-240q-47 0-77.5 36T352-120H200Zm600-154-80-80v-86h80q8 0 14-6t6-14q0-8-6-14t-14-6h-80v-240H480v-80q0-8-6-14t-14-6q-8 0-14 6t-6 14v80h-86l-80-80h86q0-42 29-71t71-29q42 0 71 29t29 71h160q33 0 56.5 23.5T800-720v160q42 0 71 29t29 71q0 42-29 71t-71 29v86Zm-9 219L55-791l57-57 736 736-57 57ZM537-537Zm-77 77Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M790-57 488-359q-32 54-87 86.5T280-240q-100 0-170-70T40-480q0-66 32.5-121t86.5-87L57-790l57-56 732 733-56 56Zm50-543 120 120-183 183-127-126 50-37 72 54 75-74-40-40H553l-80-80h367ZM280-320q51 0 90.5-27.5T428-419l-56-56-48.5-48.5L275-572l-56-56q-44 18-71.5 57.5T120-480q0 66 47 113t113 47Zm0-80q-33 0-56.5-23.5T200-480q0-33 23.5-56.5T280-560q33 0 56.5 23.5T360-480q0 33-23.5 56.5T280-400Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M562-231 311-482l251-251 52 52-199 199 199 199-52 52Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M522-482 323-681l52-52 251 251-251 251-52-52 199-199Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M441-231 191-481l250-250 52 51-199 199 199 198-52 52Zm278 0L469-481l250-250 51 51-199 199 199 198-51 52Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M390-481 191-680l52-51 250 250-250 250-52-52 199-198Zm278 0L469-680l51-51 250 250-250 250-51-52 199-198Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800l240 240H160v320h560l80 80H160Zm711-44-71-71v-285H514L274-800h66l67 133 27 27h106l-80-160h100l80 160h120l-80-160h120q33 0 56.5 23.5T880-720v480q0 10-2 19t-7 17ZM791-55 55-791l57-57 736 736-57 57ZM446-400Zm211-18Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M480-160q-134 0-227-93t-93-227q0-134 93-227t227-93q69 0 132 28.5T720-690v-110h80v280H520v-80h168q-32-56-87.5-88T480-720q-100 0-170 70t-70 170q0 100 70 170t170 70q77 0 139-44t87-116h84q-28 106-114 173t-196 67Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M560-32v-80q117 0 198.5-81.5T840-392h80q0 75-28.5 140.5t-77 114q-48.5 48.5-114 77T560-32Zm0-160v-80q50 0 85-35t35-85h80q0 83-58.5 141.5T560-192ZM222-57q-15 0-30-6t-27-17L23-222q-11-12-17-27t-6-30q0-16 6-30.5T23-335l127-127q23-23 57-23.5t57 22.5l50 50 28-28-50-50q-23-23-23-56t23-56l57-57q23-23 56.5-23t56.5 23l50 50 28-28-50-50q-23-23-23-56.5t23-56.5l127-127q12-12 27-18t30-6q15 0 29.5 6t26.5 18l142 142q12 11 17.5 25.5T895-730q0 15-5.5 30T872-673L745-546q-23 23-56.5 23T632-546l-50-50-28 28 50 50q23 23 22.5 56.5T603-405l-56 56q-23 23-56.5 23T434-349l-50-50-28 28 50 50q23 23 22.5 57T405-207L278-80q-11 11-25.5 17T222-57Zm0-79 42-42-142-142-42 42 142 142Zm85-85 42-42-142-142-42 42 142 142Zm184-184 56-56-142-142-56 56 142 142Zm198-198 42-42-142-142-42 42 142 142Zm85-85 42-42-142-142-42 42 142 142ZM448-504Z"/></svg>