├── scraper ├── requirements.txt ├── config.example.py └── scrape.py ├── assets ├── pi.jpg └── breadboard.jpg ├── eye-lighter ├── dotstar.so └── eye-lighter.py ├── led-tester ├── dotstar.so └── test.py ├── .gitignore └── README.md /scraper/requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.13.0 2 | -------------------------------------------------------------------------------- /assets/pi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orangespaceman/eye-lighter/master/assets/pi.jpg -------------------------------------------------------------------------------- /assets/breadboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orangespaceman/eye-lighter/master/assets/breadboard.jpg -------------------------------------------------------------------------------- /eye-lighter/dotstar.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orangespaceman/eye-lighter/master/eye-lighter/dotstar.so -------------------------------------------------------------------------------- /led-tester/dotstar.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orangespaceman/eye-lighter/master/led-tester/dotstar.so -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.pyc 3 | scraper/config.py 4 | scraper/env 5 | scraper/data.json 6 | eye-lighter/people.json 7 | deploy.sh 8 | assets/breadboard.psd 9 | -------------------------------------------------------------------------------- /scraper/config.example.py: -------------------------------------------------------------------------------- 1 | login = { 2 | "url": "", 3 | "username": "", 4 | "password": "" 5 | } 6 | 7 | people = [ 8 | { 9 | "name": "Name", 10 | "eyes": "ff9900", 11 | "devices": [ 12 | "mac:address" 13 | ] 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /led-tester/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import time 4 | from dotstar import Adafruit_DotStar 5 | 6 | numpixels = 24 7 | 8 | strip = Adafruit_DotStar(numpixels) 9 | strip.begin() 10 | strip.setBrightness(50) 11 | 12 | head = 0 13 | tail = -10 14 | color = 0xFF0000 15 | 16 | while True: 17 | strip.setPixelColor(head, color) 18 | strip.setPixelColor(tail, 0) 19 | strip.show() 20 | time.sleep(1.0 / 50) 21 | 22 | head += 1 23 | 24 | if(head >= numpixels): 25 | head = 0 26 | color >>= 8 27 | 28 | if(color == 0): 29 | color = 0xFF0000 30 | 31 | tail += 1 32 | 33 | if(tail >= numpixels): 34 | tail = 0 35 | -------------------------------------------------------------------------------- /eye-lighter/eye-lighter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import time 4 | import json 5 | from dotstar import Adafruit_DotStar 6 | 7 | 8 | class AwkwardLights(): 9 | brightness = 100 10 | fade = 1 11 | update = 30 12 | update_interval = 30 13 | light_interval = 1.0 / 50 14 | 15 | def __init__(self): 16 | self.update_data() 17 | self.strip = Adafruit_DotStar(len(self.people) * 2) 18 | self.strip.begin() 19 | self.loop() 20 | 21 | def loop(self): 22 | while True: 23 | for index, person in enumerate(self.people): 24 | left_eye_index = index * 2 25 | right_eye_index = left_eye_index + 1 26 | 27 | if person["in"] is True: 28 | colour = int(person["eyes"], 16) 29 | else: 30 | colour = 0 31 | 32 | self.strip.setPixelColor(left_eye_index, colour) 33 | self.strip.setPixelColor(right_eye_index, colour) 34 | 35 | self.check_update_interval() 36 | self.update_brightness() 37 | self.strip.setBrightness(self.brightness) 38 | self.strip.show() 39 | time.sleep(self.light_interval) 40 | 41 | def update_data(self): 42 | # set absolute path if running on pi load 43 | with open('people.json') as data_file: 44 | self.people = json.load(data_file) 45 | print self.people 46 | 47 | def update_brightness(self): 48 | self.brightness = self.brightness + self.fade 49 | if (self.brightness <= 1 or self.brightness > 100): 50 | self.fade = -self.fade 51 | 52 | def check_update_interval(self): 53 | self.update_interval = self.update_interval - self.light_interval 54 | if self.update_interval <= 0: 55 | self.update_data() 56 | self.update_interval = self.update 57 | 58 | 59 | # init 60 | awk_lights = AwkwardLights() 61 | -------------------------------------------------------------------------------- /scraper/scrape.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import config 4 | import requests 5 | import re 6 | import time 7 | import json 8 | 9 | 10 | class Scraper(): 11 | def __init__(self): 12 | self.session = requests.Session() 13 | self.login() 14 | 15 | while True: 16 | self.request_json() 17 | time.sleep(60) 18 | 19 | def login(self): 20 | r = self.session.head(config.login["url"]) 21 | self.session_id = re.search("%s(.*)%s" % ("=", "; "), 22 | r.headers["Set-Cookie"]).group(1) 23 | 24 | url = "%s/goform/login" % config.login["url"] 25 | data = { 26 | "usr": config.login["username"], 27 | "pwd": config.login["password"], 28 | "preSession": self.session_id 29 | } 30 | r = self.session.post(url, data=data) 31 | self.session_cookie = re.search("%s(.*)%s" % ("sessionindex=", "; "), 32 | r.headers["Set-Cookie"]).group(1) 33 | print "%s: Logging in, %s" % (time.ctime(), self.session_cookie) 34 | 35 | def request_json(self): 36 | url = "%s/data/getConnectInfo.asp" % config.login["url"] 37 | cookies = { 38 | "preSession": self.session_id, 39 | "sessionindex": self.session_cookie 40 | } 41 | r = self.session.get(url, cookies=cookies, stream=True, 42 | allow_redirects=False) 43 | 44 | if r.status_code == 302: 45 | self.login() 46 | else: 47 | devices = r.json() 48 | people = [] 49 | for person in config.people: 50 | is_in = False 51 | for device in devices: 52 | if (device["macAddr"] in person["devices"] and 53 | device["online"] == "active"): 54 | is_in = True 55 | people.append({ 56 | "name": person["name"], 57 | "eyes": person["eyes"], 58 | "in": is_in 59 | }) 60 | 61 | # set absolute path if running on pi load 62 | with open("../eye-lighter/people.json", "w") as fp: 63 | json.dump(people, fp) 64 | 65 | print "%s: Data exported, %d devices found" % (time.ctime(), 66 | len(devices)) 67 | 68 | 69 | # uncomment if running on pi load 70 | # time.sleep(60) 71 | 72 | scraper = Scraper() 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Eye-lighter 2 | 3 | Light up a strip of Dotstar APA102 LED lights with a Raspberry Pi, based on when people are in the studio. 4 | 5 | We have sliced up our LED strip and re-soldered them together behind pictures, so that they become glowing eyes. 6 | 7 | ## Setup 8 | 9 | This repo should be checked out onto the Raspberry Pi. 10 | 11 | There are two main parts to the codebase: 12 | 13 | - **Scraper**: This accesses our router regularly and identifies who is in 14 | - **Eye Lighter**: This uses the data generated by the scraper to control the LEDs 15 | 16 | 17 | ## Scraper 18 | 19 | Note that this is quite specific to our router, although the logic could be reapplied to any router. 20 | 21 | ### Installation 22 | 23 | From the `/scraper` directory, follow these instructions: 24 | 25 | Install virtualenv: 26 | 27 | ``` 28 | sudo pip install virtualenv 29 | ``` 30 | 31 | Create a virtualenv: 32 | 33 | ``` 34 | virtualenv env 35 | source env/bin/activate 36 | ``` 37 | 38 | Install the requirements: 39 | 40 | ``` 41 | pip install -r requirements.txt 42 | ``` 43 | 44 | ### Config 45 | 46 | Duplicate the `config.example.py` file, call it `config.py` 47 | 48 | Update the router login details, and the list of people 49 | 50 | 51 | ### Running the scraper 52 | 53 | ``` 54 | $ python scrape.py 55 | ``` 56 | 57 | This needs to be run as a background process when the Pi is powered up, so that it can keep the data updated. 58 | 59 | 60 | ## Eye-lighter 61 | 62 | Once the scraper is running, it will keep the `eye-lighter/people.json` file up-to-date. 63 | 64 | To start the eye-lighter, move into the `eye-lighter` directory and run: 65 | 66 | ``` 67 | $ python eye-lighter.py 68 | ``` 69 | 70 | This should be run as a background process when the Pi is powered up, as it updates the state of the LEDs. 71 | 72 | 73 | ## Pi setup 74 | 75 | To start this on boot, edit the following file: 76 | 77 | ``` 78 | sudo nano /etc/rc.local 79 | ``` 80 | 81 | Add to the bottom of the file: 82 | 83 | ``` 84 | # eye lighter 85 | . /home/pi/eye-lighter/scraper/env/bin/activate # may not be necessary 86 | python /home/pi/eye-lighter/scraper/scrape.py & 87 | python /home/pi/eye-lighter/eye-lighter/eye-lighter.py & 88 | ``` 89 | 90 | If you need to debug output: 91 | 92 | ``` 93 | exec 2> /tmp/rc.local.log 94 | exec 1>&2 95 | set -x 96 | ``` 97 | 98 | There are a few places in the `scrape.py` file and `eye-lighter.py` file that you may need to change code to get it to run on Pi load - absolute paths to files, and sleeping before initialisation. Look at the relevant comments to see what might need to be changed. 99 | 100 | 101 | ## LEDs 102 | 103 | This setup guide assumes that the _eye-lighter_ will be run on a Raspberry Pi. 104 | 105 | Due to differences in voltage between the Pi (3.3V) and the Dotstar APA102 LED lights (5V), a [74AHCT125 chip](https://thepihut.com/products/adafruit-74ahct125-quad-level-shifter-3v-to-5v-74ahct125) is used between the two. 106 | 107 | ![Breadboard](assets/breadboard.jpg) 108 | 109 | ![Pi](assets/pi.jpg) 110 | 111 | - connect the pi to a breadboard - [pins 10, 11 and ground](https://www.raspberrypi.org/documentation/usage/gpio-plus-and-raspi2/) 112 | - connect the pi to the [74AHCT125 chip](https://learn.adafruit.com/dotstar-pi-painter/assembly-part-1#test-dotstar-strip) 113 | - connect the 5V power supply to the breadboard 114 | - connect the power supply to the 74AHCT125 chip 115 | - connect the leds to the breadboard 116 | - connect the leds to the 74AHCT125 chip 117 | 118 | To test that the LEDs are wired up correctly, you can run the `led-tester/test.py` script (taken from [Adafruit](https://learn.adafruit.com/adafruit-dotstar-leds)) 119 | --------------------------------------------------------------------------------