Browse Source

restructured project and reinitialized sphinx

digital 7 years ago
parent
commit
0e881c723b
12 changed files with 1331 additions and 2 deletions
  1. 2 2
      Makefile
  2. 16 0
      NOTICE
  3. 126 0
      beewatch/__init__.py
  4. 106 0
      beewatch/config_/__init__.py
  5. 270 0
      beewatch/gui/__init__.py
  6. 74 0
      beewatch/pinapi/__init__.py
  7. 174 0
      beewatch/server/__init__.py
  8. 131 0
      sample/gui.py
  9. 184 0
      sample/server.py
  10. 66 0
      sample/test_config.py
  11. 162 0
      sphinx/conf.py
  12. 20 0
      sphinx/index.rst

+ 2 - 2
Makefile

@@ -6,7 +6,7 @@ SPHINXOPTS    =
 SPHINXBUILD   = sphinx-build
 SPHINXPROJ    = BeeWatch
 SOURCEDIR     = .
-BUILDDIR      = ._build
+BUILDDIR      = build
 
 # Put it first so that "make" without argument is like "make help".
 help:
@@ -17,4 +17,4 @@ help:
 # Catch-all target: route all unknown targets to Sphinx using the new
 # "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
 %: Makefile
-	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

+ 16 - 0
NOTICE

@@ -0,0 +1,16 @@
+Copyright 2017 Digital
+
+This file is part of BeeWatch.
+
+BeeWatch is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+BeeWatch is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with BeeWatch.  If not, see <http://www.gnu.org/licenses/>.

+ 126 - 0
beewatch/__init__.py

@@ -0,0 +1,126 @@
+#!/usr/bin/env python3.5
+# Copyright 2017 Digital
+#
+# This file is part of BeeWatch.
+#
+# BeeWatch is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# BeeWatch is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with BeeWatch.  If not, see <http://www.gnu.org/licenses/>.
+## Imports
+# Python imports
+import logging
+import logging.handlers
+import sys
+import time
+import traceback
+# Third party imports
+import blinker
+import curio
+
+import digilib.network
+
+log = logging.getLogger(__name__+"")
+
+_controllers = {}
+_commands = {}
+
+
+class Configure(object):
+    warnings = True
+    """
+    Configure BeeWatch from a dict
+    This class is inspired by logging.config
+    (https://github.com/python/cpython/blob/3.6/Lib/logging/config.py)
+    """
+
+    def __init__(self,config):
+        super(Configure,self).__init__()
+        self.configure(config)
+
+    def configure(self,config):
+        retconf = {
+            "controllers":{},
+            "commands":{},
+        }
+        # Configure the Controllers first
+        for name,properties in config.get("controllers",{}).items():
+            log.debug("configuring {}".format(name))
+            target = properties.pop("target")
+            if not callable(target):
+                target = self.str_to_callable(target)
+            ctrl = target(**properties)
+            if name in _controllers.keys() and self.warnings:
+                log.warn("overwriting controller "+name)
+            _controllers[name] = ctrl
+            retconf["controllers"][name] = ctrl
+
+        # Next configure the commands
+        for name,properties in config.get("commands",{}).items():
+            log.debug("configuring {}".format(name))
+            function = properties.pop("function")
+            ctrl = properties.pop("controller")
+            if not callable(ctrl):
+                if ctrl not in _controllers.keys():
+                    raise ValueError("No controller found with name " + ctrl)
+            ctrl = _controllers[ctrl]
+            if not hasattr(ctrl,function):
+                raise ValueError(
+                    "{} doesn't have attribute {}".format(ctrl,function))
+            function = getattr(ctrl,function)
+            if name in _commands.keys() and self.warnings:
+                log.warn("overwriting command "+name)
+            _commands[name] = function
+            retconf["commands"][name] = function
+
+    def str_to_callable(self,dotted_str):
+        parts = dotted_str.split(".")
+        next_to_import = parts.pop(0)
+        converted = __import__(next_to_import)
+        for p in parts:
+            next_to_import += "." + p
+            if not hasattr(converted,p):
+                converted = __import__(next_to_import)
+            converted = getattr(converted,p)
+        return converted
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#

+ 106 - 0
beewatch/config_/__init__.py

@@ -0,0 +1,106 @@
+#!/usr/bin/env python3.5
+# Copyright 2017 Digital
+#
+# This file is part of BeeWatch.
+#
+# BeeWatch is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# BeeWatch is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with BeeWatch.  If not, see <http://www.gnu.org/licenses/>.
+
+import logging
+import beewatch
+
+log = logging.getLogger(__name__+"")
+
+
+class Configure(object):
+    warnings = True
+    """
+    Configure BeeWatch from a dict
+    This class is inspired by logging.config
+    (https://github.com/python/cpython/blob/3.6/Lib/logging/config.py)
+    """
+
+    def __init__(self,config):
+        super(Configure,self).__init__()
+        self.configure(config)
+
+    def configure(self,config):
+        retconf = {
+            "controllers":{},
+            "commands":{},
+        }
+        # Configure the Controllers first
+        for name,properties in config.get("controllers",{}).items():
+            target = properties.pop("target")
+            if not callable(target):
+                target = self.str_to_callable(target)
+            ctrl = target(**properties)
+            if name in _controllers.keys() and self.warnings:
+                log.warn("overwriting controller "+name)
+            _controllers[name] = ctrl
+            retconf["controllers"][name] = ctrl
+        # Next configure the commands
+        for name,properties in config.get("commands",{}).items():
+            function = properties.pop("function")
+            ctrl = properties.pop("controller")
+            if not callable(ctrl):
+                if ctrl not in _controllers.keys():
+                    raise ValueError("No controller found with name " + ctrl)
+                ctrl = _controllers[ctrl]
+            if not hasattr(ctrl,function):
+                raise ValueError(
+                    "{} doesn't have attribute {}".format(ctrl,function)
+                )
+            function = getattr(ctrl,function)
+            if name in _commands.keys() and self.warnings:
+                log.warn("overwriting command "+name)
+            _commands[name] = function
+            retconf["commands"][name] = function
+
+    def str_to_callable(self,dotted_str):
+        parts = dotted_str.split(".")
+        next_to_import = parts.pop(0)
+        converted = __import__(next_to_import)
+        # converted = None
+        # next_to_import = parts.pop(0)
+        for p in parts:
+            next_to_import += "." + p
+            if not hasattr(converted,p):
+                converted = __import__(next_to_import)
+            converted = getattr(converted,p)
+        return converted
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#

+ 270 - 0
beewatch/gui/__init__.py

@@ -0,0 +1,270 @@
+#!/usr/bin/env python3.5
+# Copyright 2017 Digital
+#
+# This file is part of BeeWatch.
+#
+# BeeWatch is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# BeeWatch is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with BeeWatch.  If not, see <http://www.gnu.org/licenses/>.
+
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+from gi.repository import Gdk
+import logging
+import logging.config
+import os
+import pprint
+import random
+import string
+import sys
+import time
+import yaml
+# My libraries
+import beewatch
+import beewatch.gui
+import digilib.misc
+import digilib.gui
+import digilib.network
+## Logging
+lgui = logging.getLogger(__name__+".gui")
+
+class ChildConsole(digilib.gui.ChildConsole):
+    def __init__(self,get_client=None):
+        super(ChildConsole,self).__init__(get_client)
+        self.text_buffer_log.set_text("program:HAIIII")
+        self.add_msg(digilib.misc.LOREM_IPSUM,"code")
+        self.add_msg(digilib.misc.LOREM_IPSUM,"code")
+        self.add_msg(digilib.misc.LOREM_IPSUM,"code")
+
+
+class ChildOverview(digilib.gui.ChildOverview):
+    def __init__(self):
+        super(ChildOverview,self).__init__()
+        self.prepare()
+    def prepare(self):
+        self.infobox1 = digilib.gui.InfoFrame()
+        self.infobox2 = digilib.gui.InfoFrame()
+        self.make_infobox_controller()
+        self.make_infobox_weight()
+        self.make_infobox_test(self.infobox1)
+        self.make_infobox_test(self.infobox2)
+        self.layout.attach(self.infobox_controller,0,0,1,1)
+        self.layout.attach(self.infobox_weight,    1,0,1,1)
+        self.layout.attach(self.infobox1,          0,1,1,1)
+        self.layout.attach(self.infobox2,          1,1,1,1)
+    def make_infobox_controller(self):
+        levelbar_cpu = Gtk.LevelBar()
+        levelbar_cpu.set_hexpand(True)
+        levelbar_mem = Gtk.LevelBar()
+        levelbar_mem.set_hexpand(True)
+        self.infobox_controller = digilib.gui.InfoFrame(label="Weight")
+        self.infobox_controller.add_line("uptime","Uptime:","99:59:592017-03-06 12:56")
+        self.infobox_controller.add_line("ip","IP:","30.47.10.O9",None)
+        self.infobox_controller.add_line("cpu","CPU:",30,"%",
+            levelbar_cpu,True)
+        self.infobox_controller.add_line("mem","Memory:",0.40,"%",
+            levelbar_mem,True)
+    def make_infobox_weight(self):
+        self.infobox_weight = digilib.gui.InfoFrame(label="Weight")
+        self.infobox_weight.add_line("total","Total:","50 kg")
+        self.infobox_weight.add_line("bees","Bees:","20 kg")
+        self.infobox_weight.add_line("honey","Honey:","42 kg")
+        self.infobox_weight.add_line("time","Time:","2017-03-06 12:56")
+    def make_infobox_test(self,infobox):
+        infobox.add_line("1","Label:","Value")
+        infobox.add_line("2","Baum:","Haus")
+        infobox.add_line("3","Weight:","40 kg")
+        infobox.add_line("4","Wiff","Woff")
+
+class ChildControl(digilib.gui.ChildControl):
+    def __init__(self):
+        super(ChildControl, self).__init__()
+        self.prepare()
+    def prepare(self):
+        pass
+
+class ChildSettings(digilib.gui.ChildSettings):
+    def __init__(self):
+        super(ChildSettings, self).__init__()
+        self.prepare()
+    def prepare(self):
+        pass
+
+class BeeWindow(digilib.gui.WindowBase):
+    def __init__(self,host,port,af_family):
+        super(BeeWindow,self).__init__(title="BeeWatch")
+        self.host = host
+        self.port = port
+        self.af_family = af_family
+        self.client = None
+        self.set_border_width(10)
+        self.tb = digilib.misc.Container()
+        self.prepare()
+        self.add_key_shortcut("Control_L q",self.quit)
+        self.add_key_shortcut("Control_L h e",lgui.info,["hello world"])
+        self.add_key_shortcut("Control_L h i",lgui.info,["hi world"])
+        # self.client = self.make_client()
+    def prepare(self):
+        self.child_console = ChildConsole(self.get_client)
+        self.child_overview = ChildOverview()
+        self.child_control = ChildControl()
+        self.child_settings = ChildSettings()
+        self.child_test = self.make_child_test()
+        self.child_label = self.make_child_label()
+        self.make_toolbar()
+        self.stack = Gtk.Stack()
+        self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
+        self.stack.set_transition_duration(750)
+        self.stack.add_titled(self.child_console, "child_console", "Console")
+        self.stack.add_titled(self.child_overview, "child_overview", "Overview")
+        self.stack.add_titled(self.child_control, "child_control", "Control")
+        self.stack.add_titled(self.child_settings, "child_settings", "Settings")
+        self.stack.add_titled(self.child_test, "check", "Check Button")
+        self.stack.add_titled(self.child_label, "label", "A label")
+        self.stack_switcher = Gtk.StackSwitcher()
+        self.stack_switcher.set_stack(self.stack)
+        self.scrolled_switcher = Gtk.ScrolledWindow()
+        self.scrolled_switcher.add(self.stack_switcher)
+        self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
+        self.vbox.add(self.tb.toolbar)
+        self.vbox.add(self.scrolled_switcher)
+        self.vbox.add(self.stack)
+        self.add(self.vbox)
+        # self.vbox.pack_start(self.stack_switcher, True, True, 0)
+        # self.vbox.pack_start(self.stack, True, True, 0)
+    def make_toolbar(self):
+        self.tb.toolbar = Gtk.Toolbar()
+        self.tb.img_connect = Gtk.Image(stock=Gtk.STOCK_CONNECT)
+        self.tb.img_connect.set_tooltip_text("Connect")
+        self.tb.bconnect = Gtk.ToolButton()
+        self.tb.bconnect.set_icon_widget(self.tb.img_connect)
+        self.tb.bconnect.connect("clicked",self.on_connect_request)
+        self.tb.img_disconnect = Gtk.Image(stock=Gtk.STOCK_DISCONNECT)
+        self.tb.img_disconnect.set_tooltip_text("Disconnect")
+        self.tb.bdisconnect = Gtk.ToolButton()
+        self.tb.bdisconnect.set_icon_widget(self.tb.img_disconnect)
+        self.tb.bdisconnect.connect("clicked",self.on_disconnect_request)
+        self.tb.img_auto_scroll = Gtk.Image(stock=Gtk.STOCK_GOTO_BOTTOM)
+        self.tb.img_auto_scroll.set_tooltip_text("Toggle auto scrolling")
+        self.tb.bauto_scroll = Gtk.ToolButton()
+        self.tb.bauto_scroll.set_icon_widget(self.tb.img_auto_scroll)
+        self.tb.bauto_scroll.connect(
+            "clicked",self.child_console.toggle_scroll)
+        self.tb.img_scroll_down = Gtk.Image(stock=Gtk.STOCK_GO_DOWN)
+        self.tb.img_scroll_down.set_tooltip_text("Toggle auto scrolling")
+        self.tb.bscroll_down = Gtk.ToolButton()
+        self.tb.bscroll_down.set_icon_widget(self.tb.img_scroll_down)
+        self.tb.bscroll_down.connect(
+            "clicked",self.child_console.scroll_down)
+        self.tb.iconnected = Gtk.ToolItem()
+        self.tb.img_connected = Gtk.Image(stock=Gtk.STOCK_YES)
+        self.tb.iconnected.set_visible_horizontal(False)
+        self.tb.iconnected.set_visible_vertical(False)
+        self.tb.img_connected.set_tooltip_text("Connection Status: Connected")
+        self.tb.idisconnected = Gtk.ToolItem()
+        self.tb.img_disconnected = Gtk.Image(stock=Gtk.STOCK_NO)
+        self.tb.img_disconnected.set_tooltip_text(
+            "Connection Status: Disconnected")
+        self.tb.iconnected.add(self.tb.img_connected)
+        self.tb.idisconnected.add(self.tb.img_disconnected)
+        self.tb.toolbar.add(self.tb.iconnected)
+        self.tb.toolbar.add(self.tb.idisconnected)
+        self.tb.toolbar.add(self.tb.bconnect)
+        self.tb.toolbar.add(self.tb.bdisconnect)
+        self.tb.toolbar.add(self.tb.bauto_scroll)
+        self.tb.toolbar.add(self.tb.bscroll_down)
+    def make_child_test(self):
+        box_test = Gtk.FlowBox()
+        for i in range(0,10):
+            checkbutton = Gtk.CheckButton("Click me!")
+            box_test.add(checkbutton)
+        return box_test
+    def make_child_label(self):
+        grid = Gtk.Grid()
+        label = Gtk.Label()
+        label.set_markup("<big>A fancy label</big>")
+        grid.add(label)
+        return grid
+    def make_client(self):
+        client = digilib.network.Client(
+            host=self.host,
+            port=self.port,
+            af_family=self.af_family,
+            handle_data_func=self.child_console.add_msg_threadsafe,
+        )
+        return client
+    def get_client(self):
+        return self.client
+    def quit(self,*args):
+        lgui.debug("quit")
+        if self.client:
+            self.client.stop()
+            # if self.client.is_connected:
+        Gtk.main_quit()
+    def start(self):
+        self.css_provider = Gtk.CssProvider()
+        self.css_provider.load_from_path("config/style.css")
+        screen = Gdk.Screen.get_default()
+        self.style_context = Gtk.StyleContext()
+        self.style_context.add_provider_for_screen(
+            screen,
+            self.css_provider,
+            Gtk.STYLE_PROVIDER_PRIORITY_USER,
+        )
+        self.show_all()
+        self.stack.set_visible_child(self.child_console)
+        Gdk.threads_init()
+        Gtk.main()
+    def on_connect_request(self,widget):
+        try:
+            lgui.debug("connect request")
+            if self.client == None:
+                self.client = self.make_client()
+            if not self.client.is_running():
+                self.client.start()
+            if not self.client.is_connected:
+                if self.client.connect():
+                    self.tb.iconnected.set_visible_horizontal(True)
+                    self.tb.iconnected.set_visible_vertical(True)
+                    self.tb.idisconnected.set_visible_horizontal(False)
+                    self.tb.idisconnected.set_visible_vertical(False)
+                    lgui.debug("client started")
+                else:
+                    lgui.debug(
+                        "client failed to connect, maybe it is connected")
+        except Exception as e:
+            lgui.error("error trying to connect",exc_info=e)
+    def on_disconnect_request(self,widget):
+        try:
+            lgui.debug("disconnect request")
+            if self.client != None:
+                if self.client.is_connected:
+                    self.client.disconnect()
+                    self.tb.iconnected.set_visible_horizontal(False)
+                    self.tb.iconnected.set_visible_vertical(False)
+                    self.tb.idisconnected.set_visible_horizontal(True)
+                    self.tb.idisconnected.set_visible_vertical(True)
+                    lgui.debug("client disconnected")
+                    return
+            lgui.debug(
+                "client failed to disconnect, maybe it wasn't connected")
+        except Exception as e:
+            lgui.error("error trying to disconnect",exc_info=e)
+    def on_scroll_down_request(self,widget):
+        self.child_console.scroll_down()
+    def on_toggle_scroll_request(self,widget):
+        self.child_console.scroll = not self.child_console.scroll
+    def on_exit_button_press(self,*args):
+        self.destroy()
+        Gtk.main_quit()
+        return True

+ 74 - 0
beewatch/pinapi/__init__.py

@@ -0,0 +1,74 @@
+#!/usr/bin/env python3.5
+# Copyright 2017 Digital
+#
+# This file is part of BeeWatch.
+#
+# BeeWatch is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# BeeWatch is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with BeeWatch.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Python Libraries
+import curio
+import logging
+import threading
+import time
+import traceback
+# My Libraries
+import digilib.pin
+log = logging.getLogger(__name__+"")
+lpin = logging.getLogger(__name__+".pin")
+# log = logging.getLogger(__name__+"")
+
+print("*"*20)
+print("USE CURIO WHEN CODING THE PIN STUFF")
+print("this message is printed by {}".format(__file__))
+print("*"*20)
+
+class LoadCell(digilib.pin.PinControllerBase):
+    """interface for a single load cell"""
+    pass
+
+class BeeWeighingScale(digilib.pin.PinAPIBase):
+    """handles all load cells"""
+    pass
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#

+ 174 - 0
beewatch/server/__init__.py

@@ -0,0 +1,174 @@
+#! /usr/bin/env python3.5
+# Copyright 2017 Digital
+#
+# This file is part of BeeWatch.
+#
+# BeeWatch is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# BeeWatch is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with BeeWatch.  If not, see <http://www.gnu.org/licenses/>.
+
+import curio
+import blinker
+import logging
+import logging.handlers
+import digilib.network
+import os
+import queue
+import select
+import socket
+import sys
+import threading
+import time
+import traceback
+
+import digilib.pin
+import digilib.network
+import beewatch
+# import beewatch.server
+import beewatch.pinapi
+
+log = logging.getLogger(__name__+"")
+# lapi = logging.getLogger(__name__+".api")
+# lschat = logging.getLogger(__name__+".schat")
+lch = logging.getLogger(__name__+".chandler")
+lserver = logging.getLogger(__name__+".server")
+
+
+class ConnHandlerBeeWatch(digilib.network.ConnHandler):
+    """
+    ConnHandlerBeeWatch is the connection handler for the BeeWatch server.
+
+    It parses commands to api calls.
+
+    Todo
+    ----
+        Implement a permission system
+    """
+    def __init__(self, socket, addr, server):
+        super(ConnHandlerBeeWatch, self).__init__(socket, addr, server)
+
+    async def handle(self, data):
+        """
+        The handle method parses commands and responds if a command was not
+        found. It executes synchronous and asynchronous methods correctly and
+        logs the traceback if an exception occured.
+        """
+        data = data.strip()
+        data = data.split(" ")
+        cmd,*args = data
+        func = beewatch._commands.get(cmd,False)
+        if not func:
+            await self.respond("Unknown command")
+            return
+        kwargs = {"args":args,"command":cmd,"respond":self.respond}
+        task = None
+        try:
+            coro = func(**kwargs)
+            if hasattr(coro,"__await__"):
+                task = await coro
+        except Exception as e:
+            lch.error("api_func raised an error:",exc_info=e)
+            tb = traceback.format_exc()
+            await self.respond(tb,log_msg="traceback of '{}'"
+                .format(e.__cause__))
+        finally:
+            pass
+        if task:
+            lch.debug("exec: "+task.exception.__cause__)
+        # task joins iself to suppress the "task not joined" warning
+        cur_task = await curio.current_task()
+        await curio.ignore_after(0,cur_task.wait)
+
+    async def respond(self,text,*args,log_msg=False):
+        """
+        this method is passed to apis so they can give feedback to the user
+        """
+        await self.send(text,log_msg)
+
+
+class BeeWatchServer(digilib.network.Server):
+    """
+    BeeWatchServer opens a secured connection to the outside world (or a file socket).
+
+    BeeWatchServer inherits from digilib.network.Server. It connects to the gpio control socket and adds protetction by requireing user authentification.
+
+    Warning
+    -------
+        as these features aren't implemented yet, the connection is *not* secure at all
+    Todo
+    ----
+        actually implement these features, right now BeeWatchServer does only debug stuff
+
+    Parameters are identical to `digilib.network.Server` if not specified in the following list:
+
+    Parameters
+    ----------
+        *args:
+            passed to :obj:`digilib.network.Server`
+        handler_class:
+            Same as in `digilib.network.Server` but defaults to :obj:`ConnHandlerBeeWatch`.
+        **kwargs:
+            passed to :obj:`digilib.network.Server`
+    """
+    def __init__(
+            self,
+            *args,
+            handler_class=ConnHandlerBeeWatch,
+            **kwargs
+        ):
+        super(BeeWatchServer, self).__init__(
+            *args,
+            handler_class=handler_class,
+            **kwargs,
+        )
+
+    def make_socket(self) -> socket.socket :
+        s = super(BeeWatchServer,self).make_socket()
+        # only for debugging because I'm impatient and don't want to wait
+        lch.warning("setting sokopt SO_REUSEADDR to 1. DEBUGGING ONLY")
+        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        return s
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#

+ 131 - 0
sample/gui.py

@@ -0,0 +1,131 @@
+#!/usr/bin/env python3.5
+# Copyright 2017 Digital
+#
+# This file is part of BeeWatch.
+#
+# BeeWatch is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# BeeWatch is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with BeeWatch.  If not, see <http://www.gnu.org/licenses/>.
+#
+## Imports
+# Python Libraries
+import argparse
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+from gi.repository import Gdk
+import logging
+import random
+import string
+import sys
+import time
+import traceback
+import yaml
+# My Libraries
+import beewatch
+import beewatch.gui
+import digilib.gui
+import digilib.misc
+
+lgui = logging.getLogger(__name__+".gui")
+
+
+parser = argparse.ArgumentParser(
+    # usage="use this to play",
+    description="BeeWatch Client",
+)
+
+parser.add_argument(
+    "host",
+    type=str,
+    help="hostname/ip address/filename (only if --use-file was specified)"
+)
+parser.add_argument(
+    "port",
+    type=int,
+    default=8054,
+    nargs="?",
+    help="the port on wich the client will connect to. ignored if --use-file \
+        is given"
+)
+parser.add_argument(
+    "-d","--debug",
+    default=False,
+    action="store_true",
+    help="enables logging"
+)
+group_af_family = parser.add_mutually_exclusive_group(required=True)
+group_af_family.add_argument(
+    "-u","--af-unix",
+    dest="af_family",
+    # default=False,
+    action="store_const",
+    const="AF_UNIX",
+    help="""use an AF_UNIX socket (a file). can't be used in combination with
+        --af-inet."""
+    # " will be used \
+    #     instead of an AF_INET socket (hostname/ip address + port)."
+)
+group_af_family.add_argument(
+    "-i","--af_inet",
+    default="AF_INET",
+    dest="af_family",
+    action="store_const",
+    const="AF_INET",
+    help="""use an AF_INET socket (hostname/ip address + port). can't be used
+        with --af-unix"""
+)
+args = parser.parse_args()
+if args.debug:
+    digilib.misc.load_files("config/logging.yaml",logging.config.dictConfig)
+
+win = beewatch.gui.BeeWindow(
+    host=args.host,
+    port=args.port,
+    af_family=args.af_family
+)
+win.start()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#

+ 184 - 0
sample/server.py

@@ -0,0 +1,184 @@
+#!/usr/bin/env python3.5
+# Copyright 2017 Digital
+#
+# This file is part of BeeWatch.
+#
+# BeeWatch is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# BeeWatch is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with BeeWatch.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import argparse
+import gi
+gi.require_version('Notify', '0.7')
+from gi.repository import Notify
+import logging
+import logging.config
+import logging.handlers
+import sys
+# import mylibnetwork
+import threading
+import socket
+import traceback
+import time
+import yaml
+
+import blinker
+import curio
+
+import beewatch
+# import beewatch
+import beewatch.server
+# import beewatch.server as beeserver
+import digilib.misc
+
+
+Notify.init("Bee Watch Server")
+
+log = logging.getLogger(__name__+"")
+
+parser = argparse.ArgumentParser(
+    # usage="use this to play",
+    description="BeeWatch Server",
+)
+
+parser.add_argument(
+    "host",
+    type=str,
+    help="hostname/ip address/filename (only if --use-file was specified)"
+)
+parser.add_argument(
+    "port",
+    type=int,
+    default=8054,
+    nargs="?",
+    help="the port on wich the connection will open up. ignored if --use-file \
+        is given"
+)
+parser.add_argument(
+    "-d","--debug",
+    default=False,
+    action="store_true",
+    help="enables logging"
+)
+parser.add_argument(
+    "--log-ip",
+    dest="log_ip",
+    default=False,
+    action="store_false",
+    help="WARNING: THIS FLAG ENABLES IP LOGGING!"
+)
+group_af_family = parser.add_mutually_exclusive_group()
+group_af_family.add_argument(
+    "-u","--af-unix",
+    dest="af_family",
+    # default=False,
+    action="store_const",
+    const="AF_UNIX",
+    help="""use an AF_UNIX socket (a file). can't be used in combination with
+        --af-inet."""
+    # " will be used \
+    #     instead of an AF_INET socket (hostname/ip address + port)."
+)
+group_af_family.add_argument(
+    "-i","--af_inet",
+    default="AF_INET",
+    dest="af_family",
+    action="store_const",
+    const="AF_INET",
+    help="""use an AF_INET socket (hostname/ip address + port). can't be used
+        with --af-unix"""
+        # (hostname/ip address + port) will be used instead of an AF_INET \
+        # socket (a file)."
+)
+
+args = parser.parse_args()
+
+if args.debug:
+    digilib.misc.load_files("config/logging.yaml",logging.config.dictConfig)
+
+if args.af_family == "AF_UNIX":
+    port = None
+elif args.af_family == "AF_INET":
+    port = args.port
+
+config_files = [
+    "config/controllers.yaml",
+    "config/commands.yaml",
+]
+
+digilib.misc.load_files(config_files,beewatch.Configure)
+
+bee_server = beewatch.server.BeeWatchServer(
+    host=args.host,
+    port=args.port,
+    af_family=args.af_family,
+    log_ip=args.log_ip,
+)
+
+sig_exit = blinker.signal("global-exit")
+
+try:
+    bee_server.start()
+    print(bee_server.run)
+except Exception as e:
+    log.warn("an error occured, aborting")
+    log.error(e, exc_info=True)
+except KeyboardInterrupt:
+    print("\r    ", end="\r")
+    logging.warn("KeyboardInterrupt! aborting!")
+finally:
+    bee_server.shutdown()
+    sig_exit.send()
+    log.info("have a good time :)")
+    pass
+
+
+sys.exit()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#

+ 66 - 0
sample/test_config.py

@@ -0,0 +1,66 @@
+#!/usr/bin/env python3.5
+# Copyright 2017 Digital
+#
+# This file is part of BeeWatch.
+#
+# BeeWatch is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# BeeWatch is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with BeeWatch.  If not, see <http://www.gnu.org/licenses/>.
+
+from pprint import pprint
+import yaml
+import beewatch
+import digilib.misc
+
+config_files = [
+    "config/controllers.yaml",
+    "config/commands.yaml",
+]
+
+digilib.misc.load_files(config_files,beewatch.Configure)
+
+pprint(beewatch._controllers)
+pprint(beewatch._commands)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#

+ 162 - 0
sphinx/conf.py

@@ -0,0 +1,162 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# BeeWatch documentation build configuration file, created by
+# sphinx-quickstart on Fri Mar 24 15:11:40 2017.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = ['sphinx.ext.autodoc',
+    'sphinx.ext.intersphinx',
+    'sphinx.ext.todo']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['sphinx/templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'BeeWatch'
+copyright = '2017, Digital'
+author = 'Digital'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = 'unreleased'
+# The full version, including alpha/beta/rc tags.
+release = 'unreleased'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = []
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = True
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'alabaster'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['sphinx/static']
+
+
+# -- Options for HTMLHelp output ------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'BeeWatchdoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+    # The paper size ('letterpaper' or 'a4paper').
+    #
+    # 'papersize': 'letterpaper',
+
+    # The font size ('10pt', '11pt' or '12pt').
+    #
+    # 'pointsize': '10pt',
+
+    # Additional stuff for the LaTeX preamble.
+    #
+    # 'preamble': '',
+
+    # Latex figure (float) alignment
+    #
+    # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    (master_doc, 'BeeWatch.tex', 'BeeWatch Documentation',
+     'Digital', 'manual'),
+]
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    (master_doc, 'beewatch', 'BeeWatch Documentation',
+     [author], 1)
+]
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+    (master_doc, 'BeeWatch', 'BeeWatch Documentation',
+     author, 'BeeWatch', 'One line description of project.',
+     'Miscellaneous'),
+]
+
+
+
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {'https://docs.python.org/': None}

+ 20 - 0
sphinx/index.rst

@@ -0,0 +1,20 @@
+.. BeeWatch documentation master file, created by
+   sphinx-quickstart on Fri Mar 24 15:11:40 2017.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Welcome to BeeWatch's documentation!
+====================================
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`