diff --git a/I2C.bme280.oled/YANPIWS.service b/I2C.bme280.oled/YANPIWS.service new file mode 100644 index 0000000..85df4f6 --- /dev/null +++ b/I2C.bme280.oled/YANPIWS.service @@ -0,0 +1,14 @@ +[Unit] +Description=YANPIWS +After=network-online.target + +[Service] +ExecStart=/usr/bin/python3 /home/pi/YANPIWS/I2C.bme280.oled/remote_all.py -b 1 -ip 192.168.2.30 -id1 57 -id2 74 +# todo - the After above doesn't seem to work so we put this sleep in here :( +ExecStartPre=/bin/sleep 10 +ExecStop=/usr/bin/python3 /home/pi/YANPIWS/I2C.bme280.oled/clear_scren.py -b 1 +Restart=always +RestartSec=30 + +[Install] +WantedBy=multi-user.etarget diff --git a/I2C.bme280.oled/clear_scren.py b/I2C.bme280.oled/clear_scren.py new file mode 100644 index 0000000..7e8f5f5 --- /dev/null +++ b/I2C.bme280.oled/clear_scren.py @@ -0,0 +1,27 @@ +#!/usr/bin/python3 + +from luma.core.interface.serial import i2c +from luma.core.render import canvas +from luma.oled.device import ssd1306 +import argparse +import atexit + + +parser = argparse.ArgumentParser() + +# Rev 2 Pi, Pi 2 & Pi 3 uses bus 1 +# Rev 1 Pi uses bus 0 +# Orange Pi Zero uses bus 0 for pins 1-5 (other pins for bus 1 & 2) +parser.add_argument('--bus', '-b', default=0, type=int, help='Bus Number, defaults to 0') + +args = parser.parse_args() +bus_number = args.bus + + +if __name__ == "__main__": + serial = i2c(port=bus_number, address=0x3C) + + device = ssd1306(serial) + + with canvas(device) as draw: + draw.rectangle(device.bounding_box, outline="black", fill="black") diff --git a/I2C.bme280.oled/readme.md b/I2C.bme280.oled/readme.md index 4f74331..f849271 100644 --- a/I2C.bme280.oled/readme.md +++ b/I2C.bme280.oled/readme.md @@ -67,7 +67,7 @@ device IDs after running `i2cdetect 0` or `i2cdetect 1`. These are likley `76` f It defaults to `--bus/-b` of `1` and `--device/-d` of `0x76`. Likely if you're on a Pi, you won't need to change anything, so you can just call it with out any arguments. - 1. `remote_all.py` takes teh following arguments: +1. `remote_all.py` takes teh following arguments: ```$xslt usage: remote_all.py [-h] [--bus BUS] [--remote_ip REMOTE_IP] [--temp_id1 TEMP_ID1] [--temp_id2 TEMP_ID2] @@ -84,6 +84,16 @@ device IDs after running `i2cdetect 0` or `i2cdetect 1`. These are likley `76` f * * * * * cd /var/www/html/I2C.bme280.oled; /usr/bin/python3 live_temp_hum_bme280.py * * * * * /usr/bin/python3 /var/www/html/I2C.bme280.oled/remote_all.py -ip 192.168.68.105 -id1 73 -id2 231 ``` + +todo - implement this systmed based crontab: + +``` +* * * * * cd /var/www/html/I2C.bme280.oled; /usr/bin/python3 remote_temps_humid.py -b 0 -ip 10.0.40.219 -id1 96 -id2 97 +# at 8pm turn off screen +0 20 * * * /usr/bin/systemctl stop weathercaster +# at 6am start screen +0 6 * * * /usr/bin/systemctl start weathercaster +``` If you need more help - read up on the "Long Start" below. diff --git a/I2C.bme280.oled/remote_all.py b/I2C.bme280.oled/remote_all.py index 707f8ac..eb3af58 100644 --- a/I2C.bme280.oled/remote_all.py +++ b/I2C.bme280.oled/remote_all.py @@ -1,8 +1,19 @@ -#!/usr/bin/python +#!/usr/bin/python3 - -# grab args from CLI +from luma.core.interface.serial import i2c +from luma.core.render import canvas +from luma.oled.device import ssd1306 +from PIL import ImageFont +import os import argparse +import json +import time +import logging.handlers + +my_logger = logging.getLogger('MyLogger') +my_logger.setLevel(logging.DEBUG) +handler = logging.handlers.SysLogHandler(address='/dev/log') +my_logger.addHandler(handler) parser = argparse.ArgumentParser() @@ -21,117 +32,106 @@ parser.add_argument('--temp_id2', '-id2', type=int, help='remote temp ID #2, defaults to 63') args = parser.parse_args() - yanpiws_ip = args.remote_ip yanpiws_temp_1 = args.temp_id1 yanpiws_temp_2 = args.temp_id2 - bus_number = args.bus -temp1url = 'http://' + str(yanpiws_ip) + '/ajax.php?content=humidity&id=' + str(yanpiws_temp_1) -temp2url = 'http://' + str(yanpiws_ip) + '/ajax.php?content=humidity&id=' + str(yanpiws_temp_2) -forecastUrl = 'http://' + str(yanpiws_ip) + '/ajax.php?content=forecast_full_json' -sunsetUrl = 'http://' + str(yanpiws_ip) + '/ajax.php?content=sunset' -sunriseUrl = 'http://' + str(yanpiws_ip) + '/ajax.php?content=sunrise' -datetimeUrl = 'http://' + str(yanpiws_ip) + '/ajax.php?content=datetime' - def get_string_from_url(url): import urllib.request raw_html = urllib.request.urlopen(url).read().decode('utf-8').rstrip() return raw_html -# fetch the cooked up html -> strings -import json -temp1 = json.loads(get_string_from_url(temp1url)) - -if temp1[0]['temp'] != 'NA': - temp1final = str(int(float(temp1[0]['temp']))) + temp1[0]['label'] -else: - temp1final = 'NA' -if yanpiws_temp_2 != None: - temp2 = json.loads(get_string_from_url(temp2url)) - if temp2[0]['temp'] != 'NA': - temp2final = ' ' + str(int(float(temp2[0]['temp']))) + temp2[0]['label'] - else: - temp2final = '' -else: - temp2final = '' - -forecast = json.loads(get_string_from_url(forecastUrl)) - -sunset = json.loads(get_string_from_url(sunsetUrl)) -sunrise = json.loads(get_string_from_url(sunriseUrl)) +def get_forecast(): + forecastUrl = 'http://' + str(yanpiws_ip) + '/ajax.php?content=forecast_full_json' + return json.loads(get_string_from_url(forecastUrl)) -date_time = json.loads(get_string_from_url(datetimeUrl)) - -import os -import Adafruit_SSD1306 -from PIL import Image -from PIL import ImageDraw -from PIL import ImageFont -# set full puth for incling libs below -full_path = os.path.dirname(os.path.abspath(__file__)) + "/" +def get_sunrise_sunset(string): + url = 'http://' + str(yanpiws_ip) + '/ajax.php?content=' + string + data = json.loads(get_string_from_url(url)) + string = data[string].split(' ')[0] + return string -# Raspberry Pi pin configuration: -RST = None # on the PiOLED this pin isnt used -# Note the following are only used with SPI: -DC = 23 -SPI_PORT = 0 -SPI_DEVICE = 0 -# Rev 2 Pi, Pi 2 & Pi 3 uses bus 1 -# Rev 1 Pi uses bus 0 -# Orange Pi Zero uses bus 0 for pins 1-5 (other pins for bus 1 & 2) -disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST, i2c_bus=bus_number) - -# Initialize library. -disp.begin() - -# Clear display. -disp.clear() -disp.display() - -# Create blank image for drawing. -# Make sure to create image with mode '1' for 1-bit color. -width = disp.width -height = disp.height -image = Image.new('1', (width, height)) - -# Get drawing object to draw on image. -draw = ImageDraw.Draw(image) - -# Draw a black filled box to clear the image. -draw.rectangle((-20,-20,width,height), outline=0, fill=0) - -# Draw some shapes. -# First define some constants to allow easy resizing of shapes. -padding = -2 -top = padding -bottom = height-padding - -# Load default font. -font = ImageFont.truetype(full_path + "Lato-Heavy.ttf", 10) -font_small = ImageFont.truetype(full_path + "Lato-Heavy.ttf", 12) -# Alternatively load a TTF font. Make sure the .ttf font file is in the same directory as the python script! -# Some other nice fonts to try: http://www.dafont.com/bitmap.php -# font = ImageFont.truetype('Minecraftia.ttf', 8) - -# Draw a black filled box to clear the image. -draw.rectangle((0,0,width,height), outline=0, fill=0) -sunriseFinal = sunrise['sunrise'].split(' ')[0] -sunsetFinal = sunset['sunset'].split(' ')[0] - -# render the data -draw.text((0, top ), date_time['date'] + ' ' + date_time['time'] , font=font_small, fill=255) -draw.text((0, top + 17), temp1final + temp2final + ' ' + sunriseFinal + ' ' + sunsetFinal, font=font_small, fill=255) -draw.text((0, top + 35), 'H: ' + str(int(forecast[0]['temperatureHigh'])) + ' L: ' - + str(int(forecast[0]['temperatureLow'])) + ' ' + forecast[0]['icon'], font=font_small, fill=255) -draw.text((0, top + 52), 'H: ' + str(int(forecast[1]['temperatureHigh'])) + ' L: ' - + str(int(forecast[1]['temperatureLow'])) + ' ' + forecast[1]['icon'], font=font_small, fill=255) - -# Display image. -disp.image(image) -disp.display() +def get_temp_string(temp_id): + if temp_id != None: + url = 'http://' + str(yanpiws_ip) + '/ajax.php?content=humidity&id=' + str(temp_id) + temp = json.loads(get_string_from_url(url)) + if temp[0]['temp'] != 'NA': + the_string = ' ' + str(int(float(temp[0]['temp']))) + temp[0]['label'] + else: + the_string = '' + else: + the_string = '' + + return the_string + + +def get_date_time(): + url = 'http://' + str(yanpiws_ip) + '/ajax.php?content=datetime' + date_time = json.loads(get_string_from_url(url)) + string = date_time['date'] + ' ' + date_time['time'] + return string + + +def show_info(device): + first_line = get_date_time() + temp1final = get_temp_string(yanpiws_temp_1) + temp2final = get_temp_string(yanpiws_temp_2) + sunrise_final = get_sunrise_sunset('sunrise') + sunset_final = get_sunrise_sunset('sunset') + forecast = get_forecast() + + full_path = os.path.dirname(os.path.abspath(__file__)) + "/" + font2 = ImageFont.truetype(full_path + "Lato-Heavy.ttf", 12) + + second_line = temp1final + temp2final + ' ' + sunrise_final + ' ' + sunset_final + third_line = 'H: ' + str(int(forecast[0]['temperatureHigh'])) + ' L: ' + str(int(forecast[0]['temperatureLow'])) +\ + ' ' + forecast[0]['icon'] + fourth_line = 'H: ' + str(int(forecast[1]['temperatureHigh'])) + ' L: ' + str(int(forecast[1]['temperatureLow'])) +\ + ' ' + forecast[1]['icon'] + + with canvas(device) as draw: + # draw.rectangle(device.bounding_box, outline="white", fill="black") + draw.text((0, 0), first_line, font=font2, fill="white") + draw.text((0, 17), second_line, font=font2, fill="white") + draw.text((0, 35), third_line, font=font2, fill="white") + draw.text((0, 52), fourth_line, font=font2, fill="white") + + +def full_stack(): + import traceback, sys + exc = sys.exc_info()[0] + stack = traceback.extract_stack()[:-1] # last one would be full_stack() + if exc is not None: # i.e. an exception is present + del stack[-1] # remove call of full_stack, the printed exception + # will contain the caught exception caller instead + trc = 'Traceback (most recent call last):\n' + stackstr = trc + ''.join(traceback.format_list(stack)) + if exc is not None: + stackstr += ' ' + traceback.format_exc().lstrip(trc) + return stackstr + + +def main(device): + + while True: + show_info(device) + time.sleep(50) + + +if __name__ == "__main__": + try: + my_logger.debug('YANPIWS: Starting') + serial = i2c(port=bus_number, address=0x3C) + device = ssd1306(serial) + + main(device) + except KeyboardInterrupt: + my_logger.debug("YANPIWS: Stopping(Ctrl + C)") + pass + finally: + my_logger.debug("YANPIWS exit trace: " + full_stack()) diff --git a/I2C.bme280.oled/remote_temps_humid.py b/I2C.bme280.oled/remote_temps_humid.py index 7475ea1..38b5f23 100644 --- a/I2C.bme280.oled/remote_temps_humid.py +++ b/I2C.bme280.oled/remote_temps_humid.py @@ -1,8 +1,17 @@ -#!/usr/bin/python +#!/usr/bin/python3 # grab args from CLI import argparse +from luma.core.interface.serial import i2c +from luma.core.render import canvas +from luma.oled.device import ssd1306 +from PIL import ImageFont +import os +import argparse +import json +import time +import logging.handlers parser = argparse.ArgumentParser() @@ -28,94 +37,91 @@ bus_number = args.bus -humidAndTemp1url = 'http://' + str(yanpiws_ip) + '/ajax.php?content=humidity&id=' + str(yanpiws_temp_1) -humidAndTemp2url = 'http://' + str(yanpiws_ip) + '/ajax.php?content=humidity&id=' + str(yanpiws_temp_2) -datetime = 'http://' + str(yanpiws_ip) + '/ajax.php?content=datetime' +my_logger = logging.getLogger('MyLogger') +my_logger.setLevel(logging.DEBUG) +handler = logging.handlers.SysLogHandler(address='/dev/log') +my_logger.addHandler(handler) + def get_string_from_url(url): import urllib.request raw_html = urllib.request.urlopen(url).read().decode('utf-8').rstrip() return raw_html -# fetch the cooked up json -> strings -import json -humidAndTemp1 = json.loads(get_string_from_url(humidAndTemp1url)) - -if humidAndTemp1[0]['temp'] != 'NA': - temp1final = str(int(float(humidAndTemp1[0]['temp']))) + '°' + humidAndTemp1[0]['label'] - if 'humidity' in humidAndTemp1[0]: - temp1final = str(int(float(humidAndTemp1[0]['humidity']))) + '% ' + temp1final -if yanpiws_temp_2 is not None and humidAndTemp1[0]['humidity'] != '' and humidAndTemp1[0]['temp'] != 'NA': - humidAndTemp2 = json.loads(get_string_from_url(humidAndTemp2url)) - temp2final = str(int(float(humidAndTemp2[0]['temp']))) + '°' \ - + str(humidAndTemp2[0]['label']) - if 'humidity' in humidAndTemp2[0] and humidAndTemp2[0]['humidity'] != '': - temp2final = str(int(float(humidAndTemp2[0]['humidity']))) + '% ' + temp2final - -date_time = json.loads(get_string_from_url(datetime)) -import os -import Adafruit_SSD1306 -from PIL import Image -from PIL import ImageDraw -from PIL import ImageFont - -# set full puth for incling libs below -full_path = os.path.dirname(os.path.abspath(__file__)) + "/" - -# Raspberry Pi pin configuration: -RST = None # on the PiOLED this pin isnt used -# Note the following are only used with SPI: -DC = 23 -SPI_PORT = 0 -SPI_DEVICE = 0 - -# Rev 2 Pi, Pi 2 & Pi 3 uses bus 1 -# Rev 1 Pi uses bus 0 -# Orange Pi Zero uses bus 0 for pins 1-5 (other pins for bus 1 & 2) -disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST, i2c_bus=bus_number) - -# Initialize library. -disp.begin() - -# Clear display. -disp.clear() -disp.display() - -# Create blank image for drawing. -# Make sure to create image with mode '1' for 1-bit color. -width = disp.width -height = disp.height -image = Image.new('1', (width, height)) - -# Get drawing object to draw on image. -draw = ImageDraw.Draw(image) - -# Draw a black filled box to clear the image. -draw.rectangle((-20,-20,width,height), outline=0, fill=0) - -# Draw some shapes. -# First define some constants to allow easy resizing of shapes. -padding = -2 -top = padding -bottom = height-padding - -# Load default font. -font = ImageFont.truetype(full_path + "Lato-Heavy.ttf", 20) -font_small = ImageFont.truetype(full_path + "Lato-Heavy.ttf", 12) -# Alternatively load a TTF font. Make sure the .ttf font file is in the same directory as the python script! -# Some other nice fonts to try: http://www.dafont.com/bitmap.php -# font = ImageFont.truetype('Minecraftia.ttf', 8) - -# Draw a black filled box to clear the image. -draw.rectangle((0,0,width,height), outline=0, fill=0) - -# render the data -draw.text((0, top ), date_time['date'] + ' ' + date_time['time'] , font=font_small, fill=255) -draw.text((0, top + 18), temp1final , font=font, fill=255) -if yanpiws_temp_2 is not None: - draw.text((0, top + 46), temp2final , font=font, fill=255) - -# Display image. -disp.image(image) -disp.display() +def get_humid_and_temp(id): + forecastUrl = 'http://' + str(yanpiws_ip) + '/ajax.php?content=humidity&id=' + str(id) + return json.loads(get_string_from_url(forecastUrl)) + + +def get_date_time(): + url = 'http://' + str(yanpiws_ip) + '/ajax.php?content=datetime' + date_time = json.loads(get_string_from_url(url)) + string = date_time['date'] + ' ' + date_time['time'] + return string + + +def show_info(device): + my_logger.debug("Weathercaster: remote_all start") + + # fetch the cooked up json -> strings + humid_and_temp1 = get_humid_and_temp(yanpiws_temp_1) + first_line = get_date_time() + second_line = '' + third_line = '' + + if humid_and_temp1[0]['temp'] != 'NA': + second_line = str(int(float(humid_and_temp1[0]['temp']))) + '°' + humid_and_temp1[0]['label'] + if 'humidity' in humid_and_temp1[0]: + second_line = str(int(float(humid_and_temp1[0]['humidity']))) + '% ' + second_line + if yanpiws_temp_2 is not None and humid_and_temp1[0]['humidity'] != '' and humid_and_temp1[0]['temp'] != 'NA': + humid_and_temp2 = get_humid_and_temp(yanpiws_temp_2) + third_line = str(int(float(humid_and_temp2[0]['temp']))) + '°' + str(humid_and_temp2[0]['label']) + if 'humidity' in humid_and_temp2[0] and humid_and_temp2[0]['humidity'] != '': + third_line = str(int(float(humid_and_temp2[0]['humidity']))) + '% ' + third_line + + full_path = os.path.dirname(os.path.abspath(__file__)) + "/" + font2 = ImageFont.truetype(full_path + "Lato-Heavy.ttf", 12) + font1 = ImageFont.truetype(full_path + "Lato-Heavy.ttf", 22) + + my_logger.debug("Weathercaster: remote_all draw") + + with canvas(device) as draw: + # draw.rectangle(device.bounding_box, outline="white", fill="black") + draw.text((0, 0), first_line, font=font2, fill="white") + draw.text((0, 17), second_line, font=font1, fill="white") + draw.text((0, 41), third_line, font=font1, fill="white") + + +def full_stack(): + import traceback, sys + exc = sys.exc_info()[0] + stack = traceback.extract_stack()[:-1] # last one would be full_stack() + if exc is not None: # i.e. an exception is present + del stack[-1] # remove call of full_stack, the printed exception + # will contain the caught exception caller instead + trc = 'Traceback (most recent call last):\n' + stackstr = trc + ''.join(traceback.format_list(stack)) + if exc is not None: + stackstr += ' ' + traceback.format_exc().lstrip(trc) + return stackstr + + +def main(device): + while True: + show_info(device) + time.sleep(5) + + +if __name__ == "__main__": + try: + my_logger.debug('Weathercaster: remote_all Starting ') + serial = i2c(port=bus_number, address=0x3C) + device = ssd1306(serial) + + main(device) + except KeyboardInterrupt: + my_logger.debug("Weathercaster: remote_all Stopping(Ctrl + C) ") + pass + finally: + my_logger.debug("Weathercaster remote_all exit trace: " + full_stack())