Skip to content

Instantly share code, notes, and snippets.

@prueker

prueker/metar.py Secret

Last active October 22, 2020 00:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save prueker/12945cbf4220ce0221e79174db7c5cc9 to your computer and use it in GitHub Desktop.
Save prueker/12945cbf4220ce0221e79174db7c5cc9 to your computer and use it in GitHub Desktop.
metar.py with motion trigger
#!/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