• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

newAM / monitorcontrol / 28679740045

03 Jul 2026 07:26PM UTC coverage: 59.973% (-4.3%) from 64.276%
28679740045

Pull #410

github

web-flow
Merge 259a30b4e into dc57ebc7a
Pull Request #410: ci: add Pyrefly type validation and fix existing errors

12 of 44 new or added lines in 4 files covered. (27.27%)

30 existing lines in 1 file now uncovered.

445 of 742 relevant lines covered (59.97%)

3.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.13
/monitorcontrol/monitorcontrol.py
1
from . import vcp, vcp_codes
5✔
2
from types import TracebackType
5✔
3
from typing import List, Optional, Type, Union
5✔
4
import enum
5✔
5
import sys
5✔
6

7

8
@enum.unique
5✔
9
class ColorPreset(enum.IntEnum):
5✔
10
    """Monitor color presets."""
11

12
    COLOR_TEMP_4000K = 0x03
5✔
13
    COLOR_TEMP_5000K = 0x04
5✔
14
    COLOR_TEMP_6500K = 0x05
5✔
15
    COLOR_TEMP_7500K = 0x06
5✔
16
    COLOR_TEMP_8200K = 0x07
5✔
17
    COLOR_TEMP_9300K = 0x08
5✔
18
    COLOR_TEMP_10000K = 0x09
5✔
19
    COLOR_TEMP_11500K = 0x0A
5✔
20
    COLOR_TEMP_USER1 = 0x0B
5✔
21
    COLOR_TEMP_USER2 = 0x0C
5✔
22
    COLOR_TEMP_USER3 = 0x0D
5✔
23

24

25
@enum.unique
5✔
26
class PowerMode(enum.IntEnum):
5✔
27
    """Monitor power modes."""
28

29
    #: On.
30
    on = 0x01
5✔
31
    #: Standby.
32
    standby = 0x02
5✔
33
    #: Suspend.
34
    suspend = 0x03
5✔
35
    #: Software power off.
36
    off_soft = 0x04
5✔
37
    #: Hardware power off.
38
    off_hard = 0x05
5✔
39

40

41
@enum.unique
5✔
42
class AudioMuteMode(enum.Enum):
5✔
43
    """Monitor audio mute modes."""
44

45
    #: On.
46
    on = 0x01
5✔
47
    #: Off.
48
    off = 0x02
5✔
49

50

51
@enum.unique
5✔
52
class InputSource(enum.IntEnum):
5✔
53
    """Monitor input sources."""
54

55
    OFF = 0x00
5✔
56
    ANALOG1 = 0x01
5✔
57
    ANALOG2 = 0x02
5✔
58
    DVI1 = 0x03
5✔
59
    DVI2 = 0x04
5✔
60
    COMPOSITE1 = 0x05
5✔
61
    COMPOSITE2 = 0x06
5✔
62
    SVIDEO1 = 0x07
5✔
63
    SVIDEO2 = 0x08
5✔
64
    TUNER1 = 0x09
5✔
65
    TUNER2 = 0x0A
5✔
66
    TUNER3 = 0x0B
5✔
67
    CMPONENT1 = 0x0C
5✔
68
    CMPONENT2 = 0x0D
5✔
69
    CMPONENT3 = 0x0E
5✔
70
    DP1 = 0x0F
5✔
71
    DP2 = 0x10
5✔
72
    HDMI1 = 0x11
5✔
73
    HDMI2 = 0x12
5✔
74

75

76
class Monitor:
5✔
77
    """
78
    A physical monitor attached to a Virtual Control Panel (VCP).
79

80
    Typically, you do not use this class directly and instead use
81
    :py:meth:`get_monitors` to get a list of initialized monitors.
82

83
    All class methods must be called from within a context manager unless
84
    otherwise stated.
85

86
    Args:
87
        vcp: Virtual control panel for the monitor.
88
    """
89

90
    def __init__(self, vcp: vcp.VCP):
5✔
91
        self.vcp = vcp
5✔
92
        self.code_maximum = {}
5✔
93
        self._in_ctx = False
5✔
94

95
    def __enter__(self):
5✔
96
        self.vcp.__enter__()
5✔
97
        self._in_ctx = True
5✔
98
        return self
5✔
99

100
    def __exit__(
5✔
101
        self,
102
        exception_type: Optional[Type[BaseException]],
103
        exception_value: Optional[BaseException],
104
        exception_traceback: Optional[TracebackType],
105
    ) -> Optional[bool]:
106
        try:
5✔
107
            return self.vcp.__exit__(
5✔
108
                exception_type, exception_value, exception_traceback
109
            )
110
        finally:
111
            self._in_ctx = False
5✔
112

113
    def _get_code_maximum(self, code: vcp.VCPCode) -> int:
5✔
114
        """
115
        Gets the maximum values for a given code, and caches in the
116
        class dictionary if not already found.
117

118
        Args:
119
            code: Feature code definition class.
120

121
        Returns:
122
            Maximum value for the given code.
123

124
        Raises:
125
            TypeError: Code is write only.
126
        """
127
        assert self._in_ctx, "This function must be run within the context manager"
5✔
128
        if not code.readable:
5✔
129
            raise TypeError(f"code is not readable: {code.name}")
5✔
130

131
        if code.value in self.code_maximum:
5✔
132
            return self.code_maximum[code.value]
5✔
133
        else:
134
            _, maximum = self.vcp.get_vcp_feature(code.value)
5✔
135
            self.code_maximum[code.value] = maximum
5✔
136
            return maximum
5✔
137

138
    def _set_vcp_feature(self, code: vcp.VCPCode, value: int):
5✔
139
        """
140
        Sets the value of a feature on the virtual control panel.
141

142
        Args:
143
            code: Feature code.
144
            value: Feature value.
145

146
        Raises:
147
            TypeError: Code is ready only.
148
            ValueError: Value is greater than the maximum allowable.
149
            VCPError: Failed to get VCP feature.
150
        """
151
        assert self._in_ctx, "This function must be run within the context manager"
5✔
152
        if code.type == "ro":
5✔
153
            raise TypeError(f"cannot write read-only code: {code.name}")
5✔
154
        elif code.type == "rw" and code.function == "c":
5✔
155
            maximum = self._get_code_maximum(code)
5✔
156
            if value > maximum:
5✔
157
                raise ValueError(f"value of {value} exceeds code maximum of {maximum}")
5✔
158

159
        self.vcp.set_vcp_feature(code.value, value)
5✔
160

161
    def _get_vcp_feature(self, code: vcp.VCPCode) -> int:
5✔
162
        """
163
        Gets the value of a feature from the virtual control panel.
164

165
        Args:
166
            code: Feature code.
167

168
        Returns:
169
            Current feature value.
170

171
        Raises:
172
            TypeError: Code is write only.
173
            VCPError: Failed to get VCP feature.
174
        """
175
        assert self._in_ctx, "This function must be run within the context manager"
5✔
176
        if code.type == "wo":
5✔
177
            raise TypeError(f"cannot read write-only code: {code.name}")
5✔
178

179
        current, maximum = self.vcp.get_vcp_feature(code.value)
5✔
180
        return current
5✔
181

182
    def get_vcp_capabilities(self) -> dict:
5✔
183
        """
184
        Gets the capabilities of the monitor
185

186
        Returns:
187
            Dictionary of capabilities in the following example format::
188

189
                {
190
                    "prot": "monitor",
191
                    "type": "LCD",
192
                    "cmds": {
193
                            1: [],
194
                            2: [],
195
                            96: [15, 17, 18],
196
                    },
197
                    "inputs": [
198
                        InputSource.DP1,
199
                        InputSource.HDMI1,
200
                        InputSource.HDMI2
201
                        # this may return integers for out-of-spec values,
202
                        # such as USB Type-C monitors
203
                    ],
204
                }
205
        """
206
        assert self._in_ctx, "This function must be run within the context manager"
5✔
207

208
        cap_str = self.vcp.get_vcp_capabilities()
5✔
209

210
        res = _parse_capabilities(cap_str)
5✔
211
        return res
5✔
212

213
    def get_luminance(self) -> int:
5✔
214
        """
215
        Gets the monitors back-light luminance.
216

217
        Returns:
218
            Current luminance value.
219

220
        Example:
221
            Basic Usage::
222

223
                from monitorcontrol import get_monitors
224

225
                for monitor in get_monitors():
226
                    with monitor:
227
                        print(monitor.get_luminance())
228

229
        Raises:
230
            VCPError: Failed to get luminance from the VCP.
231
        """
232
        return self._get_vcp_feature(vcp_codes.image_luminance)
5✔
233

234
    def set_luminance(self, value: int):
5✔
235
        """
236
        Sets the monitors back-light luminance.
237

238
        Args:
239
            value: New luminance value (typically 0-100).
240

241
        Example:
242
            Basic Usage::
243

244
                from monitorcontrol import get_monitors
245

246
                for monitor in get_monitors():
247
                    with monitor:
248
                        monitor.set_luminance(50)
249

250
        Raises:
251
            ValueError: Luminance outside of valid range.
252
            VCPError: Failed to set luminance in the VCP.
253
        """
254
        self._set_vcp_feature(vcp_codes.image_luminance, value)
5✔
255

256
    def get_volume(self) -> int:
5✔
257
        """
258
        Gets the monitors sound volume level.
259

260
        Returns:
261
            Current sound volume value.
262

263
        Example:
264
            Basic Usage::
265

266
                from monitorcontrol import get_monitors
267

268
                for monitor in get_monitors():
269
                    with monitor:
270
                        print(monitor.get_volume())
271

272
        Raises:
273
            VCPError: Failed to get sound volume from the VCP.
274
        """
275
        return self._get_vcp_feature(vcp_codes.sound_volume)
5✔
276

277
    def set_volume(self, value: int):
5✔
278
        """
279
        Sets the monitors set_volume level.
280

281
        Args:
282
            value: New set_volume value (typically 0-100).
283

284
        Example:
285
            Basic Usage::
286

287
                from monitorcontrol import get_monitors
288

289
                for monitor in get_monitors():
290
                    with monitor:
291
                        monitor.set_volume(50)
292

293
        Raises:
294
            ValueError: Luminance outside of valid range.
295
            VCPError: Failed to set sound volume in the VCP.
296
        """
297
        self._set_vcp_feature(vcp_codes.sound_volume, value)
5✔
298

299
    def get_color_preset(self) -> int:
5✔
300
        """
301
        Gets the monitors color preset.
302

303
        Returns:
304
            Current color preset.
305
            Valid values are enumerated in :py:class:`ColorPreset`.
306

307
        Example:
308
            Basic Usage::
309

310
                from monitorcontrol import get_monitors
311

312
                for monitor in get_monitors():
313
                    with monitor:
314
                        print(monitor.get_color_preset())
315

316
        Raises:
317
            VCPError: Failed to get color preset from the VCP.
318
        """
319
        return self._get_vcp_feature(vcp_codes.image_color_preset)
×
320

321
    def set_color_preset(self, value: Union[int, str, ColorPreset]):
5✔
322
        """
323
        Sets the monitors color preset.
324

325
        Args:
326
            value:
327
                An integer color preset,
328
                or a string representing the color preset,
329
                or a value from :py:class:`ColorPreset`.
330

331
        Example:
332
            Basic Usage::
333

334
                from monitorcontrol import get_monitors, ColorPreset
335

336
                for monitor in get_monitors():
337
                    with monitor:
338
                        monitor.set_color_preset(ColorPreset.COLOR_TEMP_5000K)
339

340
        Raises:
341
            VCPError: Failed to set color preset in the VCP.
342
            ValueError: Color preset outside valid range.
343
            AttributeError: Color preset string is invalid.
344
            TypeError: Unsupported value
345
        """
346
        if isinstance(value, str):
×
347
            mode_value = getattr(ColorPreset, value).value
×
348
        elif isinstance(value, int):
×
349
            mode_value = ColorPreset(value).value
×
350
        elif isinstance(value, ColorPreset):
×
351
            mode_value = value.value
×
352
        else:
353
            raise TypeError("unsupported color preset: " + repr(type(value)))
×
354

355
        self._set_vcp_feature(vcp_codes.image_color_preset, mode_value)
×
356

357
    def get_contrast(self) -> int:
5✔
358
        """
359
        Gets the monitors contrast.
360

361
        Returns:
362
            Current contrast value.
363

364
        Example:
365
            Basic Usage::
366

367
                from monitorcontrol import get_monitors
368

369
                for monitor in get_monitors():
370
                    with monitor:
371
                        print(monitor.get_contrast())
372

373
        Raises:
374
            VCPError: Failed to get contrast from the VCP.
375
        """
376
        return self._get_vcp_feature(vcp_codes.image_contrast)
5✔
377

378
    def set_contrast(self, value: int):
5✔
379
        """
380
        Sets the monitors back-light contrast.
381

382
        Args:
383
            value: New contrast value (typically 0-100).
384

385
        Example:
386
            Basic Usage::
387

388
                from monitorcontrol import get_monitors
389

390
                for monitor in get_monitors():
391
                    with monitor:
392
                        print(monitor.set_contrast(50))
393

394
        Raises:
395
            ValueError: Contrast outside of valid range.
396
            VCPError: Failed to set contrast in the VCP.
397
        """
398
        self._set_vcp_feature(vcp_codes.image_contrast, value)
5✔
399

400
    def get_power_mode(self) -> PowerMode:
5✔
401
        """
402
        Get the monitor power mode.
403

404
        Returns:
405
            Value from the :py:class:`PowerMode` enumeration.
406

407
        Example:
408
            Basic Usage::
409

410
                from monitorcontrol import get_monitors
411

412
                for monitor in get_monitors():
413
                    with monitor:
414
                        print(monitor.get_power_mode())
415

416
        Raises:
417
            VCPError: Failed to get the power mode.
418
        """
419
        value = self._get_vcp_feature(vcp_codes.display_power_mode)
5✔
420
        return PowerMode(value)
5✔
421

422
    def set_power_mode(self, value: Union[int, str, PowerMode]):
5✔
423
        """
424
        Set the monitor power mode.
425

426
        Args:
427
            value:
428
                An integer power mode,
429
                or a string representing the power mode,
430
                or a value from :py:class:`PowerMode`.
431

432
        Example:
433
            Basic Usage::
434

435
                from monitorcontrol import get_monitors
436

437
                for monitor in get_monitors():
438
                    with monitor:
439
                        monitor.set_power_mode("standby")
440

441
        Raises:
442
            VCPError: Failed to get or set the power mode
443
            ValueError: Power state outside of valid range.
444
            AttributeError: Power mode string is invalid.
445
        """
446
        if isinstance(value, str):
5✔
447
            mode_value = getattr(PowerMode, value).value
5✔
448
        elif isinstance(value, int):
5✔
449
            mode_value = PowerMode(value).value
5✔
450
        elif isinstance(value, PowerMode):
5✔
451
            mode_value = value.value
×
452
        else:
453
            raise TypeError("unsupported mode type: " + repr(type(value)))
5✔
454

455
        self._set_vcp_feature(vcp_codes.display_power_mode, mode_value)
5✔
456

457
    def get_audio_mute_mode(self) -> AudioMuteMode:
5✔
458
        """
459
        Get the monitor audio mute mode.
460

461
        Returns:
462
            Value from the :py:class:`AudioMuteMode` enumeration.
463

464
        Example:
465
            Basic Usage::
466

467
                from monitorcontrol import get_monitors
468

469
                for monitor in get_monitors():
470
                    with monitor:
471
                        print(monitor.get_audio_mute_mode())
472

473
        Raises:
474
            VCPError: Failed to get the audio mute mode.
475
            ValueError: Set audio mute state outside of valid range.
476
            KeyError: Set audio mute mode string is invalid.
477
        """
478
        value = self._get_vcp_feature(vcp_codes.display_audio_mute_mode)
5✔
479
        return AudioMuteMode(value)
5✔
480

481
    def set_audio_mute_mode(self, value: Union[int, str, AudioMuteMode]):
5✔
482
        """
483
        Set the monitor audio mute mode.
484

485
        Args:
486
            value:
487
                An integer audio mute mode,
488
                or a string representing the audio mute mode,
489
                or a value from :py:class:`AudioMuteMode`.
490

491
        Example:
492
            Basic Usage::
493

494
                from monitorcontrol import get_monitors
495

496
                for monitor in get_monitors():
497
                    with monitor:
498
                        monitor.set_audio_mute_mode("standby")
499

500
        Raises:
501
            VCPError: Failed to get or set the audio mute mode
502
            ValueError: audio mute state outside of valid range.
503
            AttributeError: audio mute mode string is invalid.
504
        """
505
        if isinstance(value, str):
5✔
506
            mode_value = getattr(AudioMuteMode, value).value
5✔
507
        elif isinstance(value, int):
5✔
508
            mode_value = AudioMuteMode(value).value
5✔
509
        elif isinstance(value, AudioMuteMode):
5✔
510
            mode_value = value.value
×
511
        else:
512
            raise TypeError("unsupported mode type: " + repr(type(value)))
5✔
513

514
        self._set_vcp_feature(vcp_codes.display_audio_mute_mode, mode_value)
5✔
515

516
    def get_input_source(self) -> int:
5✔
517
        """
518
        Gets the monitors input source
519

520
        Returns:
521
            Current input source.
522

523
        Example:
524
            Basic Usage::
525

526
                from monitorcontrol import get_monitors, InputSource
527

528
                for monitor in get_monitors():
529
                    with monitor:
530
                        input_source_raw: int = monitor.get_input_source()
531
                        print(InputSource(input_source_raw).name)
532

533
        Raises:
534
            VCPError: Failed to get input source from the VCP.
535
        """
536
        return self._get_vcp_feature(vcp_codes.input_select) & 0xFF
5✔
537

538
    def set_input_source(self, value: Union[int, str, InputSource]):
5✔
539
        """
540
        Sets the monitors input source.
541

542
        Args:
543
            value: New input source
544

545
        Example:
546
            Basic Usage::
547

548
                from monitorcontrol import get_monitors
549

550
                for monitor in get_monitors():
551
                    with monitor:
552
                        print(monitor.set_input_source("DP1"))
553

554
        Raises:
555
            VCPError: Failed to get the input source.
556
            KeyError: Set input source string is invalid.
557
        """
558
        if isinstance(value, str):
5✔
559
            if value.isdigit():
5✔
560
                mode_value = int(value)
5✔
561
            else:
562
                mode_value = getattr(InputSource, value.upper()).value
5✔
563
        elif isinstance(value, InputSource):
5✔
564
            mode_value = value.value
5✔
565
        elif isinstance(value, int):
5✔
566
            mode_value = value
5✔
567
        else:
568
            raise TypeError("unsupported input type: " + repr(type(value)))
5✔
569

570
        self._set_vcp_feature(vcp_codes.input_select, mode_value)
5✔
571

572

573
def get_input_name(input_code: int) -> str:
5✔
574
    """
575
    Returns the input name for a given input code.
576

577
    Args:
578
        input_code: an integer representing a known (standard) input identifier
579

580
    Returns:
581
        A string containing the input name, or "UNKNOWN" plus the unknown code as hex
582
    """
583
    try:
5✔
584
        input_name = InputSource(input_code).name
5✔
585
    except ValueError:
5✔
586
        input_name = f"UNKNOWN (code {input_code:#04x})"
5✔
587
    return input_name
5✔
588

589

590
def get_vcps() -> List[vcp.VCP]:
5✔
591
    """
592
    Discovers virtual control panels.
593

594
    This function should not be used directly in most cases, use
595
    :py:func:`get_monitors` get monitors with VCPs.
596

597
    Returns:
598
        List of VCPs in a closed state.
599

600
    Raises:
601
        NotImplementedError: not implemented for your operating system
602
        VCPError: failed to list VCPs
603
    """
604
    if sys.platform == "win32" or sys.platform.startswith("linux"):
5✔
605
        return vcp.get_vcps()
5✔
606
    else:
607
        raise NotImplementedError(f"not implemented for {sys.platform}")
×
608

609

610
def get_monitors() -> List[Monitor]:
5✔
611
    """
612
    Creates a list of all monitors.
613

614
    Returns:
615
        List of monitors in a closed state.
616

617
    Raises:
618
        VCPError: Failed to list VCPs.
619

620
    Example:
621
        Setting the power mode of all monitors to standby::
622

623
            for monitor in get_monitors():
624
                with monitor:
625
                    monitor.set_power_mode("standby")
626

627
        Setting all monitors to the maximum brightness using the
628
        context manager::
629

630
            for monitor in get_monitors():
631
                with monitor:
632
                    monitor.set_luminance(100)
633
    """
634
    return [Monitor(v) for v in vcp.get_vcps()]
5✔
635

636

637
def _extract_a_cap(caps_str: str, key: str) -> str:
5✔
638
    """
639
    Splits the capabilities string into individual sets.
640

641
    Returns:
642
        Dict of all values for the capability
643
    """
644
    start_of_filter = caps_str.upper().find(key.upper())
5✔
645

646
    if start_of_filter == -1:
5✔
647
        # not all keys are returned by monitor.
648
        # Also, sometimes the string has errors.
649
        return ""
5✔
650

651
    start_of_filter += len(key)
5✔
652
    filtered_caps_str = caps_str[start_of_filter:]
5✔
653
    end_of_filter = 0
5✔
654
    for i in range(len(filtered_caps_str)):
5✔
655
        if filtered_caps_str[i] == "(":
5✔
656
            end_of_filter += 1
5✔
657
        if filtered_caps_str[i] == ")":
5✔
658
            end_of_filter -= 1
5✔
659
        if end_of_filter == 0:
5✔
660
            # don't change end_of_filter to remove the closing ")"
661
            break
5✔
662

663
    # 1:i to remove the first character "("
664
    return filtered_caps_str[1:i]
5✔
665

666

667
def _convert_to_dict(caps_str: str) -> dict:
5✔
668
    """
669
    Parses the VCP capabilities string to a dictionary.
670
    Non-continuous capabilities will include an array of
671
    all supported values.
672

673
    Returns:
674
        Dict with all capabilities in hex
675

676
    Example:
677
        Expected string "04 14(05 06) 16" is converted to::
678

679
            {
680
                0x04: {},
681
                0x14: {0x05: {}, 0x06: {}},
682
                0x16: {},
683
            }
684
    """
685

686
    if len(caps_str) == 0:
5✔
687
        # Sometimes the keys aren't found and the extracting of
688
        # capabilities returns an empty string.
689
        return {}
×
690

691
    result_dict = {}
5✔
692
    group = []
5✔
693
    prev_val = None
5✔
694
    for chunk in caps_str.replace("(", " ( ").replace(")", " ) ").split(" "):
5✔
695
        if chunk == "":
5✔
696
            continue
5✔
697
        elif chunk == "(":
5✔
698
            group.append(prev_val)
5✔
699
        elif chunk == ")":
5✔
700
            group.pop(-1)
5✔
701
        else:
702
            val = int(chunk, 16)
5✔
703
            if len(group) == 0:
5✔
704
                result_dict[val] = {}
5✔
705
            else:
706
                d = result_dict
5✔
707
                for g in group:
5✔
708
                    assert g is not None
5✔
709
                    d = d[g]
5✔
710
                d[val] = {}
5✔
711
            prev_val = val
5✔
712

713
    return result_dict
5✔
714

715

716
def _parse_capabilities(caps_str: str) -> dict:
5✔
717
    """
718
    Converts the capabilities string into a nice dict
719
    """
720

721
    caps_dict: dict[str, str | list | dict[int, dict]] = {
5✔
722
        # Used to specify the protocol class
723
        "prot": "",
724
        # Identifies the type of display
725
        "type": "",
726
        # The display model number
727
        "model": "",
728
        # A list of supported VCP codes. Somehow not the same as "vcp"
729
        "cmds": {},
730
        # A list of supported VCP codes with a list of supported values
731
        # for each nc code
732
        "vcp": {},
733
        # undocumented
734
        "mswhql": "",
735
        # undocumented
736
        "asset_eep": "",
737
        # MCCS version implemented
738
        "mccs_ver": "",
739
        # Specifies the window, window type (PIP or Zone) safe area size
740
        # (bounded safe area) maximum size of the window, minimum size of
741
        # the window, and window supports VCP codes for control/adjustment.
742
        "window": "",
743
        # Alternate name to be used for control
744
        "vcpname": "",
745
        # Parsed input sources into text. Not part of capabilities string.
746
        "inputs": [],
747
        # Parsed color presets into text. Not part of capabilities string.
748
        "color_presets": "",
749
    }
750

751
    for key in caps_dict:
5✔
752
        # The "cmds" and "vcp" caps can be a mapping
753
        if key in ["cmds", "vcp"]:
5✔
754
            caps_dict[key] = _convert_to_dict(_extract_a_cap(caps_str, key))
5✔
755
        else:
756
            caps_dict[key] = _extract_a_cap(caps_str, key)
5✔
757

758
    # Parse the input sources into a text list for readability
759
    input_source_cap = vcp_codes.input_select.value
5✔
760

761
    # Put this check here to appease the type checker
762
    if isinstance(caps_dict["vcp"], str):
5✔
NEW
763
        raise ValueError("VCP capabilities dictionary is the wrong type!")
×
764
    if input_source_cap in caps_dict["vcp"]:
5✔
765
        caps_dict["inputs"] = []
5✔
766
        input_val_list = list(caps_dict["vcp"][input_source_cap].keys())
5✔
767
        input_val_list.sort()
5✔
768

769
        for val in input_val_list:
5✔
770
            try:
5✔
771
                input_source = InputSource(val)
5✔
772
            except ValueError:
5✔
773
                input_source = val
5✔
774

775
            caps_dict["inputs"].append(input_source)
5✔
776

777
    # Parse the color presets into a text list for readability
778
    color_preset_cap = vcp_codes.image_color_preset.value
5✔
779
    if color_preset_cap in caps_dict["vcp"]:
5✔
780
        caps_dict["color_presets"] = []
5✔
781
        color_val_list = list(caps_dict["vcp"][color_preset_cap])
5✔
782
        color_val_list.sort()
5✔
783

784
        for val in color_val_list:
5✔
785
            try:
5✔
786
                color_source = ColorPreset(val)
5✔
787
            except ValueError:
×
788
                color_source = val
×
789

790
            caps_dict["color_presets"].append(color_source)
5✔
791

792
    return caps_dict
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc