Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend_py/src/services/cameras/ehd.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def _get_options(self) -> Dict[str, Option]:
# Standard integer options
options['bitrate'] = Option(
self.cameras[2], '>I', xu.Unit.USR_ID, xu.Selector.USR_H264_CTRL, xu.Command.H264_BITRATE_CTRL, 'Bitrate',
lambda bitrate: int(bitrate * 1000000), # convert to bps from mpbs
lambda bitrate: int(round(bitrate * 1000000)), # convert to bps from mpbs (round for float imprecision)
lambda bitrate: bitrate / 1000000 # convert to mpbs from bps
)

Expand Down
6 changes: 4 additions & 2 deletions frontend/src/components/dwe/app/command-palette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,13 @@ export function CommandPalette() {
Cameras
</CommandItem>
<CommandItem
onSelect={() => runCommand(() => navigate("/videos"))}
onSelect={() => runCommand(() => navigate("/recordings"))}
>
Recordings
</CommandItem>
<CommandItem onSelect={() => runCommand(() => navigate("/log-viewer"))}>
<CommandItem
onSelect={() => runCommand(() => navigate("/log-viewer"))}
>
Logs
</CommandItem>
<CommandItem
Expand Down
116 changes: 91 additions & 25 deletions frontend/src/components/dwe/cameras/camera-controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
DialogTrigger,
// DialogFooter, // Optional: if you want a dedicated footer
} from "@/components/ui/dialog";
import { subscribe } from "valtio";
import { subscribe, useSnapshot } from "valtio";
import { RotateCcwIcon, SlidersHorizontal } from "lucide-react";

import IntegerControl from "./controls/integer-control";
Expand Down Expand Up @@ -63,6 +63,33 @@ const ControlWrapper = ({
});
};

// handles disabling associated controls based on another (ie Auto Exposure turns off Exposure Time, Absolute)
// add any dependency / disabling pairings here
const dependencyName = control.name.includes("Exposure Time, Absolute")
? "Auto Exposure"
: control.name.includes("White Balance Temperature")
? "White Balance, Auto"
: control.name.includes("Bitrate")
? "Variable Bitrate"
: null;

const dependencyControl = dependencyName
? device.controls.find((c) => c.name.includes(dependencyName))
: null;

const dependencySnap = dependencyControl
? useSnapshot(dependencyControl)
: null;

let isDisabled = false;
if (dependencySnap) {
if (control.name.includes("Exposure Time, Absolute")) {
isDisabled = dependencySnap.value !== 1;
} else {
isDisabled = !!dependencySnap.value;
}
}

useEffect(() => {
const unsub = subscribe(control, () => {
setUVCControl(bus_info, control.value, control.control_id);
Expand All @@ -79,7 +106,9 @@ const ControlWrapper = ({

switch (control.flags.control_type) {
case "INTEGER":
return <IntegerControl key={key} control={control} />;
return (
<IntegerControl key={key} control={control} isDisabled={isDisabled} />
);
case "BOOLEAN":
return <BooleanControl key={key} control={control} />;
case "MENU":
Expand Down Expand Up @@ -118,6 +147,17 @@ export const CameraControls = () => {
return "Miscellaneous";
};

const getTypeRank = (a: ControlModel, b: ControlModel, order: string[]) => {
const typeRankA = order.indexOf(a.flags.control_type);
const typeRankB = order.indexOf(b.flags.control_type);

if (typeRankA !== typeRankB) {
return typeRankA - typeRankB;
}

return a.name.localeCompare(b.name);
};

const groupedControls = supportedControls.reduce<GroupedControls>(
(acc, control) => {
const groupName = getGroupName(control.name);
Expand All @@ -131,8 +171,9 @@ export const CameraControls = () => {
},
{}
);
const typeOrder = ["INTEGER", "MENU"];

const order = [
const groupOrder = [
"Exposure Controls",
"Image Controls",
"System Controls",
Expand All @@ -146,7 +187,7 @@ export const CameraControls = () => {
<SlidersHorizontal />
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-xl max-h-[80vh] flex flex-col bg-card/30 backdrop-blur p-0 overflow-hidden">
<DialogContent className="sm:max-w-xl max-h-[80vh] flex flex-col p-0 overflow-hidden">
<DialogHeader className="sticky top-0 z-50 pt-8 px-8">
<DialogTitle>Camera Controls</DialogTitle>
<DialogDescription>
Expand All @@ -158,27 +199,52 @@ export const CameraControls = () => {
{supportedControls.length > 0 ? (
<div className="space-y-4">
{Object.keys(groupedControls)
.sort((a, b) => order.indexOf(a) - order.indexOf(b))
.map((groupName) => (
<Accordion type="multiple">
<AccordionItem value={groupName}>
<AccordionTrigger className=" hover:text-foreground">
{groupName}
</AccordionTrigger>
<AccordionContent>
<div className="grid gap-4 mt-1">
{groupedControls[groupName].map((control, index) => (
<ControlWrapper
key={control.control_id ?? index}
control={control}
index={index}
/>
))}
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
))}
.sort((a, b) => groupOrder.indexOf(a) - groupOrder.indexOf(b))
.map((groupName) => {
const booleans = groupedControls[groupName].filter(
(c) => c.flags.control_type === "BOOLEAN"
);
const others = groupedControls[groupName].filter(
(c) => c.flags.control_type !== "BOOLEAN"
);
return (
<Accordion type="multiple">
<AccordionItem value={groupName}>
<AccordionTrigger className="">
{groupName}
</AccordionTrigger>
<AccordionContent>
{others && (
<div className="grid gap-4 mt-1">
{others
.sort((a, b) => {
return getTypeRank(a, b, typeOrder);
})
.map((control, index) => (
<ControlWrapper
key={control.control_id ?? index}
control={control}
index={index}
/>
))}
</div>
)}
{booleans && (
<div className="flex flex-wrap gap-2 m-4 p-2 rounded-xl justify-center">
{booleans.map((control, index) => (
<ControlWrapper
key={control.control_id ?? index}
control={control}
index={index}
/>
))}
</div>
)}
</AccordionContent>
</AccordionItem>
</Accordion>
);
})}
</div>
) : (
<p className="text-sm text-muted-foreground text-center py-4">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Switch } from "@/components/ui/switch";
import { Toggle } from "@/components/ui/toggle";
import { components } from "@/schemas/dwe_os_2";
import { useState } from "react";
import { subscribe } from "valtio";
Expand Down Expand Up @@ -32,8 +32,10 @@ const BooleanControl = ({

return (
<div className="flex items-center justify-between">
<span className="text-sm font-medium">{control.name}</span>
<Switch checked={value} onCheckedChange={toggle} />
<span className="text-sm font-medium"></span>
<Toggle pressed={value} onPressedChange={toggle}>
<div>{control.name}</div>
</Toggle>
</div>
);
};
Expand Down
Loading