Skip to content

Commit

Permalink
Simplify the access to Output API
Browse files Browse the repository at this point in the history
  • Loading branch information
Sebastian Mangelsen committed Dec 29, 2020
1 parent cc14064 commit 5900ee5
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 32 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ The goals is to provide a bridge that converts received IR commands

## Configuration

- Roon zone name can be configured in Ansible host specific variable
- Roon zone name can be configured as Ansible host specific variable
- service name, user and group can be configured in host specific variables
- mapping between key codes and transport command can be configured
in `config/app_info.json`
Expand Down
2 changes: 1 addition & 1 deletion app/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def zones(self) -> List:
return self._api.zones.keys()

def get_output(self, name: str) -> RoonOutput:
return RoonOutput(self._api, name, True)
return RoonOutput(self._api, name, register_callback=False)

@staticmethod
def _read_as_json(path) -> Dict:
Expand Down
51 changes: 37 additions & 14 deletions app/output.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
"""Class handling the logic and commands provided by an Roon output!"""
import json
import logging
from typing import Dict, List

from .controller import RoonApi
from typing import Dict, List
import logging
import json

logger = logging.getLogger('zone')


class RoonOutputE(BaseException):

"""Basic Class for Output """
def __init__(self, msg):
self.msg = msg


class RoonOutput:
"""Implment logic for Roon Outputs"""

EVENT_FILTER = ['outputs_changed', 'zones_changed']

Expand All @@ -29,12 +31,13 @@ def __init__(self, api: RoonApi, output_name: str, register_callback=False):
self._oid = oid
if register_callback:
logger.debug('enabling callback for zone updates')
self._api.register_state_callback(self.callback,
self._api.register_state_callback(self._callback,
event_filter=self.EVENT_FILTER,
id_filter=None)
logger.debug('instantiated RoonOutput {}'.format(output_name))

def callback(self, event: str, ids_changed: List):
def _callback(self, event: str, ids_changed: List):
"""Callback for debugging purposes."""
logger.debug('==> callback triggered: {} => {}'.format(event, repr(ids_changed)))
if 'zones_changed' in event:
for zid_changed in ids_changed:
Expand All @@ -51,13 +54,23 @@ def callback(self, event: str, ids_changed: List):

@property
def zone_id(self):
return self._api.outputs[self._oid]['zone_id']
if self._oid and self._oid in self._api.outputs.keys():
return self._api.outputs[self._oid]['zone_id']
else:
logger.error('failed to retrieve the ZID for given OID "{}"'.format(self._oid))
return None

def status(self):
"""retrieve the current status of a zone"""
pass
@property
def state(self):
z = self.zone_id
if z and z in self._api.zones.keys():
return self._api.zones[z]['state']
else:
logger.error('failed to retrieve the state for OID {}'.format(self._oid))
return None

def _get_output_id(self, name: str):
"""Try to find the OID based on names."""
logger.debug('finding output "{}"'.format(name))
o = self._api.output_by_name(name)
if not o:
Expand All @@ -69,6 +82,7 @@ def _get_output_id(self, name: str):
return oid

def _get_output(self) -> Dict:
"""Simplify the access to the output dictionary for current OID"""
if self._oid in self._api.outputs.keys():
return self._api.outputs[self._oid]
else:
Expand All @@ -89,6 +103,7 @@ def pause(self):
def stop(self):
"""Stop Player and Clear Playlist"""
self._api.playback_control(self.zone_id, "stop")
self._api.seek(self.zone_id, 0)

def playpause(self):
self._api.playback_control(self.zone_id, "playpause")
Expand All @@ -112,12 +127,20 @@ def volume_down(self, value: int = 5):
self._api.mute(self._oid, False)
self._api.change_volume(self._oid, -1 * value, method="relative_step")

def mute(self):
self._api.mute(self._oid, True)
def mute(self, enabled=False):
self._api.mute(self._oid, enabled)

def play_playlist(self, playlist_name):
def play_playlist(self, playlist_name, volume: int = 20):
self._api.mute(self._oid, False)
self._api.change_volume(self._oid, 20, method="absolute")
self._api.change_volume(self._oid, volume, method="absolute")
self._api.play_playlist(self.zone_id, playlist_title=playlist_name)
self._api.shuffle(self.zone_id, shuffle=False)
self._api.repeat(self.zone_id, repeat=True)

def is_muted(self) -> bool:
"""Return True if output is muted, otherwise False."""
o = self._get_output()
if 'volume' in o.keys():
return o['volume']['is_muted']
else:
return False
2 changes: 1 addition & 1 deletion deployment/inventory.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ all:
vars:
github:
url: https://github.com/smangels/roon-ir-remote.git
branch: release
branch: main
service:
name: roon-remote
user: roon-remote
Expand Down
1 change: 0 additions & 1 deletion deployment/site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
state: stopped
enabled: false


- name: Create a user
user:
name: "{{ service.user }}"
Expand Down
32 changes: 18 additions & 14 deletions roon_remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,41 @@
from app import RoonController, RoonOutput, RemoteConfig, RemoteConfigE, RemoteKeycodeMapping

logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(levelname)s %(module)s.%(funcName)s: %(message)s')
format='%(asctime)s %(levelname)s %(module)s: %(message)s')
logger = logging.getLogger('roon_remote')


def exit_handler(_received_signal, _frame):
"""Handle SIGINT and SIGTERM signals"""
logging.info("Signaling internal jobs to stop...")
logger.info("Signaling internal jobs to stop...")
sys.exit(0)


def get_event_device_for_string(dev_name: str):
"""Scan the Input Device tree for Flirc unit and return the device"""
dev = None
logging.debug('looking for input device "%s"', dev_name)
logger.debug('looking for input device "%s"', dev_name)
devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
if not devices:
logging.error('no device found, invalid permissions?')
logger.error('no device found, invalid permissions?')
return None
for device in devices:
logging.debug("name: %s" % device.name)
logger.debug("name: %s" % device.name)
if dev_name in device.name:
dev = device
logging.debug('found device with name: "%s" on path: %s', device.name, device.path)
logger.debug('found device with name: "%s" on path: %s', device.name, device.path)
break
return dev


def monitor_remote(zone: RoonOutput, dev: InputDevice, mapping: RemoteKeycodeMapping):
"""start an event loop on InputDevice"""
logging.info("Job monitorRemote started")
logger.info("Job monitorRemote started")

if not dev:
raise BaseException('could not open DEV')

logging.debug('opening exclusively InputDevice: %s', dev.path)
logger.debug('opening exclusively InputDevice: %s', dev.path)
for event in dev.read_loop():

if event.value != 1:
Expand All @@ -66,26 +67,29 @@ def monitor_remote(zone: RoonOutput, dev: InputDevice, mapping: RemoteKeycodeMap
elif event.code in mapping.to_key_code('stop'):
zone.stop()
elif event.code in mapping.to_key_code('play_pause'):
zone.playpause()
if zone.state == "playing":
zone.pause()
else:
zone.play()
elif event.code in mapping.to_key_code('vol_up'):
zone.volume_up(5)
elif event.code in mapping.to_key_code('vol_down'):
zone.volume_down(5)
elif event.code in mapping.to_key_code('mute'):
zone.mute()
zone.mute(not zone.is_muted())
elif event.code in mapping.to_key_code('fall_asleep'):
zone.play_playlist('wellenrauschen')

logging.debug("Received Code: %s", repr(event.code))
logger.debug("Received Code: %s", repr(event.code))

except Exception as exception:
logging.error("Caught exception: %s (%s)", exception, type(exception))
logging.info("Job monitorRemote stopped")
logger.info("Job monitorRemote stopped")


def main():
"""main function, initiate InputDevice and runs the forever loop"""
logging.info("starting %s", __file__)
logger.info("starting %s", __file__)
signal.signal(signal.SIGINT, exit_handler)
signal.signal(signal.SIGTERM, exit_handler)

Expand All @@ -98,7 +102,7 @@ def main():
mapping = config.key_mapping
logging.info(mapping.edge)

input_dev_name = "flirc"
input_dev_name = "flirc Keyboard"
event_dev = get_event_device_for_string(input_dev_name)
if not event_dev:
logging.error('Could not find any InputDevice with name: "%s"', input_dev_name)
Expand Down

0 comments on commit 5900ee5

Please sign in to comment.