August 2025 update 3

This commit is contained in:
oasis6212 2025-08-17 21:24:42 -04:00
parent 86a026651e
commit ddc28caba7
7 changed files with 196 additions and 59 deletions

100
README.md
View File

@ -36,8 +36,9 @@
![](img/wind.png)
MeshBot Weather is a spinoff of [MeshBot](https://github.com/868meshbot/meshbot) with a detailed focus on weather. Designed to run on a computer or a
Raspberry Pi with a connected Meshtastic device.
[MeshBot Weather](https://github.com/oasis6212/Meshbot_weather) is a spinoff of [MeshBot](https://github.com/868meshbot/meshbot) that brings you
accurate, real-time forecasts and instant weather alerts. Designed to run on a computer or a
Raspberry Pi with a connected Meshtastic radio.
Our Mission:
@ -62,9 +63,10 @@ Our Mission:
- Forecasts are generated for any location. Not limited to towns or cities.
- Optional firewall, when enabled, the bot will only respond to messages from nodes that have been included in its whitelist.
![](img/automaticgrid.png)
![](img/Newfeatures.png)
![](img/newfeatures1.png)
Thanks [davidfries](https://github.com/davidfries)!
@ -182,6 +184,16 @@ cd meshbot_weather
See above under "How to run the program on various operating systems."
## Location setup for alerts and forecast
You will need to edit the settings.yaml file. Look for:
ALERT_LAT: "37.7654"
ALERT_LON: "-100.0151"
Change these coordinates to match the location you want weather info and alerts for. Do not use more than four digits
past the decimal point.
## Configuration
The ''settings.yaml'' file; it's where you can configure different options. Can be edited in notepad.
@ -195,18 +207,19 @@ MYNODES:
FIREWALL: false
DM_MODE: true
DUTYCYCLE: false
NWS_OFFICE: "BGM"
NWS_GRID_X: "84"
NWS_GRID_Y: "89"
ALERT_LAT: "37.7654"
ALERT_LON: "-100.0151"
NWS_OFFICE: ""
NWS_GRID_X: ""
NWS_GRID_Y: ""
ALERT_CHECK_INTERVAL: 300
ALERT_INCLUDE_DESCRIPTION:
ALERT_CHANNEL_INDEX: 0
FIRST_MESSAGE_DELAY: 0
MESSAGE_DELAY: 15
ENABLE_FULL_ALERT_COMMAND: true
ENABLE_CUSTOM_LOOKUP: true
ENABLE_ALERT_COMMAND: true
SHOW_ALERT_COMMAND_IN_MENU: false
SHOW_CUSTOM_LOOKUP_COMMAND_IN_MENU: false
ENABLE_7DAY_FORECAST: true
ENABLE_5DAY_FORECAST: true
ENABLE_HOURLY_WEATHER: true
@ -235,11 +248,13 @@ Description
- DUTYCYCLE: false: If true, limits itself to 10% Dutycycle
- NWS_OFFICE: NWS_GRID_X: NWS_GRID_Y: #settings for the weather forecast api calls, see below to learn how to set up.
- ALERT_LAT: "34.0522" ALERT_LON: "-118.2433" # Location settings for alerts and forecast, put in the latitude and
longitude of the area you want coverage for. Make sure you only go up to 4 places past the decimal point on each.
- ALERT_LAT: "34.0522" ALERT_LON: "-118.2433" #settings for alerts, put in the latitude, and longitude of the area you
want alerts for. Make sure you only go up to 4 places past the decimal point on each.
- NWS_OFFICE: NWS_GRID_X: NWS_GRID_Y: #Can be left blank. These settings are used for manual entry of the weather
forecast api parameters. May be useful if you want your forecast generation for a different area than your alerts or if
the automatic configuration fails. See below for more info.
- ALERT_CHECK_INTERVAL: # Time in seconds. How often the alert API is called. NWS does not publish allowable limits.
@ -247,7 +262,7 @@ From what I have gathered, they allow up to once a minute for alert checking. Yo
- ALERT_INCLUDE_DESCRIPTION: #Set to false to exclude description from alerts. Descriptions will include alot of detail
such as every county, town, and area affected. You can expect about 4 or 5 messages when description is set to "true" vs
such as every county, town, and area affected. You can expect about 4 to 8 messages when description is set to "true" vs
a single message when set to false.
@ -259,21 +274,24 @@ experimental. Hoping this may help with dropped 1st part of reply's, by giving t
feel free to experiment with different values.
- MESSAGE_DELAY: Delay in seconds between split messages. To short of a delay can cause messages to arrive out of order.
- MESSAGE_DELAY: # Delay in seconds between split messages. To short of a delay can cause messages to arrive out of order.
- ENABLE_FULL_ALERT_COMMAND: #set to false to disable the "alert" command. Can produce up to 8 messages, may want to
disable on a high traffic mesh.
- ENABLE_ALERT_COMMAND: # Set to false to disable the alert request command, automatic alerts will not be affected.
- ENABLE_CUSTOM_LOOKUP: # Enable/disable custom lat/lon lookup via message. More info below.
- SHOW_ALERT_COMMAND_IN_MENU: # When false, hides the command from the menu but keeps it enabled, if enabled.
- SHOW_CUSTOM_LOOKUP_COMMAND_IN_MENU: # Set to false to hide the custom lookup command from the menu. Command is always
accessible.
- ENABLE_7DAY_FORECAST: ENABLE_5DAY_FORECAST: ENABLE_HOURLY_WEATHER: # These calls produce 2 to 4 messages each. If you
are on a high-traffic mesh, you may want to disable these.
- FULL_MENU: true # When true, includes all weather commands. When false, shows only forecast options that return a
- FULL_MENU: # When true, includes all weather commands. When false, shows only forecast options that return a
single message.
@ -303,7 +321,33 @@ unique the better. This is what NWS uses instead of an API key.
Gives you the opportunity to fix the issue and stop getting throttled.
## How to get your NWS_OFFICE, NWS_GRID_X, and NWS_GRID_Y
## Closing the program
Press "Ctrl + c" once to tell the program to close. If Node shutdown is enabled in the settings.yaml The program will
command the node to shutdown and give it time to do so.
Pressing "Ctrl + c" twice will force a hard exit of the program.
## Using the "Loc" custom location lookup command.
The loc command allows you to get a forecast for an area that is not the bots primary location. Input the locations
latitude and longitude along with the forecast type you want.
Full command example: "loc 39.0453/-98.2077 hourly"
Structure: loc {Latitude/longitude Command} command can be any of the regular commands like wind, 2day, 7day etc.
To ensure compatibility of your coordinates, only use up to 4 digits past the decimal point like in the example.
## Advance setup: How to get your NWS_OFFICE, NWS_GRID_X, and NWS_GRID_Y
Note: As of the latest update, these values are automatically set based on the ALERT_LAT and ALERT_LON coordinates.
Leave the grid parameters blank to enable automatic configuration. Only enter grid coordinates if you want to override
the automatic settings.
To get your NWS office and grid coordinates:
1. Go to (https://weather.gov)
2. Enter your address
@ -327,24 +371,6 @@ For the alert settings in the settings.yaml file, enter your gps coordinates or
earlier in this process. Use no more than four digits after the decimal point.
## Closing the program
Press "Ctrl + c" once to tell the program to close. If Node shutdown is enabled in the settings.yaml The program will
command the node to shutdown and give it time to do so.
Pressing "Ctrl + c" twice will force a hard exit of the program.
## Using the "Loc" custom location lookup command.
The loc command allows you to get a forecast for an area that is not the bots primary location. Input the locations
latitude and longitude along with the forecast type you want.
Full command example: "loc 39.0453/-98.2077 hourly"
Structure: loc {Latitude/longitude Command} command can be any of the regular commands like wind, 2day, 7day etc.
To ensure compatibility of your coordinates, only use up to 4 digits past the decimal point like in the example.
## API Handling details
To prevent excessive api calls, the bot will check if it currently has the data being requested and if it is
@ -388,6 +414,4 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
This project is neither endorsed by nor supported by Meshtastic.
Meshtastic® is a registered trademark of Meshtastic LLC. Meshtastic software components are released under various
licenses, see GitHub for details. No warranty is provided - use at your own risk.

BIN
img/Automaticgrid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

View File

Before

Width:  |  Height:  |  Size: 322 KiB

After

Width:  |  Height:  |  Size: 322 KiB

View File

@ -34,6 +34,9 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
print(r"""
-----------------Welcome to Meshbot Weather------------------
""")
@ -59,6 +62,89 @@ except ImportError:
import serial.tools.list_ports
import requests
def infer_nws_grid_from_coords(settings, logger=None):
"""
Fill in NWS_OFFICE, NWS_GRID_X, NWS_GRID_Y using ALERT_LAT and ALERT_LON.
Only runs if any NWS_* value is missing or empty. Updates settings in-place.
Returns True if values were inferred and updated; otherwise False.
"""
# If already complete, nothing to do
have_office = bool(str(settings.get("NWS_OFFICE", "")).strip())
have_x = bool(str(settings.get("NWS_GRID_X", "")).strip())
have_y = bool(str(settings.get("NWS_GRID_Y", "")).strip())
if have_office and have_x and have_y:
office = str(settings.get("NWS_OFFICE", "")).strip()
grid_x = str(settings.get("NWS_GRID_X", "")).strip()
grid_y = str(settings.get("NWS_GRID_Y", "")).strip()
msg = f"Using NWS grid from settings.yaml: office={office}, grid=({grid_x}, {grid_y})"
logger_obj = globals().get("logger")
if logger_obj:
logger_obj.info(msg)
else:
print(msg)
return False
# Need lat/lon for inference
lat_raw = settings.get("ALERT_LAT")
lon_raw = settings.get("ALERT_LON")
if not lat_raw or not lon_raw:
if logger:
logger.warning("Cannot determine NWS grid: ALERT_LAT/ALERT_LON not set.")
return False
# Prepare request
try:
lat = float(lat_raw)
lon = float(lon_raw)
except (TypeError, ValueError):
if logger:
logger.warning("Cannot infer NWS grid: ALERT_LAT/ALERT_LON are not valid numbers.")
return False
lat_s = f"{lat:.4f}"
lon_s = f"{lon:.4f}"
url = f"https://api.weather.gov/points/{lat_s},{lon_s}"
user_agent_app = str(settings.get("USER_AGENT_APP", "meshbot-weather"))
user_agent_email = str(settings.get("USER_AGENT_EMAIL", "contact@example.com"))
headers = {
"Accept": "application/geo+json",
"User-Agent": f"({user_agent_app}, {user_agent_email})",
}
try:
resp = requests.get(url, headers=headers, timeout=10)
resp.raise_for_status()
data = resp.json()
props = data.get("properties", {}) or {}
office = props.get("gridId")
grid_x = props.get("gridX")
grid_y = props.get("gridY")
if not office or grid_x is None or grid_y is None:
if logger:
logger.warning("NWS Points API did not return grid info; leaving NWS_* unchanged.")
return False
# Update as strings to match settings.yaml style
settings["NWS_OFFICE"] = str(office)
settings["NWS_GRID_X"] = str(grid_x)
settings["NWS_GRID_Y"] = str(grid_y)
if logger:
logger.info(f"NWS grid auto-config: office={office}, x={grid_x}, y={grid_y}")
return True
except requests.RequestException as e:
if logger:
logger.warning(f"Failed to fetch NWS grid from Points API: {e}")
return False
from modules.temperature_24hour import Temperature24HourFetcher
from modules.forecast_2day import Forecast2DayFetcher
from modules.hourly_weather import EmojiWeatherFetcher
@ -107,11 +193,22 @@ alerts = None
with open("settings.yaml", "r") as file:
settings = yaml.safe_load(file)
ALERT_LAT = settings.get("ALERT_LAT")
ALERT_LON = settings.get("ALERT_LON")
logger.info(f"ALERT_LAT:{ALERT_LAT} ALERT_LON:{ALERT_LON}")
infer_nws_grid_from_coords(settings, logger=logger if 'logger' in globals() else None)
MYNODES = settings.get("MYNODES")
DM_MODE = settings.get("DM_MODE")
FIREWALL = settings.get("FIREWALL")
DUTYCYCLE = settings.get("DUTYCYCLE")
NWS_OFFICE = settings.get("NWS_OFFICE", "HNX")
NWS_GRID_X = settings.get("NWS_GRID_X", "67")
NWS_GRID_Y = settings.get("NWS_GRID_Y", "80")
@ -120,9 +217,14 @@ USER_AGENT_APP = settings.get("USER_AGENT_APP", "myweatherapp")
USER_AGENT_EMAIL = settings.get("USER_AGENT_EMAIL", "contact@example.com")
USER_AGENT = f"({USER_AGENT_APP}, {USER_AGENT_EMAIL})"
logger.info(f"DUTYCYCLE: {DUTYCYCLE}")
logger.info(f"DM_MODE: {DM_MODE}")
logger.info(f"FIREWALL: {FIREWALL}")
#logger.info(f"DUTYCYCLE: {DUTYCYCLE}")
#logger.info(f"DM_MODE: {DM_MODE}")
#logger.info(f"FIREWALL: {FIREWALL}")
#logger.info(f"MYNODES: {MYNODES}")
transmission_count = 0
cooldown = False
@ -336,10 +438,20 @@ def message_listener(packet, interface):
"rain - 24h precipitation\n" \
"temp - 24h temperature\n"
# Add alert command if enabled
if settings.get('ENABLE_FULL_ALERT_COMMAND', True):
if settings.get("ENABLE_ALERT_COMMAND", True) and settings.get(
"SHOW_ALERT_COMMAND_IN_MENU", True):
menu_text_2 += "alert - show active alerts\n"
if settings.get('ENABLE_CUSTOM_LOOKUP', False):
if settings.get('SHOW_CUSTOM_LOOKUP_COMMAND_IN_MENU', True):
menu_text_2 += "loc lat/lon - custom location lookup\n"
#check if both show_alert and loc command are disabled
if not settings.get('SHOW_ALERT_COMMAND_IN_MENU', True) and not settings.get(
'SHOW_CUSTOM_LOOKUP_COMMAND_IN_MENU',
False):
combined_menu = f"{menu_text_1}\n{menu_text_2}".strip()
# If both commands are disabled send menu without using split_message
interface.sendText(combined_menu,wantAck=True, destinationId=sender_id)
return
if settings.get('FULL_MENU', True):
combined_menu = menu_text_1 + "\n" + menu_text_2
messages = split_message(combined_menu, message_type="Menu")
@ -350,10 +462,10 @@ def message_listener(packet, interface):
"4day - 4 day forecast\n" \
"temp - 24h temperature\n" \
"rain - 24h precipitation"
if settings.get('ENABLE_FULL_ALERT_COMMAND', True):
if settings.get('ENABLE_ALERT_COMMAND', True):
simple_menu += "\nalert - show active alerts"
if settings.get('ENABLE_CUSTOM_LOOKUP', False):
simple_menu += "loc lat/lon - custom location lookup"
simple_menu += "\nloc lat/lon - custom location lookup"
messages = split_message(simple_menu, message_type="Menu")
send_message_sequence(messages, message_type="Menu")
elif "loc" in message:
@ -447,7 +559,7 @@ def message_listener(packet, interface):
if alerts:
if not alerts.broadcast_full_alert(sender_id):
time.sleep(first_message_delay)
if not settings.get('ENABLE_FULL_ALERT_COMMAND', True):
if not settings.get('ENABLE_ALERT_COMMAND', True):
messages = split_message(
"The full-alert command is disabled in settings.", message_type="Alert"
)
@ -477,7 +589,7 @@ def message_listener(packet, interface):
def signal_handler(sig, frame):
"""Perform a graceful shutdown when CTRL+C is pressed"""
global interface
logger.info("\nInitiating shutdown...")
logger.info("\nClosing program. Please wait...")
try:
if interface is not None:
if settings.get('SHUTDOWN_NODE_ON_EXIT', False):
@ -492,7 +604,7 @@ def signal_handler(sig, frame):
logger.error(f"Error sending shutdown command: {e}")
else:
logger.info("Node shutdown disabled in settings, skipping shutdown command")
logger.info("Node shutdown disabled in settings, skipping sending power off command.")
logger.info("Closing Meshtastic interface...")
interface.close()
@ -536,7 +648,7 @@ def main():
logger.info("No serial ports found.")
exit(0)
logger.info(f"Press CTRL-C to terminate the program")
logger.info(f"Press CTRL-C to close the program")
# Create interface
if args.host:

View File

@ -81,7 +81,7 @@ class WeatherAlerts:
def broadcast_full_alert(self, destination_id):
"""Broadcast the full alert information including description."""
# Check if full-alert command is enabled
if not self.settings.get('ENABLE_FULL_ALERT_COMMAND', True):
if not self.settings.get('ENABLE_ALERT_COMMAND', True):
return False # Do nothing if full-alert command is disabled
# Check if there's a current alert

View File

@ -1,21 +1,22 @@
MYNODES:
- "1234567890" #these are examples, fill in with your node numbers if needed, you can add lines as needed.
- "1234567890"
FIREWALL: false # If true, only responds to node ids listed under "MYNODES:"
FIREWALL: false # If true, only responds to node ids listed under "MYNODES"
DM_MODE: true # If true, bot responds to direct messages only. Recommend not changing this
DUTYCYCLE: false # If true, will limit to 10% duty cycle
NWS_OFFICE: "BGM" # Location settings for the weather forecast api calls, see readme for details
NWS_GRID_X: "84"
NWS_GRID_Y: "89"
ALERT_LAT: "37.7654" # Location settings for alerts, use your coordinates. No more than 4 digits past the decimal point
ALERT_LAT: "37.7654" # Primary location settings for alerts and forecast. No more than 4 digits past the decimal point
ALERT_LON: "-100.0151"
NWS_OFFICE: "" #Advance setup options, leave blank unless needed. See readme for details.
NWS_GRID_X: ""
NWS_GRID_Y: ""
ALERT_CHECK_INTERVAL: 300 # Time in seconds between alert checks (default: 300 = 5 minutes)
ALERT_INCLUDE_DESCRIPTION: false # Set to false to exclude the full description from automatically issued alerts
ALERT_CHANNEL_INDEX: 0 # Channel index for weather alerts, default is 0 (first channel)
FIRST_MESSAGE_DELAY: 0 # Delay in seconds between receiving a request and sending the first message back.
MESSAGE_DELAY: 15 # Delay in seconds between subsequent messages of a multi-message response
ENABLE_FULL_ALERT_COMMAND: true # Set to false to disable the alert request command
ENABLE_CUSTOM_LOOKUP: true # Enable/disable custom lat/lon lookup via message
ENABLE_ALERT_COMMAND: true # Set to false to disable the alert request command, automatic alerts will not be affected.
SHOW_ALERT_COMMAND_IN_MENU: false # When false, hides the command from the menu but keeps it enabled, if enabled.
SHOW_CUSTOM_LOOKUP_COMMAND_IN_MENU: true # When false, hides the command from the menu, but it is always enabled
ENABLE_7DAY_FORECAST: true # Set to false to disable 7-day forecast module
ENABLE_5DAY_FORECAST: true # Set to false to disable 5-day forecast module
ENABLE_HOURLY_WEATHER: true # Set to false to disable hourly weather module