#! /usr/bin/env python3.3 #-*- coding: utf-8 -*- # Auteur : Phyks (phyks@phyks.me) # Site web : http://phyks.me # Licence : BEERWARE # Description : # ============= # Récupérer et afficher la météo et le nombre de vélibs aux stations # prédéfinies sur un écran LCD nokia 3310 (connecté via les pins GPIO) # Plus d'informations sur le montage : # http://blog.idleman.fr/raspberry-pi-11-relier-a-un-ecran-et-afficher-du-texte import sys import urllib3 import xml.etree.ElementTree as ET import datetime import wiringpi2 as wp2 import time as time_lib import signal import threading wp2.wiringPiSetup() # =========== # Pins du LCD # =========== PIN_SCE = 3 PIN_RESET = 4 PIN_DC = 5 PIN_SDIN = 6 PIN_SCLK = 7 LCD_C = 0 LCD_D = 1 LCD_X = 84 LCD_Y = 48 ASCII = [ [0x00, 0x00, 0x00, 0x00, 0x00], # 20 " " [0x00, 0x00, 0x5f, 0x00, 0x00], # 21 ! [0x00, 0x07, 0x00, 0x07, 0x00], # 22 " [0x14, 0x7f, 0x14, 0x7f, 0x14], # 23 # [0x24, 0x2a, 0x7f, 0x2a, 0x12], # 24 $ [0x23, 0x13, 0x08, 0x64, 0x62], # 25 % [0x36, 0x49, 0x55, 0x22, 0x50], # 26 & [0x00, 0x05, 0x03, 0x00, 0x00], # 27 ' [0x00, 0x1c, 0x22, 0x41, 0x00], # 28 ( [0x00, 0x41, 0x22, 0x1c, 0x00], # 29 ) [0x14, 0x08, 0x3e, 0x08, 0x14], # 2a * [0x08, 0x08, 0x3e, 0x08, 0x08], # 2b + [0x00, 0x50, 0x30, 0x00, 0x00], # 2c , [0x08, 0x08, 0x08, 0x08, 0x08], # 2d - [0x00, 0x60, 0x60, 0x00, 0x00], # 2e . [0x20, 0x10, 0x08, 0x04, 0x02], # 2f / [0x3e, 0x51, 0x49, 0x45, 0x3e], # 30 0 [0x00, 0x42, 0x7f, 0x40, 0x00], # 31 1 [0x42, 0x61, 0x51, 0x49, 0x46], # 32 2 [0x21, 0x41, 0x45, 0x4b, 0x31], # 33 3 [0x18, 0x14, 0x12, 0x7f, 0x10], # 34 4 [0x27, 0x45, 0x45, 0x45, 0x39], # 35 5 [0x3c, 0x4a, 0x49, 0x49, 0x30], # 36 6 [0x01, 0x71, 0x09, 0x05, 0x03], # 37 7 [0x36, 0x49, 0x49, 0x49, 0x36], # 38 8 [0x06, 0x49, 0x49, 0x29, 0x1e], # 39 9 [0x00, 0x36, 0x36, 0x00, 0x00], # 3a : [0x00, 0x56, 0x36, 0x00, 0x00], # 3b ; [0x08, 0x14, 0x22, 0x41, 0x00], # 3c < [0x14, 0x14, 0x14, 0x14, 0x14], # 3d = [0x00, 0x41, 0x22, 0x14, 0x08], # 3e > [0x02, 0x01, 0x51, 0x09, 0x06], # 3f ? [0x32, 0x49, 0x79, 0x41, 0x3e], # 40 @ [0x7e, 0x11, 0x11, 0x11, 0x7e], # 41 A [0x7f, 0x49, 0x49, 0x49, 0x36], # 42 B [0x3e, 0x41, 0x41, 0x41, 0x22], # 43 C [0x7f, 0x41, 0x41, 0x22, 0x1c], # 44 D [0x7f, 0x49, 0x49, 0x49, 0x41], # 45 E [0x7f, 0x09, 0x09, 0x09, 0x01], # 46 F [0x3e, 0x41, 0x49, 0x49, 0x7a], # 47 G [0x7f, 0x08, 0x08, 0x08, 0x7f], # 48 H [0x00, 0x41, 0x7f, 0x41, 0x00], # 49 I [0x20, 0x40, 0x41, 0x3f, 0x01], # 4a J [0x7f, 0x08, 0x14, 0x22, 0x41], # 4b K [0x7f, 0x40, 0x40, 0x40, 0x40], # 4c L [0x7f, 0x02, 0x0c, 0x02, 0x7f], # 4d M [0x7f, 0x04, 0x08, 0x10, 0x7f], # 4e N [0x3e, 0x41, 0x41, 0x41, 0x3e], # 4f O [0x7f, 0x09, 0x09, 0x09, 0x06], # 50 P [0x3e, 0x41, 0x51, 0x21, 0x5e], # 51 Q [0x7f, 0x09, 0x19, 0x29, 0x46], # 52 R [0x46, 0x49, 0x49, 0x49, 0x31], # 53 S [0x01, 0x01, 0x7f, 0x01, 0x01], # 54 T [0x3f, 0x40, 0x40, 0x40, 0x3f], # 55 U [0x1f, 0x20, 0x40, 0x20, 0x1f], # 56 V [0x3f, 0x40, 0x38, 0x40, 0x3f], # 57 W [0x63, 0x14, 0x08, 0x14, 0x63], # 58 X [0x07, 0x08, 0x70, 0x08, 0x07], # 59 Y [0x61, 0x51, 0x49, 0x45, 0x43], # 5a Z [0x00, 0x7f, 0x41, 0x41, 0x00], # 5b [ [0x02, 0x04, 0x08, 0x10, 0x20], # 5c ¥ [0x00, 0x41, 0x41, 0x7f, 0x00], # 5d ] [0x04, 0x02, 0x01, 0x02, 0x04], # 5e ^ [0x40, 0x40, 0x40, 0x40, 0x40], # 5f _ [0x00, 0x01, 0x02, 0x04, 0x00], # 60 ` [0x20, 0x54, 0x54, 0x54, 0x78], # 61 a [0x7f, 0x48, 0x44, 0x44, 0x38], # 62 b [0x38, 0x44, 0x44, 0x44, 0x20], # 63 c [0x38, 0x44, 0x44, 0x48, 0x7f], # 64 d [0x38, 0x54, 0x54, 0x54, 0x18], # 65 e [0x08, 0x7e, 0x09, 0x01, 0x02], # 66 f [0x0c, 0x52, 0x52, 0x52, 0x3e], # 67 g [0x7f, 0x08, 0x04, 0x04, 0x78], # 68 h [0x00, 0x44, 0x7d, 0x40, 0x00], # 69 i [0x20, 0x40, 0x44, 0x3d, 0x00], # 6a j [0x7f, 0x10, 0x28, 0x44, 0x00], # 6b k [0x00, 0x41, 0x7f, 0x40, 0x00], # 6c l [0x7c, 0x04, 0x18, 0x04, 0x78], # 6d m [0x7c, 0x08, 0x04, 0x04, 0x78], # 6e n [0x38, 0x44, 0x44, 0x44, 0x38], # 6f o [0x7c, 0x14, 0x14, 0x14, 0x08], # 70 p [0x08, 0x14, 0x14, 0x18, 0x7c], # 71 q [0x7c, 0x08, 0x04, 0x04, 0x08], # 72 r [0x48, 0x54, 0x54, 0x54, 0x20], # 73 s [0x04, 0x3f, 0x44, 0x40, 0x20], # 74 t [0x3c, 0x40, 0x40, 0x20, 0x7c], # 75 u [0x1c, 0x20, 0x40, 0x20, 0x1c], # 76 v [0x3c, 0x40, 0x30, 0x40, 0x3c], # 77 w [0x44, 0x28, 0x10, 0x28, 0x44], # 78 x [0x0c, 0x50, 0x50, 0x50, 0x3c], # 79 y [0x44, 0x64, 0x54, 0x4c, 0x44], # 7a z [0x00, 0x08, 0x36, 0x41, 0x00], # 7b { [0x00, 0x00, 0x7f, 0x00, 0x00], # 7c | [0x00, 0x41, 0x36, 0x08, 0x00], # 7d ] [0x10, 0x08, 0x08, 0x10, 0x08], # 7e ? [0x78, 0x46, 0x41, 0x46, 0x78], # 7f ? ] def writeLCD(dc, data): wp2.digitalWrite(PIN_DC, dc) wp2.digitalWrite(PIN_SCE, 0) wp2.shiftOut(PIN_SDIN, PIN_SCLK, 1, data) wp2.digitalWrite(PIN_SCE, 1) def characterLCD(character): writeLCD(LCD_D, 0x00) for i in range(0, 5): writeLCD(LCD_D, ASCII[ord(character) - 0x20][i]) writeLCD(LCD_D, 0x00) def stringLCD(string): for character in string: characterLCD(character) # X ranges from 0 to 84 # Y ranges from 0 to 5 def gotoXYLCD(x, y): writeLCD(0, 0x80 | x) writeLCD(0, 0x40 | y) def initLCD(): wp2.pinMode(PIN_SCE, 1) wp2.pinMode(PIN_RESET, 1) wp2.pinMode(PIN_DC, 1) wp2.pinMode(PIN_SDIN, 1) wp2.pinMode(PIN_SCLK, 1) wp2.digitalWrite(PIN_RESET, 0) wp2.digitalWrite(PIN_RESET, 1) writeLCD(LCD_C, 0x21) # LCD Extended commands writeLCD(LCD_C, 0x98) # Set LCD Vop (contrast) writeLCD(LCD_C, 0x04) # Set Temp coefficient writeLCD(LCD_C, 0x13) # LCD bias mode (1:48) writeLCD(LCD_C, 0x0C) # LCD in normal mode writeLCD(LCD_C, 0x20) writeLCD(LCD_C, 0x0C) def clearLCD(): for i in range(0, int(LCD_X*LCD_Y/8)): writeLCD(LCD_D, 0x00) characterLCD(' ') def kill_handler(signum, frame): global running global update_thread print("Exiting...") running = False update_thread.cancel() def update(): print("Updating...") global available global free global weather_now global temp_now global weather_3h global temp_3h global weather_6h global temp_6h global weather_9h global temp_9h global running # ======================= # Requête sur l'API Vélib # ======================= for station in stations: r = http.request('GET', base_url+str(station)) if r.status == 200: station_xml = ET.fromstring(r.data) for child in station_xml.iter('available'): available[station] = child.text for child in station_xml.iter('free'): free[station] = child.text else: available[station] = -1 free[station] = -1 # ================================== # Requête pour les prédictions météo # ================================== url_weather = ('http://api.openweathermap.org/data/2.5/forecast' + '?q=Paris,fr&mode=xml&lang=fr&units=metric') r = http.request('GET', url_weather) if r.status == 200: weather_xml = ET.fromstring(r.data) for child in weather_xml.iter('forecast'): for time in child.iter('time'): time_from = datetime.datetime.strptime(time.attrib['from'], "%Y-%m-%dT%H:%M:%S") time_to = datetime.datetime.strptime(time.attrib['to'], "%Y-%m-%dT%H:%M:%S") if time_to > now and time_from < now: weather_now = time.find('clouds').attrib['value'] temp_now = time.find('temperature').attrib['value'] if (time_to > now + datetime.timedelta(hours=3) and time_from < now+datetime.timedelta(hours=3)): weather_3h = time.find('clouds').attrib['value'] temp_3h = time.find('temperature').attrib['value'] if (time_to > now+datetime.timedelta(hours=6) and time_from < now+datetime.timedelta(hours=6)): weather_6h = time.find('clouds').attrib['value'] temp_6h = time.find('temperature').attrib['value'] if (time_to > now+datetime.timedelta(hours=9) and time_from < now+datetime.timedelta(hours=9)): weather_9h = time.find('clouds').attrib['value'] temp_9h = time.find('temperature').attrib['value'] # ========== # Paramètres # ========== if(len(sys.argv) == 1): sys.exit('You must provide at least one station number as ' + 'argument on the command line.') stations = [] for station in sys.argv[1:]: stations.append(int(station)) base_url = ('http://www.velib.paris.fr/service/stationdetails/paris/') # ============================= # Initialisation des librairies # ============================= http = urllib3.PoolManager() initLCD() clearLCD() gotoXYLCD(0, 0) stringLCD("Loading...") running = True update_thread = threading.Timer(900, update) update_thread.start() signal.signal(signal.SIGINT, kill_handler) # ==== # Date # ==== now = datetime.datetime.now() date = now.strftime('%d/%m/%Y') time = now.strftime('%H:%M') # ==== # Vars # ==== available = {} free = {} weather_now = '' temp_now = -1 weather_3h = '' temp_3h = -1 weather_6h = '' temp_6h = -1 weather_9h = '' temp_9h = -1 update() # ============================ # Afficher les infos à l'écran # ============================ while running: for station in stations: clearLCD() gotoXYLCD(0, 0) stringLCD("Station:") gotoXYLCD(0, 1) stringLCD(str(station)) gotoXYLCD(0, 3) stringLCD("Avail.: "+str(available[station])) gotoXYLCD(0, 5) stringLCD("Free: "+str(free[station])) time_lib.sleep(2) if running is False: continue clearLCD() gotoXYLCD(0, 0) stringLCD("[Weather]") gotoXYLCD(0, 1) stringLCD("Now:") gotoXYLCD(0, 2) stringLCD(weather_now.capitalize()) gotoXYLCD(0, 4) stringLCD("Temp:"+str(temp_now)+"C") time_lib.sleep(2) if running is False: continue clearLCD() gotoXYLCD(0, 0) stringLCD("[Weather]") gotoXYLCD(0, 1) stringLCD("In 3h:") gotoXYLCD(0, 2) stringLCD(weather_3h.capitalize()) gotoXYLCD(0, 4) stringLCD("Temp:"+str(temp_3h)+"C") time_lib.sleep(2) if running is False: continue clearLCD() gotoXYLCD(0, 0) stringLCD("[Weather]") gotoXYLCD(0, 1) stringLCD("In 6h:") gotoXYLCD(0, 2) stringLCD(weather_6h.capitalize()) gotoXYLCD(0, 4) stringLCD("Temp:"+str(temp_6h)+"C") time_lib.sleep(2) if running is False: continue clearLCD() gotoXYLCD(0, 0) stringLCD("[Weather]") gotoXYLCD(0, 1) stringLCD("In 9h:") gotoXYLCD(0, 2) stringLCD(weather_9h.capitalize()) gotoXYLCD(0, 4) stringLCD("Temp:"+str(temp_9h)+"C") time_lib.sleep(2) clearLCD()