@camstack/ui-library 1.1.18 → 1.1.19
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.
package/dist/index.cjs
CHANGED
|
@@ -8492,6 +8492,16 @@ var Blinds = createLucideIcon("blinds", [
|
|
|
8492
8492
|
* This source code is licensed under the ISC license.
|
|
8493
8493
|
* See the LICENSE file in the root directory of this source tree.
|
|
8494
8494
|
*/
|
|
8495
|
+
var Bone = createLucideIcon("bone", [["path", {
|
|
8496
|
+
d: "M17 10c.7-.7 1.69 0 2.5 0a2.5 2.5 0 1 0 0-5 .5.5 0 0 1-.5-.5 2.5 2.5 0 1 0-5 0c0 .81.7 1.8 0 2.5l-7 7c-.7.7-1.69 0-2.5 0a2.5 2.5 0 0 0 0 5c.28 0 .5.22.5.5a2.5 2.5 0 1 0 5 0c0-.81-.7-1.8 0-2.5Z",
|
|
8497
|
+
key: "w610uw"
|
|
8498
|
+
}]]);
|
|
8499
|
+
/**
|
|
8500
|
+
* @license lucide-react v0.576.0 - ISC
|
|
8501
|
+
*
|
|
8502
|
+
* This source code is licensed under the ISC license.
|
|
8503
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
8504
|
+
*/
|
|
8495
8505
|
var Bot = createLucideIcon("bot", [
|
|
8496
8506
|
["path", {
|
|
8497
8507
|
d: "M12 8V4H8",
|
|
@@ -10459,6 +10469,36 @@ var Pause = createLucideIcon("pause", [["rect", {
|
|
|
10459
10469
|
* This source code is licensed under the ISC license.
|
|
10460
10470
|
* See the LICENSE file in the root directory of this source tree.
|
|
10461
10471
|
*/
|
|
10472
|
+
var PawPrint = createLucideIcon("paw-print", [
|
|
10473
|
+
["circle", {
|
|
10474
|
+
cx: "11",
|
|
10475
|
+
cy: "4",
|
|
10476
|
+
r: "2",
|
|
10477
|
+
key: "vol9p0"
|
|
10478
|
+
}],
|
|
10479
|
+
["circle", {
|
|
10480
|
+
cx: "18",
|
|
10481
|
+
cy: "8",
|
|
10482
|
+
r: "2",
|
|
10483
|
+
key: "17gozi"
|
|
10484
|
+
}],
|
|
10485
|
+
["circle", {
|
|
10486
|
+
cx: "20",
|
|
10487
|
+
cy: "16",
|
|
10488
|
+
r: "2",
|
|
10489
|
+
key: "1v9bxh"
|
|
10490
|
+
}],
|
|
10491
|
+
["path", {
|
|
10492
|
+
d: "M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z",
|
|
10493
|
+
key: "1ydw1z"
|
|
10494
|
+
}]
|
|
10495
|
+
]);
|
|
10496
|
+
/**
|
|
10497
|
+
* @license lucide-react v0.576.0 - ISC
|
|
10498
|
+
*
|
|
10499
|
+
* This source code is licensed under the ISC license.
|
|
10500
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
10501
|
+
*/
|
|
10462
10502
|
var Pencil = createLucideIcon("pencil", [["path", {
|
|
10463
10503
|
d: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",
|
|
10464
10504
|
key: "1a8usu"
|
|
@@ -11658,6 +11698,30 @@ var Upload = createLucideIcon("upload", [
|
|
|
11658
11698
|
* This source code is licensed under the ISC license.
|
|
11659
11699
|
* See the LICENSE file in the root directory of this source tree.
|
|
11660
11700
|
*/
|
|
11701
|
+
var UtensilsCrossed = createLucideIcon("utensils-crossed", [
|
|
11702
|
+
["path", {
|
|
11703
|
+
d: "m16 2-2.3 2.3a3 3 0 0 0 0 4.2l1.8 1.8a3 3 0 0 0 4.2 0L22 8",
|
|
11704
|
+
key: "n7qcjb"
|
|
11705
|
+
}],
|
|
11706
|
+
["path", {
|
|
11707
|
+
d: "M15 15 3.3 3.3a4.2 4.2 0 0 0 0 6l7.3 7.3c.7.7 2 .7 2.8 0L15 15Zm0 0 7 7",
|
|
11708
|
+
key: "d0u48b"
|
|
11709
|
+
}],
|
|
11710
|
+
["path", {
|
|
11711
|
+
d: "m2.1 21.8 6.4-6.3",
|
|
11712
|
+
key: "yn04lh"
|
|
11713
|
+
}],
|
|
11714
|
+
["path", {
|
|
11715
|
+
d: "m19 5-7 7",
|
|
11716
|
+
key: "194lzd"
|
|
11717
|
+
}]
|
|
11718
|
+
]);
|
|
11719
|
+
/**
|
|
11720
|
+
* @license lucide-react v0.576.0 - ISC
|
|
11721
|
+
*
|
|
11722
|
+
* This source code is licensed under the ISC license.
|
|
11723
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
11724
|
+
*/
|
|
11661
11725
|
var Vibrate = createLucideIcon("vibrate", [
|
|
11662
11726
|
["path", {
|
|
11663
11727
|
d: "m2 8 2 2-2 2 2 2-2 2",
|
|
@@ -17439,6 +17503,28 @@ var useNumericSensorGetStatus = trpc.numericSensor.getStatus.useQuery;
|
|
|
17439
17503
|
var useOsdSetOverlay = trpc.osd.setOverlay.useMutation;
|
|
17440
17504
|
/** Generated alias around `trpc.osd.getStatus.useQuery`. */
|
|
17441
17505
|
var useOsdGetStatus = trpc.osd.getStatus.useQuery;
|
|
17506
|
+
/** Generated alias around `trpc.petFeeder.feed.useMutation`. */
|
|
17507
|
+
var usePetFeederFeed = trpc.petFeeder.feed.useMutation;
|
|
17508
|
+
/** Generated alias around `trpc.petFeeder.cancelFeed.useMutation`. */
|
|
17509
|
+
var usePetFeederCancelFeed = trpc.petFeeder.cancelFeed.useMutation;
|
|
17510
|
+
/** Generated alias around `trpc.petFeeder.resetDesiccant.useMutation`. */
|
|
17511
|
+
var usePetFeederResetDesiccant = trpc.petFeeder.resetDesiccant.useMutation;
|
|
17512
|
+
/** Generated alias around `trpc.petFeeder.markFoodReplenished.useMutation`. */
|
|
17513
|
+
var usePetFeederMarkFoodReplenished = trpc.petFeeder.markFoodReplenished.useMutation;
|
|
17514
|
+
/** Generated alias around `trpc.petFeeder.callPet.useMutation`. */
|
|
17515
|
+
var usePetFeederCallPet = trpc.petFeeder.callPet.useMutation;
|
|
17516
|
+
/** Generated alias around `trpc.petFeeder.playSound.useMutation`. */
|
|
17517
|
+
var usePetFeederPlaySound = trpc.petFeeder.playSound.useMutation;
|
|
17518
|
+
/** Generated alias around `trpc.petFeeder.setChildLock.useMutation`. */
|
|
17519
|
+
var usePetFeederSetChildLock = trpc.petFeeder.setChildLock.useMutation;
|
|
17520
|
+
/** Generated alias around `trpc.petFeeder.setIndicatorLight.useMutation`. */
|
|
17521
|
+
var usePetFeederSetIndicatorLight = trpc.petFeeder.setIndicatorLight.useMutation;
|
|
17522
|
+
/** Generated alias around `trpc.petFeeder.setFeedSound.useMutation`. */
|
|
17523
|
+
var usePetFeederSetFeedSound = trpc.petFeeder.setFeedSound.useMutation;
|
|
17524
|
+
/** Generated alias around `trpc.petFeeder.setVolume.useMutation`. */
|
|
17525
|
+
var usePetFeederSetVolume = trpc.petFeeder.setVolume.useMutation;
|
|
17526
|
+
/** Generated alias around `trpc.petFeeder.getStatus.useQuery`. */
|
|
17527
|
+
var usePetFeederGetStatus = trpc.petFeeder.getStatus.useQuery;
|
|
17442
17528
|
/** Generated alias around `trpc.pipelineAnalytics.getActiveTracks.useQuery`. */
|
|
17443
17529
|
var usePipelineAnalyticsGetActiveTracks = trpc.pipelineAnalytics.getActiveTracks.useQuery;
|
|
17444
17530
|
/** Generated alias around `trpc.pipelineAnalytics.getTrack.useQuery`. */
|
|
@@ -21381,7 +21467,7 @@ function renderScene(glyph, uid) {
|
|
|
21381
21467
|
default: return null;
|
|
21382
21468
|
}
|
|
21383
21469
|
}
|
|
21384
|
-
function Readout({ icon, label, value }) {
|
|
21470
|
+
function Readout$1({ icon, label, value }) {
|
|
21385
21471
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
21386
21472
|
className: "flex items-center gap-1.5 text-xs text-foreground-subtle",
|
|
21387
21473
|
title: label,
|
|
@@ -21436,12 +21522,12 @@ function WeatherHeroCard({ trpc, deviceId }) {
|
|
|
21436
21522
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
21437
21523
|
className: "flex flex-col gap-1.5",
|
|
21438
21524
|
children: [
|
|
21439
|
-
slice.humidity !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Readout, {
|
|
21525
|
+
slice.humidity !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Readout$1, {
|
|
21440
21526
|
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Droplets, { className: "h-3.5 w-3.5" }),
|
|
21441
21527
|
label: "Humidity",
|
|
21442
21528
|
value: `${Math.round(slice.humidity)}%`
|
|
21443
21529
|
}),
|
|
21444
|
-
slice.windSpeed !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Readout, {
|
|
21530
|
+
slice.windSpeed !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Readout$1, {
|
|
21445
21531
|
icon: slice.windBearing !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Navigation, {
|
|
21446
21532
|
className: "h-3.5 w-3.5",
|
|
21447
21533
|
style: { transform: `rotate(${slice.windBearing}deg)` }
|
|
@@ -21449,7 +21535,7 @@ function WeatherHeroCard({ trpc, deviceId }) {
|
|
|
21449
21535
|
label: "Wind",
|
|
21450
21536
|
value: `${Math.round(slice.windSpeed)} ${slice.windSpeedUnit ?? ""}`.trim()
|
|
21451
21537
|
}),
|
|
21452
|
-
slice.pressure !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Readout, {
|
|
21538
|
+
slice.pressure !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Readout$1, {
|
|
21453
21539
|
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Gauge, { className: "h-3.5 w-3.5" }),
|
|
21454
21540
|
label: "Pressure",
|
|
21455
21541
|
value: `${Math.round(slice.pressure)} ${slice.pressureUnit ?? ""}`.trim()
|
|
@@ -21662,6 +21748,522 @@ function ImageHeroCard({ trpc, deviceId, deviceName }) {
|
|
|
21662
21748
|
});
|
|
21663
21749
|
}
|
|
21664
21750
|
//#endregion
|
|
21751
|
+
//#region src/composites/device-controls/pet-feeder-panel.tsx
|
|
21752
|
+
/**
|
|
21753
|
+
* Pet-feeder controls for a `DeviceType.PetFeeder` device.
|
|
21754
|
+
*
|
|
21755
|
+
* Reads the `pet-feeder` cap status slice (foodLevel / food1 / food2 /
|
|
21756
|
+
* lowFood / batteryPower / desiccantLeftDays / feeding / status / error /
|
|
21757
|
+
* errorCode / isDualHopper + the four persisted settings) and renders:
|
|
21758
|
+
* - PetFeederInlineControl (device-list row): a compact food-level +
|
|
21759
|
+
* battery readout with a one-tap Feed button (default portion).
|
|
21760
|
+
* - PetFeederHeroCard (device-detail hero): the food-bowl fill gauge
|
|
21761
|
+
* signature, battery + desiccant readouts, a manual-feed portion
|
|
21762
|
+
* stepper honouring the 4-200 g hardware range (per-hopper on
|
|
21763
|
+
* dual-hopper models), a maintenance action row (Call pet / Reset
|
|
21764
|
+
* desiccant / Mark refilled) and the child-lock / indicator-light /
|
|
21765
|
+
* feed-sound / volume settings.
|
|
21766
|
+
*
|
|
21767
|
+
* Mirrors the AC card's `useDeviceCapability` + inferred-cap pattern so
|
|
21768
|
+
* the row and hero never diverge. Actions are gated on `available`.
|
|
21769
|
+
*/
|
|
21770
|
+
/** Default one-tap manual-feed portion (grams) — the low end of the
|
|
21771
|
+
* hardware range, a safe snack-sized dispense. */
|
|
21772
|
+
var DEFAULT_PORTION = 20;
|
|
21773
|
+
var VOLUME_MAX = 9;
|
|
21774
|
+
/** Amber kibble accent for the bowl fill; destructive red when low. */
|
|
21775
|
+
var KIBBLE = "#f59e0b";
|
|
21776
|
+
var KIBBLE_LOW = "#ef4444";
|
|
21777
|
+
function clampPortion(grams) {
|
|
21778
|
+
return Math.min(_camstack_types.PET_FEEDER_MANUAL_FEED_MAX, Math.max(_camstack_types.PET_FEEDER_MANUAL_FEED_MIN, grams));
|
|
21779
|
+
}
|
|
21780
|
+
var KEYFRAMES$1 = `
|
|
21781
|
+
@keyframes pf-eat { 0%,100%{transform:translateY(0)} 50%{transform:translateY(2px)} }
|
|
21782
|
+
@keyframes pf-pulse { 0%,100%{opacity:.55} 50%{opacity:1} }
|
|
21783
|
+
@media (prefers-reduced-motion: reduce) {
|
|
21784
|
+
.pf-eat, .pf-pulse { animation: none !important; }
|
|
21785
|
+
}
|
|
21786
|
+
`;
|
|
21787
|
+
function PetFeederPanel({ trpc, deviceId, layout, optimistic }) {
|
|
21788
|
+
const { cap, slice, available } = useDeviceCapability({
|
|
21789
|
+
trpc,
|
|
21790
|
+
deviceId,
|
|
21791
|
+
cap: (d) => d.petFeeder,
|
|
21792
|
+
slice: (d) => d.state.petFeeder
|
|
21793
|
+
});
|
|
21794
|
+
const { display, applyOptimistic } = useOptimisticSlice(slice, { enabled: optimistic ?? true });
|
|
21795
|
+
const [portion, setPortion] = (0, react$1.useState)(DEFAULT_PORTION);
|
|
21796
|
+
const [hopper1, setHopper1] = (0, react$1.useState)(DEFAULT_PORTION);
|
|
21797
|
+
const [hopper2, setHopper2] = (0, react$1.useState)(DEFAULT_PORTION);
|
|
21798
|
+
if (cap === void 0) return null;
|
|
21799
|
+
if (display === void 0) return null;
|
|
21800
|
+
const busy = !available;
|
|
21801
|
+
const faultMsg = display.error && display.error !== "no_error" ? display.error : null;
|
|
21802
|
+
const offline = display.status === "offline";
|
|
21803
|
+
const onBatteries = display.status === "on_batteries";
|
|
21804
|
+
if (layout === "compact") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
21805
|
+
className: "inline-flex items-center gap-2",
|
|
21806
|
+
children: [
|
|
21807
|
+
(faultMsg !== null || offline) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
21808
|
+
className: "inline-flex items-center text-destructive",
|
|
21809
|
+
title: faultMsg ?? "Offline",
|
|
21810
|
+
"aria-label": faultMsg ?? "Offline",
|
|
21811
|
+
children: offline ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WifiOff, {
|
|
21812
|
+
className: "h-3.5 w-3.5",
|
|
21813
|
+
"aria-hidden": true
|
|
21814
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriangleAlert, {
|
|
21815
|
+
className: "h-3.5 w-3.5",
|
|
21816
|
+
"aria-hidden": true
|
|
21817
|
+
})
|
|
21818
|
+
}),
|
|
21819
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
21820
|
+
className: cn("inline-flex items-center gap-1 text-xs tabular-nums font-medium", display.lowFood ? "text-destructive" : "text-foreground"),
|
|
21821
|
+
children: [
|
|
21822
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UtensilsCrossed, {
|
|
21823
|
+
className: "h-3.5 w-3.5",
|
|
21824
|
+
"aria-hidden": true
|
|
21825
|
+
}),
|
|
21826
|
+
display.foodLevel ?? "—",
|
|
21827
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
21828
|
+
className: "text-foreground-subtle",
|
|
21829
|
+
children: "g"
|
|
21830
|
+
})
|
|
21831
|
+
]
|
|
21832
|
+
}),
|
|
21833
|
+
display.batteryPower !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
21834
|
+
className: "inline-flex items-center gap-0.5 text-xs tabular-nums text-foreground-subtle",
|
|
21835
|
+
children: [
|
|
21836
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BatteryMedium, {
|
|
21837
|
+
className: "h-3.5 w-3.5",
|
|
21838
|
+
"aria-hidden": true
|
|
21839
|
+
}),
|
|
21840
|
+
display.batteryPower,
|
|
21841
|
+
"%"
|
|
21842
|
+
]
|
|
21843
|
+
}),
|
|
21844
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
21845
|
+
type: "button",
|
|
21846
|
+
"aria-label": "Feed now",
|
|
21847
|
+
disabled: busy || display.feeding,
|
|
21848
|
+
onClick: (e) => {
|
|
21849
|
+
e.stopPropagation();
|
|
21850
|
+
if (busy || display.feeding) return;
|
|
21851
|
+
applyOptimistic({ feeding: true });
|
|
21852
|
+
cap.feed({ grams: DEFAULT_PORTION });
|
|
21853
|
+
},
|
|
21854
|
+
className: cn("inline-flex items-center gap-1 rounded-md border border-border bg-surface px-2 py-1 text-xs font-medium text-foreground transition-colors hover:bg-surface-hover", (busy || display.feeding) && "opacity-40 cursor-not-allowed"),
|
|
21855
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PawPrint, {
|
|
21856
|
+
className: "h-3.5 w-3.5",
|
|
21857
|
+
"aria-hidden": true
|
|
21858
|
+
}), "Feed"]
|
|
21859
|
+
})
|
|
21860
|
+
]
|
|
21861
|
+
});
|
|
21862
|
+
const feedSingle = (grams) => {
|
|
21863
|
+
if (busy || display.feeding) return;
|
|
21864
|
+
applyOptimistic({ feeding: true });
|
|
21865
|
+
cap.feed({ grams: clampPortion(grams) });
|
|
21866
|
+
};
|
|
21867
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
21868
|
+
className: "relative flex flex-col items-stretch gap-3 overflow-hidden rounded-lg border border-border bg-surface p-4",
|
|
21869
|
+
children: [
|
|
21870
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: KEYFRAMES$1 }),
|
|
21871
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
21872
|
+
className: "flex items-center gap-4 rounded-lg border border-border/50 bg-surface-subtle/40 p-3",
|
|
21873
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BowlGauge, {
|
|
21874
|
+
grams: display.foodLevel,
|
|
21875
|
+
low: display.lowFood,
|
|
21876
|
+
eating: display.feeding
|
|
21877
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
21878
|
+
className: "flex min-w-0 flex-1 flex-col gap-1.5",
|
|
21879
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
21880
|
+
className: "flex items-baseline gap-1",
|
|
21881
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
21882
|
+
className: "text-3xl font-bold leading-none tabular-nums",
|
|
21883
|
+
style: { color: display.lowFood ? KIBBLE_LOW : KIBBLE },
|
|
21884
|
+
children: display.foodLevel ?? "—"
|
|
21885
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
21886
|
+
className: "text-sm text-foreground-subtle",
|
|
21887
|
+
children: "g in bowl"
|
|
21888
|
+
})]
|
|
21889
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
21890
|
+
className: "flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-foreground-subtle",
|
|
21891
|
+
children: [
|
|
21892
|
+
display.batteryPower !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Readout, {
|
|
21893
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BatteryMedium, { className: "h-3.5 w-3.5" }),
|
|
21894
|
+
label: `${display.batteryPower}%`
|
|
21895
|
+
}),
|
|
21896
|
+
display.desiccantLeftDays !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Readout, {
|
|
21897
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Wind, { className: "h-3.5 w-3.5" }),
|
|
21898
|
+
label: `${display.desiccantLeftDays}d desiccant`
|
|
21899
|
+
}),
|
|
21900
|
+
onBatteries && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
21901
|
+
className: "inline-flex items-center gap-1 font-medium text-amber-500",
|
|
21902
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BatteryWarning, {
|
|
21903
|
+
className: "h-3.5 w-3.5",
|
|
21904
|
+
"aria-hidden": true
|
|
21905
|
+
}), "On battery"]
|
|
21906
|
+
}),
|
|
21907
|
+
offline ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
21908
|
+
className: "inline-flex items-center gap-1 font-medium text-destructive",
|
|
21909
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WifiOff, {
|
|
21910
|
+
className: "h-3.5 w-3.5",
|
|
21911
|
+
"aria-hidden": true
|
|
21912
|
+
}), "Offline"]
|
|
21913
|
+
}) : display.feeding ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
21914
|
+
className: "pf-pulse inline-flex items-center gap-1 font-medium text-amber-500",
|
|
21915
|
+
style: { animation: "pf-pulse 1.2s ease-in-out infinite" },
|
|
21916
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PawPrint, {
|
|
21917
|
+
className: "h-3.5 w-3.5",
|
|
21918
|
+
"aria-hidden": true
|
|
21919
|
+
}), "Feeding…"]
|
|
21920
|
+
}) : display.lowFood ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
21921
|
+
className: "inline-flex items-center gap-1 font-medium text-destructive",
|
|
21922
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Ban, {
|
|
21923
|
+
className: "h-3.5 w-3.5",
|
|
21924
|
+
"aria-hidden": true
|
|
21925
|
+
}), "Low food"]
|
|
21926
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
21927
|
+
className: "inline-flex items-center gap-1 font-medium text-success",
|
|
21928
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CircleCheck, {
|
|
21929
|
+
className: "h-3.5 w-3.5",
|
|
21930
|
+
"aria-hidden": true
|
|
21931
|
+
}), "Ready"]
|
|
21932
|
+
})
|
|
21933
|
+
]
|
|
21934
|
+
})]
|
|
21935
|
+
})]
|
|
21936
|
+
}),
|
|
21937
|
+
faultMsg !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
21938
|
+
className: "flex items-start gap-2 rounded-lg border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs font-medium text-destructive",
|
|
21939
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(TriangleAlert, {
|
|
21940
|
+
className: "mt-px h-4 w-4 shrink-0",
|
|
21941
|
+
"aria-hidden": true
|
|
21942
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
21943
|
+
className: "min-w-0 break-words",
|
|
21944
|
+
children: faultMsg
|
|
21945
|
+
})]
|
|
21946
|
+
}),
|
|
21947
|
+
display.isDualHopper ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
21948
|
+
className: "grid grid-cols-2 gap-2",
|
|
21949
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(HopperFeed, {
|
|
21950
|
+
label: "Hopper 1",
|
|
21951
|
+
grams: hopper1,
|
|
21952
|
+
busy: busy || display.feeding,
|
|
21953
|
+
onGrams: setHopper1,
|
|
21954
|
+
onFeed: () => {
|
|
21955
|
+
if (busy || display.feeding) return;
|
|
21956
|
+
applyOptimistic({ feeding: true });
|
|
21957
|
+
cap.feed({ hopper1: clampPortion(hopper1) });
|
|
21958
|
+
}
|
|
21959
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HopperFeed, {
|
|
21960
|
+
label: "Hopper 2",
|
|
21961
|
+
grams: hopper2,
|
|
21962
|
+
busy: busy || display.feeding,
|
|
21963
|
+
onGrams: setHopper2,
|
|
21964
|
+
onFeed: () => {
|
|
21965
|
+
if (busy || display.feeding) return;
|
|
21966
|
+
applyOptimistic({ feeding: true });
|
|
21967
|
+
cap.feed({ hopper2: clampPortion(hopper2) });
|
|
21968
|
+
}
|
|
21969
|
+
})]
|
|
21970
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HopperFeed, {
|
|
21971
|
+
label: "Manual feed",
|
|
21972
|
+
grams: portion,
|
|
21973
|
+
busy: busy || display.feeding,
|
|
21974
|
+
onGrams: setPortion,
|
|
21975
|
+
onFeed: () => feedSingle(portion)
|
|
21976
|
+
}),
|
|
21977
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
21978
|
+
className: "grid grid-cols-3 gap-2",
|
|
21979
|
+
children: [
|
|
21980
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton, {
|
|
21981
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PawPrint, { className: "h-4 w-4" }),
|
|
21982
|
+
label: "Call pet",
|
|
21983
|
+
busy,
|
|
21984
|
+
onClick: () => void cap.callPet({})
|
|
21985
|
+
}),
|
|
21986
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton, {
|
|
21987
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RefreshCw, { className: "h-4 w-4" }),
|
|
21988
|
+
label: "Reset desiccant",
|
|
21989
|
+
busy,
|
|
21990
|
+
onClick: () => void cap.resetDesiccant({})
|
|
21991
|
+
}),
|
|
21992
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActionButton, {
|
|
21993
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UtensilsCrossed, { className: "h-4 w-4" }),
|
|
21994
|
+
label: "Mark refilled",
|
|
21995
|
+
busy,
|
|
21996
|
+
onClick: () => void cap.markFoodReplenished({})
|
|
21997
|
+
})
|
|
21998
|
+
]
|
|
21999
|
+
}),
|
|
22000
|
+
display.feeding && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
22001
|
+
type: "button",
|
|
22002
|
+
disabled: busy,
|
|
22003
|
+
onClick: () => {
|
|
22004
|
+
applyOptimistic({ feeding: false });
|
|
22005
|
+
cap.cancelFeed({});
|
|
22006
|
+
},
|
|
22007
|
+
className: cn("flex items-center justify-center gap-2 rounded-lg border border-destructive/40 bg-destructive/10 px-2 py-1.5 text-xs font-medium text-destructive transition-colors hover:bg-destructive/20", busy && "opacity-40 cursor-not-allowed"),
|
|
22008
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Ban, {
|
|
22009
|
+
className: "h-4 w-4",
|
|
22010
|
+
"aria-hidden": true
|
|
22011
|
+
}), "Cancel feed"]
|
|
22012
|
+
}),
|
|
22013
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field$1, {
|
|
22014
|
+
label: "Settings",
|
|
22015
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
22016
|
+
className: "grid grid-cols-3 gap-2",
|
|
22017
|
+
children: [
|
|
22018
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingToggle, {
|
|
22019
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Lock, { className: "h-4 w-4" }),
|
|
22020
|
+
label: "Child lock",
|
|
22021
|
+
on: display.childLock,
|
|
22022
|
+
busy,
|
|
22023
|
+
onToggle: (next) => {
|
|
22024
|
+
applyOptimistic({ childLock: next });
|
|
22025
|
+
cap.setChildLock({ on: next });
|
|
22026
|
+
}
|
|
22027
|
+
}),
|
|
22028
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingToggle, {
|
|
22029
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Lightbulb, { className: "h-4 w-4" }),
|
|
22030
|
+
label: "Light",
|
|
22031
|
+
on: display.indicatorLight,
|
|
22032
|
+
busy,
|
|
22033
|
+
onToggle: (next) => {
|
|
22034
|
+
applyOptimistic({ indicatorLight: next });
|
|
22035
|
+
cap.setIndicatorLight({ on: next });
|
|
22036
|
+
}
|
|
22037
|
+
}),
|
|
22038
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingToggle, {
|
|
22039
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Bell, { className: "h-4 w-4" }),
|
|
22040
|
+
label: "Feed sound",
|
|
22041
|
+
on: display.feedSound,
|
|
22042
|
+
busy,
|
|
22043
|
+
onToggle: (next) => {
|
|
22044
|
+
applyOptimistic({ feedSound: next });
|
|
22045
|
+
cap.setFeedSound({ on: next });
|
|
22046
|
+
}
|
|
22047
|
+
})
|
|
22048
|
+
]
|
|
22049
|
+
})
|
|
22050
|
+
}),
|
|
22051
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field$1, {
|
|
22052
|
+
label: "Volume",
|
|
22053
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
22054
|
+
className: "flex items-center gap-3",
|
|
22055
|
+
children: [
|
|
22056
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Volume2, {
|
|
22057
|
+
className: "h-4 w-4 shrink-0 text-foreground-subtle",
|
|
22058
|
+
"aria-hidden": true
|
|
22059
|
+
}),
|
|
22060
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RoundStep$1, {
|
|
22061
|
+
label: "Decrease volume",
|
|
22062
|
+
symbol: "−",
|
|
22063
|
+
disabled: busy || display.volume <= 0,
|
|
22064
|
+
onClick: () => {
|
|
22065
|
+
const next = Math.max(0, display.volume - 1);
|
|
22066
|
+
applyOptimistic({ volume: next });
|
|
22067
|
+
cap.setVolume({ level: next });
|
|
22068
|
+
}
|
|
22069
|
+
}),
|
|
22070
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
22071
|
+
className: "w-6 text-center text-lg font-semibold tabular-nums text-foreground",
|
|
22072
|
+
children: display.volume
|
|
22073
|
+
}),
|
|
22074
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RoundStep$1, {
|
|
22075
|
+
label: "Increase volume",
|
|
22076
|
+
symbol: "+",
|
|
22077
|
+
disabled: busy || display.volume >= VOLUME_MAX,
|
|
22078
|
+
onClick: () => {
|
|
22079
|
+
const next = Math.min(VOLUME_MAX, display.volume + 1);
|
|
22080
|
+
applyOptimistic({ volume: next });
|
|
22081
|
+
cap.setVolume({ level: next });
|
|
22082
|
+
}
|
|
22083
|
+
})
|
|
22084
|
+
]
|
|
22085
|
+
})
|
|
22086
|
+
})
|
|
22087
|
+
]
|
|
22088
|
+
});
|
|
22089
|
+
}
|
|
22090
|
+
/** Labelled control group — a small uppercase header above its control. */
|
|
22091
|
+
function Field$1({ label, children }) {
|
|
22092
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
22093
|
+
className: "flex flex-col gap-1",
|
|
22094
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
22095
|
+
className: "text-[10px] font-semibold uppercase tracking-wider text-foreground-subtle",
|
|
22096
|
+
children: label
|
|
22097
|
+
}), children]
|
|
22098
|
+
});
|
|
22099
|
+
}
|
|
22100
|
+
function Readout({ icon, label }) {
|
|
22101
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
22102
|
+
className: "inline-flex items-center gap-1 tabular-nums",
|
|
22103
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
22104
|
+
className: "text-foreground-subtle",
|
|
22105
|
+
children: icon
|
|
22106
|
+
}), label]
|
|
22107
|
+
});
|
|
22108
|
+
}
|
|
22109
|
+
/**
|
|
22110
|
+
* Signature element — a food bowl whose kibble fills proportionally to the
|
|
22111
|
+
* bowl reading (0–200 g). Amber when stocked, red when the low-food flag is
|
|
22112
|
+
* set; the kibble mound bobs while the pet is eating.
|
|
22113
|
+
*/
|
|
22114
|
+
function BowlGauge({ grams, low, eating }) {
|
|
22115
|
+
const frac = Math.min(1, Math.max(0, (grams ?? 0) / _camstack_types.PET_FEEDER_MANUAL_FEED_MAX));
|
|
22116
|
+
const fill = low ? KIBBLE_LOW : KIBBLE;
|
|
22117
|
+
const surfaceY = 52 - frac * 24;
|
|
22118
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
22119
|
+
width: 72,
|
|
22120
|
+
height: 72,
|
|
22121
|
+
viewBox: "0 0 72 72",
|
|
22122
|
+
role: "img",
|
|
22123
|
+
"aria-label": grams === null ? "Food level unknown" : `${grams} grams in bowl`,
|
|
22124
|
+
className: "shrink-0",
|
|
22125
|
+
children: [
|
|
22126
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("clipPath", {
|
|
22127
|
+
id: "pf-bowl-clip",
|
|
22128
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M14 26 H58 L52 54 A6 6 0 0 1 46 58 H26 A6 6 0 0 1 20 54 Z" })
|
|
22129
|
+
}) }),
|
|
22130
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", {
|
|
22131
|
+
clipPath: "url(#pf-bowl-clip)",
|
|
22132
|
+
className: eating ? "pf-eat" : void 0,
|
|
22133
|
+
style: eating ? { animation: "pf-eat 1.1s ease-in-out infinite" } : void 0,
|
|
22134
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
22135
|
+
x: "14",
|
|
22136
|
+
y: surfaceY,
|
|
22137
|
+
width: "44",
|
|
22138
|
+
height: "34",
|
|
22139
|
+
fill,
|
|
22140
|
+
opacity: .9
|
|
22141
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ellipse", {
|
|
22142
|
+
cx: "36",
|
|
22143
|
+
cy: surfaceY,
|
|
22144
|
+
rx: "22",
|
|
22145
|
+
ry: "4",
|
|
22146
|
+
fill
|
|
22147
|
+
})]
|
|
22148
|
+
}),
|
|
22149
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
22150
|
+
d: "M14 26 H58 L52 54 A6 6 0 0 1 46 58 H26 A6 6 0 0 1 20 54 Z",
|
|
22151
|
+
fill: "none",
|
|
22152
|
+
stroke: "currentColor",
|
|
22153
|
+
strokeWidth: "2.5",
|
|
22154
|
+
className: "text-border"
|
|
22155
|
+
}),
|
|
22156
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
22157
|
+
x: "11",
|
|
22158
|
+
y: "22",
|
|
22159
|
+
width: "50",
|
|
22160
|
+
height: "6",
|
|
22161
|
+
rx: "3",
|
|
22162
|
+
fill: "currentColor",
|
|
22163
|
+
className: "text-border"
|
|
22164
|
+
})
|
|
22165
|
+
]
|
|
22166
|
+
});
|
|
22167
|
+
}
|
|
22168
|
+
/** Manual-feed portion stepper + Feed button, honouring the 4-200 g range. */
|
|
22169
|
+
function HopperFeed({ label, grams, busy, onGrams, onFeed }) {
|
|
22170
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
22171
|
+
className: "flex flex-col gap-1",
|
|
22172
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
22173
|
+
className: "text-[10px] font-semibold uppercase tracking-wider text-foreground-subtle",
|
|
22174
|
+
children: label
|
|
22175
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
22176
|
+
className: "flex items-center gap-2",
|
|
22177
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
22178
|
+
className: "flex items-center gap-1 rounded-lg border border-border/60 bg-surface-subtle px-1 py-1",
|
|
22179
|
+
children: [
|
|
22180
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RoundStep$1, {
|
|
22181
|
+
label: `Decrease ${label} portion`,
|
|
22182
|
+
symbol: "−",
|
|
22183
|
+
disabled: busy || grams <= _camstack_types.PET_FEEDER_MANUAL_FEED_MIN,
|
|
22184
|
+
onClick: () => onGrams(clampPortion(grams - 1))
|
|
22185
|
+
}),
|
|
22186
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
22187
|
+
className: "w-12 text-center text-sm font-semibold tabular-nums text-foreground",
|
|
22188
|
+
children: [grams, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
22189
|
+
className: "text-xs text-foreground-subtle",
|
|
22190
|
+
children: "g"
|
|
22191
|
+
})]
|
|
22192
|
+
}),
|
|
22193
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RoundStep$1, {
|
|
22194
|
+
label: `Increase ${label} portion`,
|
|
22195
|
+
symbol: "+",
|
|
22196
|
+
disabled: busy || grams >= _camstack_types.PET_FEEDER_MANUAL_FEED_MAX,
|
|
22197
|
+
onClick: () => onGrams(clampPortion(grams + 1))
|
|
22198
|
+
})
|
|
22199
|
+
]
|
|
22200
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
22201
|
+
type: "button",
|
|
22202
|
+
disabled: busy,
|
|
22203
|
+
onClick: onFeed,
|
|
22204
|
+
className: cn("flex flex-1 items-center justify-center gap-1.5 rounded-lg border border-transparent bg-amber-400 px-2 py-2 text-sm font-semibold text-amber-950 transition-colors hover:bg-amber-300 active:scale-95", busy && "opacity-40 cursor-not-allowed"),
|
|
22205
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PawPrint, {
|
|
22206
|
+
className: "h-4 w-4",
|
|
22207
|
+
"aria-hidden": true
|
|
22208
|
+
}), "Feed"]
|
|
22209
|
+
})]
|
|
22210
|
+
})]
|
|
22211
|
+
});
|
|
22212
|
+
}
|
|
22213
|
+
/** Circular ± button. */
|
|
22214
|
+
function RoundStep$1({ label, symbol, disabled, onClick }) {
|
|
22215
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
22216
|
+
type: "button",
|
|
22217
|
+
"aria-label": label,
|
|
22218
|
+
disabled,
|
|
22219
|
+
onClick,
|
|
22220
|
+
className: cn("flex h-7 w-7 shrink-0 items-center justify-center rounded-full border border-border bg-surface text-base leading-none text-foreground-subtle transition-colors hover:bg-surface-hover hover:text-foreground active:scale-95", disabled && "opacity-40 cursor-not-allowed"),
|
|
22221
|
+
children: symbol
|
|
22222
|
+
});
|
|
22223
|
+
}
|
|
22224
|
+
/** Neutral maintenance action button (icon over label). */
|
|
22225
|
+
function ActionButton({ icon, label, busy, onClick }) {
|
|
22226
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
22227
|
+
type: "button",
|
|
22228
|
+
disabled: busy,
|
|
22229
|
+
onClick,
|
|
22230
|
+
className: cn("flex flex-col items-center justify-center gap-1 rounded-lg border border-border/60 bg-surface-subtle px-1 py-2 text-[11px] font-medium text-foreground-subtle transition-colors hover:bg-surface-hover hover:text-foreground", busy && "opacity-40 cursor-not-allowed"),
|
|
22231
|
+
children: [icon, label]
|
|
22232
|
+
});
|
|
22233
|
+
}
|
|
22234
|
+
/** Setting toggle card — reflects the persisted setting; tap to flip. */
|
|
22235
|
+
function SettingToggle({ icon, label, on, busy, onToggle }) {
|
|
22236
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
22237
|
+
type: "button",
|
|
22238
|
+
"aria-label": label,
|
|
22239
|
+
"aria-pressed": on,
|
|
22240
|
+
disabled: busy,
|
|
22241
|
+
onClick: () => {
|
|
22242
|
+
if (!busy) onToggle(!on);
|
|
22243
|
+
},
|
|
22244
|
+
className: cn("flex flex-col items-center justify-center gap-1 rounded-lg border px-1 py-2 text-[11px] font-medium transition-colors", on ? "border-transparent bg-indigo-400 text-indigo-950 shadow-sm" : "border-border/60 bg-surface-subtle text-foreground-subtle hover:bg-surface-hover hover:text-foreground", busy && "opacity-40 cursor-not-allowed"),
|
|
22245
|
+
children: [icon, label]
|
|
22246
|
+
});
|
|
22247
|
+
}
|
|
22248
|
+
function PetFeederInlineControl({ trpc, deviceId, features, optimistic }) {
|
|
22249
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PetFeederPanel, {
|
|
22250
|
+
trpc,
|
|
22251
|
+
deviceId,
|
|
22252
|
+
features,
|
|
22253
|
+
layout: "compact",
|
|
22254
|
+
optimistic
|
|
22255
|
+
});
|
|
22256
|
+
}
|
|
22257
|
+
function PetFeederHeroCard({ trpc, deviceId, features, optimistic }) {
|
|
22258
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PetFeederPanel, {
|
|
22259
|
+
trpc,
|
|
22260
|
+
deviceId,
|
|
22261
|
+
features,
|
|
22262
|
+
layout: "full",
|
|
22263
|
+
optimistic
|
|
22264
|
+
});
|
|
22265
|
+
}
|
|
22266
|
+
//#endregion
|
|
21665
22267
|
//#region src/composites/device-controls/switch-panel.tsx
|
|
21666
22268
|
function SwitchPanel({ trpc, deviceId, optimistic }) {
|
|
21667
22269
|
const { cap, slice, available } = useDeviceCapability({
|
|
@@ -24847,6 +25449,10 @@ var IMAGE_CONTROL = {
|
|
|
24847
25449
|
InlineControl: ImageInlineControl,
|
|
24848
25450
|
HeroCard: ImageHeroCard
|
|
24849
25451
|
};
|
|
25452
|
+
var PET_FEEDER_CONTROL = {
|
|
25453
|
+
InlineControl: PetFeederInlineControl,
|
|
25454
|
+
HeroCard: PetFeederHeroCard
|
|
25455
|
+
};
|
|
24850
25456
|
/**
|
|
24851
25457
|
* Exhaustive record over DeviceType. Omitting a member causes a
|
|
24852
25458
|
* TypeScript build error (same pattern as DEVICE_TYPE_ACTIONS).
|
|
@@ -24884,7 +25490,8 @@ var DEVICE_TYPE_CONTROL = {
|
|
|
24884
25490
|
[_camstack_types.DeviceType.Vacuum]: VACUUM_CONTROL,
|
|
24885
25491
|
[_camstack_types.DeviceType.LawnMower]: LAWN_MOWER_CONTROL,
|
|
24886
25492
|
[_camstack_types.DeviceType.Container]: DUMMY_CONTROL,
|
|
24887
|
-
[_camstack_types.DeviceType.Image]: IMAGE_CONTROL
|
|
25493
|
+
[_camstack_types.DeviceType.Image]: IMAGE_CONTROL,
|
|
25494
|
+
[_camstack_types.DeviceType.PetFeeder]: PET_FEEDER_CONTROL
|
|
24888
25495
|
};
|
|
24889
25496
|
/**
|
|
24890
25497
|
* Per-type FILL flag.
|
|
@@ -24935,7 +25542,8 @@ var CONTROL_FILLS = {
|
|
|
24935
25542
|
[_camstack_types.DeviceType.Generic]: false,
|
|
24936
25543
|
[_camstack_types.DeviceType.Weather]: false,
|
|
24937
25544
|
[_camstack_types.DeviceType.Container]: false,
|
|
24938
|
-
[_camstack_types.DeviceType.Image]: false
|
|
25545
|
+
[_camstack_types.DeviceType.Image]: false,
|
|
25546
|
+
[_camstack_types.DeviceType.PetFeeder]: false
|
|
24939
25547
|
};
|
|
24940
25548
|
var DEVICE_TYPE_VALUES$1 = new Set(Object.values(_camstack_types.DeviceType));
|
|
24941
25549
|
/**
|
|
@@ -25184,6 +25792,10 @@ var DEVICE_TYPE_META = {
|
|
|
25184
25792
|
[_camstack_types.DeviceType.Image]: {
|
|
25185
25793
|
icon: Image,
|
|
25186
25794
|
label: "Image"
|
|
25795
|
+
},
|
|
25796
|
+
[_camstack_types.DeviceType.PetFeeder]: {
|
|
25797
|
+
icon: Bone,
|
|
25798
|
+
label: "Pet feeder"
|
|
25187
25799
|
}
|
|
25188
25800
|
};
|
|
25189
25801
|
var DEVICE_ROLE_META = {
|
|
@@ -29011,7 +29623,8 @@ var CONTROL_CAP_NAMES = [
|
|
|
29011
29623
|
"fan-control",
|
|
29012
29624
|
"climate-control",
|
|
29013
29625
|
"media-player",
|
|
29014
|
-
"control"
|
|
29626
|
+
"control",
|
|
29627
|
+
"pet-feeder"
|
|
29015
29628
|
];
|
|
29016
29629
|
//#endregion
|
|
29017
29630
|
//#region src/composites/device-controls/popover-row-action.tsx
|
|
@@ -45028,6 +45641,17 @@ exports.useOptionalWidgetRegistry = useOptionalWidgetRegistry;
|
|
|
45028
45641
|
exports.useOsdGetStatus = useOsdGetStatus;
|
|
45029
45642
|
exports.useOsdSetOverlay = useOsdSetOverlay;
|
|
45030
45643
|
exports.usePTZ = usePTZ;
|
|
45644
|
+
exports.usePetFeederCallPet = usePetFeederCallPet;
|
|
45645
|
+
exports.usePetFeederCancelFeed = usePetFeederCancelFeed;
|
|
45646
|
+
exports.usePetFeederFeed = usePetFeederFeed;
|
|
45647
|
+
exports.usePetFeederGetStatus = usePetFeederGetStatus;
|
|
45648
|
+
exports.usePetFeederMarkFoodReplenished = usePetFeederMarkFoodReplenished;
|
|
45649
|
+
exports.usePetFeederPlaySound = usePetFeederPlaySound;
|
|
45650
|
+
exports.usePetFeederResetDesiccant = usePetFeederResetDesiccant;
|
|
45651
|
+
exports.usePetFeederSetChildLock = usePetFeederSetChildLock;
|
|
45652
|
+
exports.usePetFeederSetFeedSound = usePetFeederSetFeedSound;
|
|
45653
|
+
exports.usePetFeederSetIndicatorLight = usePetFeederSetIndicatorLight;
|
|
45654
|
+
exports.usePetFeederSetVolume = usePetFeederSetVolume;
|
|
45031
45655
|
exports.usePipelineAnalyticsApplyDeviceSettingsPatch = usePipelineAnalyticsApplyDeviceSettingsPatch;
|
|
45032
45656
|
exports.usePipelineAnalyticsClearTracks = usePipelineAnalyticsClearTracks;
|
|
45033
45657
|
exports.usePipelineAnalyticsGetActiveTracks = usePipelineAnalyticsGetActiveTracks;
|