mirror of
https://github.com/oasis6212/Meshbot_weather.git
synced 2025-12-10 00:06:12 -06:00
Merge pull request #4 from davidfries/fries_features_customloc_docker
Add Dockerfile and implement custom location lookup in meshbot.py
This commit is contained in:
commit
29983db0c3
33
Dockerfile
Normal file
33
Dockerfile
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Efficient Dockerfile for Meshbot Weather
|
||||||
|
FROM python:3.13-slim
|
||||||
|
|
||||||
|
# Set environment variables for Python
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
# Create and set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install system dependencies (for serial, etc.)
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential \
|
||||||
|
libffi-dev \
|
||||||
|
libssl-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Copy only requirements first for better caching
|
||||||
|
COPY requirements.txt ./
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Copy the rest of the code
|
||||||
|
COPY meshbot.py ./
|
||||||
|
COPY settings.yaml ./
|
||||||
|
COPY modules/ ./modules/
|
||||||
|
COPY img/ ./img/
|
||||||
|
|
||||||
|
# Create a non-root user for security
|
||||||
|
RUN useradd -m meshbotuser
|
||||||
|
USER meshbotuser
|
||||||
|
|
||||||
|
# Entrypoint
|
||||||
|
ENTRYPOINT ["python", "meshbot.py"]
|
||||||
183
meshbot.py
183
meshbot.py
@ -191,29 +191,26 @@ def reset_cooldown():
|
|||||||
threading.Timer(240.0, reset_cooldown).start()
|
threading.Timer(240.0, reset_cooldown).start()
|
||||||
|
|
||||||
|
|
||||||
def split_message(message, max_length=175, message_type="Hourly"):
|
def split_message(message, max_length=200, message_type="Hourly", start_index=1, total_count=None):
|
||||||
lines = message.split('\n')
|
lines = message.split('\n')
|
||||||
messages = []
|
messages = []
|
||||||
current_message = []
|
current_message = []
|
||||||
current_length = 0
|
current_length = 0
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
line_length = len(line.encode('utf-8')) + (1 if current_message else 0)
|
line_length = len(line.encode('utf-8')) + (1 if current_message else 0)
|
||||||
|
|
||||||
if current_length + line_length > max_length:
|
if current_length + line_length > max_length:
|
||||||
messages.append('\n'.join(current_message))
|
messages.append('\n'.join(current_message))
|
||||||
current_message = []
|
current_message = []
|
||||||
current_length = 0
|
current_length = 0
|
||||||
|
|
||||||
current_message.append(line)
|
current_message.append(line)
|
||||||
current_length += line_length
|
current_length += line_length
|
||||||
|
|
||||||
if current_message:
|
if current_message:
|
||||||
messages.append('\n'.join(current_message))
|
messages.append('\n'.join(current_message))
|
||||||
|
# If total_count is provided, use it for page count
|
||||||
|
if total_count is None:
|
||||||
|
total_count = len(messages)
|
||||||
for i in range(len(messages)):
|
for i in range(len(messages)):
|
||||||
messages[i] = f"--({i + 1}/{len(messages)}) {message_type}\n" + messages[i]
|
messages[i] = f"--({start_index + i}/{total_count}) {message_type}\n" + messages[i]
|
||||||
|
|
||||||
return messages
|
return messages
|
||||||
|
|
||||||
|
|
||||||
@ -229,6 +226,51 @@ def get_wind_24hour():
|
|||||||
return wind_24hour_info
|
return wind_24hour_info
|
||||||
|
|
||||||
|
|
||||||
|
def get_custom_lookup(message):
|
||||||
|
"""
|
||||||
|
Parse message like 'loc lat/lon command' and return the weather info for that location.
|
||||||
|
Uses api.weather.gov /points/{lat},{lon} to get grid/office.
|
||||||
|
Supported commands: 2day, 4day, 5day, 7day, hourly, temp, rain, wind
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
import requests
|
||||||
|
match = re.match(r"loc\s+([+-]?\d+\.\d+)/([+-]?\d+\.\d+)\s*(\w+)?", message)
|
||||||
|
if not match:
|
||||||
|
return "Invalid location format. Use 'loc lat/lon [command]'."
|
||||||
|
lat, lon, command = match.groups()
|
||||||
|
# Get NWS grid info
|
||||||
|
try:
|
||||||
|
url = f"https://api.weather.gov/points/{lat},{lon}"
|
||||||
|
resp = requests.get(url, headers={"User-Agent": USER_AGENT})
|
||||||
|
resp.raise_for_status()
|
||||||
|
data = resp.json()
|
||||||
|
office = data['properties']['cwa']
|
||||||
|
grid_x = str(data['properties']['gridX'])
|
||||||
|
grid_y = str(data['properties']['gridY'])
|
||||||
|
except Exception as e:
|
||||||
|
return f"Entered grid is invalid or not found for {lat},{lon}: Not part of NWS coverage area."
|
||||||
|
# Create a temporary weather manager for this location
|
||||||
|
temp_manager = WeatherDataManager(office, grid_x, grid_y, USER_AGENT)
|
||||||
|
# Map commands to fetchers
|
||||||
|
fetchers = {
|
||||||
|
'2day': lambda: Forecast2DayFetcher(temp_manager).get_daily_weather(),
|
||||||
|
'4day': lambda: Forecast4DayFetcher(temp_manager).get_weekly_emoji_weather(),
|
||||||
|
'5day': lambda: NWSWeatherFetcher5Day(temp_manager).get_daily_weather(),
|
||||||
|
'7day': lambda: Forecast7DayFetcher(temp_manager).get_weekly_emoji_weather(),
|
||||||
|
'hourly': lambda: EmojiWeatherFetcher(temp_manager).get_emoji_weather(),
|
||||||
|
'temp': lambda: Temperature24HourFetcher(temp_manager).get_temperature_24hour(),
|
||||||
|
'rain': lambda: RainChanceFetcher(temp_manager).get_rain_chance(),
|
||||||
|
'wind': lambda: Wind24HourFetcher(temp_manager).get_wind_24hour(),
|
||||||
|
}
|
||||||
|
if command and command in fetchers:
|
||||||
|
result = fetchers[command]()
|
||||||
|
if isinstance(result, list):
|
||||||
|
return '\n'.join(result)
|
||||||
|
return str(result)
|
||||||
|
else:
|
||||||
|
return f"Custom location lookup: lat={lat}, lon={lon}, office={office}, grid=({grid_x},{grid_y})\nSupported commands: {', '.join(fetchers.keys())}"
|
||||||
|
|
||||||
|
|
||||||
def message_listener(packet, interface):
|
def message_listener(packet, interface):
|
||||||
global transmission_count
|
global transmission_count
|
||||||
global cooldown
|
global cooldown
|
||||||
@ -278,74 +320,105 @@ def message_listener(packet, interface):
|
|||||||
if "test" in message:
|
if "test" in message:
|
||||||
transmission_count += 1
|
transmission_count += 1
|
||||||
time.sleep(first_message_delay)
|
time.sleep(first_message_delay)
|
||||||
|
# Send the temperature message directly without split_message
|
||||||
interface.sendText(" ACK", wantAck=True, destinationId=sender_id)
|
interface.sendText(" ACK", wantAck=True, destinationId=sender_id)
|
||||||
elif "?" in message or "menu" in message:
|
elif "?" in message or "menu" in message:
|
||||||
transmission_count += 1
|
transmission_count += 1
|
||||||
time.sleep(first_message_delay)
|
time.sleep(first_message_delay)
|
||||||
|
menu_text_1 = " --Multi-Message--\n" \
|
||||||
|
"hourly - 24h outlook\n" \
|
||||||
|
"7day - 7 day simple\n" \
|
||||||
|
"5day - 5 day detailed\n" \
|
||||||
|
"wind - 24h wind\n"
|
||||||
|
menu_text_2 = " --Single Message--\n" \
|
||||||
|
"2day - 2 day detailed\n" \
|
||||||
|
"4day - 4 day simple\n" \
|
||||||
|
"rain - 24h precipitation\n" \
|
||||||
|
"temp - 24h temperature\n"
|
||||||
|
# Add alert command if enabled
|
||||||
|
if settings.get('ENABLE_FULL_ALERT_COMMAND', True):
|
||||||
|
menu_text_2 += "alert - show active alerts\n"
|
||||||
|
if settings.get('ENABLE_CUSTOM_LOOKUP', False):
|
||||||
|
menu_text_2 += "loc lat/lon - custom location lookup\n"
|
||||||
if settings.get('FULL_MENU', True):
|
if settings.get('FULL_MENU', True):
|
||||||
interface.sendText(
|
combined_menu = menu_text_1 + "\n" + menu_text_2
|
||||||
" --Multi-Message--\n"
|
messages = split_message(combined_menu, message_type="Menu")
|
||||||
"hourly - 24h outlook\n"
|
send_message_sequence(messages, message_type="Menu")
|
||||||
"7day - 7 day simple\n"
|
|
||||||
"5day - 5 day detailed\n"
|
|
||||||
"wind - 24h wind\n\n"
|
|
||||||
" --Single Message--\n"
|
|
||||||
"2day - 2 day detailed\n"
|
|
||||||
"4day - 4 day simple\n"
|
|
||||||
"rain - 24h precipitation\n"
|
|
||||||
"temp - 24h temperature\n"
|
|
||||||
, wantAck=True, destinationId=sender_id)
|
|
||||||
else:
|
else:
|
||||||
interface.sendText(
|
simple_menu = " --Weather Commands--\n" \
|
||||||
" --Weather Commands--\n"
|
"2day - 2 day forecast\n" \
|
||||||
"2day - 2 day forecast\n"
|
"4day - 4 day forecast\n" \
|
||||||
"4day - 4 day forecast\n"
|
"temp - 24h temperature\n" \
|
||||||
"temp - 24h temperature\n"
|
|
||||||
"rain - 24h precipitation"
|
"rain - 24h precipitation"
|
||||||
, wantAck=True, destinationId=sender_id)
|
if settings.get('ENABLE_FULL_ALERT_COMMAND', True):
|
||||||
|
simple_menu += "\nalert - show active alerts"
|
||||||
|
if settings.get('ENABLE_CUSTOM_LOOKUP', False):
|
||||||
|
simple_menu += "loc lat/lon - custom location lookup"
|
||||||
|
messages = split_message(simple_menu, message_type="Menu")
|
||||||
|
send_message_sequence(messages, message_type="Menu")
|
||||||
|
elif "loc" in message:
|
||||||
|
transmission_count += 1
|
||||||
|
time.sleep(first_message_delay)
|
||||||
|
custom_lookup_result = get_custom_lookup(message)
|
||||||
|
messages = split_message(str(custom_lookup_result), message_type="Custom")
|
||||||
|
send_message_sequence(messages, message_type="Custom")
|
||||||
elif "temp" in message:
|
elif "temp" in message:
|
||||||
transmission_count += 1
|
transmission_count += 1
|
||||||
time.sleep(first_message_delay)
|
time.sleep(first_message_delay)
|
||||||
|
# Send the temperature message directly without split_message
|
||||||
interface.sendText(get_temperature_24hour(), wantAck=True, destinationId=sender_id)
|
interface.sendText(get_temperature_24hour(), wantAck=True, destinationId=sender_id)
|
||||||
|
|
||||||
elif "2day" in message:
|
elif "2day" in message:
|
||||||
transmission_count += 1
|
transmission_count += 1
|
||||||
time.sleep(first_message_delay)
|
time.sleep(first_message_delay)
|
||||||
|
# Send the 2-day forecast directly without split_message
|
||||||
interface.sendText(get_forecast_2day(), wantAck=True, destinationId=sender_id)
|
interface.sendText(get_forecast_2day(), wantAck=True, destinationId=sender_id)
|
||||||
|
|
||||||
elif "hourly" in message:
|
elif "hourly" in message:
|
||||||
if settings.get('ENABLE_HOURLY_WEATHER', True):
|
if settings.get('ENABLE_HOURLY_WEATHER', True):
|
||||||
transmission_count += 1
|
transmission_count += 1
|
||||||
weather_data = get_emoji_weather()
|
weather_data = get_emoji_weather()
|
||||||
messages = split_message(weather_data)
|
messages = split_message(weather_data, message_type="Hourly")
|
||||||
send_message_sequence(messages)
|
send_message_sequence(messages, message_type="Hourly")
|
||||||
else:
|
else:
|
||||||
time.sleep(first_message_delay)
|
time.sleep(first_message_delay)
|
||||||
interface.sendText("Hourly weather module is disabled.", wantAck=True, destinationId=sender_id)
|
messages = split_message("Hourly weather module is disabled.", message_type="Hourly")
|
||||||
|
send_message_sequence(messages, message_type="Hourly")
|
||||||
elif "rain" in message:
|
elif "rain" in message:
|
||||||
transmission_count += 1
|
transmission_count += 1
|
||||||
time.sleep(first_message_delay)
|
time.sleep(first_message_delay)
|
||||||
|
# Send the rain message directly without split_message
|
||||||
interface.sendText(get_rain_chance(), wantAck=True, destinationId=sender_id)
|
interface.sendText(get_rain_chance(), wantAck=True, destinationId=sender_id)
|
||||||
|
|
||||||
elif "5day" in message:
|
elif "5day" in message:
|
||||||
if settings.get('ENABLE_5DAY_FORECAST', True):
|
if settings.get('ENABLE_5DAY_FORECAST', True):
|
||||||
transmission_count += 1
|
transmission_count += 1
|
||||||
weather_messages = nws_weather_fetcher_5day.get_daily_weather()
|
weather_messages = nws_weather_fetcher_5day.get_daily_weather()
|
||||||
send_message_sequence(weather_messages)
|
messages = split_message('\n'.join(weather_messages), message_type="5day")
|
||||||
|
send_message_sequence(messages, message_type="5day")
|
||||||
else:
|
else:
|
||||||
time.sleep(first_message_delay)
|
time.sleep(first_message_delay)
|
||||||
interface.sendText("5-day forecast module is disabled.", wantAck=True, destinationId=sender_id)
|
messages = split_message("5-day forecast module is disabled.", message_type="5day")
|
||||||
|
send_message_sequence(messages, message_type="5day")
|
||||||
elif "4day" in message:
|
elif "4day" in message:
|
||||||
transmission_count += 1
|
transmission_count += 1
|
||||||
time.sleep(first_message_delay)
|
time.sleep(first_message_delay)
|
||||||
|
# Send the 4-day forecast directly without split_message
|
||||||
interface.sendText(get_forecast_4day(), wantAck=True, destinationId=sender_id)
|
interface.sendText(get_forecast_4day(), wantAck=True, destinationId=sender_id)
|
||||||
|
|
||||||
elif "wind" in message:
|
elif "wind" in message:
|
||||||
transmission_count += 1
|
transmission_count += 1
|
||||||
weather_data = wind_24hour.get_wind_24hour()
|
weather_data = wind_24hour.get_wind_24hour()
|
||||||
if isinstance(weather_data, list):
|
if isinstance(weather_data, list):
|
||||||
weather_text = '\n'.join(weather_data)
|
weather_text = '\n'.join(weather_data)
|
||||||
messages = split_message(weather_text, max_length=180, message_type="Wind")
|
messages = split_message(weather_text, message_type="Wind")
|
||||||
send_message_sequence(messages)
|
send_message_sequence(messages, message_type="Wind")
|
||||||
else:
|
else:
|
||||||
|
|
||||||
|
|
||||||
time.sleep(first_message_delay)
|
time.sleep(first_message_delay)
|
||||||
interface.sendText(weather_data, wantAck=True, destinationId=sender_id)
|
messages = split_message(weather_data, message_type="Wind")
|
||||||
|
send_message_sequence(messages, message_type="Wind")
|
||||||
elif "advertise" in message:
|
elif "advertise" in message:
|
||||||
transmission_count += 1
|
transmission_count += 1
|
||||||
interface.sendText(
|
interface.sendText(
|
||||||
@ -355,15 +428,17 @@ def message_listener(packet, interface):
|
|||||||
wantAck=True,
|
wantAck=True,
|
||||||
destinationId="^all"
|
destinationId="^all"
|
||||||
)
|
)
|
||||||
|
|
||||||
elif "7day" in message:
|
elif "7day" in message:
|
||||||
if settings.get('ENABLE_7DAY_FORECAST', True):
|
if settings.get('ENABLE_7DAY_FORECAST', True):
|
||||||
transmission_count += 1
|
transmission_count += 1
|
||||||
weather_data = forecast_7day.get_weekly_emoji_weather()
|
weather_data = forecast_7day.get_weekly_emoji_weather()
|
||||||
messages = split_message(weather_data, message_type="7day")
|
messages = split_message(weather_data, message_type="7day")
|
||||||
send_message_sequence(messages)
|
send_message_sequence(messages, message_type="7day")
|
||||||
else:
|
else:
|
||||||
time.sleep(first_message_delay)
|
time.sleep(first_message_delay)
|
||||||
interface.sendText("7-day forecast module is disabled.", wantAck=True, destinationId=sender_id)
|
messages = split_message("7-day forecast module is disabled.", message_type="7day")
|
||||||
|
send_message_sequence(messages, message_type="7day")
|
||||||
elif "alert-status" in message:
|
elif "alert-status" in message:
|
||||||
transmission_count += 1
|
transmission_count += 1
|
||||||
interface.sendText(get_weather_alert_status(), wantAck=True, destinationId=sender_id)
|
interface.sendText(get_weather_alert_status(), wantAck=True, destinationId=sender_id)
|
||||||
@ -373,17 +448,15 @@ def message_listener(packet, interface):
|
|||||||
if not alerts.broadcast_full_alert(sender_id):
|
if not alerts.broadcast_full_alert(sender_id):
|
||||||
time.sleep(first_message_delay)
|
time.sleep(first_message_delay)
|
||||||
if not settings.get('ENABLE_FULL_ALERT_COMMAND', True):
|
if not settings.get('ENABLE_FULL_ALERT_COMMAND', True):
|
||||||
interface.sendText(
|
messages = split_message(
|
||||||
"The full-alert command is disabled in settings.",
|
"The full-alert command is disabled in settings.", message_type="Alert"
|
||||||
wantAck=True,
|
|
||||||
destinationId=sender_id
|
|
||||||
)
|
)
|
||||||
|
send_message_sequence(messages, message_type="Alert")
|
||||||
else:
|
else:
|
||||||
interface.sendText(
|
messages = split_message(
|
||||||
"No active alerts at this time.",
|
"No active alerts at this time.", message_type="Alert"
|
||||||
wantAck=True,
|
|
||||||
destinationId=sender_id
|
|
||||||
)
|
)
|
||||||
|
send_message_sequence(messages, message_type="Alert")
|
||||||
else:
|
else:
|
||||||
# If it's a DM but doesn't match any command, send a random help message
|
# If it's a DM but doesn't match any command, send a random help message
|
||||||
if is_direct_message:
|
if is_direct_message:
|
||||||
@ -393,24 +466,13 @@ def message_listener(packet, interface):
|
|||||||
wantAck=True,
|
wantAck=True,
|
||||||
destinationId=sender_id
|
destinationId=sender_id
|
||||||
)
|
)
|
||||||
|
|
||||||
if transmission_count >= 11 and DUTYCYCLE == True:
|
|
||||||
if not cooldown:
|
|
||||||
interface.sendText(
|
|
||||||
"❌ Bot has reached duty cycle, entering cool down... ❄",
|
|
||||||
wantAck=False,
|
|
||||||
)
|
|
||||||
logger.info("Cooldown enabled.")
|
|
||||||
cooldown = True
|
|
||||||
logger.info(
|
|
||||||
"Duty cycle limit reached. Please wait before transmitting again."
|
|
||||||
)
|
|
||||||
|
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
node_name = interface.getMyNodeInfo().get('user', {}).get('longName', 'Unknown')
|
node_name = interface.getMyNodeInfo().get('user', {}).get('longName', 'Unknown')
|
||||||
logger.error(f'Attached node "{node_name}" was unable to decode incoming message, possible key mismatch in its node-database.')
|
logger.error(f'Attached node "{node_name}" was unable to decode incoming message, possible key mismatch in its node-database.')
|
||||||
return
|
return
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Unexpected error in message_listener: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
def signal_handler(sig, frame):
|
def signal_handler(sig, frame):
|
||||||
"""Perform a graceful shutdown when CTRL+C is pressed"""
|
"""Perform a graceful shutdown when CTRL+C is pressed"""
|
||||||
@ -418,7 +480,7 @@ def signal_handler(sig, frame):
|
|||||||
logger.info("\nInitiating shutdown...")
|
logger.info("\nInitiating shutdown...")
|
||||||
try:
|
try:
|
||||||
if interface is not None:
|
if interface is not None:
|
||||||
if settings.get('SHUTDOWN_NODE_ON_EXIT', false):
|
if settings.get('SHUTDOWN_NODE_ON_EXIT', False):
|
||||||
logger.info("Sending shutdown command to node...")
|
logger.info("Sending shutdown command to node...")
|
||||||
try:
|
try:
|
||||||
# Send shutdown command
|
# Send shutdown command
|
||||||
@ -439,7 +501,6 @@ def signal_handler(sig, frame):
|
|||||||
logger.error(f"Error sending shutdown command: {e}")
|
logger.error(f"Error sending shutdown command: {e}")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
global interface, alerts # Add alerts to global declaration
|
global interface, alerts # Add alerts to global declaration
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
@ -596,4 +657,4 @@ def get_weather_alert_status():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@ -16,6 +16,7 @@ class WeatherAlerts:
|
|||||||
self.check_interval = check_interval
|
self.check_interval = check_interval
|
||||||
self.message_delay = message_delay
|
self.message_delay = message_delay
|
||||||
self.settings = settings or {}
|
self.settings = settings or {}
|
||||||
|
self.channel_index = self.settings.get('ALERT_CHANNEL_INDEX', 0)
|
||||||
|
|
||||||
# Add storage for current alert data
|
# Add storage for current alert data
|
||||||
self.current_alert = None
|
self.current_alert = None
|
||||||
@ -67,7 +68,7 @@ class WeatherAlerts:
|
|||||||
self.interface.sendText(
|
self.interface.sendText(
|
||||||
formatted_msg,
|
formatted_msg,
|
||||||
wantAck=False,
|
wantAck=False,
|
||||||
destinationId='^all'
|
channelIndex=self.channel_index,
|
||||||
)
|
)
|
||||||
if i < len(messages): # Don't sleep after last message
|
if i < len(messages): # Don't sleep after last message
|
||||||
time.sleep(self.message_delay)
|
time.sleep(self.message_delay)
|
||||||
@ -101,7 +102,8 @@ class WeatherAlerts:
|
|||||||
self.interface.sendText(
|
self.interface.sendText(
|
||||||
formatted_msg,
|
formatted_msg,
|
||||||
wantAck=True,
|
wantAck=True,
|
||||||
destinationId=destination_id
|
destinationId=destination_id,
|
||||||
|
channelIndex=self.channel_index
|
||||||
)
|
)
|
||||||
if i < len(messages):
|
if i < len(messages):
|
||||||
time.sleep(self.message_delay)
|
time.sleep(self.message_delay)
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
MYNODES:
|
MYNODES:
|
||||||
- "1234567890"
|
|
||||||
- "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
|
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
|
DUTYCYCLE: false # If true, will limit to 10% duty cycle
|
||||||
@ -24,4 +23,6 @@ ENABLE_AUTO_REBOOT: false # Set to true to enable automatic daily reboot of the
|
|||||||
AUTO_REBOOT_HOUR: 3 # Hour for daily reboot (24-hour format)
|
AUTO_REBOOT_HOUR: 3 # Hour for daily reboot (24-hour format)
|
||||||
AUTO_REBOOT_MINUTE: 0 # Minute for daily reboot
|
AUTO_REBOOT_MINUTE: 0 # Minute for daily reboot
|
||||||
REBOOT_DELAY_SECONDS: 10 # Delay in seconds before reboot occurs (recommend not changing this)
|
REBOOT_DELAY_SECONDS: 10 # Delay in seconds before reboot occurs (recommend not changing this)
|
||||||
SHUTDOWN_NODE_ON_EXIT: false #Set to true to shut down the node when you close the program
|
ENABLE_CUSTOM_LOOKUP: true # Enable/disable custom lat/lon lookup via message
|
||||||
|
SHUTDOWN_NODE_ON_EXIT: false # If true, shutdown node on exit. If false, only close interface
|
||||||
|
ALERT_CHANNEL_INDEX: 0 # Channel index for weather alerts, default is 0 (first channel)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user