import arrayNonUniq from "array-non-uniq" ; import filterObject from "filter-obj" ; import assert from "node:assert" ; import sortKeys from "sort-keys" ; import { ReadonlyDeep , } from "type-fest" ; import Camera , { CameraControl , UvcControl , } from "uvc-control" ; import WrappedError from "./utilities/wrapped-error.js" ; interface ControlFlags { readonly isGettable : boolean ; readonly isRanged : boolean ; readonly isSettable : boolean ; } type ControlsFlags = Record < string , ControlFlags > ; export default class CameraControlHelper { private cachedMappedSupportedControls : ReadonlyDeep < ControlsFlags > | null = null ; constructor ( private readonly UvcControl , private readonly camera ) { assert . strictEqual ( arguments . length , 2 ) ; assert ( typeof this . UvcControl === "function" ) ; assert ( typeof this . camera === "object" ) ; } async getControlNames ( ) { const controls = await this . getSupportedControls ( ) ; return Object . keys ( controls ) ; } async getGettableControlNames ( ) { const gettableControls = await this . getGettableControls ( ) ; return Object . keys ( gettableControls ) ; } async getRangedControlNames ( ) { const rangedControls = await this . getRangedControls ( ) ; return Object . keys ( rangedControls ) ; } async getSettableControlNames ( ) { const settableControls = await this . getSettableControls ( ) ; return Object . keys ( settableControls ) ; } private isGettableControl ( control ) { return control . requests . includes ( this . UvcControl . REQUEST . GET_CUR ) ; } private isRangedControl ( control ) { return control . requests . includes ( this . UvcControl . REQUEST . GET_MIN ) && control . requests . includes ( this . UvcControl . REQUEST . GET_MAX ) ; } private isSettableControl ( control ) { return control . requests . includes ( this . UvcControl . REQUEST . SET_CUR ) || ( Array . isArray ( control . optional_requests ) && control . optional_requests . includes ( this . UvcControl . REQUEST . SET_CUR ) ) ; } private async mapSupportedControls ( ) { const supportedControls = this . camera . supportedControls . map ( ( supportedControlName ) => this . UvcControl . controls [ supportedControlName ] ) . filter ( ( t ) : t is CameraControl => Boolean ( t ) ) ; const missingControls = supportedControls . filter ( ( control ) => ! control ) ; if ( missingControls . length > 0 ) { throw new Error ( `Controls were not found: ${ JSON . stringify ( missingControls ) }` ) ; } const nonUniqueControls = arrayNonUniq ( supportedControls ) ; if ( nonUniqueControls . length > 0 ) { throw new Error ( `Controls were already found: ${ JSON . stringify ( nonUniqueControls ) }` ) ; } const mappedControls = supportedControls . map ( ( control ) => { try { const uvccControl = [ control . name , { isGettable : this . isGettableControl ( control ) , isRanged : this . isRangedControl ( control ) , isSettable : this . isSettableControl ( control ) , } , ] ; return uvccControl ; } catch ( error ) { if ( error instanceof Error ) { const wrappedError = new WrappedError ( error , `Could not map control: ${ JSON . stringify ( control . name ) }` ) ; throw wrappedError ; } throw error ; } } ) ; const controlsObject = { } ; for ( const mappedControl of mappedControls ) { controlsObject [ mappedControl [ 0 ] ] = mappedControl [ 1 ] ; } const sortedControls = sortKeys ( controlsObject ) ; return sortedControls ; } private async getSupportedControls ( ) { if ( this . cachedMappedSupportedControls === null ) { this . cachedMappedSupportedControls = await this . mapSupportedControls ( ) ; } return this . cachedMappedSupportedControls ; } private async getGettableControls ( ) { const controls = await this . getSupportedControls ( ) ; return filterObject ( controls , ( _key , control ) => control . isGettable ) ; } private async getRangedControls ( ) { const controls = await this . getSupportedControls ( ) ; return filterObject ( controls , ( _key , control ) => control . isRanged ) ; } private async getSettableControls ( ) { const controls = await this . getSupportedControls ( ) ; return filterObject ( controls , ( _key , control ) => control . isSettable ) ; } }