Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

25 add output select #26

Merged
merged 4 commits into from
Dec 15, 2023
Merged
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
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
}