LIRC libraries
Linux Infrared Remote Control
database.py
1 ''' Read-only configuration database. '''
2 
7 
8 
34 
35 
37 
38 
39 import glob
40 import os
41 import os.path
42 import subprocess
43 import sys
44 
45 try:
46  import yaml
47  try:
48  from yaml import CLoader as Loader, CDumper as Dumper
49  except ImportError:
50  from yaml import Loader, Dumper
51 except ImportError:
52  _YAML_MSG = '''
53 "Cannot import the yaml library. Please install the python3
54 yaml package, on many distributions known as python3-PyYAML. It is also
55 available as a pypi package at https://pypi.python.org/pypi/PyYAML.'''
56  print(_YAML_MSG)
57  sys.exit(1)
58 
59 import config
60 
61 
62 def _here(path):
63  ''' Return path added to current dir for __file__. '''
64  return os.path.join(os.path.dirname(os.path.abspath(__file__)), path)
65 
66 
67 def _load_kerneldrivers(configdir):
68  ''' Parse the kerneldrivers.yaml file, discard unavailable
69  drivers.
70  '''
71 
72  with open(os.path.join(configdir, "kernel-drivers.yaml")) as f:
73  cf = yaml.load(f.read(), Loader = Loader)
74  drivers = cf['drivers'].copy()
75  for driver in cf['drivers']:
76  if driver == 'default':
77  continue
78  with open('/dev/null', 'w') as f:
79  try:
80  subprocess.check_output([config.MODINFO, driver],
81  stderr=f)
82  except subprocess.CalledProcessError:
83  del drivers[driver]
84  return drivers
85 
86 
87 class ItemLookupError(Exception):
88  """A lookup failed, either too namy or no matches found. """
89  pass
90 
91 
92 class Config(object):
93  ''' The configuration selected, and it's sources. '''
94  # pylint: disable=too-many-instance-attributes
95 
96  def __init__(self, cf=None):
97  self.device = None
98  self.driver = {}
99  self.config = {}
100  self.modinit = ""
101  self.modprobe = ""
102  self.lircd_conf = ""
103  self.lircmd_conf = ""
104  self.label = ""
105  if cf:
106  for key, value in cf.items():
107  setattr(self, key, value)
108 
109  @property
110  def note(self):
111  ''' The possible note to display when selected. '''
112  if 'note' in self.config:
113  return self.config['note']
114  else:
115  return None
116 
117 
118 class Database(object):
119  ''' Reflects the *.yaml files in the configs/ directory. '''
120 
121  def __init__(self, path=None, yamlpath=None):
122 
123  devel_path = _here('../../configs')
124  installed_path = os.path.join(config.DATADIR, 'lirc','configs')
125  if path and os.path.exists(path):
126  configdir = path
127  elif path:
128  raise FileNotFoundError(path)
129  elif os.path.exists(devel_path):
130  configdir = devel_path
131  elif os.path.exists(installed_path):
132  configdir = installed_path
133  else:
134  raise FileNotFoundError(devel_path + ':' + installed_path)
135  if not yamlpath:
136  yamlpath = configdir
137  db = {}
138  with open(os.path.join(yamlpath, "confs_by_driver.yaml")) as f:
139  cf = yaml.load(f.read(), Loader = Loader)
140  db['lircd_by_driver'] = cf['lircd_by_driver'].copy()
141  db['lircmd_by_driver'] = cf['lircmd_by_driver'].copy()
142 
143  db['kernel-drivers'] = _load_kerneldrivers(configdir)
144  db['drivers'] = db['kernel-drivers'].copy()
145  with open(os.path.join(yamlpath, "drivers.yaml")) as f:
146  cf = yaml.load(f.read(), Loader = Loader)
147  db['drivers'].update(cf['drivers'].copy())
148  for key, d in db['drivers'].items():
149  d['id'] = key
150  hint = d['device_hint']
151  if not hint:
152  continue
153  hint = hint.strip()
154  if hint.startswith('"') and hint.endswith('"'):
155  hint = hint[1:-1]
156  hint = hint.replace(r'\"', "@$#!")
157  hint = hint.replace('"', '')
158  hint = hint.replace("@$#!", '"')
159  hint = hint.replace("\\\\", "\\")
160  d['device_hint'] = hint
161 
162  configs = {}
163  for path in glob.glob(configdir + '/*.conf'):
164  with open(path) as f:
165  cf = yaml.load(f.read(), Loader = Loader)
166  configs[cf['config']['id']] = cf['config']
167  db['configs'] = configs
168  self.db = db
169 
170  @property
171  def kernel_drivers(self):
172  ''' The kernel-drivers dictionary, drivers.yaml + kernel-drivers.yaml.
173  '''
174  return self.db['kernel-drivers']
175 
176  @property
177  def drivers(self):
178  ''' The drivers dictionary, drivers.yaml + kernel-drivers.yaml. '''
179  return self.db['drivers']
180 
181  @property
182  def configs(self):
183  ''' Return dict of parsed config/*.conf files, keyd by id. '''
184  return self.db['configs']
185 
186  def remotes_by_driver(self, driver):
187  ''' Return the list of remotes suggested for a given driver. '''
188  if isinstance(driver, dict):
189  driver = driver['id']
190  try:
191  return self.db['lircd_by_driver'][driver]
192  except KeyError:
193  return []
194 
195  def lircmd_by_driver(self, driver):
196  ''' Return list of lircmd.conf file for given driver or None. '''
197  if isinstance(driver, dict):
198  driver = driver['id']
199  try:
200  return self.db['lircmd_by_driver'][driver]
201  except KeyError:
202  return []
203 
204  def driver_by_remote(self, remote):
205  ''' Return the driver (possibly None) suggested for a remote. '''
206  for driver, files in self.db['lircd_by_driver'].items():
207  if remote in files:
208  return self.db['drivers'][driver]
209  return None
210 
211  def find_config(self, key, value):
212  ''' Return item (a config) in configs where config[key] == value. '''
213  found = [c for c in self.db['configs'].values()
214  if key in c and c[key] == value]
215  if len(found) > 1:
216  raise ItemLookupError(
217  "find_config: Too many matches for %s, %s): " % (key, value)
218  + ', '.join([c['id'] for c in found]))
219  elif not found:
220  raise ItemLookupError(
221  "find_config: Nothing found for %s, %s): " % (key, value))
222  found = dict(found[0])
223  if 'device_hint' not in found:
224  try:
225  found['device_hint'] = \
226  self.db['drivers'][found['driver']]['device_hint']
227  except KeyError:
228  found['device_hint'] = \
229  self.db['kernel-drivers'][found['driver']]['device_hint']
230  return found
231 
232 
234 
235 # vim: set expandtab ts=4 sw=4: