diff options
Diffstat (limited to 'fun/idual')
-rw-r--r-- | fun/idual/README.md | 34 | ||||
-rw-r--r-- | fun/idual/default.nix | 24 | ||||
-rw-r--r-- | fun/idual/idual/__init__.py | 65 | ||||
-rw-r--r-- | fun/idual/idualctl | 39 | ||||
-rw-r--r-- | fun/idual/setup.py | 15 |
5 files changed, 177 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..1ac87ab47f0f --- /dev/null +++ b/fun/idual/default.nix @@ -0,0 +1,24 @@ +{ pkgs, lib, ... }: + +let + inherit (pkgs) python python3 python3Packages; + + opts = { + pname = "idualctl"; + version = "0.1"; + src = ./.; + + propagatedBuildInputs = [ + python.broadlink + ]; + }; + package = python3Packages.buildPythonPackage opts; + script = python3Packages.buildPythonApplication opts; +in { + 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..4c5519d0adc5 --- /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('sun') + 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, +) |