#!/usr/bin/env python import os from socket import * import datetime import math import sqlite3 import pytz def roundTime(dt=None, roundTo=300): if dt == None : dt = datetime.datetime.now() seconds = (dt - dt.min).seconds rounding = (seconds+roundTo/2) // roundTo * roundTo return dt + datetime.timedelta(0,rounding-seconds,-dt.microsecond) conn = sqlite3.connect('/home/stoat/abewx/stoat.db') c = conn.cursor() c.execute('CREATE TABLE IF NOT EXISTS precipitation (dtg TEXT PRIMARY KEY, precip real)') os.environ["AMBIENT_ENDPOINT"] = 'https://api.ambientweather.net/v1' os.environ["AMBIENT_API_KEY"] = '5eeaadda004744a284ecbfdc22dcb852ca86b8a488d2456bb638a346ee5adf7f' os.environ["AMBIENT_APPLICATION_KEY"] = '7eb31955d49c41f08e447b23a9dfc8be23992e8ffe3b41f98d9c9c68319e0e2e' from ambient_api.ambientapi import AmbientAPI callsign = 'FW6696' latitude = 38.344299 longitude = -81.6789 devicename = 'WS2902A' #This identifies your equipment/software. You can put anything you want. I use 'WS2902A', which is the model of weather station I have #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. 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' latitude = '3820.66N' 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) dailyrain = str(int(round(weather.get('dailyrainin')*100))).zfill(3) #this value IS supposed to be "rain since local midnight," so it is always reported rainhour = str(int(weather.get('hourlyrainin')*100)).zfill(3) 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' dtg = roundTime(datetime.datetime.utcnow()) olddtg = dtg - datetime.timedelta(days=3) local_tz = pytz.timezone('US/Eastern') utc = pytz.utc localdate = datetime.datetime.now(local_tz).date() localmidnight = local_tz.localize(datetime.datetime.combine(localdate, datetime.time(0, 0)), is_dst=None) utc_midnighttoday = localmidnight.astimezone(utc).replace(tzinfo=None) utc_midnightyesterday = utc_midnighttoday - datetime.timedelta(hours=24) prev24 = dtg - datetime.timedelta(hours=24) c.execute("INSERT OR REPLACE INTO precipitation (dtg, precip) values (?, ?)",(dtg, dailyrain)) conn.commit() try: c.execute("SELECT precip FROM precipitation WHERE dtg = (?)",(prev24,)) prev24value = c.fetchone()[0] print(prev24value) c.execute("SELECT precip FROM precipitation WHERE dtg = (?)",(utc_midnightyesterday,)) prevmidnightvalue = c.fetchone()[0] print(prevmidnightvalue) c.execute("SELECT precip FROM precipitation WHERE dtg = (?)",(utc_midnighttoday,)) currentmidnightvalue = c.fetchone()[0] print(currentmidnightvalue) rain24 = currentmidnightvalue - prev24value + int(dailyrain) print(rain24) past24hoursrain = str(int(rain24)).zfill(3) print(past24hoursrain) except: past24hoursrain = '...' print(past24hoursrain) # 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'r''r' #'r' + past24hoursrain + 'p' #past24hoursrain = '...' #packet = callsign + '>APRS,TCPIP*:@' + datetime.datetime.utcnow().strftime("%d%H%M") + 'z' + latitude + '/' + longitude + '_' + winddir + '/' + windspeed + 'g' + windgust + 't' + temp + 'r' + rainhour + 'p' + past24hoursrain.zfill(3) + 'P' + dailyrain + 'h' + humidity + 'b' + pressure + luminosity + devicename packet = callsign + '>APRS,TCPIP*:@' + datetime.datetime.utcnow().strftime("%d%H%M") + 'z' + latitude + '/' + longitude + '_' + winddir + '/' + windspeed + 'g' + windgust + 't' + temp + 'r' + rainhour + 'P' + dailyrain + 'h' + humidity + 'b' + pressure + luminosity + devicename print(packet) #prints the assembled packet for debugging purposes #send the packet s = socket(AF_INET, SOCK_STREAM) s.connect(('cwop.aprs.net', 14580)) s.send(('user ' + callsign + ' pass -1 vers Python\n').encode()) s.send((packet+'\n').encode()) s.shutdown(0) s.close() c.close() conn.close()