Created
July 25, 2021 08:49
-
-
Save trevor229/ae01bed2184312756822633fd1831701 to your computer and use it in GitHub Desktop.
Script for using the Ambient Weather API to upload data to APRS/CWOP
This file contains hidden or 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
# Ambient Weather API APRS/CWOP upload script from this thread https://www.wxforum.net/index.php?topic=36181.0 | |
# Originally written by uajqq and Ron B. | |
# Modified by me (trevor229) for amateur radio use (added a few new variables) | |
# Be aware there are apparently some issues with the coordinates not being rounded properly in some cases. Read the thread above for more info. It works fine for me though. | |
# When using this script, make sure it is not running more than once per 5 minutes, as CWOP requests. | |
import os | |
from socket import * | |
from datetime import datetime, time | |
import math | |
os.environ["AMBIENT_ENDPOINT"] = 'https://api.ambientweather.net/v1' | |
os.environ["AMBIENT_API_KEY"] = '????' # Your API key here | |
os.environ["AMBIENT_APPLICATION_KEY"] = '????' # Your Application API key here | |
from ambient_api.ambientapi import AmbientAPI | |
callsign = '????' # Your CWOP/Amateur callsign here | |
passwd = '-1' # Leave as -1 unless you are an amateur radio operator and have a passcode from [email protected] | |
aprs_server = 'cwop.aprs.net' # Leave alone unless you are an amateur radio operator and have a passcode from [email protected] | |
#IMPORTANT: lat/long must be listed in DECIMAL DEGREES (DD.DDDD). Number of digits doesn't really matter. Use positive values for N/E, negative for S/W. The program then converts to degrees decimal minutes (DD MM.MMMM), which is the format APRS requires. | |
latitude = 00.000 | |
longitude = 00.000 | |
devicename = 'Ambient WS2000' #This identifies your equipment/software. You can put anything you want. I use 'Ambient WS2000', which is the model of weather station I have | |
api = AmbientAPI() | |
devices = api.get_devices() | |
home = devices[0] #this assumes you have only one station. Increase number accordingly if you want to get data from others | |
weather= home.last_data | |
#convert coordinates to degrees decimal minutes | |
if latitude < 0: | |
latitude = abs(latitude) | |
latitude = str(int(latitude)).zfill(2) + str(round(60*(latitude - int(latitude)),2)).zfill(2) + 'S' | |
else: | |
latitude = str(int(latitude)).zfill(2) + str(round(60*(latitude - int(latitude)),2)).zfill(2) + 'N' | |
if longitude < 0: | |
longitude = abs(longitude) | |
longitude = str(int(longitude)).zfill(3) + str(round(60*(longitude - int(longitude)),2)).zfill(2) + 'W' | |
else: | |
longitude = str(int(longitude)).zfill(3) + str(round(60*(longitude - int(longitude)),2)).zfill(2) + 'E' | |
winddir = str(weather.get('winddir')).zfill(3) | |
windspeed = str(int(math.ceil(weather.get('windspeedmph')))).zfill(3) | |
windgust = str(int(math.ceil(weather.get('windgustmph')))).zfill(3) | |
if weather.get('tempf') < 0: | |
temp = '-' + str(int(round(weather.get('tempf')))).zfill(2) | |
else: | |
temp = str(int(round(weather.get('tempf')))).zfill(3) | |
rainhour = str(int(weather.get('hourlyrainin')*100)).zfill(3) #technically this is RATE of rain per hour, not AMOUNT per hour, but seems to be tolerated? | |
past24hoursrain = str(int(weather.get('dailyrainin')*100)).zfill(3) #at the moment, the Ambient API does not provide "rain in last hour", so no way to calculate "rain in last 24 hours." The API can only report "rain since local midnight." Therefore this only gets reported after 23:45 local time, so rain since midnight is reasonably close to rain in last 24 hours | |
dailyrain = str(int(weather.get('dailyrainin')*100)).zfill(3) #this value IS supposed to be "rain since local midnight," so it is always reported | |
pressure = str(int(weather.get('baromrelin')/0.0029529983071445)).zfill(5) #pressure is supposed to be reported to APRS in "altimiter" (QNH) format, that is, relative. The system itself corrects the pressure to sea level based on your station's listed elevation, so make sure that's accurate | |
humidity = str(int(weather.get('humidity')%100)).zfill(2) #uses modulus operator % so that 100% is given as '00' | |
# If luminosity is above 999 W/m^2, APRS wants a lowercase L | |
if weather.get('solarradiation') >= 1000: | |
luminosity = 'l' + str(int(round(weather.get('solarradiation'))) % 1000).zfill(3) | |
else: | |
luminosity = 'L' + str(int(round(weather.get('solarradiation')))).zfill(3) | |
# Time reported in Zulu (UTC). 24-hour rain workaround still has to be local time, though | |
packet = callsign + '>APRS,TCPIP*:@' + datetime.utcnow().strftime("%d%H%M") + 'z' + latitude + '/' + longitude + '_' + winddir + '/' + windspeed + 'g' + windgust + 't' + temp + 'r' + rainhour + 'p' + (past24hoursrain if datetime.now().time() >= time(23,45) else '...') + 'P' + dailyrain + 'h' + humidity + 'b' + pressure + luminosity + devicename + '\n' | |
mylogin = 'user ' + callsign + ' pass ' + passwd + ' vers Python\n' | |
print(mylogin) #print user string for debugging purposes | |
print(packet) #prints the assembled packet for debugging purposes | |
#send the packet | |
s = socket(AF_INET, SOCK_STREAM) | |
s.connect((aprs_server, 14580)) | |
s.send(mylogin.encode()) | |
s.send(packet.encode()) | |
s.shutdown(0) | |
s.close() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment