Skip to content

Commit

Permalink
Merge pull request #26 from hchris1/25-add-output-select
Browse files Browse the repository at this point in the history
25 add output select
  • Loading branch information
hchris1 authored Dec 15, 2023
2 parents d009540 + 0f69401 commit d3e13b5
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 38 deletions.
40 changes: 39 additions & 1 deletion custom_components/eversolo/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ async def async_get_data(self):
"knob_brightness": await self.async_get_knob_brightness(),
"music_control_state": await self.async_get_music_control_state(),
"vu_mode_state": await self.async_get_vu_mode_state(),
"vu_mode_options": {
"VU-Meter 1": "VU-Meter 1",
"VU-Meter 2": "VU-Meter 2",
"VU-Meter 3": "VU-Meter 3",
"VU-Meter 4": "VU-Meter 4",
},
}
LOGGER.debug("Fetched data from API: %s", result)
return result
Expand All @@ -55,12 +61,44 @@ async def async_get_music_control_state(self):
)
return result

def transform_sources(self, input_output_state: dict) -> dict:
"""Return available input sources."""
sources = input_output_state.get("inputData", None)

if sources is None:
return None

transformed_sources = {}

for source in list(sources):
transformed_sources[source["tag"].replace("/", "")] = source["name"]

return transformed_sources

def transform_outputs(self, input_output_state: dict) -> dict:
"""Return available input sources."""
outputs = input_output_state.get("outputData", None)

if outputs is None:
return None

transformed_sources = {}

for source in [output for output in outputs if output["enable"]]:
transformed_sources[source["tag"].replace("/", "")] = source["name"]

return transformed_sources

async def async_get_input_output_state(self):
"""Return input/output state."""
result = await self._api_wrapper(
method="get",
url=f"http://{self._host}:{self._port}/ZidooMusicControl/v2/getInputAndOutputList",
)

result["transformed_sources"] = self.transform_sources(result)
result["transformed_outputs"] = self.transform_outputs(result)

return result

async def async_get_vu_mode_state(self):
Expand Down Expand Up @@ -150,7 +188,7 @@ async def async_trigger_cycle_screen_mode(self) -> any:
parseJson=False,
)

async def async_select_vu_mode_option(self, index) -> any:
async def async_select_vu_mode_option(self, index, tag) -> any:
"""Select the VU meter style."""
await self._api_wrapper(
method="get",
Expand Down
2 changes: 1 addition & 1 deletion custom_components/eversolo/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"documentation": "https://github.com/hchris1/Eversolo",
"iot_class": "local_polling",
"issue_tracker": "https://github.com/hchris1/Eversolo/issues",
"version": "0.2.1"
"version": "0.3.0"
}
47 changes: 30 additions & 17 deletions custom_components/eversolo/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,6 @@
| MediaPlayerEntityFeature.SEEK
)

AVAILABLE_SOURCES = {
"XMOS": "Internal Player",
"BT": "Bluetooth",
"USB": "USB-C",
"SPDIF": "SPDIF",
"RCA": "Coaxial",
}


async def async_setup_entry(hass, entry, async_add_devices):
"""Set up the Media Player platform."""
Expand Down Expand Up @@ -130,17 +122,32 @@ def source(self):
if input_output_state is None:
return None

sources = self.coordinator.data.get("input_output_state", {}).get(
"transformed_sources", None
)

if sources is None:
return None

input_index = input_output_state.get("inputIndex", -1)
if input_index < 0 or input_index >= len(AVAILABLE_SOURCES):
if input_index < 0 or input_index >= len(sources):
LOGGER.debug("Input index %s is out of range", input_index)
return None

return list(AVAILABLE_SOURCES.values())[input_index]
return list(sources.values())[input_index]

@property
def source_list(self):
"""List of available input sources."""
return list(AVAILABLE_SOURCES.values())
# NoneType object has no values
sources = self.coordinator.data.get("input_output_state", {}).get(
"transformed_sources", None
)

if sources is None:
return None

return list(sources.values())

@property
def media_title(self):
Expand Down Expand Up @@ -316,12 +323,18 @@ async def async_mute_volume(self, mute):

async def async_select_source(self, source):
"""Set the input source."""
index = 0
for key, value in AVAILABLE_SOURCES.items():
if value == source:
await self.coordinator.client.async_set_input(index, key)
break
index += 1
sources = self.coordinator.data.get("input_output_state", {}).get(
"transformed_sources", None
)

if sources is None:
return

index, tag = next(
(index, key) for index, key in enumerate(sources) if sources[key] == source
)

await self.coordinator.client.async_set_input(index, tag)

async def async_media_play_pause(self):
"""Simulate play pause Media Player."""
Expand Down
73 changes: 55 additions & 18 deletions custom_components/eversolo/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
class EversoloSelectDescriptionMixin(Generic[_EversoloDataUpdateCoordinatorT]):
"""Mixin to describe a Select entity."""

available_options: list[str]
get_selected_option: Callable[[_EversoloDataUpdateCoordinatorT], int]
get_available_options: Callable[[_EversoloDataUpdateCoordinatorT], list[str]]
select_option: Callable[
[_EversoloDataUpdateCoordinatorT], Coroutine[Any, Any, None]
[_EversoloDataUpdateCoordinatorT, int, str], Coroutine[Any, Any, None]
]


Expand All @@ -39,11 +40,30 @@ class EversoloSelectDescription(
key="vu_style",
name="Eversolo VU Style",
icon="mdi:gauge-low",
available_options=["VU-Meter 1", "VU-Meter 2", "VU-Meter 3", "VU-Meter 4"],
select_option=lambda coordinator, option: coordinator.client.async_select_vu_mode_option(
option
get_selected_option=lambda coordinator: coordinator.data.get(
"vu_mode_state", {}
).get("currentIndex", -1),
get_available_options=lambda coordinator: coordinator.data.get(
"vu_mode_options", []
),
)
select_option=lambda coordinator, index, tag: coordinator.client.async_select_vu_mode_option(
index, tag
),
),
EversoloSelectDescription[EversoloDataUpdateCoordinator](
key="output_mode",
name="Eversolo Output Mode",
icon="mdi:transmission-tower",
get_selected_option=lambda coordinator: coordinator.data.get(
"input_output_state", {}
).get("outputIndex", -1),
get_available_options=lambda coordinator: coordinator.data.get(
"input_output_state", {}
).get("transformed_outputs", None),
select_option=lambda coordinator, index, tag: coordinator.client.async_set_output(
index, tag
),
),
]


Expand Down Expand Up @@ -71,31 +91,48 @@ def __init__(
"""Initialize the Select class."""
super().__init__(coordinator)
self.entity_description = entity_description
self._attr_options = self.entity_description.available_options
self._attr_unique_id = (
f"{coordinator.config_entry.entry_id}_{entity_description.key}"
)

@property
def options(self) -> list[str]:
"""Return the list of available options."""
return list(
self.entity_description.get_available_options(self.coordinator).values()
)

@property
def current_option(self) -> str:
"""Return current VU style."""
vu_mode_state = self.coordinator.data.get("vu_mode_state", None)
"""Return current state."""
current_index = self.entity_description.get_selected_option(self.coordinator)

if vu_mode_state is None:
return None
options = self.entity_description.get_available_options(self.coordinator)

current_index = int(vu_mode_state.get("currentIndex", -1))
if options is None:
LOGGER.debug("No options found")
return None

if current_index < 0 or current_index >= len(self._attr_options):
if current_index < 0 or current_index >= len(options):
LOGGER.debug("Current index %s is out of range", current_index)
return None

return self._attr_options[current_index]
return list(options.values())[current_index]

async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
"""Change to selected option."""

await self.entity_description.select_option(
self.coordinator, self.entity_description.available_options.index(option)
)
options = self.entity_description.get_available_options(self.coordinator)

index, tag = None, None
for i, (key, value) in enumerate(options.items()):
if value == option:
index, tag = i, key
break

if index is None or tag is None:
LOGGER.debug("Option %s not found", option)
return

await self.entity_description.select_option(self.coordinator, index, tag)
self._attr_current_option = option
2 changes: 1 addition & 1 deletion hacs.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "Eversolo Integration",
"filename": "eversolo.zip",
"hide_default_branch": true,
"homeassistant": "2023.10.0",
"homeassistant": "2023.12.3",
"render_readme": true,
"zip_release": true
}

0 comments on commit d3e13b5

Please sign in to comment.