# HG changeset patch # User Louis Opter # Date 1477081904 25200 # Node ID d250169c1a69219d0c6a1bc5a8a5e93956311a0b # Parent 2da15caf4d44ca2053dca4c9911ece8e6e6a1c7b wip, completely break monolight again diff -r 2da15caf4d44 -r d250169c1a69 add_monolight.patch --- a/add_monolight.patch Tue Oct 18 10:15:19 2016 -0700 +++ b/add_monolight.patch Fri Oct 21 13:31:44 2016 -0700 @@ -84,11 +84,292 @@ + loop.run_until_complete(lightsd.close()) + + loop.close() +diff --git a/apps/monolight/monolight/components/__init__.py b/apps/monolight/monolight/components/__init__.py +new file mode 100644 +--- /dev/null ++++ b/apps/monolight/monolight/components/__init__.py +@@ -0,0 +1,20 @@ ++# Copyright (c) 2016, Louis Opter ++# ++# This file is part of lightsd. ++# ++# lightsd 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. ++# ++# lightsd 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 lightsd. If not, see . ++ ++from .base import MonomeGrid # noqa ++from .layers import Layer # noqa ++from .button import button # noqa +diff --git a/apps/monolight/monolight/components/base.py b/apps/monolight/monolight/components/base.py +new file mode 100644 +--- /dev/null ++++ b/apps/monolight/monolight/components/base.py +@@ -0,0 +1,139 @@ ++# Copyright (c) 2016, Louis Opter ++# ++# This file is part of lightsd. ++# ++# lightsd 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. ++# ++# lightsd 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 lightsd. If not, see . ++ ++from typing import ( ++ NamedTuple, ++) ++ ++from ..osc import ( ++ MONOME_LED_OFF, ++ MonomeLedLevel, ++) ++ ++_Dimensions = NamedTuple("Dimensions", [("height", int), ("width", int)]) ++_Position = NamedTuple("Position", [("x", int), ("y", int)]) ++ ++ ++class Dimensions(_Dimensions): ++ ++ def __repr__(self) -> str: ++ return "height={}, width={}".format(*self) ++ ++ ++class Position(_Position): ++ ++ def __repr__(self) -> str: ++ return "{}, {}".format(*self) ++ ++ ++class MonomeGrid: ++ ++ def __init__(self, size: Dimensions) -> None: ++ from .layers import Layer # noqa; break import loop ++ self.size = size ++ self.layers = [] # z-order, type: List[Layer] ++ ++ ++class LedSprite: ++ ++ def __init__( ++ self, ++ size: Dimensions, ++ level: MonomeLedLevel = MONOME_LED_OFF ++ ) -> None: ++ self.size = size ++ self._levels = [level] * size.width * size.height ++ ++ def _index(self, offset: Position): ++ return self.size.height * offset.y + self.size.width * offset.x ++ ++ def set(self, offset: Position, level: MonomeLedLevel): ++ self._levels[self._index(offset)] = level ++ ++ def get(self, offset: Position): ++ return self._levels[self._index(offset)] ++ ++ def __iter__(self): ++ for off_x in range(self.size.width): ++ for off_y in range(self.size.height): ++ yield off_x, off_y, self.get(Position(x=off_x, y=off_y)) ++ ++ ++class UIComponentInsertionError(Exception): ++ pass ++ ++ ++class UIComponentBase: ++ ++ def __init__(self, name: str, size: Dimensions, offset: Position) -> None: ++ self.name = name ++ self.size = size ++ self.offset = offset ++ self._nw_corner = offset ++ self._se_corner = Position( ++ x=self.offset.x + self.size.width, ++ y=self.offset.y + self.size.height ++ ) ++ self.children = set() ++ ++ def __repr__(self): ++ return "<{}({}, size=({!r}), offset=({!r})>".format( ++ self.__class__.__name__, self.name, self.size, self.offset ++ ) ++ ++ def insert(self, new: "UIComponentBase") -> None: ++ if new in self.children: ++ raise UIComponentInsertionError( ++ "{!r} is already part of {!r}".format(new, self) ++ ) ++ if not new.within(self): ++ raise UIComponentInsertionError( ++ "{!r} doesn't fit into {!r}".format(new, self) ++ ) ++ for child in self.children: ++ if child.collides(new): ++ raise UIComponentInsertionError( ++ "{!r} conflicts with {!r}".format(new, child) ++ ) ++ self.children.add(new) ++ ++ def collides(self, other: "UIComponentBase") -> bool: ++ """Return True if ``self`` and ``other`` overlap in any way.""" ++ ++ return all( ++ self._nw_corner.x <= other._se_corner.x, ++ self._se_corner.x >= other._nw_corner.x, ++ self._nw_corner.y <= other._se_corner.y, ++ self._se_corner.y >= other._nw_corner.y, ++ ) ++ ++ def within(self, other: "UIComponentBase") -> bool: ++ """Return True if ``self`` fits within ``other``.""" ++ ++ return all( ++ other._nw_corner.x >= self._nw_corner.x, ++ other._nw_corner.y >= self._nw_corner.y, ++ other._se_corner.x <= self._se_corner.x, ++ other._se_corner.y <= self._se_corner.y ++ ) ++ ++ def to_sprite(self) -> LedSprite: ++ return LedSprite(self.size) ++ ++ # maybe that bool return type could become an enum or a composite: ++ def submit_input(self, offset: Position) -> bool: ++ return False +diff --git a/apps/monolight/monolight/components/button.py b/apps/monolight/monolight/components/button.py +new file mode 100644 +--- /dev/null ++++ b/apps/monolight/monolight/components/button.py +@@ -0,0 +1,57 @@ ++# Copyright (c) 2016, Louis Opter ++# ++# This file is part of lightsd. ++# ++# lightsd 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. ++# ++# lightsd 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 lightsd. If not, see . ++ ++from .base import ( ++ Dimensions, ++ Position, ++ LedSprite, ++ UIComponentBase, ++) ++ ++from ..osc import ( ++ MONOME_LED_OFF, ++ MONOME_LED_ON, ++) ++ ++ ++class Button(UIComponentBase): ++ ++ OFF = 0 ++ ON = 1 ++ ++ # make the size configurable too? ++ def __init__(self, offset: Position, state: int) -> None: ++ self.offset = offset ++ self.state = Button.ON ++ self.children = None ++ ++ def toggle(self) -> bool: # previous state ++ rv = self.state ++ self.state = not self.state ++ return bool(rv) ++ ++ def to_sprite(self): ++ return LedSprite( ++ Dimensions(1, 1), ++ MONOME_LED_ON if self.state is Button.ON else MONOME_LED_OFF, ++ ) ++ ++ def submit_input(self, offset: Position) -> bool: ++ if self.offset == offset: ++ self.toggle() ++ return True ++ return False +diff --git a/apps/monolight/monolight/components/layers.py b/apps/monolight/monolight/components/layers.py +new file mode 100644 +--- /dev/null ++++ b/apps/monolight/monolight/components/layers.py +@@ -0,0 +1,45 @@ ++# Copyright (c) 2016, Louis Opter ++# ++# This file is part of lightsd. ++# ++# lightsd 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. ++# ++# lightsd 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 lightsd. If not, see . ++ ++import monome ++ ++from .base import ( ++ Dimensions, ++ UIComponentBase, ++) ++from ..osc import MONOME_LED_OFF ++from ..types import TimeMonotonic ++ ++ ++class Layer(UIComponentBase): ++ ++ def __init_(self, size: Dimensions): ++ self.size = size ++ self.led_buffer = monome.LedBuffer( ++ width=size.width, height=size.height ++ ) ++ ++ def _blit(self, component: UIComponentBase): ++ for off_x, off_y, level in component.to_sprite(): ++ self.led_buffer.led_set( ++ component.offset.x + off_x, component.offset.y + off_y, level ++ ) ++ ++ def render(self, frame_ts: TimeMonotonic) -> None: ++ self.led_buffer.led_level_all(MONOME_LED_OFF) ++ for component in self.children: ++ self._blit(component) diff --git a/apps/monolight/monolight/osc.py b/apps/monolight/monolight/osc.py new file mode 100644 --- /dev/null +++ b/apps/monolight/monolight/osc.py -@@ -0,0 +1,45 @@ +@@ -0,0 +1,82 @@ +# Copyright (c) 2016, Louis Opter +# +# This file is part of lightsd. @@ -115,6 +396,43 @@ + MONOME_KEYPRESS_UP, +}) + ++MONOME_LED_OFF = 0 ++MONOME_LED_VERY_LOW_1 = 1 ++MONOME_LED_VERY_LOW_2 = 2 ++MONOME_LED_VERY_LOW_3 = 3 ++MONOME_LED_LOW = MONOME_LED_LOW_1 = 4 ++MONOME_LED_LOW_2 = 5 ++MONOME_LED_LOW_3 = 6 ++MONOME_LED_LOW_4 = 7 ++MONOME_LED_MEDIUM = MONOME_LED_MEDIUM_1 = 8 ++MONOME_LED_MEDIUM_2 = 9 ++MONOME_LED_MEDIUM_3 = 10 ++MONOME_LED_MEDIUM_4 = 11 ++MONOME_LED_HIGH = MONOME_LED_HIGH_1 = 12 ++MONOME_LED_HIGH_2 = 13 ++MONOME_LED_HIGH_3 = 14 ++MONOME_LED_HIGH_4 = MONOME_LED_ON = 15 ++MONOME_VARIBRIGHT_LEVELS = ( ++ MONOME_LED_OFF, ++ MONOME_LED_VERY_LOW_1, ++ MONOME_LED_VERY_LOW_2, ++ MONOME_LED_VERY_LOW_3, ++ MONOME_LED_LOW, ++ MONOME_LED_LOW_2, ++ MONOME_LED_LOW_3, ++ MONOME_LED_LOW_4, ++ MONOME_LED_MEDIUM, ++ MONOME_LED_MEDIUM_2, ++ MONOME_LED_MEDIUM_3, ++ MONOME_LED_MEDIUM_4, ++ MONOME_LED_HIGH, ++ MONOME_LED_HIGH_2, ++ MONOME_LED_HIGH_3, ++ MONOME_LED_HIGH_4, ++) ++ ++MonomeLedLevel = int ++ + +class MonomeApplication(monome.Monome): + @@ -134,11 +452,34 @@ + for app in device: + if isinstance(app, MonomeApplication): + method(app, *args, **kwargs) +diff --git a/apps/monolight/monolight/types.py b/apps/monolight/monolight/types.py +new file mode 100644 +--- /dev/null ++++ b/apps/monolight/monolight/types.py +@@ -0,0 +1,18 @@ ++# Copyright (c) 2016, Louis Opter ++# ++# This file is part of lightsd. ++# ++# lightsd 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. ++# ++# lightsd 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 lightsd. If not, see . ++ ++TimeMonotonic = float diff --git a/apps/monolight/monolight/ui.py b/apps/monolight/monolight/ui.py new file mode 100644 --- /dev/null +++ b/apps/monolight/monolight/ui.py -@@ -0,0 +1,114 @@ +@@ -0,0 +1,124 @@ +# Copyright (c) 2016, Louis Opter +# +# This file is part of lightsd. @@ -157,43 +498,51 @@ +# along with lightsd. If not, see . + +import asyncio -+import collections +import monome +import logging + ++from typing import ( ++ NamedTuple, ++) ++ ++from lightsc import LightsClient +from lightsc.requests import ( + SetLightFromHSBK, + PowerOff, + PowerOn, + PowerToggle, +) -+ +from .osc import ( + MONOME_KEYPRESS_DOWN, + monome_apply, +) ++ +logger = logging.getLogger("monolight.ui") + +_event_queue = None + -+_KeyPress = collections.namedtuple("_KeyPress", ("x", "y", "state")) ++_KeyPress = NamedTuple("_KeyPress", [("x", int), ("y", int), ("state", int)]) + +_STOP_SENTINEL = object() + + -+def draw(serialosc): ++def draw(serialosc: monome.SerialOsc): + buf = monome.LedBuffer(8, 8) + buf.led_set(0, 0, 1) -+ for x in range(0, 5): ++ for x in range(0, 6): + buf.led_set(x, 7, 1) + monome_apply(serialosc, buf.render) + + -+def hide(serialosc): ++def hide(serialosc: monome.SerialOsc): + monome_apply(serialosc, monome.Monome.led_all, 0) + + -+async def start(loop, lightsd, serialosc): ++async def start( ++ loop: asyncio.AbstractEventLoop, ++ lightsd: LightsClient, ++ serialosc: monome.SerialOsc, ++): + global _event_queue + + _event_queue = asyncio.Queue() @@ -243,6 +592,8 @@ + ["candle"], 47.469443, 0.2, 0.15, 3500, 600 + )) + batch.apply(PowerOn(["#br"])) ++ elif keypress.x == 5: ++ pass + + +def stop(): @@ -250,7 +601,7 @@ + _event_queue.put_nowait(_STOP_SENTINEL) + + -+def submit_keypress(x, y, state): ++def submit_keypress(x: int, y: int, state: int): + if _event_queue is not None: + _event_queue.put_nowait(_KeyPress(x, y, state)) diff --git a/apps/monolight/setup.py b/apps/monolight/setup.py @@ -382,7 +733,7 @@ new file mode 100644 --- /dev/null +++ b/clients/python/lightsc/lightsc/__init__.py -@@ -0,0 +1,24 @@ +@@ -0,0 +1,25 @@ +# Copyright (c) 2016, Louis Opter +# +# This file is part of lightsd. @@ -401,6 +752,7 @@ +# along with lightsd. If not, see . + +from .client import ( # noqa ++ LightsClient, + create_lightsd_connection, + create_async_lightsd_connection, +) @@ -411,7 +763,7 @@ new file mode 100644 --- /dev/null +++ b/clients/python/lightsc/lightsc/client.py -@@ -0,0 +1,321 @@ +@@ -0,0 +1,326 @@ +# Copyright (c) 2016, Louis Opter +# +# This file is part of lightsd. @@ -666,6 +1018,11 @@ + return _AsyncJSONRPCBatch(self) + + ++# LightsClient could eventually point to a different but api-compatible class ++# someday: ++LightsClient = AsyncJSONRPCLightsClient ++ ++ +class _AsyncJSONRPCBatch: + + def __init__(self, client: AsyncJSONRPCLightsClient) -> None: diff -r 2da15caf4d44 -r d250169c1a69 add_slides.patch --- a/add_slides.patch Tue Oct 18 10:15:19 2016 -0700 +++ b/add_slides.patch Fri Oct 21 13:31:44 2016 -0700 @@ -1,5 +1,5 @@ # HG changeset patch -# Parent 37f2e84f1c449f77606c68f2a74452dfc883fa5e +# Parent 3539266939bc4808efd5c32db5e1627b2948210f Start to setup some slides for 33C3 Hopefully my talk proposal will be selected! @@ -1692,7 +1692,7 @@ new file mode 100644 --- /dev/null +++ b/slides/33c3/33c3.tex -@@ -0,0 +1,54 @@ +@@ -0,0 +1,86 @@ +\documentclass[xcolor={usenames,svgnames}]{beamer} + +\usepackage[american]{babel} @@ -1713,13 +1713,29 @@ + +\section[design]{Design choices} + ++% TODO: strip les notes lorsque tu es en release via cmake. (voir faire ++% carrément des targets différentes pour être sûr de pas te tromper). ++ ++\pdfnote{% Notes: ++- Monolithic approach to solve the problem,\ ++- Est-ce que l'argument de l'isolation fonctionne ou IoTivity le résoud aussi?\ ++- Je ressens pas le besoin d'investir dans plus de domotique donc que lightsd\ ++soit restreint ne me dérange pas, puis la stabilitité m'arrange.\ ++- Ce qui peut faire la différence avec IoTivity c'est la documentation, en ++bonus un plan pour vraiment extend.\ ++- Interfaces are bad because they encourage inheritance, I believe composition\ ++is a better design pattern. (Though you can't really say the \"mixin\" approach\ ++is composition, it really is inheritance, like interfaces, in the sense that it\ ++delegates all attributes of the composite object).\ ++} ++ +\begin{frame}{Being a daemon} +\pdfnote{% +Pros:\ +\ +- Instant devices ``discovery'';\ +- Conflict resolution;\ -+- Good network location;\ ++- Good network location.\ +\ +Cons:\ +\ @@ -1746,6 +1762,22 @@ +} +\end{frame} + ++\section[related]{Related efforts} ++ ++\begin{frame}{Related efforts} ++\pdfnote{% ++- AllJoyn (churned by IoTivity): https://allseenalliance.org/;\ ++- IoTivity: https://wiki.iotivity.org/architecture;\ ++- IBM Node-RED: http://nodered.org/;\ ++- Homekit: ;\ ++- openHAB: http://www.openhab.org/.\ ++} ++\end{frame} ++ ++\pdfnote{% Side-project ideas: ++- ARP ping implementation for latency measurement + better debug;\ ++} ++ +\end{document} diff --git a/slides/33c3/CMakeLists.txt b/slides/33c3/CMakeLists.txt new file mode 100644