about summary refs log tree commit diff
path: root/fun/idual
diff options
context:
space:
mode:
Diffstat (limited to 'fun/idual')
-rw-r--r--fun/idual/README.md34
-rw-r--r--fun/idual/default.nix25
-rw-r--r--fun/idual/idual/__init__.py65
-rw-r--r--fun/idual/idualctl39
-rw-r--r--fun/idual/setup.py15
5 files changed, 178 insertions, 0 deletions
diff --git a/fun/idual/README.md b/fun/idual/README.md
new file mode 100644
index 000000000000..922047617f25
--- /dev/null
+++ b/fun/idual/README.md
@@ -0,0 +1,34 @@
+# iDual light control
+
+This folder contains some tooling for controlling iDual LED lights
+(which use infrared controls) using a "Broadlink RM Pro" infrared
+controller.
+
+The supported colour codes of the iDual remote are stored in
+`codes.txt`.
+
+The point of this is to make it possible for me to automate my lights
+in the morning, so that I can actually get out of bed.
+
+## Capturing codes
+
+Capturing codes is relatively easy, assuming that the broadlink device
+is set up:
+
+```python
+import broadlink
+import base64
+
+devices = broadlink.discover(timeout=5)
+devices[0].auth()
+```
+
+For each code, the procedure is as follows:
+
+```python
+devices[0].find_rf_packet()
+# wait until this returns True
+
+devices[0].check_data()
+# this will return the code
+```
diff --git a/fun/idual/default.nix b/fun/idual/default.nix
new file mode 100644
index 000000000000..1acf287bfbd7
--- /dev/null
+++ b/fun/idual/default.nix
@@ -0,0 +1,25 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  inherit (pkgs) python3 python3Packages;
+
+  opts = {
+    pname = "idualctl";
+    version = "0.1";
+    src = ./.;
+
+    propagatedBuildInputs = [
+      depot.third_party.python.broadlink
+    ];
+  };
+  package = python3Packages.buildPythonPackage opts;
+  script = python3Packages.buildPythonApplication opts;
+in
+depot.nix.readTree.drvTargets {
+  inherit script;
+  python = python3.withPackages (_: [ package ]);
+  setAlarm = pkgs.writeShellScriptBin "set-alarm" ''
+    echo "setting an alarm for ''${1}"
+    ${pkgs.systemd}/bin/systemd-run --user --on-calendar="''${1} Europe/London" --unit=light-alarm.service
+  '';
+}
diff --git a/fun/idual/idual/__init__.py b/fun/idual/idual/__init__.py
new file mode 100644
index 000000000000..35c3bbae9039
--- /dev/null
+++ b/fun/idual/idual/__init__.py
@@ -0,0 +1,65 @@
+import base64
+import broadlink
+import time
+import sys
+
+commands = {
+    # system commands
+    'on'       : 'JgBIAAABK5AVERQ2FBEUERQSFBEUERQSFBEUNhQ2FDUUNhQ2FDYUNRU1FBIUERQRFBIUERQRFBIUERQ2FDYUNRQ2FDYUNhQ1FQANBQ==',
+    'off'      : 'JgBIAAABLJAUERQ2FBEUEhQRFBEUEhQRFBEUNhQ2FDUVNRQ2FDYUNhQRFDYUERQSFBEUERQSFBEUNhQRFDYUNhQ2FDUUNhQ2FAANBQ==',
+    'darker'   : 'JgBIAAABLI8VERQ2FBEUERURFBEUEhQRFBEUNhQ2FDYUNRU1FDYUNhQRFBIUERQRFDYUEhQRFBEVNRQ2FDYUNhQRFDYUNhQ1FQANBQ==',
+    'brighter' : 'JgBIAAABLI8VERQ2FBEUERQSFBEUEhQRFBEUNhQ2FDUVNRQ2FDYUNRU1FBIUERQ2FBEUEhQRFBEUEhQ1FTUUEhQ1FTUUNhQ2FAANBQ==',
+
+    # presets
+    'candle'   : 'JgBQAAABKZISExI4EhMSFBITEhQRFBITEhQROBI4EjgSOBE4EjgSOBI4ERQSExIUEjgRFBITEhQSExI4EjgROBIUEjcSOBI4EgAFJgABKUkSAA0FAAAAAAAAAAA=',
+    'bulb'     : 'JgBYAAABK5AUERQ2FBEVERQRFBEVERQRFBEVNRQ2FDYUNRU1FDYUNhQRFDYUNRURFBEUEhQRFBEUNhQRFREUNhQ1FDYUNhQ2FAAFIwABKkgVAAxOAAErRxUADQU=',
+    'sun'      : 'JgBQAAABLI8VERQ2FBEUERURFBEUEhQRFBEUNhQ2FDYUNRU1FTUUNhQSFDUUEhQ2FBEUERURFBEUNhQSFDUUEhQ2FDUVNRQ2FAAFJQABK0cVAA0FAAAAAAAAAAA=',
+    'cold'     : 'JgBQAAABK5AUERQ2FBEUEhQRFBEUEhQRFBEUNhQ2FDYUNRQ2FDYUNhQ1FTUUEhQRFBEUEhQRFBIUERQRFDYUNhQ2FDYUNRQ2FAAFJAABK0cVAA0FAAAAAAAAAAA=',
+    'eve_dark' : 'JgBQAAABK5AUERQ2FBEUEhQRFBEUEhQRFBEUNhQ2FDYUNRU1FDYUNhQRFDYUERQSFDUUEhQRFBIUNRQSFDUUNhQSFDUUNhQ2FAAFIwABLEYVAA0FAAAAAAAAAAA=',
+    'eve_fade' : 'JgBIAAABK5AUERQ2FBEUEhQRFBEUEhQRFBEUNhQ2FDYUNRQ2FDYUNhQ1FDYUNhQRFDYUERQSFBEUEhQRFBEUNhQSEzYUNhQ2FAANBQ==',
+    'reading'  : 'JgBQAAABK5AUERQ2FBEUEhQRFBEUEhQRFBEUNhQ2FDUVNRQ2FDYUNhQ1FDYUEhQ1FBIUERQRFBIUERQSFDUUEhQ1FTUUNhQ2FAAFJAABK0YVAA0FAAAAAAAAAAA=',
+    'yoga'     : 'JgBYAAABLI8VERQ1FREUERQSFBEUERURFBEUNhQ1FTUUNhQ2FDYUNRURFBEUNhQRFBIUERMSExMTNxM2ExMTNxM2EzcTNxM3EwAFJQABKkgTAAxRAAErRxUADQU=',
+    'morning'  : 'JgBQAAABK5AUERQ2FBEUEhQRFBEUEhQRFBEUNhQ2FDUVNRQ2FDYUNRU1FDYUERURFDUVERQRFBIUERQRFTUUNhQRFDYUNhQ2FAAFIwABK0cVAA0FAAAAAAAAAAA=',
+    'colours'  : 'JgBQAAABLI8VERQ2FBEUERQSFBEUERURFBEUNhQ1FTUUNhQ2FDYUNRQSFBEUERQ2FDYUERQSFBEUNhQ1FTUUEhQRFDYUNRU1FAAFJAABKkcVAA0FAAAAAAAAAAA=',
+    'random'   : 'JgBQAAABK5AUEhQ1FREUERQSFBEUERQSExIUNhQ1EzcTNxM3EzYTNxMTEhMTNxM2ExMTEhMSExMTNxM2ExMTEhM3EzcTNhM3EwAFJQABK0cVAA0FAAAAAAAAAAA=',
+    'island'   : 'JgBQAAABK5AUERQ2FBEUEhQRFBEUEhQRFBEUNhQ2FDUVNRQ2FDYUNRU1FBIUNRURFBEUEhQRFBEUEhQ1FREUNhQ1FDYUNhQ2FAAFIwABK0cVAA0FAAAAAAAAAAA=',
+    'forest'   : 'JgBQAAABK5AUEhQ1FREUERQSFBEUERQSFBEUNhQ1FTUUNhQ2FDUVNRQSFBEUNhQRFDYUERQSFBEUNhQ2FBEUNhQRFDYUNhQ1FQAFIwABK0cVAA0FAAAAAAAAAAA=',
+    'ocean'    : 'JgBQAAABK5AUEhQ1FREUERQSFBEUERQRFREUNhQ1FTUUNhQ2FDUVNRQ2FBEUEhQ1FTUUEhQRFBEUEhQ1FTUUEhQRFDYUNRU1FAAFJAABK0cVAA0FAAAAAAAAAAA=',
+    'fire'     : 'JgBQAAABK5AUERQ2FBEUEhQRFBEUEhQRFBEUNhQ2FDUVNRQ2FDYUNRU1FBIUNRU1FBIUERQRFBIUERQ2FBEUEhQ1FTUUNhQ2FAAFIwABLEYVAA0FAAAAAAAAAAA=',
+    'love'     : 'JgBQAAABL5AUEhQ1FREUERQSFBEUERQSFBEUNhQ1FDYUNhQ2FDUVNRQSFDUUNhQSFBEUERQSFBEUNhQRFBIUNRQ2FDYUNhQ1FAAFJAABK0cVAA0FAAAAAAAAAAA=',
+
+    # colour commands
+    'red'        : 'JgBIAAABK5AUERQ2FBEUEhQRFBEVERQRFBEUNhQ2FDYUNRU1FDYUNhQRFDYUNhQ1FREUERQSFBEUNhQRFBIUERQ2FDUVNRQ2FAANBQ==',
+    'yellow'     : 'JgBIAAABLI8UEhQ2FBEUERQSFBEUEhMSFBEUNhQ2FDUUNhQ2FDYUNRQ2FDYUNhQRFBIUERQRFBIUERQSExIUNhQ1FDYUNhQ2FAANBQ==',
+    'green'      : 'JgBIAAABK5AUEhQ1FREUERQSFBEUERQSFBEUNhQ1FDYUNhQ2FDUVNRQSFBEUERQ2FBIUERQRFBIUNRU1FDYUERQ2FDYUNhQ1FQANBQ==',
+    'blue'       : 'JgBIAAABK5AUERQ2FBEUEhQRFBITEhQRFBEUNhQ2FDYUNRQ2FDYUNhQ2ExIUNhQRFDYUERQSExIUERQ2FBEUNhQSEzYUNhQ2FAANBQ==',
+    'saturate'   : 'JgBIAAABK5AUERQ2FBEUEhQRFBIUERQRFBEUNhQ2FDYUNRU1FDYUNhQRFDYUERQ2FDYUERQSFBEUNhQRFDYUERQSFDUUNhQ2FAANBQ==',
+    'desaturate' : 'JgBIAAABLI8VERQ2FBEUERQSFBEUERURFBEUNhQ1FTUUNhQ2FDYUNRQ2FDYUNhQ1FREUERQSFBEUERQSFBEUERQ2FDYUNhQ1FQANBQ==',
+}
+
+def cmd(name):
+    return base64.b64decode(commands[name])
+
+class LightController:
+    def __init__(self):
+        self.devices = broadlink.discover(timeout=10, max_devices=2)
+        if self.devices == []:
+            raise Exception('no devices found')
+        for device in self.devices:
+            device.auth()
+
+    def send_cmd(self, name, iterations=5):
+        "Send a command, repeatedly for reliability"
+        packet = cmd(name)
+        for i in range(iterations):
+            for device in self.devices:
+                device.send_data(packet)
+
+    def wakey(self):
+        "Turn on the lights in the morning, try repeatedly for reasons."
+        print('Turning on the lights. Wakey, wakey!')
+        for i in range(5):
+            self.send_cmd('random')
+            time.sleep(0.3)
+            self.send_cmd('on')
+            time.sleep(0.3)
diff --git a/fun/idual/idualctl b/fun/idual/idualctl
new file mode 100644
index 000000000000..10a85eba0af0
--- /dev/null
+++ b/fun/idual/idualctl
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+import idual
+import sys
+
+def help():
+    print('Available commands:')
+    for cmd in idual.commands:
+        print('- ' + cmd)
+    sys.exit(0)
+
+def handle(ctrl, cmd):
+    if cmd == 'help':
+        help()
+    elif cmd == 'wakey':
+        ctrl.wakey()
+        sys.exit(0)
+    elif cmd == 'on':
+        print('Turning on the lights')
+        ctrl.send_cmd(cmd)
+    elif cmd == 'off':
+        print('Turning off the lights')
+        ctrl.send_cmd(cmd)
+    elif cmd in idual.commands:
+        print('Sending ' + cmd + '-command')
+        ctrl.send_cmd(cmd)
+    else:
+        print('unknown command \'' + cmd + '\'')
+        sys.exit(1)
+
+if __name__ == "__main__":
+    if len(sys.argv) == 1:
+        help()
+
+    print('Initialising light controller')
+    ctrl = idual.LightController()
+
+    for cmd in sys.argv[1:]:
+        handle(ctrl, cmd)
diff --git a/fun/idual/setup.py b/fun/idual/setup.py
new file mode 100644
index 000000000000..f10c3b86f24e
--- /dev/null
+++ b/fun/idual/setup.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+from setuptools import setup
+
+setup(
+    name='idualctl',
+    version='0.1',
+    author='Vincent Ambo',
+    author_email='mail@tazj.in',
+    url='https://git.tazj.in/about/fun/idual',
+    packages=['idual'],
+    scripts = ['idualctl'],
+    install_requires=['broadlink>=0.13.2'],
+    include_package_data=True,
+)