from __future__ import annotations from collections.abc import Callable from typing import TYPE_CHECKING, Any from .capabilities import BinarySetter, Capability, EntityType, NumberSetter, OptionSetter from .const import DISPLAY_DIMMER_SPECIALS from .features import DeviceFeature, ZoneFeature if TYPE_CHECKING: from .musiccast_device import MusicCastDevice DeviceCapabilityFactory = Callable[[str, Any], Capability] ZoneCapabilityFactory = Callable[[str, Any, str], Capability] def normalize_option(option: object) -> str: option_str = str(option) return option_str.replace(" ", "_").lower() """Dictionary of all DeviceFeatures with a callable as value. The callable expects an ID and a MusicCastDevice as parameters.""" _device_capabilities: dict[DeviceFeature, DeviceCapabilityFactory | dict[str, DeviceCapabilityFactory]] = { DeviceFeature.DIMMER: lambda capability_id, device: OptionSetter( capability_id, "Display Brightness", EntityType.CONFIG, lambda: device.data.dimmer.dimmer_current, lambda value: device.set_dimmer(int(value)), { x: normalize_option(DISPLAY_DIMMER_SPECIALS.get(x, x)) for x in range( int(device.data.dimmer.minimum), int(device.data.dimmer.maximum + 1), int(device.data.dimmer.step) ) }, ), DeviceFeature.SPEAKER_A: lambda capability_id, device: BinarySetter( capability_id, "Speaker A", EntityType.CONFIG, lambda: device.data.speaker_a, lambda val: device.set_speaker_a(val), ), DeviceFeature.SPEAKER_B: lambda capability_id, device: BinarySetter( capability_id, "Speaker B", EntityType.CONFIG, lambda: device.data.speaker_b, lambda val: device.set_speaker_b(val), ), DeviceFeature.PARTY_MODE: lambda capability_id, device: BinarySetter( capability_id, "Party Mode", EntityType.CONFIG, lambda: device.data.party_enable, lambda val: device.set_party_mode(val), ), } """Dictionary of all ZoneFeatures with a callable as value. The callable expects an ID, a MusicCastDevice and a zone_id as parameters. """ _zone_capabilities: dict[ZoneFeature, ZoneCapabilityFactory | dict[str, ZoneCapabilityFactory]] = { ZoneFeature.SURR_DECODER_TYPE: lambda capability_id, device, zone_id: OptionSetter( capability_id, "Surround Decoder Device", EntityType.CONFIG, lambda: device.data.zones[zone_id].surr_decoder_type, lambda val: device.set_surround_decoder(zone_id, val), {key: normalize_option(key) for key in device.data.zones[zone_id].surr_decoder_type_list}, ), ZoneFeature.SLEEP: lambda capability_id, device, zone_id: OptionSetter( capability_id, "Sleep Timer", EntityType.CONFIG, lambda: device.data.zones[zone_id].sleep_time, lambda val: device.set_sleep_timer(zone_id, val), {0: "off", 30: "30_min", 60: "60_min", 90: "90_min", 120: "120_min"}, ), ZoneFeature.EQUALIZER: { "mode": lambda capability_id, device, zone_id: OptionSetter( capability_id, "Equalizer Mode", EntityType.CONFIG, lambda: device.data.zones[zone_id].equalizer_mode, lambda val: device.set_equalizer(zone_id, mode=val), {key: normalize_option(key) for key in device.data.zones[zone_id].equalizer_mode_list}, ), "low": lambda capability_id, device, zone_id: NumberSetter( capability_id, "Equalizer Low", EntityType.CONFIG, lambda: device.data.zones[zone_id].equalizer_low, lambda val: device.set_equalizer(zone_id, low=int(val)), device.data.zones[zone_id].range_step["equalizer"].minimum, device.data.zones[zone_id].range_step["equalizer"].maximum, device.data.zones[zone_id].range_step["equalizer"].step, ), "mid": lambda capability_id, device, zone_id: NumberSetter( capability_id, "Equalizer Mid", EntityType.CONFIG, lambda: device.data.zones[zone_id].equalizer_mid, lambda val: device.set_equalizer(zone_id, mid=int(val)), device.data.zones[zone_id].range_step["equalizer"].minimum, device.data.zones[zone_id].range_step["equalizer"].maximum, device.data.zones[zone_id].range_step["equalizer"].step, ), "high": lambda capability_id, device, zone_id: NumberSetter( capability_id, "Equalizer High", EntityType.CONFIG, lambda: device.data.zones[zone_id].equalizer_high, lambda val: device.set_equalizer(zone_id, high=int(val)), device.data.zones[zone_id].range_step["equalizer"].minimum, device.data.zones[zone_id].range_step["equalizer"].maximum, device.data.zones[zone_id].range_step["equalizer"].step, ), }, ZoneFeature.TONE_CONTROL: { "mode": lambda capability_id, device, zone_id: OptionSetter( capability_id, "Tone Control Mode", EntityType.CONFIG, lambda: device.data.zones[zone_id].tone_mode, lambda val: device.set_tone_control(zone_id, mode=val), {key: normalize_option(key) for key in device.data.zones[zone_id].tone_control_mode_list}, ), "bass": lambda capability_id, device, zone_id: NumberSetter( capability_id, "Tone Control Bass", EntityType.CONFIG, lambda: device.data.zones[zone_id].tone_bass, lambda val: device.set_tone_control(zone_id, bass=int(val)), device.data.zones[zone_id].range_step["tone_control"].minimum, device.data.zones[zone_id].range_step["tone_control"].maximum, device.data.zones[zone_id].range_step["tone_control"].step, ), "treble": lambda capability_id, device, zone_id: NumberSetter( capability_id, "Tone Control Treble", EntityType.CONFIG, lambda: device.data.zones[zone_id].tone_treble, lambda val: device.set_tone_control(zone_id, treble=int(val)), device.data.zones[zone_id].range_step["tone_control"].minimum, device.data.zones[zone_id].range_step["tone_control"].maximum, device.data.zones[zone_id].range_step["tone_control"].step, ), }, ZoneFeature.DIALOGUE_LEVEL: lambda capability_id, device, zone_id: NumberSetter( capability_id, "Dialogue Level", EntityType.CONFIG, lambda: device.data.zones[zone_id].dialogue_level, lambda val: device.set_dialogue_level(zone_id, int(val)), device.data.zones[zone_id].range_step["dialogue_level"].minimum, device.data.zones[zone_id].range_step["dialogue_level"].maximum, device.data.zones[zone_id].range_step["dialogue_level"].step, ), ZoneFeature.DIALOGUE_LIFT: lambda capability_id, device, zone_id: NumberSetter( capability_id, "Dialogue Lift", EntityType.CONFIG, lambda: device.data.zones[zone_id].dialogue_lift, lambda val: device.set_dialogue_lift(zone_id, int(val)), device.data.zones[zone_id].range_step["dialogue_lift"].minimum, device.data.zones[zone_id].range_step["dialogue_lift"].maximum, device.data.zones[zone_id].range_step["dialogue_lift"].step, ), ZoneFeature.DTS_DIALOGUE_CONTROL: lambda capability_id, device, zone_id: NumberSetter( capability_id, "DTS Dialogue Control", EntityType.CONFIG, lambda: device.data.zones[zone_id].dts_dialogue_control, lambda val: device.set_dts_dialogue_control(zone_id, int(val)), device.data.zones[zone_id].range_step["dts_dialogue_control"].minimum, device.data.zones[zone_id].range_step["dts_dialogue_control"].maximum, device.data.zones[zone_id].range_step["dts_dialogue_control"].step, ), ZoneFeature.LINK_AUDIO_DELAY: lambda capability_id, device, zone_id: OptionSetter( capability_id, "Link Audio Delay", EntityType.CONFIG, lambda: device.data.zones[zone_id].link_audio_delay, lambda val: device.set_link_audio_delay(zone_id, val), {key: normalize_option(key) for key in device.data.zones[zone_id].link_audio_delay_list}, ), ZoneFeature.LINK_CONTROL: lambda capability_id, device, zone_id: OptionSetter( capability_id, "Link Control", EntityType.CONFIG, lambda: device.data.zones[zone_id].link_control, lambda val: device.set_link_control(zone_id, val), {key: normalize_option(key) for key in device.data.zones[zone_id].link_control_list}, ), ZoneFeature.LINK_AUDIO_QUALITY: lambda capability_id, device, zone_id: OptionSetter( capability_id, "Link Audio Quality", EntityType.CONFIG, lambda: device.data.zones[zone_id].link_audio_quality, lambda val: device.set_link_audio_quality(zone_id, val), {key: normalize_option(key) for key in device.data.zones[zone_id].link_audio_quality_list}, ), ZoneFeature.BASS_EXTENSION: lambda capability_id, device, zone_id: BinarySetter( capability_id, "Bass Extension", EntityType.CONFIG, lambda: device.data.zones[zone_id].bass_extension, lambda val: device.set_bass_extension(zone_id, val), ), ZoneFeature.EXTRA_BASS: lambda capability_id, device, zone_id: BinarySetter( capability_id, "Extra Bass", EntityType.CONFIG, lambda: device.data.zones[zone_id].extra_bass, lambda val: device.set_extra_bass(zone_id, val), ), ZoneFeature.ENHANCER: lambda capability_id, device, zone_id: BinarySetter( capability_id, "Enhancer", EntityType.CONFIG, lambda: device.data.zones[zone_id].enhancer, lambda val: device.set_enhancer(zone_id, val), ), ZoneFeature.PURE_DIRECT: lambda capability_id, device, zone_id: BinarySetter( capability_id, "Pure Direct", EntityType.CONFIG, lambda: device.data.zones[zone_id].pure_direct, lambda val: device.set_pure_direct(zone_id, val), ), ZoneFeature.ADAPTIVE_DRC: lambda capability_id, device, zone_id: BinarySetter( capability_id, "Adaptive DRC", EntityType.CONFIG, lambda: device.data.zones[zone_id].adaptive_drc, lambda val: device.set_adaptive_drc(zone_id, val), ), ZoneFeature.SUBWOOFER_VOLUME: lambda capability_id, device, zone_id: NumberSetter( capability_id, "Subwoofer Volume", EntityType.CONFIG, lambda: device.data.zones[zone_id].subwoofer_volume, lambda val: device.set_subwoofer_volume(zone_id, int(val)), device.data.zones[zone_id].range_step["subwoofer_volume"].minimum, device.data.zones[zone_id].range_step["subwoofer_volume"].maximum, device.data.zones[zone_id].range_step["subwoofer_volume"].step, ), ZoneFeature.CLEAR_VOICE: lambda capability_id, device, zone_id: BinarySetter( capability_id, "Clear Voice", EntityType.CONFIG, lambda: device.data.zones[zone_id].clear_voice, lambda val: device.set_clear_voice(zone_id, val), ), ZoneFeature.SURROUND_3D: lambda capability_id, device, zone_id: BinarySetter( capability_id, "3D Surround", EntityType.CONFIG, lambda: device.data.zones[zone_id].surround_3d, lambda val: device.set_surround_3d(zone_id, val), ), } def build_device_capabilities(device: MusicCastDevice) -> list[Capability]: """Build capabilities for the given device. The resulting identifiers follow the pattern ``{feature.name.lower()}_{key}``. """ result: list[Capability] = [] for feature in [f for f in DeviceFeature if f in device.features]: feature_entry = _device_capabilities.get(feature) if feature_entry is not None: if isinstance(feature_entry, dict): for key, capability in feature_entry.items(): capability_id = f"{feature.name}_{key}" result.append(capability(capability_id, device)) else: result.append(feature_entry(feature.name, device)) return result def build_zone_capabilities(device: MusicCastDevice, zone_id: str) -> list[Capability]: """Build capabilities for a specific device zone. Identifiers follow the pattern ``zone_{feature.name.lower()}_{key}``. """ result: list[Capability] = [] for feature in [f for f in ZoneFeature if f in device.data.zones[zone_id].features]: feature_entry = _zone_capabilities.get(feature) if feature_entry is not None: if isinstance(feature_entry, dict): for key, capability in feature_entry.items(): capability_id = f"zone_{feature.name}_{key}" result.append(capability(capability_id, device, zone_id)) else: result.append(feature_entry(f"zone_{feature.name}", device, zone_id)) return result