Files
test/stoat.py
2025-11-27 22:25:36 +00:00

122 lines
5.8 KiB
Python

#!/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()