ソースを参照

Initial upload

Deben Oldert 7 年 前
コミット
015478370d

+ 0 - 0
hometv/api/__init__.py


+ 0 - 0
hometv/engine/__init__.py


+ 0 - 0
hometv/engine/config/__init__.py


+ 111 - 0
hometv/engine/config/flatfilehandler.py

@@ -0,0 +1,111 @@
+import os.path
+
+from engine.log import debug
+
+
+class FlatFileHandler:
+    def __init__(self, filename, directory='config/', base='/etc/hometv/'):
+        self.filename = filename
+        self.directory = directory
+        self.base = base
+
+        self.attributes = {}
+        self.cache = {}
+
+        if os.path.exists(self.base + self.directory + self.filename):
+            content = self.read_file()
+            debug('Loading config')
+            self.parse_config(content)
+
+        else:
+            self.create_file()
+
+    def handle(self, mode='r'):
+        return open(self.base + self.directory + self.filename, mode)
+
+    def is_empty(self):
+        return len(self.attributes) == 0
+
+    def create_file(self):
+        self.handle(mode='x').close()
+
+    def write_to_file(self):
+        file = self.handle('w+')
+
+        for attribute in self.attributes:
+            file.write(str(self.attributes[attribute]) + '\n')
+
+    def read_file(self):
+        file = self.handle()
+        content = None
+        if file.mode == 'r':
+            content = file.readlines()
+        file.close()
+        return content
+
+    def parse_config(self, lines):
+        debug('Parse settings. {} lines'.format(len(lines)))
+        for line in lines:
+            content = line.split('=')
+            debug('{}=>{}'.format(content[0], content[1].strip()))
+            self.cache[content[0]] = content[1]
+
+    def set_attribute(self, name, _type=str, value=None, default=None, raw=None):
+        self.attributes[name] = self.Attribute(name, _type=_type, value=value, default=default, raw=(self.cache[name] if name in self.cache else raw))
+
+    def remove_attribute(self, name):
+        self.attributes.pop(name)
+
+    def get(self, key):
+        debug(key)
+        return self.attributes[key].get()
+
+    class Attribute:
+        def __init__(self, name, _type=str, value=None, default=None, raw=None):
+            self.name = name
+            self.value = value
+            self.default = default
+            self.type = _type
+
+            self.raw = raw
+
+            if raw and not value:
+                self.set_from_raw(self.raw)
+
+        def __str__(self):
+            output = self.get()
+            if self.type is list:
+                output = ','.join(str(x) for x in output)
+            return '{}={}'.format(self.name, output)
+
+        def parse(self):
+            if self.type is str:
+                self.value = str(self.raw)
+            if self.type is bool:
+                self.value = self.raw.upper() in ['TRUE', 'YES', 'Y', 'T', '1']
+            if self.type is int:
+                self.value = int(self.raw)
+            if self.type is float:
+                self.value = float(self.raw)
+            if self.type is list:
+                self.value = self.raw.split(',')
+            if self.type is dict:
+                self.value = {}
+                attributes = self.raw.split(',')
+                for attribute in attributes:
+                    attr = attribute.split('>')
+                    self.value[attr[0]] = attr[1]
+
+        def set_from_raw(self, raw):
+            self.raw = raw.strip()
+            self.parse()
+
+        def set(self, value):
+            if type(value) is self.type:
+                self.value = value
+            else:
+                raise TypeError('{} expected but {} given'.format(self.type.__name__, type(value).__name__))
+
+        def get(self):
+            debug('Get value: {}'.format(self.value))
+            return self.value if self.value else self.default

+ 7 - 0
hometv/engine/config/profile.py

@@ -0,0 +1,7 @@
+from engine.config.flatfilehandler import FlatFileHandler
+
+
+class ProfileConfig(FlatFileHandler):
+    def __init__(self, name):
+        super().__init__(name, 'config/profiles/')
+        self.set_attribute('Button', default='Blue')

+ 18 - 0
hometv/engine/config/settings.py

@@ -0,0 +1,18 @@
+from engine.config.flatfilehandler import FlatFileHandler
+
+
+class SettingsConfig(FlatFileHandler):
+    def __init__(self):
+        super().__init__('settings.cnf')
+
+        self.set_attribute('CEC', default=True, _type=bool)
+        self.set_attribute('DeviceName', default='HomeTV')
+        self.set_attribute('ActiveProfile', default='red')
+        self.set_attribute('Plugins', default=[], _type=list)
+        self.set_attribute('CECTVName', default='TV')
+        self.set_attribute('ControlOtherCECDevices', default=False, _type=bool)
+
+        self.write_to_file()
+
+
+

+ 22 - 0
hometv/engine/log.py

@@ -0,0 +1,22 @@
+def log(level, msg):
+    print('[{}]\t{}'.format(level, msg))
+
+
+def debug(msg):
+    log('DEBUG', msg)
+
+
+def info(msg):
+    log('INFO', msg)
+
+
+def warn(msg):
+    log('WARN', msg)
+
+
+def error(msg):
+    log('ERROR', msg)
+
+
+def fatal(msg):
+    log('FATAL', msg)

+ 40 - 0
hometv/engine/main.py

@@ -0,0 +1,40 @@
+import cec
+
+from engine.config import settings
+from engine import log
+from engine.plugins import importer
+from tv.cec import control, events
+from tv.screen import display as screen
+
+
+PROFILES = ['red', 'green', 'yellow', 'blue']
+SETTINGS = settings.SettingsConfig()
+
+
+def main():
+    log.info('Starting')
+
+    log.debug('Name: {}'.format(SETTINGS.get('DeviceName')))
+
+    log.info('Load plugins')
+    importer.find_plugins()
+
+    importer.load_plugins(SETTINGS.get('Plugins'))
+
+    for plugin in importer.ENABLED_PLUGINS:
+        importer.ENABLED_PLUGINS[plugin].get_class()().start()
+
+    if SETTINGS.get('CEC'):
+        control.TVControl()
+
+        def event_start():
+            log.debug('Callback event called!')
+
+        ev = events.Callback(event_start)
+        events.register_event(callback=ev, event=cec.EVENT_ALL)
+
+    screen.Display()
+
+
+if __name__ == '__main__':
+    main()

+ 0 - 0
hometv/engine/plugins/__init__.py


+ 24 - 0
hometv/engine/plugins/base.py

@@ -0,0 +1,24 @@
+from engine.log import debug
+from engine.plugins.view import View
+
+
+class BasePlugin(View):
+    def __init__(self, name=None):
+        self.name = name
+
+    def start(self):
+        debug('Start plugin')
+
+    class Icon:
+        def __init__(self):
+            pass
+
+    class Data:
+        def __init__(self):
+            pass
+
+    class Timer:
+        def __init__(self):
+            pass
+
+

+ 36 - 0
hometv/engine/plugins/importer.py

@@ -0,0 +1,36 @@
+import importlib
+from importlib import util
+import os
+
+from engine import log
+from engine.plugins.plugin import LoadablePlugin
+
+PLUGIN_DIR = '/etc/hometv/plugins'
+LOADED_PLUGINS = {}
+ENABLED_PLUGINS = {}
+
+
+def find_plugins():
+    for package in os.listdir(PLUGIN_DIR):
+        modules = _import(package, '__init__')
+
+        for module in modules.__all__:
+            _class = _import(package, module)
+            if _class.__name__.endswith('plugin'):
+                LOADED_PLUGINS[package] = _class
+
+
+def load_plugins(plugins):
+    for plugin in plugins:
+        if plugin in LOADED_PLUGINS:
+            ENABLED_PLUGINS[plugin] = LoadablePlugin(LOADED_PLUGINS[plugin])
+
+
+def _import(package, module):
+    spec = util.spec_from_file_location(
+        '{}.{}'.format(package, module),
+        '{}/{}/{}.py'.format(PLUGIN_DIR, package, module))
+    load = util.module_from_spec(spec)
+    spec.loader.exec_module(load)
+    log.debug('Imported: {}.{}'.format(package, module))
+    return load

+ 17 - 0
hometv/engine/plugins/plugin.py

@@ -0,0 +1,17 @@
+from engine.log import debug
+from engine.plugins.base import BasePlugin
+import inspect
+
+
+class LoadablePlugin:
+    def __init__(self, package):
+        self.name = package.__name__
+        self._class = None
+        for name, _class in inspect.getmembers(package):
+            if inspect.isclass(_class) and issubclass(_class, BasePlugin) and _class is not BasePlugin:
+                debug('Loaded {}=>{}'.format(self.name, _class.__name__))
+                self._class = _class
+                continue
+
+    def get_class(self):
+        return self._class

+ 40 - 0
hometv/engine/plugins/view.py

@@ -0,0 +1,40 @@
+class View:
+    def __init__(self, config={}):
+        self.x = config['x'] if 'x' in config else 0
+        self.y = config['y'] if 'y' in config else 0
+        self.width = config['width'] if 'width' in config else 0
+        self.height = config['height'] if 'height' in config else 0
+
+    def dimensions(self):
+        pass
+
+# Major -> Minor -> Menu -> Item
+#
+#  MAJOR
+#  _____________________
+# |                     |
+# |                     |
+# |                     |
+# |                     |
+# |                     |
+# |_____________________|
+#
+#
+# MINOR
+#  _____
+# |     |
+# |     |
+# |_____|
+#
+#
+# MENU
+#  _________________
+# |                 |
+# |_________________|
+#
+#
+# ITEM
+#
+#  ____
+# |____|
+#

+ 0 - 0
hometv/engine/plugins/views/__init__.py


+ 0 - 0
hometv/engine/plugins/views/item.py


+ 0 - 0
hometv/engine/plugins/views/major.py


+ 0 - 0
hometv/engine/plugins/views/menu.py


+ 0 - 0
hometv/engine/plugins/views/minor.py


+ 0 - 0
hometv/tv/__init__.py


+ 0 - 0
hometv/tv/cec/__init__.py


+ 58 - 0
hometv/tv/cec/control.py

@@ -0,0 +1,58 @@
+import cec
+
+from engine import log
+from engine.main import SETTINGS
+
+TV = None
+
+
+class TVControl:
+    def __init__(self):
+        cec.init()
+
+        devices = cec.list_devices()
+        # self.debug()
+        self.tv = None
+
+        for device in devices:
+            if devices[device].osd_string == SETTINGS.get('CECTVName'):
+                self.tv = devices[device]
+
+        if not self.tv:
+            raise RuntimeError('TV not found. Run TVControl.debug() to find device name')
+
+        self.devices = {}
+
+        if SETTINGS.get('ControlOtherCECDevices'):
+            for device in devices:
+                if devices[device] is not self.tv:
+                    self.devices[devices[device].osd_string] = devices[device]
+        global TV
+        TV = self
+
+    def debug(self):
+        log.debug(cec.list_adapters())
+
+        devices = cec.list_devices()
+        log.debug(devices)
+
+        for device in devices:
+            log.debug(devices[device].osd_string)
+            log.debug(devices[device].vendor)
+            log.debug(devices[device].address)
+            log.debug(devices[device].is_active())
+
+    def power_on(self):
+        self.tv.power_on()
+
+    def is_on(self):
+        self.tv.is_on()
+
+    def power_off(self):
+        self.tv.standby()
+
+    def is_active(self):
+        self.tv.is_active()
+
+    def make_active(self):
+        cec.set_active_source()

+ 20 - 0
hometv/tv/cec/events.py

@@ -0,0 +1,20 @@
+import cec
+
+from engine import log
+
+
+def register_event(callback, event):
+    cec.add_callback(callback.callback, event)
+
+
+def remove_event(callback, event):
+    cec.remove_callback(callback, event)
+
+
+class Callback:
+    def __init__(self, method):
+        self.method = method
+
+    def callback(self):
+        log.debug('Callback started')
+        self.method()

+ 0 - 0
hometv/tv/screen/__init__.py


+ 20 - 0
hometv/tv/screen/display.py

@@ -0,0 +1,20 @@
+import tkinter as tk
+
+
+class Display:
+    def __init__(self):
+        self.tk = tk.Tk()
+        self.tk.attributes('-zoomed', True)
+        self.frame = tk.Frame(self.tk)
+        self.frame.pack()
+        self.start_fullscreen()
+
+        self.tk.mainloop()
+
+    def start_fullscreen(self):
+        self.tk.attributes('-fullscreen', True)
+        return 'break'
+
+    def stop_fullscreen(self):
+        self.tk.attributes('-fullscreen', False)
+        return 'break'

+ 0 - 0
hometv/web/__init__.py


+ 1 - 0
plugins/Hello/__init__.py

@@ -0,0 +1 @@
+__all__ = ['plugin', 'mars']

+ 6 - 0
plugins/Hello/mars.py

@@ -0,0 +1,6 @@
+from engine.log import info
+
+
+class HelloMars:
+    def hey(self):
+        info('Hi i\'m Mars')

+ 13 - 0
plugins/Hello/plugin.py

@@ -0,0 +1,13 @@
+from engine import log
+from engine.plugins.base import BasePlugin
+
+
+class HelloWorld(BasePlugin):
+    def __init__(self):
+        super().__init__()
+
+    def start(self):
+        self.hey()
+
+    def hey(self):
+        log.info('hey, i\'m world')

+ 0 - 0
plugins/__init__.py