-
-
Save prueker/12945cbf4220ce0221e79174db7c5cc9 to your computer and use it in GitHub Desktop.
metar.py with motion trigger
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import urllib.request | |
import xml.etree.ElementTree as ET | |
import board | |
import neopixel | |
import time | |
# ----- add these 2 new imports ------------------------------------ | |
import datetime | |
import os | |
# ------------------------------------------------------------------ | |
# NeoPixel LED Configuration | |
LED_COUNT = 50 # Number of LED pixels. | |
LED_PIN = board.D18 # GPIO pin connected to the pixels (18 is PCM). | |
LED_BRIGHTNESS = 0.5 # Float from 0.0 (min) to 1.0 (max) | |
LED_ORDER = neopixel.GRB # Strip type and colour ordering | |
COLOR_VFR = (255,0,0) # Green | |
COLOR_VFR_FADE = (125,0,0) # Green Fade for wind | |
COLOR_MVFR = (0,0,255) # Blue | |
COLOR_MVFR_FADE = (0,0,125) # Blue Fade for wind | |
COLOR_IFR = (0,255,0) # Red | |
COLOR_IFR_FADE = (0,125,0) # Red Fade for wind | |
COLOR_LIFR = (0,125,125) # Magenta | |
COLOR_LIFR_FADE = (0,75,75) # Magenta Fade for wind | |
COLOR_CLEAR = (0,0,0) # Clear | |
COLOR_LIGHTNING = (255,255,255) # White | |
# Do you want the METARMap to be static to just show flight conditions, or do you also want blinking/fading based on current wind conditions | |
ACTIVATE_WINDCONDITION_ANIMATION = False # Set this to False for Static or True for animated wind conditions | |
#Do you want the Map to Flash white for lightning in the area | |
ACTIVATE_LIGHTNING_ANIMATION = False # Set this to False for Static or True for animated Lightning | |
# Fade instead of blink | |
FADE_INSTEAD_OF_BLINK = True # Set to False if you want blinking | |
# Blinking Windspeed Threshold | |
WIND_BLINK_THRESHOLD = 15 # Knots of windspeed | |
ALWAYS_BLINK_FOR_GUSTS = False # Always animate for Gusts (regardless of speeds) | |
# Blinking Speed in seconds | |
BLINK_SPEED = 1.0 # Float in seconds, e.g. 0.5 for half a second | |
# Total blinking time in seconds. | |
# For example set this to 300 to keep blinking for 5 minutes if you plan to run the script every 5 minutes to fetch the updated weather | |
BLINK_TOTALTIME_SECONDS = 300 | |
# Initialize the LED strip | |
pixels = neopixel.NeoPixel(LED_PIN, LED_COUNT, brightness = LED_BRIGHTNESS, pixel_order = LED_ORDER, auto_write = False) | |
# ------------------------------------------------------------------ | |
# ----- New special code here for the motion sensor timer logic ---- | |
# ------------------------------------------------------------------ | |
TIMEFILE = 'temptimestamp.txt' | |
STOP_LEDS_AFTER_MINUTES = 60 # after how many minutes should the LEDs stop unless motion triggered again | |
motionSensorTriggered = False # replace this hardcoded False with your motion sensor part | |
if motionSensorTriggered: | |
# this will create a temporary file storing the time the motion was triggered | |
print(datetime.datetime.now().timestamp(), file=open(TIMEFILE,'w')) | |
if not os.path.exists(TIMEFILE): | |
print("No motion triggered, exiting script") # if file doesn't exist, then it means there's no motion, so we can exit early | |
exit() | |
# a motion has happened recently, so we compare the current time with the time the motion was triggered | |
with open(TIMEFILE,'r') as f: | |
readdate = datetime.datetime.fromtimestamp(float(f.readline())) | |
if readdate < datetime.datetime.now() - datetime.timedelta(minutes=STOP_LEDS_AFTER_MINUTES): | |
print("Elapsed time esceeded, turning off LEDs, removing timer file and exiting") | |
pixels.deinit() # this turns off the LEDs | |
os.remove(TIMEFILE) # remove the timer file until the next motion trigger recreates it | |
exit() | |
else: | |
print("Elapsed time not esceeded, executing Metar script") | |
# --------------------------------------------------------------------- | |
# ----- End of special code here for the motion sensor timer logic ---- | |
# --------------------------------------------------------------------- | |
# Read the airports file to retrieve list of airports and use as order for LEDs | |
with open("/home/pi/airports") as f: | |
airports = f.readlines() | |
airports = [x.strip() for x in airports] | |
# Retrieve METAR from aviationweather.gov data server | |
# Details about parameters can be found here: https://www.aviationweather.gov/dataserver/example?datatype=metar | |
url = "https://www.aviationweather.gov/adds/dataserver_current/httpparam?dataSource=metars&requestType=retrieve&format=xml&hoursBeforeNow=5&mostRecentForEachStation=true&stationString=" + ",".join([item for item in airports if item != "NULL"]) | |
print(url) | |
content = urllib.request.urlopen(url).read() | |
# Retrieve flying conditions from the service response and store in a dictionary for each airport | |
root = ET.fromstring(content) | |
conditionDict = { "": {"flightCategory" : "", "windSpeed" : 0, "windGust" : False, "lightning": False } } | |
for metar in root.iter('METAR'): | |
stationId = metar.find('station_id').text | |
if metar.find('flight_category') is None: | |
print("Missing flight condition, skipping.") | |
continue | |
flightCategory = metar.find('flight_category').text | |
windGust = False | |
windSpeed = 0 | |
lightning = False | |
if metar.find('wind_gust_kt') is not None: | |
windGust = (True if (ALWAYS_BLINK_FOR_GUSTS or int(metar.find('wind_gust_kt').text) > WIND_BLINK_THRESHOLD) else False) | |
if metar.find('wind_speed_kt') is not None: | |
windSpeed = int(metar.find('wind_speed_kt').text) | |
if metar.find('raw_text') is not None: | |
rawText = metar.find('raw_text').text | |
lightning = False if rawText.find('LTG') == -1 else True | |
print(stationId + ":" + flightCategory + ":" + str(windSpeed) + ":" + str(windGust) + ":" + str(lightning)) | |
conditionDict[stationId] = { "flightCategory" : flightCategory, "windSpeed" : windSpeed, "windGust": windGust, "lightning": lightning } | |
# Setting LED colors based on weather conditions | |
looplimit = int(round(BLINK_TOTALTIME_SECONDS / BLINK_SPEED)) if (ACTIVATE_WINDCONDITION_ANIMATION or ACTIVATE_LIGHTNING_ANIMATION) else 1 | |
windCycle = False | |
while looplimit > 0: | |
i = 0 | |
for airportcode in airports: | |
# Skip NULL entries | |
if airportcode == "NULL": | |
i += 1 | |
continue | |
color = COLOR_CLEAR | |
conditions = conditionDict.get(airportcode, None) | |
windy = False | |
lightningConditions = False | |
if conditions != None: | |
windy = True if (ACTIVATE_WINDCONDITION_ANIMATION and windCycle == True and (conditions["windSpeed"] > WIND_BLINK_THRESHOLD or conditions["windGust"] == True)) else False | |
lightningConditions = True if (ACTIVATE_LIGHTNING_ANIMATION and windCycle == False and conditions["lightning"] == True) else False | |
if conditions["flightCategory"] == "VFR": | |
color = COLOR_VFR if not (windy or lightningConditions) else COLOR_LIGHTNING if lightningConditions else (COLOR_VFR_FADE if FADE_INSTEAD_OF_BLINK else COLOR_CLEAR) if windy else COLOR_CLEAR | |
elif conditions["flightCategory"] == "MVFR": | |
color = COLOR_MVFR if not (windy or lightningConditions) else COLOR_LIGHTNING if lightningConditions else (COLOR_MVFR_FADE if FADE_INSTEAD_OF_BLINK else COLOR_CLEAR) if windy else COLOR_CLEAR | |
elif conditions["flightCategory"] == "IFR": | |
color = COLOR_IFR if not (windy or lightningConditions) else COLOR_LIGHTNING if lightningConditions else (COLOR_IFR_FADE if FADE_INSTEAD_OF_BLINK else COLOR_CLEAR) if windy else COLOR_CLEAR | |
elif conditions["flightCategory"] == "LIFR": | |
color = COLOR_LIFR if not (windy or lightningConditions) else COLOR_LIGHTNING if lightningConditions else (COLOR_LIFR_FADE if FADE_INSTEAD_OF_BLINK else COLOR_CLEAR) if windy else COLOR_CLEAR | |
else: | |
color = COLOR_CLEAR | |
print("Setting LED " + str(i) + " for " + airportcode + " to " + ("lightning " if lightningConditions else "") + ("windy " if windy else "") + (conditions["flightCategory"] if conditions != None else "None") + " " + str(color)) | |
pixels[i] = color | |
i += 1 | |
# Update actual LEDs all at once | |
pixels.show() | |
# Switching between animation cycles | |
time.sleep(BLINK_SPEED) | |
windCycle = False if windCycle else True | |
looplimit -= 1 | |
print() | |
print("Done") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment