mirror of
https://github.com/SpudGunMan/meshing-around.git
synced 2026-03-28 17:32:36 +01:00
Compare commits
6 Commits
v1.9.9.3
...
copilot/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17bfb8ec3e | ||
|
|
0cfe4a39ed | ||
|
|
fc5476b5dd | ||
|
|
f40d5b24f6 | ||
|
|
f8782de291 | ||
|
|
74f4cd284c |
@@ -40,7 +40,7 @@ Mesh Bot is a feature-rich Python bot designed to enhance your [Meshtastic](http
|
||||
- **New Node Greetings**: Automatically greet new nodes via text.
|
||||
|
||||
### Interactive AI and Data Lookup
|
||||
- **Weather, Earthquake, River, and Tide Data**: Get local alerts and info from NOAA/USGS; uses Open-Meteo for areas outside NOAA coverage.
|
||||
- **Weather, Earthquake, River, and Tide Data**: Get local alerts and info from NOAA/USGS; uses Open-Meteo for areas outside NOAA coverage. Global tide predictions available via tidepredict library for worldwide locations.
|
||||
- **Wikipedia Search**: Retrieve summaries from Wikipedia.
|
||||
- **OpenWebUI, Ollama LLM Integration**: Query the [Ollama](https://github.com/ollama/ollama/tree/main/docs) AI for advanced responses. Supports RAG (Retrieval Augmented Generation) with Wikipedia/Kiwix context and [OpenWebUI](https://github.com/open-webui/open-webui) integration for enhanced AI capabilities. [LLM Readme](modules/llm.md)
|
||||
- **Satellite Passes**: Find upcoming satellite passes for your location.
|
||||
|
||||
@@ -211,6 +211,11 @@ NOAAalertCount = 2
|
||||
# use Open-Meteo API for weather data not NOAA useful for non US locations
|
||||
UseMeteoWxAPI = False
|
||||
|
||||
# Global Tide Prediction using tidepredict (for non-US locations or offline use)
|
||||
# When enabled, uses tidepredict library for global tide predictions instead of NOAA API
|
||||
# tidepredict uses University of Hawaii's Research Quality Dataset for worldwide coverage
|
||||
useTidePredict = False
|
||||
|
||||
# NOAA Coastal Data Enable NOAA Coastal Waters Forecasts and Tide
|
||||
coastalEnabled = False
|
||||
# Find the correct costal weather directory at https://tgftp.nws.noaa.gov/data/forecasts/marine/coastal/
|
||||
|
||||
19
mesh_bot.py
19
mesh_bot.py
@@ -1428,10 +1428,21 @@ def handle_repeaterQuery(message_from_id, deviceID, channel_number):
|
||||
return "Repeater lookup not enabled"
|
||||
|
||||
def handle_tide(message_from_id, deviceID, channel_number, vox=False):
|
||||
if vox:
|
||||
return get_NOAAtide(str(my_settings.latitudeValue), str(my_settings.longitudeValue))
|
||||
# Check if tidepredict (xtide) is enabled
|
||||
location = get_node_location(message_from_id, deviceID, channel_number)
|
||||
return get_NOAAtide(str(location[0]), str(location[1]))
|
||||
lat = str(location[0])
|
||||
lon = str(location[1])
|
||||
if lat == "0.0" or lon == "0.0":
|
||||
lat = str(my_settings.latitudeValue)
|
||||
lon = str(my_settings.longitudeValue)
|
||||
|
||||
if my_settings.useTidePredict:
|
||||
logger.debug("System: Location: Using tidepredict")
|
||||
return xtide.get_tide_predictions(lat, lon)
|
||||
else:
|
||||
# Fallback to NOAA tide data
|
||||
logger.debug("System: Location: Using NOAA")
|
||||
return get_NOAAtide(str(location[0]), str(location[1]))
|
||||
|
||||
def handle_moon(message_from_id, deviceID, channel_number, vox=False):
|
||||
if vox:
|
||||
@@ -1553,6 +1564,8 @@ def handle_boot(mesh=True):
|
||||
|
||||
if my_settings.coastalEnabled:
|
||||
logger.debug("System: Coastal Forecast and Tide Enabled!")
|
||||
if my_settings.useTidePredict:
|
||||
logger.debug("System: Using Local TidePredict for Tide Data")
|
||||
|
||||
if games_enabled:
|
||||
logger.debug("System: Games Enabled!")
|
||||
|
||||
@@ -287,7 +287,7 @@ The system uses SQLite with four tables:
|
||||
| `wxa` | NOAA alerts |
|
||||
| `wxalert` | NOAA alerts (expanded) |
|
||||
| `mwx` | NOAA Coastal Marine Forecast |
|
||||
| `tide` | NOAA tide info |
|
||||
| `tide` | Tide info (NOAA/tidepredict for global) |
|
||||
| `riverflow` | NOAA river flow info |
|
||||
| `earthquake` | USGS earthquake info |
|
||||
| `valert` | USGS volcano alerts |
|
||||
@@ -299,6 +299,8 @@ The system uses SQLite with four tables:
|
||||
|
||||
Configure in `[location]` section of `config.ini`.
|
||||
|
||||
**Note**: For global tide predictions outside the US, enable `useTidePredict = True` in `config.ini`. See [xtide.md](xtide.md) for setup details.
|
||||
|
||||
Certainly! Here’s a README help section for your `mapHandler` command, suitable for users of your meshbot:
|
||||
|
||||
---
|
||||
|
||||
@@ -175,6 +175,7 @@ def getArtSciRepeaters(lat=0, lon=0):
|
||||
return msg
|
||||
|
||||
def get_NOAAtide(lat=0, lon=0):
|
||||
# get tide data from NOAA for lat/lon
|
||||
station_id = ""
|
||||
location = lat,lon
|
||||
if float(lat) == 0 and float(lon) == 0:
|
||||
|
||||
@@ -315,6 +315,7 @@ try:
|
||||
n2yoAPIKey = config['location'].get('n2yoAPIKey', '') # default empty
|
||||
satListConfig = config['location'].get('satList', '25544').split(',') # default 25544 ISS
|
||||
riverListDefault = config['location'].get('riverList', '').split(',') # default None
|
||||
useTidePredict = config['location'].getboolean('useTidePredict', False) # default False use NOAA
|
||||
coastalEnabled = config['location'].getboolean('coastalEnabled', False) # default False
|
||||
myCoastalZone = config['location'].get('myCoastalZone', None) # default None
|
||||
coastalForecastDays = config['location'].getint('coastalForecastDays', 3) # default 3 days
|
||||
|
||||
@@ -125,6 +125,10 @@ if coastalEnabled:
|
||||
from modules.locationdata import * # from the spudgunman/meshing-around repo
|
||||
trap_list = trap_list + ("mwx","tide",)
|
||||
help_message = help_message + ", mwx, tide"
|
||||
if useTidePredict:
|
||||
from modules import xtide
|
||||
trap_list = trap_list + ("tide",)
|
||||
help_message = help_message + ", tide"
|
||||
|
||||
# BBS Configuration
|
||||
if bbs_enabled:
|
||||
|
||||
@@ -28,7 +28,7 @@ if os.path.isfile(checkall_path):
|
||||
|
||||
|
||||
# List of module names to exclude
|
||||
exclude = ['test_bot','udp', 'system', 'log', 'gpio', 'web',]
|
||||
exclude = ['test_bot','udp', 'system', 'log', 'gpio', 'web','test_xtide',]
|
||||
available_modules = [
|
||||
m.name for m in pkgutil.iter_modules([modules_path])
|
||||
if m.name not in exclude]
|
||||
|
||||
135
modules/test_xtide.py
Normal file
135
modules/test_xtide.py
Normal file
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script for xtide module
|
||||
Tests both NOAA (disabled) and tidepredict (when available) tide predictions
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add parent directory to path
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
def test_xtide_import():
|
||||
"""Test that xtide module can be imported"""
|
||||
print("Testing xtide module import...")
|
||||
try:
|
||||
from modules import xtide
|
||||
print(f"✓ xtide module imported successfully")
|
||||
print(f" - tidepredict available: {xtide.TIDEPREDICT_AVAILABLE}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"✗ Failed to import xtide: {e}")
|
||||
return False
|
||||
|
||||
def test_locationdata_import():
|
||||
"""Test that modified locationdata can be imported"""
|
||||
print("\nTesting locationdata module import...")
|
||||
try:
|
||||
from modules import locationdata
|
||||
print(f"✓ locationdata module imported successfully")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"✗ Failed to import locationdata: {e}")
|
||||
return False
|
||||
|
||||
def test_settings():
|
||||
"""Test that settings has useTidePredict option"""
|
||||
print("\nTesting settings configuration...")
|
||||
try:
|
||||
from modules import settings as my_settings
|
||||
has_setting = hasattr(my_settings, 'useTidePredict')
|
||||
print(f"✓ settings module loaded")
|
||||
print(f" - useTidePredict setting available: {has_setting}")
|
||||
if has_setting:
|
||||
print(f" - useTidePredict value: {my_settings.useTidePredict}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"✗ Failed to load settings: {e}")
|
||||
return False
|
||||
|
||||
def test_noaa_fallback():
|
||||
"""Test NOAA API fallback (without enabling tidepredict)"""
|
||||
print("\nTesting NOAA API (default mode)...")
|
||||
try:
|
||||
from modules import locationdata
|
||||
from modules import settings as my_settings
|
||||
|
||||
# Test with Seattle coordinates (should use NOAA)
|
||||
lat = 47.6062
|
||||
lon = -122.3321
|
||||
|
||||
print(f" Testing with Seattle coordinates: {lat}, {lon}")
|
||||
print(f" useTidePredict = {my_settings.useTidePredict}")
|
||||
|
||||
# Note: This will fail if we can't reach NOAA, but that's expected
|
||||
result = locationdata.get_NOAAtide(str(lat), str(lon))
|
||||
if result and "Error" not in result:
|
||||
print(f"✓ NOAA API returned data")
|
||||
print(f" First 100 chars: {result[:100]}")
|
||||
return True
|
||||
else:
|
||||
print(f"⚠ NOAA API returned: {result[:100]}")
|
||||
return True # Still pass as network might not be available
|
||||
except Exception as e:
|
||||
print(f"⚠ NOAA test encountered expected issue: {e}")
|
||||
return True # Expected in test environment
|
||||
|
||||
def test_parse_coords():
|
||||
"""Test coordinate parsing function"""
|
||||
print("\nTesting coordinate parsing...")
|
||||
try:
|
||||
from modules.xtide import parse_station_coords
|
||||
|
||||
test_cases = [
|
||||
(("43-36S", "172-43E"), (-43.6, 172.71666666666667)),
|
||||
(("02-45N", "072-21E"), (2.75, 72.35)),
|
||||
(("02-45S", "072-21W"), (-2.75, -72.35)),
|
||||
]
|
||||
|
||||
all_passed = True
|
||||
for (lat_str, lon_str), (expected_lat, expected_lon) in test_cases:
|
||||
result_lat, result_lon = parse_station_coords(lat_str, lon_str)
|
||||
if abs(result_lat - expected_lat) < 0.01 and abs(result_lon - expected_lon) < 0.01:
|
||||
print(f" ✓ {lat_str}, {lon_str} -> {result_lat:.2f}, {result_lon:.2f}")
|
||||
else:
|
||||
print(f" ✗ {lat_str}, {lon_str} -> expected {expected_lat}, {expected_lon}, got {result_lat}, {result_lon}")
|
||||
all_passed = False
|
||||
|
||||
return all_passed
|
||||
except Exception as e:
|
||||
print(f"✗ Coordinate parsing test failed: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def main():
|
||||
"""Run all tests"""
|
||||
print("=" * 60)
|
||||
print("xtide Module Test Suite")
|
||||
print("=" * 60)
|
||||
|
||||
results = []
|
||||
results.append(("Import xtide", test_xtide_import()))
|
||||
results.append(("Import locationdata", test_locationdata_import()))
|
||||
results.append(("Settings configuration", test_settings()))
|
||||
results.append(("Parse coordinates", test_parse_coords()))
|
||||
results.append(("NOAA fallback", test_noaa_fallback()))
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Test Results Summary")
|
||||
print("=" * 60)
|
||||
|
||||
passed = sum(1 for _, result in results if result)
|
||||
total = len(results)
|
||||
|
||||
for test_name, result in results:
|
||||
status = "✓ PASS" if result else "✗ FAIL"
|
||||
print(f"{status}: {test_name}")
|
||||
|
||||
print(f"\n{passed}/{total} tests passed")
|
||||
|
||||
return passed == total
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = main()
|
||||
sys.exit(0 if success else 1)
|
||||
129
modules/xtide.md
Normal file
129
modules/xtide.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# xtide Module - Global Tide Predictions
|
||||
|
||||
This module provides global tide prediction capabilities using the [tidepredict](https://github.com/windcrusader/tidepredict) library, which uses the University of Hawaii's Research Quality Dataset for worldwide tide station coverage.
|
||||
|
||||
## Features
|
||||
|
||||
- Global tide predictions (not limited to US locations like NOAA)
|
||||
- Offline predictions once station data is initialized
|
||||
- Automatic selection of nearest tide station
|
||||
- Compatible with existing tide command interface
|
||||
|
||||
## Installation
|
||||
|
||||
1. Install tidepredict library:
|
||||
this takes about 3-500MB of disk
|
||||
|
||||
```bash
|
||||
pip install tidepredict
|
||||
```
|
||||
note: if you see warning about system packages the override for debian OS to install it anyway is..
|
||||
|
||||
```bash
|
||||
pip install tidepredict --break-system-packages
|
||||
```
|
||||
|
||||
2. Enable in `config.ini`:
|
||||
```ini
|
||||
[location]
|
||||
useTidePredict = True
|
||||
```
|
||||
|
||||
## First-Time Setup
|
||||
|
||||
On first use, tidepredict needs to download station data from the University of Hawaii FTP server. This requires internet access and happens automatically when you:
|
||||
|
||||
1. Run the tide command for the first time with `useTidePredict = True`
|
||||
2. Or manually initialize with:
|
||||
```bash
|
||||
python3 -m tidepredict -l <location> -genharm
|
||||
```
|
||||
|
||||
The station data is cached locally in `~/.tidepredict/` for offline use afterward.
|
||||
|
||||
No other downloads will happen automatically, its offline
|
||||
|
||||
## Usage
|
||||
|
||||
Once enabled, the existing `tide` command will automatically use tidepredict for global locations:
|
||||
|
||||
```
|
||||
tide
|
||||
```
|
||||
|
||||
The module will:
|
||||
1. Find the nearest tide station to your GPS coordinates
|
||||
2. Load harmonic constituents for that station
|
||||
3. Calculate tide predictions for today
|
||||
4. Format output compatible with mesh display
|
||||
|
||||
## Configuration
|
||||
|
||||
### config.ini Options
|
||||
|
||||
```ini
|
||||
[location]
|
||||
# Enable global tide predictions using tidepredict
|
||||
useTidePredict = True
|
||||
|
||||
# Standard location settings still apply
|
||||
lat = 48.50
|
||||
lon = -123.0
|
||||
useMetric = False
|
||||
```
|
||||
|
||||
## Fallback Behavior
|
||||
|
||||
If tidepredict is not available or encounters errors, the module will automatically fall back to the NOAA API for US locations.
|
||||
|
||||
## Limitations
|
||||
|
||||
- First-time setup requires internet access to download station database
|
||||
- Station coverage depends on University of Hawaii's dataset
|
||||
- Predictions may be less accurate for locations far from tide stations
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Station database not initialized" error
|
||||
|
||||
This means the station data hasn't been downloaded yet. Ensure internet access and:
|
||||
|
||||
```bash
|
||||
# Test station download
|
||||
python3 -m tidepredict -l Sydney
|
||||
|
||||
# Or manually run initialization
|
||||
python3 -c "from tidepredict import process_station_list; process_station_list.create_station_dataframe()"
|
||||
```
|
||||
|
||||
### "No tide station found nearby"
|
||||
|
||||
The module couldn't find a nearby station. This may happen if:
|
||||
- You're in a location without nearby tide monitoring stations
|
||||
- The station database hasn't been initialized
|
||||
- Network issues prevented loading the station list
|
||||
|
||||
Tide Station Map
|
||||
[https://uhslc.soest.hawaii.edu/network/](https://uhslc.soest.hawaii.edu/network/)
|
||||
- click on Tide Guages
|
||||
- Find yourself on the map
|
||||
- Locate the closest Gauge and its name (typically the city name)
|
||||
|
||||
To manually download data for the station first location the needed station id
|
||||
- `python -m tidepredict -l "Port Angeles"` finds a station
|
||||
- `python -m tidepredict -l "Port Angeles" -genharm` downloads that datafile
|
||||
|
||||
|
||||
|
||||
## Data Source
|
||||
|
||||
Tide predictions are based on harmonic analysis of historical tide data from:
|
||||
- University of Hawaii Sea Level Center (UHSLC)
|
||||
- Research Quality Dataset
|
||||
- Global coverage with 600+ stations
|
||||
|
||||
## References
|
||||
|
||||
- [tidepredict GitHub](https://github.com/windcrusader/tidepredict)
|
||||
- [UHSLC Data](https://uhslc.soest.hawaii.edu/)
|
||||
- [pytides](https://github.com/sam-cox/pytides) - Underlying tide calculation library
|
||||
202
modules/xtide.py
Normal file
202
modules/xtide.py
Normal file
@@ -0,0 +1,202 @@
|
||||
# xtide.py - Global tide prediction using tidepredict library
|
||||
# K7MHI Kelly Keeton 2025
|
||||
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from modules.log import logger
|
||||
import modules.settings as my_settings
|
||||
|
||||
try:
|
||||
from tidepredict import processdata, process_station_list, constants, timefunc
|
||||
from tidepredict.tide import Tide
|
||||
import pandas as pd
|
||||
TIDEPREDICT_AVAILABLE = True
|
||||
except ImportError:
|
||||
TIDEPREDICT_AVAILABLE = False
|
||||
logger.error("xtide: tidepredict module not installed. Install with: pip install tidepredict")
|
||||
|
||||
def get_nearest_station(lat, lon):
|
||||
"""
|
||||
Find the nearest tide station to the given lat/lon coordinates.
|
||||
Returns station code (e.g., 'h001a') or None if not found.
|
||||
"""
|
||||
if not TIDEPREDICT_AVAILABLE:
|
||||
return None
|
||||
|
||||
try:
|
||||
# Read the station list
|
||||
try:
|
||||
stations = pd.read_csv(constants.STATIONFILE)
|
||||
except FileNotFoundError:
|
||||
# If station file doesn't exist, create it (requires network)
|
||||
logger.info("xtide: Creating station database from online source (requires network)")
|
||||
try:
|
||||
stations = process_station_list.create_station_dataframe()
|
||||
except Exception as net_error:
|
||||
logger.error(f"xtide: Failed to download station database: {net_error}")
|
||||
return None
|
||||
|
||||
if stations.empty:
|
||||
logger.error("xtide: No stations found in database")
|
||||
return None
|
||||
|
||||
# Calculate distance to each station
|
||||
# Using simple haversine-like calculation
|
||||
def calc_distance(row):
|
||||
try:
|
||||
# Parse lat/lon from the format like "43-36S", "172-43E"
|
||||
station_lat, station_lon = parse_station_coords(row['Lat'], row['Lon'])
|
||||
|
||||
# Simple distance calculation (not precise but good enough)
|
||||
dlat = lat - station_lat
|
||||
dlon = lon - station_lon
|
||||
return (dlat**2 + dlon**2)**0.5
|
||||
except:
|
||||
return float('inf')
|
||||
|
||||
stations['distance'] = stations.apply(calc_distance, axis=1)
|
||||
|
||||
# Find the nearest station
|
||||
nearest = stations.loc[stations['distance'].idxmin()]
|
||||
|
||||
if nearest['distance'] > 10: # More than ~10 degrees away, might be too far
|
||||
logger.warning(f"xtide: Nearest station is {nearest['distance']:.1f}° away at {nearest['loc_name']}")
|
||||
|
||||
station_code = "h" + nearest['stat_idx'].lower()
|
||||
logger.debug(f"xtide: Found nearest station: {nearest['loc_name']} ({station_code}) at {nearest['distance']:.2f}° away")
|
||||
|
||||
return station_code, nearest['loc_name'], nearest['country']
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"xtide: Error finding nearest station: {e}")
|
||||
return None
|
||||
|
||||
def parse_station_coords(lat_str, lon_str):
|
||||
"""
|
||||
Parse station coordinates from format like "43-36S", "172-43E"
|
||||
Returns tuple of (latitude, longitude) as floats
|
||||
"""
|
||||
try:
|
||||
# Parse latitude
|
||||
lat_parts = lat_str.split('-')
|
||||
lat_deg = float(lat_parts[0])
|
||||
lat_min = float(lat_parts[1][:-1]) # Remove N/S
|
||||
lat_dir = lat_parts[1][-1] # Get N/S
|
||||
lat_val = lat_deg + lat_min/60.0
|
||||
if lat_dir == 'S':
|
||||
lat_val = -lat_val
|
||||
|
||||
# Parse longitude
|
||||
lon_parts = lon_str.split('-')
|
||||
lon_deg = float(lon_parts[0])
|
||||
lon_min = float(lon_parts[1][:-1]) # Remove E/W
|
||||
lon_dir = lon_parts[1][-1] # Get E/W
|
||||
lon_val = lon_deg + lon_min/60.0
|
||||
if lon_dir == 'W':
|
||||
lon_val = -lon_val
|
||||
|
||||
return lat_val, lon_val
|
||||
except Exception as e:
|
||||
logger.debug(f"xtide: Error parsing coordinates {lat_str}, {lon_str}: {e}")
|
||||
return 0.0, 0.0
|
||||
|
||||
def get_tide_predictions(lat=0, lon=0, days=1):
|
||||
"""
|
||||
Get tide predictions for the given location using tidepredict library.
|
||||
Returns formatted string with tide predictions.
|
||||
|
||||
Parameters:
|
||||
- lat: Latitude
|
||||
- lon: Longitude
|
||||
- days: Number of days to predict (default: 1)
|
||||
|
||||
Returns:
|
||||
- Formatted string with tide predictions or error message
|
||||
"""
|
||||
if not TIDEPREDICT_AVAILABLE:
|
||||
return "module not installed, see logs for more ⚓️"
|
||||
|
||||
if float(lat) == 0 and float(lon) == 0:
|
||||
return "No GPS data for tide prediction"
|
||||
|
||||
try:
|
||||
# Find nearest station
|
||||
station_info = get_nearest_station(float(lat), float(lon))
|
||||
if not station_info:
|
||||
return "No tide station found nearby. Network may be required to download station data."
|
||||
|
||||
station_code, station_name, station_country = station_info
|
||||
|
||||
# Load station data
|
||||
station_dict, harmfileloc = process_station_list.read_station_info_file()
|
||||
|
||||
# Check if harmonic data exists for this station
|
||||
if station_code not in station_dict:
|
||||
logger.warning(f"xtide: No harmonic data. python -m tidepredict -l \"{station_name}\" -genharm")
|
||||
return f"Tide data not available for {station_name}. Station database may need initialization."
|
||||
|
||||
# Reconstruct tide model
|
||||
tide = processdata.reconstruct_tide_model(station_dict, station_code)
|
||||
if tide is None:
|
||||
return f"Tide model unavailable for {station_name}"
|
||||
|
||||
# Set up time range (today only)
|
||||
now = datetime.now()
|
||||
start_time = now.strftime("%Y-%m-%d 00:00")
|
||||
end_time = (now + timedelta(days=days)).strftime("%Y-%m-%d 00:00")
|
||||
|
||||
# Create time object
|
||||
timeobj = timefunc.Tidetime(
|
||||
st_time=start_time,
|
||||
en_time=end_time,
|
||||
station_tz=station_dict[station_code].get('tzone', 'UTC')
|
||||
)
|
||||
|
||||
# Get predictions
|
||||
predictions = processdata.predict_plain(tide, station_dict[station_code], 't', timeobj)
|
||||
|
||||
# Format output for mesh
|
||||
lines = predictions.strip().split('\n')
|
||||
if len(lines) > 2:
|
||||
# Skip the header lines and format for mesh display
|
||||
result = f"Tide: {station_name}\n"
|
||||
tide_lines = lines[2:] # Skip first 2 header lines
|
||||
|
||||
# Format each tide prediction
|
||||
for line in tide_lines[:8]: # Limit to 8 entries
|
||||
parts = line.split()
|
||||
if len(parts) >= 4:
|
||||
date_str = parts[0]
|
||||
time_str = parts[1]
|
||||
height = parts[3]
|
||||
tide_type = ' '.join(parts[4:])
|
||||
|
||||
# Convert to 12-hour format if not using zulu time
|
||||
if not my_settings.zuluTime:
|
||||
try:
|
||||
time_obj = datetime.strptime(time_str, "%H%M")
|
||||
hour = time_obj.hour
|
||||
minute = time_obj.minute
|
||||
if hour >= 12:
|
||||
time_str = f"{hour-12 if hour > 12 else 12}:{minute:02d} PM"
|
||||
else:
|
||||
time_str = f"{hour if hour > 0 else 12}:{minute:02d} AM"
|
||||
except:
|
||||
pass
|
||||
|
||||
result += f"{tide_type} {time_str}, {height}\n"
|
||||
|
||||
return result.strip()
|
||||
else:
|
||||
return predictions
|
||||
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"xtide: Station data file not found: {e}")
|
||||
return "Tide station database not initialized. Network access required for first-time setup."
|
||||
except Exception as e:
|
||||
logger.error(f"xtide: Error getting tide predictions: {e}")
|
||||
return f"Error getting tide data: {str(e)}"
|
||||
|
||||
def is_enabled():
|
||||
"""Check if xtide/tidepredict is enabled in config"""
|
||||
return getattr(my_settings, 'useTidePredict', False) and TIDEPREDICT_AVAILABLE
|
||||
Reference in New Issue
Block a user