This commit is contained in:
SpudGunMan
2025-09-09 15:00:54 -07:00
5 changed files with 162 additions and 15 deletions
+1
View File
@@ -138,6 +138,7 @@ git clone https://github.com/spudgunman/meshing-around
| `readnews` | returns the contents of a file (news.txt, by default) via the chunker on air | ✅ |
| `satpass` | returns the pass info from API for defined NORAD ID in config or Example: `satpass 25544,33591`| |
| `wiki:` | Searches Wikipedia and returns the first few sentences of the first result if a match. Example: `wiki: lora radio` |
| `howfar` | returns the distance you have traveled since your last HowFar. `howfar reset` to start over | ✅ |
### CheckList
| Command | Description | |
+26
View File
@@ -61,6 +61,7 @@ def auto_response(message, snr, rssi, hop, pkiStatus, message_from_id, channel_n
"hangman": lambda: handleHangman(message, message_from_id, deviceID),
"hfcond": hf_band_conditions,
"history": lambda: handle_history(message, message_from_id, deviceID, isDM),
"howfar": lambda: handle_howfar(message, message_from_id, deviceID, isDM),
"joke": lambda: tell_joke(message_from_id),
"lemonstand": lambda: handleLemonade(message, message_from_id, deviceID),
"lheard": lambda: handle_lheard(message, message_from_id, deviceID, isDM),
@@ -311,6 +312,31 @@ def handle_wxalert(message_from_id, deviceID, message):
weatherAlert = weatherAlert[0]
return weatherAlert
def handle_howfar(message, message_from_id, deviceID, isDM):
msg = ''
location = get_node_location(message_from_id, deviceID)
lat = location[0]
lon = location[1]
# if ? in message
if "?" in message.lower():
return "command returns the distance you have traveled since your last HowFar-command. Add 'reset' to reset your starting point."
# if no GPS location return
if lat == latitudeValue and lon == longitudeValue:
logger.debug(f"System: HowFar: No GPS location for {message_from_id}")
return "No GPS location available"
if "reset" in message.lower():
msg = distance(lat,lon,message_from_id, reset=True)
else:
msg = distance(lat,lon,message_from_id)
# if not a DM add the username to the beginning of msg
if not useDMForResponse and not isDM:
msg = "@" + get_name_from_number(message_from_id, 'short', deviceID) + " " + msg
return msg
def handle_wiki(message, isDM):
# location = get_node_location(message_from_id, deviceID)
msg = "Wikipedia search function. \nUsage example:📲wiki: travelling gnome"
+121 -1
View File
@@ -8,8 +8,10 @@ import requests # pip install requests
import bs4 as bs # pip install beautifulsoup4
import xml.dom.minidom
from modules.log import *
import math
trap_list_location = ("whereami", "wx", "wxa", "wxalert", "rlist", "ea", "ealert", "riverflow", "valert", "earthquake")
trap_list_location = ("whereami", "wx", "wxa", "wxalert", "rlist", "ea", "ealert", "riverflow", "valert", "earthquake", "howfar")
def where_am_i(lat=0, lon=0, short=False, zip=False):
whereIam = ""
@@ -811,3 +813,121 @@ def checkUSGSEarthQuake(lat=0, lon=0):
return NO_ALERTS
else:
return f"{quake_count} quakes in last {history} days within {radius}km of you largest was {largest_mag}. {description_text}"
howfarDB = {}
def distance(lat=0,lon=0,nodeID=0, reset=False):
# part of the howfar function, calculates the distance between two lat/lon points
msg = ""
if lat == 0 and lon == 0:
return NO_DATA_NOGPS
if nodeID == 0:
return "No NodeID provided"
if reset:
if nodeID in howfarDB:
del howfarDB[nodeID]
if nodeID not in howfarDB:
#register first point NodeID, lat, lon, time, point
howfarDB[nodeID] = [{'lat': lat, 'lon': lon, 'time': datetime.now()}]
if reset:
return "Tracking reset, new starting point registered🗺️"
else:
return "Starting point registered🗺️"
else:
#de-dupe points if same as last point
if howfarDB[nodeID][-1]['lat'] == lat and howfarDB[nodeID][-1]['lon'] == lon:
return "📍No movement detected yet"
# calculate distance from last point in howfarDB
last_point = howfarDB[nodeID][-1]
lat1 = math.radians(last_point['lat'])
lon1 = math.radians(last_point['lon'])
lat2 = math.radians(lat)
lon2 = math.radians(lon)
dlon = lon2 - lon1
dlat = lat2 - lat1
a = math.sin(dlat / 2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2)**2
c = 2 * math.asin(math.sqrt(a))
r = 6371 # Radius of earth in kilometers
distance_km = c * r
if use_metric:
msg += f"{distance_km:.2f} km"
else:
distance_miles = distance_km * 0.621371
msg += f"{distance_miles:.2f} miles"
# calculate the speed if time difference is more than 1 minute
time_diff = datetime.now() - last_point['time']
if time_diff.total_seconds() > 60:
hours = time_diff.total_seconds() / 3600
if use_metric:
speed = distance_km / hours
speed_str = f"{speed:.2f} km/h"
else:
speed_mph = (distance_km * 0.621371) / hours
speed_str = f"{speed_mph:.2f} mph"
msg += f", travel time: {int(time_diff.total_seconds()//60)} min, Speed: {speed_str}"
#calculate bearing
x = math.sin(dlon) * math.cos(lat2)
y = math.cos(lat1) * math.sin(lat2) - (math.sin(lat1) * math.cos(lat2) * math.cos(dlon))
initial_bearing = math.atan2(x, y)
initial_bearing = math.degrees(initial_bearing)
compass_bearing = (initial_bearing + 360) % 360
msg += f", Bearing from last point: {compass_bearing:.2f}°"
# if points 3+ are within 30 meters of the first point add the area of the polygon
if len(howfarDB[nodeID]) >= 3:
points = []
# loop the howfarDB to get all the points except the current nodeID
for key in howfarDB:
if key != nodeID:
points.append((howfarDB[key][-1]['lat'], howfarDB[key][-1]['lon']))
# loop the howfarDB[nodeID] to get the points
for point in howfarDB[nodeID]:
points.append((point['lat'], point['lon']))
# close the polygon by adding the first point to the end
points.append((howfarDB[nodeID][0]['lat'], howfarDB[nodeID][0]['lon']))
# calculate the area of the polygon
area = 0.0
for i in range(len(points)-1):
lat1 = math.radians(points[i][0])
lon1 = math.radians(points[i][1])
lat2 = math.radians(points[i+1][0])
lon2 = math.radians(points[i+1][1])
area += (lon2 - lon1) * (2 + math.sin(lat1) + math.sin(lat2))
area = area * (6378137 ** 2) / 2.0
area = abs(area) / 1e6 # convert to square kilometers
if use_metric:
msg += f", Area Sq.Km: {area:.2f} sq.km (approx)"
else:
area_miles = area * 0.386102
msg += f", Area Sq.Miles: {area_miles:.2f} sq.mi (approx)"
#calculate the centroid of the polygon
x = 0.0
y = 0.0
z = 0.0
for point in points[:-1]:
lat_rad = math.radians(point[0])
lon_rad = math.radians(point[1])
x += math.cos(lat_rad) * math.cos(lon_rad)
y += math.cos(lat_rad) * math.sin(lon_rad)
z += math.sin(lat_rad)
total_points = len(points) - 1
x /= total_points
y /= total_points
z /= total_points
lon_centroid = math.atan2(y, x)
hyp = math.sqrt(x * x + y * y)
lat_centroid = math.atan2(z, hyp)
lat_centroid = math.degrees(lat_centroid)
lon_centroid = math.degrees(lon_centroid)
msg += f", Centroid: {lat_centroid:.5f}, {lon_centroid:.5f}"
# update the last point in howfarDB
howfarDB[nodeID].append({'lat': lat, 'lon': lon, 'time': datetime.now()})
return msg
+13 -13
View File
@@ -128,21 +128,21 @@ def get_moon(lat=0, lon=0):
illum = moon.phase # 0 = new, 50 = first/last quarter, 100 = full
if illum < 1.0:
moon_phase = 'New Moon🌑'
moon_phase = 'New Moon 🌑'
elif illum < 49:
moon_phase = 'Waxing Crescent🌒'
moon_phase = 'Waxing Crescent 🌒'
elif 49 <= illum < 51:
moon_phase = 'First Quarter🌓'
moon_phase = 'First Quarter 🌓'
elif illum < 99:
moon_phase = 'Waxing Gibbous🌔'
moon_phase = 'Waxing Gibbous 🌔'
elif illum >= 99:
moon_phase = 'Full Moon🌕'
moon_phase = 'Full Moon 🌕'
elif illum > 51:
moon_phase = 'Waning Gibbous🌖'
moon_phase = 'Waning Gibbous 🌖'
elif 51 >= illum > 49:
moon_phase = 'Last Quarter🌗'
moon_phase = 'Last Quarter 🌗'
else:
moon_phase = 'Waning Crescent🌘'
moon_phase = 'Waning Crescent 🌘'
moon_table['phase'] = moon_phase
moon_table['illumination'] = moon.phase
@@ -167,9 +167,9 @@ def get_moon(lat=0, lon=0):
moon_table['next_full_moon'] = local_next_full_moon.strftime('%a %b %d %I:%M%p')
moon_table['next_new_moon'] = local_next_new_moon.strftime('%a %b %d %I:%M%p')
moon_data = "MoonRise:" + moon_table['rise_time'] + "\nSet:" + moon_table['set_time'] + \
"\nPhase:" + moon_table['phase'] + " @:" + str('{0:.2f}'.format(moon_table['illumination'])) + "%" \
+ "\nFullMoon:" + moon_table['next_full_moon'] + "\nNewMoon:" + moon_table['next_new_moon']
moon_data = "MoonRise: " + moon_table['rise_time'] + "\nSet: " + moon_table['set_time'] + \
"\nPhase: " + moon_table['phase'] + " @: " + str('{0:.2f}'.format(moon_table['illumination'])) + "%" \
+ "\nFullMoon: " + moon_table['next_full_moon'] + "\nNewMoon: " + moon_table['next_new_moon']
# if moon is in the sky, add azimuth and altitude
if moon_table['altitude'] > 0:
@@ -206,7 +206,7 @@ def getNextSatellitePass(satellite, lat=0, lon=0):
pass_startAzCompass = pass_json['passes'][0]['startAzCompass']
pass_set_time = datetime.fromtimestamp(pass_time + pass_duration).strftime('%a %d %I:%M%p')
pass__endAzCompass = pass_json['passes'][0]['endAzCompass']
pass_data = f"{satname} @{pass_rise_time} Az:{pass_startAzCompass} for{getPrettyTime(pass_duration)}, MaxEl:{pass_maxEl}° Set@{pass_set_time} Az:{pass__endAzCompass}"
pass_data = f"{satname} @{pass_rise_time} Az: {pass_startAzCompass} for{getPrettyTime(pass_duration)}, MaxEl: {pass_maxEl}° Set @{pass_set_time} Az: {pass__endAzCompass}"
elif pass_json['info']['passescount'] == 0:
satname = pass_json['info']['satname']
pass_data = f"{satname} has no upcoming passes"
@@ -215,5 +215,5 @@ def getNextSatellitePass(satellite, lat=0, lon=0):
pass_data = ERROR_FETCHING_DATA
except Exception as e:
logger.warning(f"System: User supplied value {satellite} unknown or invalid")
pass_data = "Provide NORAD# example use:🛰️satpass 25544,33591"
pass_data = "Provide NORAD# example use: 🛰️ satpass 25544,33591"
return pass_data
+1 -1
View File
@@ -75,7 +75,7 @@ if enableCmdHistory:
if location_enabled:
from modules.locationdata import * # from the spudgunman/meshing-around repo
trap_list = trap_list + trap_list_location
help_message = help_message + ", whereami, wx, rlist"
help_message = help_message + ", whereami, wx, rlist, howfar"
if enableGBalerts and not enableDEalerts:
from modules.globalalert import * # from the spudgunman/meshing-around repo
logger.warning(f"System: GB Alerts not functional at this time need to find a source API")