Skip to content

Commit

Permalink
Revise volume logic (#65)
Browse files Browse the repository at this point in the history
* Add new volume percent methods, remove pecent logic from old call and rename.

* Refactor for new volume method.

* Add tests for new volume methods.

* Bump library version.

* Fix formatting error.

* Improve tests.
  • Loading branch information
pavoni authored Dec 31, 2022
1 parent 442b710 commit b3a3589
Show file tree
Hide file tree
Showing 4 changed files with 274 additions and 10 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "roonapi"
version = "0.1.2"
version = "0.1.3"
description = "Provides a python interface to interact with Roon"
authors = [
"Marcel van der Veldt",
Expand Down
87 changes: 81 additions & 6 deletions roonapi/roonapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,15 +233,93 @@ def mute(self, output_id, mute=True):
data = {"output_id": output_id, "how": how}
return self._request(SERVICE_TRANSPORT + "/mute", data)

def change_volume(self, output_id, value, method="absolute"):
def set_volume_percent(self, output_id, absolute_value):
"""
Set the volume of an output to a 0-100 value.
Roon endpoints have a few different volume scales - this method scales from 0-100
to what the endpoint needs.
params:
output_id: the id of the output
"""
volume_data = self._outputs[output_id].get("volume")

if volume_data is None:
LOGGER.info("This endpoint has fixed volume.")
return None

volume_max = volume_data["max"]
volume_min = volume_data["min"]
volume_range = volume_max - volume_min
volume_percentage_factor = volume_range / 100
percentage_volume = volume_min + absolute_value * volume_percentage_factor
return self.change_volume_raw(output_id, percentage_volume)

def change_volume_percent(self, output_id, relative_value):
"""
Change the volume of an output by a relative amount.
Roon endpoints have a few different volume scales - this method scales from 0-100
to what the endpoint needs.
params:
output_id: the id of the output
relative_value: How much to increase or decrease the volume
"""
volume_data = self._outputs[output_id].get("volume")

if volume_data is None:
LOGGER.info("This endpoint has fixed volume.")
return None

volume_max = volume_data["max"]
volume_min = volume_data["min"]
volume_range = volume_max - volume_min
volume_percentage_factor = volume_range / 100

volume_percentage_change = int(round(relative_value * volume_percentage_factor))
return self.change_volume_raw(output_id, volume_percentage_change, "relative")

def get_volume_percent(self, output_id):
"""
Get the volume of an output.
Roon endpoints have a few different volumee scales - this method scales from 0-100
to what the endpoint needs.
params:
output_id: the id of the output
relative_value: How much to increase or decrease the volume
"""

volume_data = self._outputs[output_id].get("volume")

if volume_data is None:
LOGGER.info("This endpoint has fixed volume.")
return None

volume_max = volume_data["max"]
volume_min = volume_data["min"]
volume_range = volume_max - volume_min
volume_percentage_factor = volume_range / 100

raw_level = float(volume_data["value"])
percent_level = (raw_level - volume_min) / volume_percentage_factor
return int(round(percent_level))

def change_volume_raw(self, output_id, value, method="absolute"):
"""
Change the volume of an output.
For convenience you can always just give the new volume level as percentage.
Roon endpoint have a few different scales - this endpoints just used the native scale.
The percent calls may be easier to use
params:
output_id: the id of the output
value: The new volume value, or the increment value or step (as percentage)
value: The new volume value, or the increment value or step
method: How to interpret the volume ('absolute'|'relative'|'relative_step')
"""
if "volume" not in self._outputs[output_id]:
Expand All @@ -250,9 +328,6 @@ def change_volume(self, output_id, value, method="absolute"):
# Home assistant was catching this - so catch here
# to try and diagnose what needs to be checked.
try:
if method == "absolute":
if self._outputs[output_id]["volume"]["type"] == "db":
value = int((float(value) / 100) * 80) - 80
data = {"output_id": output_id, "how": method, "value": value}
return self._request(SERVICE_TRANSPORT + "/change_volume", data)
except Exception as exc: # pylint: disable=broad-except
Expand Down
190 changes: 190 additions & 0 deletions tests/test_volume.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Some simple tests for callback on the roon api."""

import os.path, pytest

from roonapi import RoonApi, LOGGER


@pytest.fixture()
def roon_api(request):
try:
host = open("test_core_server_file").read()
port = open("test_core_port_file").read()
token = open("my_token_file").read()
except OSError:
print("Please authorise first using discovery.py")
exit()

appinfo = {
"extension_id": "python_roon_test",
"display_name": "Python library for Roon",
"display_version": "1.0.0",
"publisher": "pavoni",
"email": "my@email.com",
}

def teardown():
roonapi.stop()

request.addfinalizer(teardown)

# initialize Roon api and register the callback for state changes
roonapi = RoonApi(appinfo, token, host, port, True)
return roonapi


def test_get_volume_db(roon_api):
db_zone = [
zone for zone in roon_api.zones.values() if zone["display_name"] == "95 Office"
][0]

db_zone_volume_info = db_zone["outputs"][0]["volume"]
db_zone_output_id = db_zone["outputs"][0]["output_id"]

roon_api.change_volume_raw(db_zone_output_id, -80)

vol = [
zone for zone in roon_api.zones.values() if zone["display_name"] == "95 Office"
][0]["outputs"][0]["volume"]["value"]
assert vol == -80

assert roon_api.get_volume_percent(db_zone_output_id) == 0

roon_api.change_volume_raw(db_zone_output_id, 0)

vol = [
zone for zone in roon_api.zones.values() if zone["display_name"] == "95 Office"
][0]["outputs"][0]["volume"]["value"]
assert vol == 0

assert roon_api.get_volume_percent(db_zone_output_id) == 100

roon_api.change_volume_raw(db_zone_output_id, -40)
vol = [
zone for zone in roon_api.zones.values() if zone["display_name"] == "95 Office"
][0]["outputs"][0]["volume"]["value"]
assert vol == -40

assert roon_api.get_volume_percent(db_zone_output_id) == 50


def test_get_volume_perent(roon_api):
percent_zone = [
zone
for zone in roon_api.zones.values()
if zone["display_name"] == "Gregs Mac System"
][0]

percent_zone_volume_info = percent_zone["outputs"][0]["volume"]
percent_zone_output_id = percent_zone["outputs"][0]["output_id"]

roon_api.change_volume_raw(percent_zone_output_id, 0)

vol = [
zone
for zone in roon_api.zones.values()
if zone["display_name"] == "Gregs Mac System"
][0]["outputs"][0]["volume"]["value"]
assert vol == 0

assert roon_api.get_volume_percent(percent_zone_output_id) == 0

roon_api.change_volume_raw(percent_zone_output_id, 100)
vol = [
zone
for zone in roon_api.zones.values()
if zone["display_name"] == "Gregs Mac System"
][0]["outputs"][0]["volume"]["value"]
assert vol == 100

assert roon_api.get_volume_percent(percent_zone_output_id) == 100

roon_api.change_volume_raw(percent_zone_output_id, 50)

vol = [
zone
for zone in roon_api.zones.values()
if zone["display_name"] == "Gregs Mac System"
][0]["outputs"][0]["volume"]["value"]
assert vol == 50

assert roon_api.get_volume_percent(percent_zone_output_id) == 50


def test_set_volume_db(roon_api):
db_zone = [
zone for zone in roon_api.zones.values() if zone["display_name"] == "95 Office"
][0]

db_zone_volume_info = db_zone["outputs"][0]["volume"]
db_zone_output_id = db_zone["outputs"][0]["output_id"]

roon_api.set_volume_percent(db_zone_output_id, 0)
assert roon_api.get_volume_percent(db_zone_output_id) == 0

roon_api.set_volume_percent(db_zone_output_id, 100)
assert roon_api.get_volume_percent(db_zone_output_id) == 100

roon_api.set_volume_percent(db_zone_output_id, 50)
assert roon_api.get_volume_percent(db_zone_output_id) == 50


def test_set_volume_percent(roon_api):
percent_zone = [
zone
for zone in roon_api.zones.values()
if zone["display_name"] == "Gregs Mac System"
][0]

percent_zone_volume_info = percent_zone["outputs"][0]["volume"]
percent_zone_output_id = percent_zone["outputs"][0]["output_id"]

roon_api.set_volume_percent(percent_zone_output_id, 0)
assert roon_api.get_volume_percent(percent_zone_output_id) == 0

roon_api.set_volume_percent(percent_zone_output_id, 100)
assert roon_api.get_volume_percent(percent_zone_output_id) == 100

roon_api.set_volume_percent(percent_zone_output_id, 50)
assert roon_api.get_volume_percent(percent_zone_output_id) == 50


def test_change_volume_db(roon_api):
db_zone = [
zone for zone in roon_api.zones.values() if zone["display_name"] == "95 Office"
][0]

db_zone_volume_info = db_zone["outputs"][0]["volume"]
db_zone_output_id = db_zone["outputs"][0]["output_id"]

roon_api.set_volume_percent(db_zone_output_id, 40)
assert roon_api.get_volume_percent(db_zone_output_id) == 40

roon_api.change_volume_percent(db_zone_output_id, 1)
assert roon_api.get_volume_percent(db_zone_output_id) == 41

roon_api.change_volume_percent(db_zone_output_id, -2)
assert roon_api.get_volume_percent(db_zone_output_id) == 39


def test_change_volume_percent(roon_api):
percent_zone = [
zone
for zone in roon_api.zones.values()
if zone["display_name"] == "Gregs Mac System"
][0]

percent_zone_volume_info = percent_zone["outputs"][0]["volume"]
percent_zone_output_id = percent_zone["outputs"][0]["output_id"]

roon_api.set_volume_percent(percent_zone_output_id, 40)
assert roon_api.get_volume_percent(percent_zone_output_id) == 40

roon_api.change_volume_percent(percent_zone_output_id, 1)
assert roon_api.get_volume_percent(percent_zone_output_id) == 41

roon_api.change_volume_percent(percent_zone_output_id, -2)
assert roon_api.get_volume_percent(percent_zone_output_id) == 39
5 changes: 2 additions & 3 deletions tests/test_with_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,14 @@ def state_callback(event, changed_items):
assert callback_count == 0
assert events == []

roonapi.change_volume(test_output_id, 1, method="relative")
roonapi.change_volume_raw(test_output_id, 1, method="relative")
assert callback_count == 2
assert events == ["zones_changed", "outputs_changed"]

events = []

roonapi.change_volume(test_output_id, -1, method="relative")
roonapi.change_volume_raw(test_output_id, -1, method="relative")
assert callback_count == 4
assert events == ["zones_changed", "outputs_changed"]

roonapi.stop()
token = roonapi.token

0 comments on commit b3a3589

Please sign in to comment.