123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- #!/usr/bin/env python3
- ## demure's fancy led matrix clock -- python sample
- ## This is a fancy led matrix clock written for a pi with an led matrix and
- ## and ada fruit matrix hat.
- ## NOTE: This python version is mostly a test, and the C version is the primary code.
- ## This is due to the python being pretty slow, especially on a pi zero.
- ## TODO:
- ## * Wrap weather request in `try` and supply fail case json sample
- ## * Get RGBMatrixOptions working
- ## * Remove object oriented junk?
- ## * Rename default instance
- ## * Ensure weather icons match nightmode
- ##
- ## * add wakeup mode? like not super bright before 0800 weekday, 1000 weekend
- ## * make config.json and move openweather api_key/city_id. maybe have nightmode time conf
- from samplebase import SampleBase
- from rgbmatrix import graphics
- from rgbmatrix import RGBMatrix, RGBMatrixOptions
- import time
- # from datetime import date
- from datetime import datetime
- import requests, json
- from pathlib import Path
- from PIL import Image
- from PIL import ImageDraw
- ## Configuration for the matrix
- options = RGBMatrixOptions()
- options.rows = 64
- options.cols = 64
- options.chain_length = 1
- options.parallel = 1
- options.hardware_mapping = 'adafruit-hat'
- options.gpio_slowdown = 4
- # matrix = RGBMatrix(options=options)
- ## Prep Dirs
- base_dir = Path(__file__).parent
- font_dir = str(base_dir) + '/spleen/'
- icon_dir = str(base_dir) + '/weathericons/'
- ## Load config.json
- with open(base_dir / "config.json") as f:
- try:
- conf = json.load(f)
- except:
- print("Error: invalid json in config.json")
- quit()
- base_url = conf["base_url"]
- api_key = conf["api_key"]
- city_id = conf["city_id"]
- global complete_url
- complete_url = base_url + "appid=" + api_key + "&id=" + city_id
- # x = json.loads('{"weather": [{"main": "NULL", "description": "NULL", "icon": "NULL"}], "main": {"temp": 0, "feels_like": 0, "temp_min": 0, "temp_max": 0, "pressure": 0, "humidity": 0}, "clouds": {"all": 0}, "dt": 0, "sys": {"sunrise": 0, "sunset": 0}, "cod": 0}')
- ### Night Mode Check Function ### {{{
- def night_mode_check(now, c_up, c_sunrise, c_sunset):
- now_s = int(now.strftime("%s"))
- hour = int(now.strftime("%H"))
- ## Set night mode if after sunset/before sunrise,
- ## as long as last weather pull is less than 6 hours old.
- ## Fallback of after 2200 and before 0700.
- if (c_sunset != 0 and (now_s - c_up) < 21600):
- if (now_s > c_sunset or now_s < c_sunrise):
- night_mode = True
- else:
- night_mode = False
- else:
- if (hour >= 22 or hour < 7):
- night_mode = True
- else:
- night_mode = False
- return night_mode
- ### End Night Mode Check Function ### }}}
- ### Wakeup Mode Check Function ### {{{
- def wakeup_mode_check(now, c_up):
- now_s = int(now.strftime("%s"))
- hour = int(now.strftime("%H"))
- time = int(now.strftime("%H%M"))
- day = int(now.strftime("%w")) ## return 0 thru 6; 0 = sun, 6 = sat
- ## Weekday wakeup ends at 0800
- ## Weekend wakeup ends at 1000
- if (day == 0 or day == 6):
- ## four hours (6 to 10) = 14400 sec
- wake_s = int(datetime.fromisoformat(now.strftime('%Y-%m-%d 10:00:00')).strftime('%s'))
- # if (hour < 10):
- if (time < 1300):
- wake_math = (1 - ((wake_s - now_s) / 14400))
- wake_mode = round(wake_math, 2)
- else:
- wake_mode = 1
- else:
- ## two hours (6 to 8) = 7200 sec
- wake_s = int(datetime.fromisoformat(now.strftime('%Y-%m-%d 08:00:00')).strftime('%s'))
- if (hour < 22):
- wake_math = (1 - ((wake_s - now_s) / 7200))
- wake_mode = round(wake_math, 2)
- else:
- wake_mode = 1
- ## Sanitize in case we get a negative value
- if (wake_mode < 0):
- wake_mode = 1
- return wake_mode
- ### End Wakeup Mode Check Function ### }}}
- ### Wakeup Mode Color Function ### {{{
- def wakeup_mode_color(wake_mode):
- temp_red = int((125 * wake_mode) + 25)
- temp_green = int(75 * wake_mode)
- tc_wake = graphics.Color(temp_red, temp_green, 0)
- return tc_wake
- ### End Wakeup Mode Color Function ### }}}
- def drawimage(canvas, path, x, y):
- image = Image.open(path).convert('RGB')
- image.load()
- # matrix.SetImage(image, x, y)
- canvas.SetImage(image, x, y)
- class RunText(SampleBase):
- def __init__(self, *args, **kwargs):
- super(RunText, self).__init__(*args, **kwargs)
- # self.parser.add_argument("-t", "--text", help="The text to scroll on the RGB LED panel", default="Hello world!")
- def run(self):
- canvas = self.matrix.CreateFrameCanvas()
- ### Set Fonts ### {{{
- ft_sp_12x24 = graphics.Font()
- ft_sp_12x24.LoadFont(font_dir + 'spleen-12x24.bdf')
- ft_sp_5x8 = graphics.Font()
- ft_sp_5x8.LoadFont(font_dir + 'spleen-5x8.bdf')
- ft_sp_16x32 = graphics.Font()
- ft_sp_16x32.LoadFont(font_dir + 'spleen-16x32.bdf')
- ft_sp_8x16 = graphics.Font()
- ft_sp_8x16.LoadFont(font_dir + 'spleen-8x16.bdf')
- ### End Set Fonts ### }}}
- ## Initialize before first weather fetch
- x = json.loads('{"cod": 0}')
- c_up = int(datetime.now().strftime("%s")) - 840 ## Off set to reduce initial weather load delay.
- # c_up = int(datetime.now().strftime("%s")) - 890 ## DEBUG
- c_sunrise = 0
- c_sunset = 0
- while True:
- canvas.Clear()
- ## Initialize Time Vars
- now = datetime.now()
- now_s = int(now.strftime("%s"))
- utcnow = datetime.utcnow()
- hour = int(now.strftime("%H"))
- ### Pull Weather Info ### {{{
- # if (int(now.strftime("%s")) - int(c_up)) > 10: ## for DEBUG
- if (now_s - c_up) > 900: ## Refresh every 15min
- graphics.DrawText(canvas, ft_sp_5x8, 0, 12, graphics.Color(0,35,0), "u") ## Print update indicator
- ## Request Weather
- try:
- response = requests.get(complete_url)
- # response = requests.get("http://test.lan") ##DEBUG
- x = response.json()
- except:
- print("weather fail") ##DEBUG
- x = json.loads('{"weather": [{"main": "NULL", "description": "NULL", "icon": "NULL"}], "main": {"temp": 0, "feels_like": 0, "temp_min": 0, "temp_max": 0, "pressure": 0, "humidity": 0}, "clouds": {"all": 0}, "dt": 0, "sys": {"sunrise": 0, "sunset": 0}, "cod": 0}')
- # x["dt"] = now_s ##DEBUG
- if x["cod"] != 0 and x["cod"] != 404:
- c_temp = x["main"]["temp"]
- c_temp_f = round((c_temp - 273.15) * 9/5 + 32)
- c_humid = x["main"]["humidity"]
- c_icon = x["weather"][0]["icon"]
- c_cond = x["weather"][0]["id"]
- # c_desc = x["weather"][0]["description"]
- c_sunrise = int(x["sys"]["sunrise"])
- c_sunset = int(x["sys"]["sunset"])
- c_up = int(x["dt"])
- else:
- ## Stop weather pulls until normal delay has occured.
- c_up = now_s
- ### End Pull Weather Info ### }}}
- ## Test for night mode
- night_mode = night_mode_check(now, c_up, c_sunrise, c_sunset)
- ## Test for wakeup mode
- if(night_mode == False):
- wake_mode = wakeup_mode_check(now, c_up)
- else:
- wake_mode = 1
- ### Text Colors ### {{{
- ## Set Day/Night Colors
- if (night_mode == True):
- tc_night = graphics.Color(25, 0, 0)
- tc_time = tc_night
- tc_week = tc_night
- tc_date = tc_night
- tc_utc = tc_night
- tc_epoch = tc_night
- tc_temp = tc_night
- tc_humid = tc_night
- tc_sun = tc_night
- elif (wake_mode < 1):
- tc_wake = wakeup_mode_color(wake_mode)
- tc_time = tc_wake
- tc_week = tc_wake
- tc_date = tc_wake
- tc_utc = tc_wake
- tc_epoch = tc_wake
- tc_temp = tc_wake
- tc_humid = tc_wake
- tc_sun = tc_wake
- else:
- tc_time = graphics.Color(0, 0, 200)
- tc_week = graphics.Color(0, 125, 125)
- tc_date = graphics.Color(125, 0, 125)
- tc_utc = graphics.Color(150, 0, 0)
- tc_epoch = graphics.Color(0, 150, 0)
- tc_temp = graphics.Color(125, 125, 125)
- tc_humid = graphics.Color(125, 125, 0)
- tc_sun = graphics.Color(150, 75, 0)
- ### Text Colors ### }}}
- ### Draw Day/Night/Late-Night Modes ### {{{
- ## Late-Night Output
- if (hour <= 6):
- # if True: ## DEBUG
- graphics.DrawText(canvas, ft_sp_16x32, 0, 20, tc_time, now.strftime("%H%M"))
- graphics.DrawText(canvas, ft_sp_16x32, 0, 42, tc_week, now.strftime("%a"))
- ## Day Display Weather Info
- if x["cod"] != 0 and x["cod"] != 404:
- graphics.DrawText(canvas, ft_sp_16x32, 0, 64, tc_temp, str(c_temp_f) + "F")
- ## Day/Night Output
- else:
- ## where, font, Y, X, color, string
- graphics.DrawText(canvas, ft_sp_12x24, 2, 15, tc_time, now.strftime("%R"))
- graphics.DrawText(canvas, ft_sp_5x8, 0, 43, tc_week, now.strftime("%a, %b"))
- graphics.DrawText(canvas, ft_sp_5x8, 0, 50, tc_date, now.strftime("%F"))
- graphics.DrawText(canvas, ft_sp_5x8, 0, 57, tc_utc, utcnow.strftime("%R UTC"))
- graphics.DrawText(canvas, ft_sp_5x8, 0, 64, tc_epoch, now.strftime("%s"))
- ## Day Display Weather Info
- if x["cod"] != 0 and x["cod"] != 404:
- graphics.DrawText(canvas, ft_sp_8x16, 0, 27, tc_temp, str(c_temp_f) + "F")
- graphics.DrawText(canvas, ft_sp_5x8, 0, 35, tc_humid, str(c_humid) + "%H")
- ## Either sunrise or sunset time
- if (now_s < c_sunrise):
- graphics.DrawText(canvas, ft_sp_5x8, 25, 35, tc_sun, str(datetime.fromtimestamp(c_sunrise).strftime("%H%M")) + "*")
- elif (now_s < c_sunset):
- graphics.DrawText(canvas, ft_sp_5x8, 25, 35, tc_sun, str(datetime.fromtimestamp(c_sunset).strftime("%H%M")) + "*")
- else:
- pass
- ##TODO: handle error by displaying something?
- # graphics.DrawText(canvas, ft_sp_5x8, 0, 36, tc_temp, "fail")
- ### End Draw Day/Night/Late-Night Modes ### }}}
- ## Display weather icon if not late night mode
- if x["cod"] != 0 and x["cod"] != 404:
- # if night_mode == False:
- if not (hour <= 6) or not (wake_mode == 1):
- if c_cond == 900:
- drawimage(canvas, icon_dir + 'tornado' + '.png', 49, 17)
- elif c_cond == 901 or c_cond == 902:
- drawimage(canvas, icon_dir + 'hurricane' + '.png', 49, 17)
- elif c_cond == 906 or c_cond == 611 or c_cond == 612:
- drawimage(canvas, icon_dir + 'hail' + '.png', 49, 17)
- elif c_cond == 600 or c_cond == 601 or c_cond == 602:
- drawimage(canvas, icon_dir + 'snow' + '.png', 49, 17)
- else:
- drawimage(canvas, icon_dir + c_icon + '.png', 49, 17)
- # time.sleep(0.05)
- time.sleep(0.1)
- # time.sleep(0.5)
- canvas = self.matrix.SwapOnVSync(canvas)
- # Main function
- if __name__ == "__main__":
- run_text = RunText()
- if (not run_text.process()):
- run_text.print_help()
|