Add example configuration file, moved everything from secrets.py to here.
Feat: Also refactored some of the logic in discord_webhook.py and networking.py to be more friendly towards the pico with ram usage. Fixes #26
This commit is contained in:
parent
a20bbd7cdf
commit
d95f212d2e
@ -1,8 +1,23 @@
|
||||
import urequests as requests # type: ignore
|
||||
from secrets import secrets
|
||||
# Minimal module-level state (only what we need)
|
||||
_CONFIG = {"discord_webhook_url": None, "discord_alert_webhook_url": None}
|
||||
|
||||
def set_config(cfg: dict):
|
||||
"""Initialize module with minimal values from loaded config (call from main)."""
|
||||
global _CONFIG
|
||||
if not cfg:
|
||||
_CONFIG = {"discord_webhook_url": None, "discord_alert_webhook_url": None}
|
||||
return
|
||||
_CONFIG = {
|
||||
"discord_webhook_url": cfg.get("discord_webhook_url"),
|
||||
"discord_alert_webhook_url": cfg.get("discord_alert_webhook_url"),
|
||||
}
|
||||
|
||||
def _get_webhook_url(is_alert: bool = False):
|
||||
if is_alert:
|
||||
return _CONFIG.get("discord_alert_webhook_url") or _CONFIG.get("discord_webhook_url")
|
||||
return _CONFIG.get("discord_webhook_url")
|
||||
|
||||
def _escape_json_str(s: str) -> str:
|
||||
# minimal JSON string escaper for quotes/backslashes and control chars
|
||||
s = s.replace("\\", "\\\\")
|
||||
s = s.replace('"', '\\"')
|
||||
s = s.replace("\n", "\\n")
|
||||
@ -11,44 +26,52 @@ def _escape_json_str(s: str) -> str:
|
||||
return s
|
||||
|
||||
def send_discord_message(message, username="Auto Garden Bot", is_alert=False):
|
||||
"""Send Discord message with 3-second timeout to prevent blocking."""
|
||||
"""
|
||||
Send Discord message. Import urequests locally to avoid occupying RAM when idle.
|
||||
Returns True on success, False otherwise.
|
||||
"""
|
||||
resp = None
|
||||
|
||||
# Use alert webhook if specified, otherwise normal webhook
|
||||
if is_alert:
|
||||
url = secrets.get('discord_alert_webhook_url') or secrets.get('discord_webhook_url')
|
||||
else:
|
||||
url = secrets.get('discord_webhook_url')
|
||||
url = _get_webhook_url(is_alert=is_alert)
|
||||
if not url:
|
||||
return False
|
||||
|
||||
try:
|
||||
if not url:
|
||||
return False
|
||||
# local import to save RAM
|
||||
import urequests as requests # type: ignore
|
||||
import gc
|
||||
|
||||
url = url.strip().strip('\'"')
|
||||
|
||||
# Build JSON manually to preserve emoji/unicode as UTF-8
|
||||
url = str(url).strip().strip('\'"')
|
||||
content = _escape_json_str(message)
|
||||
user = _escape_json_str(username)
|
||||
body_bytes = ('{"content":"%s","username":"%s"}' % (content, user)).encode("utf-8")
|
||||
|
||||
headers = {"Content-Type": "application/json; charset=utf-8"}
|
||||
|
||||
# Make POST request (urequests has built-in ~5s timeout)
|
||||
resp = requests.post(url, data=body_bytes, headers=headers)
|
||||
|
||||
status = getattr(resp, "status", getattr(resp, "status_code", None))
|
||||
|
||||
if status and 200 <= status < 300:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
success = bool(status and 200 <= status < 300)
|
||||
if not success:
|
||||
# optional: print status for debugging, but avoid spamming
|
||||
print("Discord webhook failed, status:", status)
|
||||
return success
|
||||
|
||||
except Exception as e:
|
||||
# Silently fail (don't spam console with Discord errors)
|
||||
# avoid raising to prevent crashing monitors; print minimal info
|
||||
print("Discord webhook exception:", e)
|
||||
return False
|
||||
|
||||
finally:
|
||||
if resp:
|
||||
try:
|
||||
try:
|
||||
if resp:
|
||||
resp.close()
|
||||
except:
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
# free large objects and modules, then force GC
|
||||
try:
|
||||
del resp
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
gc.collect()
|
||||
except:
|
||||
pass
|
||||
@ -1,5 +1,5 @@
|
||||
import time # type: ignore
|
||||
from scripts.discord_webhook import send_discord_message
|
||||
import scripts.discord_webhook as discord_webhook
|
||||
from scripts.temperature_sensor import TemperatureSensor
|
||||
|
||||
class Monitor:
|
||||
@ -108,13 +108,11 @@ class TemperatureMonitor(Monitor):
|
||||
self.alert_start_time = current_time
|
||||
print(alert_message)
|
||||
|
||||
# Send to appropriate Discord channel
|
||||
# send alert (use module-level discord_webhook; set_config must be called in main)
|
||||
if self.send_alerts_to_separate_channel:
|
||||
from scripts.discord_webhook import send_discord_message
|
||||
send_discord_message(alert_message, is_alert=True)
|
||||
discord_webhook.send_discord_message(alert_message, is_alert=True)
|
||||
else:
|
||||
from scripts.discord_webhook import send_discord_message
|
||||
send_discord_message(alert_message)
|
||||
discord_webhook.send_discord_message(alert_message)
|
||||
|
||||
self.alert_sent = True
|
||||
|
||||
@ -139,13 +137,11 @@ class TemperatureMonitor(Monitor):
|
||||
)
|
||||
print(recovery_message)
|
||||
|
||||
# Send to appropriate Discord channel
|
||||
# send recovery message
|
||||
if self.send_alerts_to_separate_channel:
|
||||
from scripts.discord_webhook import send_alert_message
|
||||
send_alert_message(recovery_message)
|
||||
discord_webhook.send_discord_message(recovery_message, is_alert=True)
|
||||
else:
|
||||
from scripts.discord_webhook import send_discord_message
|
||||
send_discord_message(recovery_message)
|
||||
discord_webhook.send_discord_message(recovery_message)
|
||||
|
||||
self.alert_sent = False
|
||||
self.alert_start_time = None
|
||||
@ -198,14 +194,14 @@ class ACMonitor(Monitor):
|
||||
# Too hot, turn AC on
|
||||
if self.ac.turn_on():
|
||||
if not self.last_notified_state:
|
||||
send_discord_message(f"❄️ AC turned ON - Current: {current_temp:.1f}°F, Target: {self.target_temp:.1f}°F")
|
||||
discord_webhook.send_discord_message(f"❄️ AC turned ON - Current: {current_temp:.1f}°F, Target: {self.target_temp:.1f}°F")
|
||||
self.last_notified_state = True
|
||||
|
||||
elif current_temp < (self.target_temp - self.temp_swing):
|
||||
# Cool enough, turn AC off
|
||||
if self.ac.turn_off():
|
||||
if self.last_notified_state:
|
||||
send_discord_message(f"✅ AC turned OFF - Current: {current_temp:.1f}°F, Target: {self.target_temp:.1f}°F")
|
||||
discord_webhook.send_discord_message(f"✅ AC turned OFF - Current: {current_temp:.1f}°F, Target: {self.target_temp:.1f}°F")
|
||||
self.last_notified_state = False
|
||||
|
||||
# Else: within temp_swing range, maintain current state
|
||||
@ -244,27 +240,28 @@ class HeaterMonitor(Monitor):
|
||||
# Too cold, turn heater on
|
||||
if self.heater.turn_on():
|
||||
if not self.last_notified_state:
|
||||
send_discord_message(f"🔥 Heater turned ON - Current: {current_temp:.1f}°F, Target: {self.target_temp:.1f}°F")
|
||||
discord_webhook.send_discord_message(f"🔥 Heater turned ON - Current: {current_temp:.1f}°F, Target: {self.target_temp:.1f}°F")
|
||||
self.last_notified_state = True
|
||||
|
||||
elif current_temp > (self.target_temp + self.temp_swing):
|
||||
# Warm enough, turn heater off
|
||||
if self.heater.turn_off():
|
||||
if self.last_notified_state:
|
||||
send_discord_message(f"✅ Heater turned OFF - Current: {current_temp:.1f}°F, Target: {self.target_temp:.1f}°F")
|
||||
discord_webhook.send_discord_message(f"✅ Heater turned OFF - Current: {current_temp:.1f}°F, Target: {self.target_temp:.1f}°F")
|
||||
self.last_notified_state = False
|
||||
|
||||
# Else: within temp_swing range, maintain current state
|
||||
|
||||
class WiFiMonitor(Monitor):
|
||||
"""Monitor WiFi connection and handle reconnection."""
|
||||
def __init__(self, wifi, led, interval=5, reconnect_cooldown=60):
|
||||
def __init__(self, wifi, led, interval=5, reconnect_cooldown=60, config=None):
|
||||
super().__init__(interval)
|
||||
self.wifi = wifi
|
||||
self.led = led
|
||||
self.reconnect_cooldown = reconnect_cooldown
|
||||
self.last_reconnect_attempt = 0
|
||||
self.was_connected = wifi.isconnected() if wifi else False
|
||||
self.config = config
|
||||
|
||||
def run(self):
|
||||
"""Check WiFi status, blink LED, attempt reconnect if needed."""
|
||||
@ -284,10 +281,10 @@ class WiFiMonitor(Monitor):
|
||||
if time.ticks_diff(now, self.last_reconnect_attempt) >= (self.reconnect_cooldown * 1000):
|
||||
self.last_reconnect_attempt = now
|
||||
# print("Attempting WiFi reconnect...")
|
||||
self.wifi = connect_wifi(self.led)
|
||||
self.wifi = connect_wifi(self.led, config=self.config)
|
||||
|
||||
if self.wifi and self.wifi.isconnected():
|
||||
send_discord_message("WiFi connection restored 🔄")
|
||||
discord_webhook.send_discord_message("WiFi connection restored 🔄")
|
||||
self.was_connected = True
|
||||
else:
|
||||
# Slow blink when connected
|
||||
@ -297,7 +294,7 @@ class WiFiMonitor(Monitor):
|
||||
|
||||
# Notify if connection was just restored
|
||||
if not self.was_connected:
|
||||
send_discord_message("WiFi connection restored 🔄")
|
||||
discord_webhook.send_discord_message("WiFi connection restored 🔄")
|
||||
self.was_connected = True
|
||||
|
||||
def run_monitors(monitors):
|
||||
|
||||
@ -1,19 +1,32 @@
|
||||
import network
|
||||
import time
|
||||
from secrets import secrets
|
||||
|
||||
def connect_wifi(led=None, max_retries=3, timeout=20):
|
||||
def connect_wifi(led=None, max_retries=3, timeout=20, config=None):
|
||||
"""
|
||||
Connect to WiFi using credentials from secrets.py
|
||||
Connect to WiFi using credentials from provided config dict.
|
||||
|
||||
Args:
|
||||
led: Optional LED pin for visual feedback
|
||||
max_retries: Number of connection attempts (default: 3)
|
||||
timeout: Seconds to wait for connection per attempt (default: 20)
|
||||
config: Dict loaded from config.json, must contain config['wifi'] with 'ssid' and 'password'
|
||||
|
||||
Returns:
|
||||
WLAN object if connected, None if failed
|
||||
"""
|
||||
if config is None:
|
||||
print("connect_wifi: config is required")
|
||||
return None
|
||||
|
||||
wifi_cfg = config.get('wifi') or {}
|
||||
# support either config['wifi'] = {'ssid','password'} OR top-level 'ssid'/'password'
|
||||
ssid = wifi_cfg.get('ssid') or config.get('ssid')
|
||||
password = wifi_cfg.get('password') or config.get('password')
|
||||
|
||||
if not ssid or not password:
|
||||
print("connect_wifi: missing wifi credentials in config['wifi']")
|
||||
return None
|
||||
|
||||
wlan = network.WLAN(network.STA_IF)
|
||||
|
||||
# Ensure clean state
|
||||
@ -41,13 +54,13 @@ def connect_wifi(led=None, max_retries=3, timeout=20):
|
||||
# Try connecting with retries
|
||||
for attempt in range(1, max_retries + 1):
|
||||
if wlan.isconnected():
|
||||
print(f"Already connected to WiFi")
|
||||
print("Already connected to WiFi")
|
||||
break
|
||||
|
||||
print(f'Connecting to WiFi (attempt {attempt}/{max_retries})...')
|
||||
print(f'Connecting to WiFi SSID: {ssid} (attempt {attempt}/{max_retries})...')
|
||||
|
||||
try:
|
||||
wlan.connect(secrets['ssid'], secrets['password'])
|
||||
wlan.connect(ssid, password)
|
||||
except Exception as e:
|
||||
print(f"Connection attempt failed: {e}")
|
||||
if attempt < max_retries:
|
||||
@ -62,7 +75,17 @@ def connect_wifi(led=None, max_retries=3, timeout=20):
|
||||
break
|
||||
|
||||
if led:
|
||||
led.toggle()
|
||||
try:
|
||||
# some LED wrappers use toggle(), others use on/off
|
||||
if hasattr(led, "toggle"):
|
||||
led.toggle()
|
||||
else:
|
||||
# flash quickly to show activity
|
||||
led.on()
|
||||
time.sleep(0.05)
|
||||
led.off()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
time.sleep(0.5)
|
||||
wait_time += 0.5
|
||||
@ -85,17 +108,24 @@ def connect_wifi(led=None, max_retries=3, timeout=20):
|
||||
if not wlan.isconnected():
|
||||
print('WiFi connection failed after all attempts!')
|
||||
if led:
|
||||
led.off()
|
||||
try:
|
||||
# prefer available method names
|
||||
if hasattr(led, "off"):
|
||||
led.off()
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
# Success feedback
|
||||
if led:
|
||||
# Double pulse on successful connection
|
||||
for _ in range(2):
|
||||
led.on()
|
||||
time.sleep(0.2)
|
||||
led.off()
|
||||
time.sleep(0.2)
|
||||
try:
|
||||
for _ in range(2):
|
||||
led.on()
|
||||
time.sleep(0.2)
|
||||
led.off()
|
||||
time.sleep(0.2)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
print('Connected to WiFi successfully!')
|
||||
|
||||
|
||||
@ -90,32 +90,61 @@ class ScheduleMonitor:
|
||||
return # Already applied
|
||||
|
||||
try:
|
||||
# Track whether we changed persisted values to avoid unnecessary writes
|
||||
changed = False
|
||||
|
||||
# Update AC settings if provided
|
||||
if 'ac_target' in schedule:
|
||||
self.ac_monitor.target_temp = float(schedule['ac_target'])
|
||||
self.config['ac_target'] = float(schedule['ac_target'])
|
||||
new_ac = float(schedule['ac_target'])
|
||||
if self.config.get('ac_target') != new_ac:
|
||||
self.config['ac_target'] = new_ac
|
||||
changed = True
|
||||
self.ac_monitor.target_temp = new_ac
|
||||
|
||||
if 'ac_swing' in schedule:
|
||||
self.ac_monitor.temp_swing = float(schedule['ac_swing'])
|
||||
self.config['ac_swing'] = float(schedule['ac_swing'])
|
||||
new_ac_swing = float(schedule['ac_swing'])
|
||||
if self.config.get('ac_swing') != new_ac_swing:
|
||||
self.config['ac_swing'] = new_ac_swing
|
||||
changed = True
|
||||
self.ac_monitor.temp_swing = new_ac_swing
|
||||
|
||||
# Update heater settings if provided
|
||||
if 'heater_target' in schedule:
|
||||
self.heater_monitor.target_temp = float(schedule['heater_target'])
|
||||
self.config['heater_target'] = float(schedule['heater_target'])
|
||||
new_ht = float(schedule['heater_target'])
|
||||
if self.config.get('heater_target') != new_ht:
|
||||
self.config['heater_target'] = new_ht
|
||||
changed = True
|
||||
self.heater_monitor.target_temp = new_ht
|
||||
|
||||
if 'heater_swing' in schedule:
|
||||
self.heater_monitor.temp_swing = float(schedule['heater_swing'])
|
||||
self.config['heater_swing'] = float(schedule['heater_swing'])
|
||||
new_ht_swing = float(schedule['heater_swing'])
|
||||
if self.config.get('heater_swing') != new_ht_swing:
|
||||
self.config['heater_swing'] = new_ht_swing
|
||||
changed = True
|
||||
self.heater_monitor.temp_swing = new_ht_swing
|
||||
|
||||
# Save updated config to file so targets persist
|
||||
try:
|
||||
import json
|
||||
with open('config.json', 'w') as f:
|
||||
json.dump(self.config, f)
|
||||
print("✅ Config updated with active schedule targets")
|
||||
except Exception as e:
|
||||
print("⚠️ Could not save config: {}".format(e))
|
||||
# Save updated config only if something changed
|
||||
if changed:
|
||||
try:
|
||||
import json
|
||||
with open('config.json', 'w') as f:
|
||||
json.dump(self.config, f)
|
||||
except Exception as e:
|
||||
print("⚠️ Could not save config: {}".format(e))
|
||||
else:
|
||||
# import once and update module-level webhook config
|
||||
try:
|
||||
import scripts.discord_webhook as discord_webhook
|
||||
discord_webhook.set_config(self.config)
|
||||
except Exception:
|
||||
pass
|
||||
print("✅ Config updated with active schedule targets")
|
||||
else:
|
||||
# nothing to persist
|
||||
try:
|
||||
import scripts.discord_webhook as discord_webhook
|
||||
except Exception:
|
||||
discord_webhook = None
|
||||
|
||||
# Log the change
|
||||
schedule_name = schedule.get('name', 'Unnamed')
|
||||
@ -126,16 +155,17 @@ class ScheduleMonitor:
|
||||
print("Heater Target: {}°F".format(self.heater_monitor.target_temp))
|
||||
print("="*50 + "\n")
|
||||
|
||||
# Send Discord notification
|
||||
# Send Discord notification (use discord_webhook if available)
|
||||
try:
|
||||
from scripts.discord_webhook import send_discord_message
|
||||
if 'discord_webhook' not in locals() or discord_webhook is None:
|
||||
import scripts.discord_webhook as discord_webhook
|
||||
message = "🕐 Schedule '{}' applied - AC: {}°F | Heater: {}°F".format(
|
||||
schedule_name,
|
||||
self.ac_monitor.target_temp,
|
||||
self.heater_monitor.target_temp
|
||||
)
|
||||
send_discord_message(message)
|
||||
except:
|
||||
discord_webhook.send_discord_message(message)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.last_applied_schedule = schedule_id
|
||||
@ -165,16 +195,24 @@ class ScheduleMonitor:
|
||||
import json
|
||||
with open('config.json', 'w') as f:
|
||||
json.dump(self.config, f)
|
||||
print("✅ Config updated - automatic mode resumed")
|
||||
except Exception as e:
|
||||
print("⚠️ Could not save config: {}".format(e))
|
||||
else:
|
||||
# ensure in-memory webhook config updated
|
||||
try:
|
||||
import scripts.discord_webhook as discord_webhook
|
||||
discord_webhook.set_config(self.config)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Notify user
|
||||
try:
|
||||
from scripts.discord_webhook import send_discord_message
|
||||
send_discord_message("⏰ Temporary hold expired - Schedule resumed automatically")
|
||||
except:
|
||||
pass
|
||||
print("✅ Config updated - automatic mode resumed")
|
||||
|
||||
# Notify user
|
||||
try:
|
||||
import scripts.discord_webhook as discord_webhook
|
||||
discord_webhook.send_discord_message("⏰ Temporary hold expired - Schedule resumed automatically")
|
||||
except Exception:
|
||||
pass
|
||||
# ===== END: Check if temporary hold has expired =====
|
||||
|
||||
# Find and apply active schedule
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import socket
|
||||
import time # type: ignore
|
||||
import json
|
||||
import scripts.discord_webhook as discord_webhook
|
||||
import os
|
||||
|
||||
|
||||
class TempWebServer:
|
||||
"""Simple web server for viewing temperatures and adjusting settings."""
|
||||
@ -106,8 +109,8 @@ class TempWebServer:
|
||||
response_bytes = response.encode('utf-8')
|
||||
|
||||
# Send headers
|
||||
conn.send('HTTP/1.1 200 OK\r\n')
|
||||
conn.send('Content-Type: text/html; charset=utf-8\r\n')
|
||||
conn.sendall(b'HTTP/1.1 200 OK\r\n')
|
||||
conn.sendall(b'Content-Type: text/html; charset=utf-8\r\n')
|
||||
conn.send('Content-Length: {}\r\n'.format(len(response_bytes)))
|
||||
conn.send('Connection: close\r\n')
|
||||
conn.send('\r\n')
|
||||
@ -224,6 +227,12 @@ class TempWebServer:
|
||||
# Rename temp to config (atomic on most filesystems)
|
||||
os.rename('config.tmp', 'config.json')
|
||||
|
||||
# Update discord module in-memory config so webhook URLs are current
|
||||
try:
|
||||
discord_webhook.set_config(config)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
print("Settings saved to config.json")
|
||||
return True
|
||||
except Exception as e:
|
||||
@ -276,8 +285,7 @@ class TempWebServer:
|
||||
|
||||
# Send Discord notification
|
||||
try:
|
||||
from scripts.discord_webhook import send_discord_message
|
||||
send_discord_message("▶️ Schedule resumed - Automatic temperature control active")
|
||||
discord_webhook.send_discord_message("▶️ Schedule resumed - Automatic temperature control active")
|
||||
except:
|
||||
pass
|
||||
|
||||
@ -302,8 +310,7 @@ class TempWebServer:
|
||||
schedule_monitor.reload_config(config)
|
||||
|
||||
try:
|
||||
from scripts.discord_webhook import send_discord_message
|
||||
send_discord_message("⏸️ Temporary hold - Schedules paused, manual control active")
|
||||
discord_webhook.send_discord_message("⏸️ Temporary hold - Schedules paused, manual control active")
|
||||
except:
|
||||
pass
|
||||
|
||||
@ -327,8 +334,7 @@ class TempWebServer:
|
||||
schedule_monitor.reload_config(config)
|
||||
|
||||
try:
|
||||
from scripts.discord_webhook import send_discord_message
|
||||
send_discord_message("🛑 Permanent hold - Schedules disabled, manual control only")
|
||||
discord_webhook.send_discord_message("🛑 Permanent hold - Schedules disabled, manual control only")
|
||||
except:
|
||||
pass
|
||||
|
||||
@ -490,12 +496,11 @@ class TempWebServer:
|
||||
|
||||
# Send Discord notification
|
||||
try:
|
||||
from scripts.discord_webhook import send_discord_message
|
||||
mode = "automatic" if config.get('schedule_enabled') else "hold"
|
||||
message = "📅 Schedules updated ({} mode) - {} schedules configured".format(
|
||||
mode, len(schedules)
|
||||
)
|
||||
send_discord_message(message)
|
||||
discord_webhook.send_discord_message(message)
|
||||
except:
|
||||
pass
|
||||
# ===== END: Handle schedule configuration save =====
|
||||
@ -611,7 +616,6 @@ class TempWebServer:
|
||||
|
||||
# ===== START: Send Discord notification =====
|
||||
try:
|
||||
from scripts.discord_webhook import send_discord_message
|
||||
hold_label = "PERMANENT HOLD" if is_permanent else "TEMPORARY HOLD"
|
||||
duration = "" if is_permanent else " (1 hour)"
|
||||
|
||||
@ -622,7 +626,7 @@ class TempWebServer:
|
||||
params.get('heater_target', 'N/A'),
|
||||
duration
|
||||
)
|
||||
send_discord_message(message)
|
||||
discord_webhook.send_discord_message(message)
|
||||
except Exception as discord_error:
|
||||
print("Discord notification failed: {}".format(discord_error))
|
||||
# ===== END: Send Discord notification =====
|
||||
@ -1754,8 +1758,7 @@ class TempWebServer:
|
||||
|
||||
# Discord notification
|
||||
try:
|
||||
from scripts.discord_webhook import send_discord_message
|
||||
send_discord_message("⚙️ Advanced settings updated")
|
||||
discord_webhook.send_discord_message("⚙️ Advanced settings updated")
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
45
config.json.Example
Normal file
45
config.json.Example
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
"static_ip": "192.168.1.69",
|
||||
"subnet": "255.255.255.0",
|
||||
"gateway": "192.168.1.1",
|
||||
"dns": "192.168.1.1",
|
||||
"timezone_offset": -5,
|
||||
"ssid": " Change_to_wifi_SSID",
|
||||
"password": "Change_to_wifi_Pasword",
|
||||
"discord_webhook_url": "https://discord.com/api/webhooks/key/long-Combination_1234-ChangeMe",
|
||||
"discord_alert_webhook_url": "https://discord.com/api/webhooks/key/long-Combination_1234-ChangeMe",
|
||||
"ac_target": 77.0,
|
||||
"ac_swing": 1.0,
|
||||
"heater_target": 72.0,
|
||||
"heater_swing": 2.0,
|
||||
"temp_hold_duration": 3600,
|
||||
"schedules": [
|
||||
{
|
||||
"time": "06:00",
|
||||
"ac_target": 75.0,
|
||||
"heater_target": 72.0,
|
||||
"name": "Morning"
|
||||
},
|
||||
{
|
||||
"time": "12:00",
|
||||
"ac_target": 75.0,
|
||||
"heater_target": 72.0,
|
||||
"name": "Midday"
|
||||
},
|
||||
{
|
||||
"time": "18:00",
|
||||
"ac_target": 75.0,
|
||||
"heater_target": 72.0,
|
||||
"name": "Evening"
|
||||
},
|
||||
{
|
||||
"time": "22:00",
|
||||
"ac_target": 75.0,
|
||||
"heater_target": 72.0,
|
||||
"name": "Night"
|
||||
}
|
||||
],
|
||||
"schedule_enabled": true,
|
||||
"permanent_hold": false,
|
||||
"temp_hold_start_time": null
|
||||
}
|
||||
10
main.py
10
main.py
@ -21,7 +21,7 @@ except Exception as e:
|
||||
|
||||
# Import after WiFi reset
|
||||
from scripts.networking import connect_wifi
|
||||
from scripts.discord_webhook import send_discord_message
|
||||
import scripts.discord_webhook as discord_webhook
|
||||
from scripts.monitors import TemperatureMonitor, WiFiMonitor, ACMonitor, HeaterMonitor, run_monitors
|
||||
from scripts.temperature_sensor import TemperatureSensor
|
||||
from scripts.air_conditioning import ACController
|
||||
@ -146,6 +146,8 @@ def load_config():
|
||||
|
||||
# Load configuration from file
|
||||
config = load_config()
|
||||
# Initialize discord webhook module with loaded config (must be done BEFORE any send_discord_message calls)
|
||||
discord_webhook.set_config(config)
|
||||
|
||||
# Get timezone offset from config (with fallback to -6 if not present)
|
||||
TIMEZONE_OFFSET = config.get('timezone_offset', -6)
|
||||
@ -171,7 +173,7 @@ except Exception as e:
|
||||
|
||||
# ===== START: WiFi Connection =====
|
||||
# Connect to WiFi using credentials from secrets.py
|
||||
wifi = connect_wifi(led)
|
||||
wifi = connect_wifi(led, config=config)
|
||||
|
||||
# Set static IP and print WiFi details
|
||||
if wifi and wifi.isconnected():
|
||||
@ -199,7 +201,7 @@ if wifi and wifi.isconnected():
|
||||
|
||||
# Send startup notification to Discord (with timeout, non-blocking)
|
||||
try:
|
||||
success = send_discord_message(f"Pico W online at http://{ifconfig[0]} ✅")
|
||||
success = discord_webhook.send_discord_message(f"Pico W online at http://{ifconfig[0]} ✅")
|
||||
if success:
|
||||
print("Discord startup notification sent")
|
||||
else:
|
||||
@ -345,7 +347,7 @@ check_memory_once()
|
||||
# Set up all monitoring systems (run in order during main loop)
|
||||
monitors = [
|
||||
# WiFi monitor: Checks connection, reconnects if needed, blinks LED
|
||||
WiFiMonitor(wifi, led, interval=5, reconnect_cooldown=60),
|
||||
WiFiMonitor(wifi, led, interval=5, reconnect_cooldown=60, config=config),
|
||||
|
||||
# Schedule monitor: Changes temp targets based on time of day
|
||||
schedule_monitor,
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
secrets = {
|
||||
'ssid': ' Change_to_wifi_SSID',
|
||||
'password': 'Change_to_wifi_Pasword',
|
||||
'discord_webhook_url': 'https://discord.com/api/webhooks/key/long-Combination_1234-ChangeMe', # normal updates
|
||||
'discord_alert_webhook_url': 'https://discord.com/api/webhooks/key/long-Combination_1234-ChangeMe', # alerts only
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user