Add {clock} field for notification timestamps; fix offline preview path and validator allowlist

This commit is contained in:
Justin Oro
2026-08-17 12:04:41 -07:00
parent 3f98e41063
commit c9451eeeb8
2 changed files with 13 additions and 6 deletions
+7
View File
@@ -166,6 +166,12 @@ def stamp():
return datetime.now().strftime("%Y-%m-%d %H:%M:%S") return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
def clock_stamp():
now = datetime.now()
hour12 = now.hour % 12 or 12
return f"{hour12}:{now.minute:02d} {now.strftime('%p')}"
class Reporter: class Reporter:
def __init__(self, log_path=None, quiet=False): def __init__(self, log_path=None, quiet=False):
self.quiet = quiet self.quiet = quiet
@@ -355,6 +361,7 @@ class Engine:
"target_host": self.target.host, "target_host": self.target.host,
"target_channel": self.target.channel, "target_channel": self.target.channel,
"time": stamp(), "time": stamp(),
"clock": clock_stamp(),
} }
) )
return context return context
+6 -6
View File
@@ -43,7 +43,7 @@ ACTIONS = {"target.on", "target.off", "none"}
DURATION_PATTERN = re.compile(r"^\s*(\d+(?:\.\d+)?)\s*([smh]?)\s*$", re.IGNORECASE) DURATION_PATTERN = re.compile(r"^\s*(\d+(?:\.\d+)?)\s*([smh]?)\s*$", re.IGNORECASE)
DEFAULT_NOTIFY_TEMPLATE = ( DEFAULT_NOTIFY_TEMPLATE = (
"{source_name} battery {battery_soc}%, solar {pv_total}W. " "{clock} {source_name} battery {battery_soc}%, solar {pv_total}W. "
"{target_name} turned {action}." "{target_name} turned {action}."
) )
DEFAULT_NOTIFY_THROTTLE = 300 DEFAULT_NOTIFY_THROTTLE = 300
@@ -197,14 +197,14 @@ class NotificationSettings:
self.stale_template = str( self.stale_template = str(
raw.get("stale_template") raw.get("stale_template")
or ( or (
"{source_name}: telemetry lost ({reason}). Last seen battery " "{clock} {source_name}: telemetry lost ({reason}). Last seen "
"{battery_soc}% at {last_seen}. {target_name} was turned {action} " "battery {battery_soc}% at {last_seen}. {target_name} was "
"as a precaution." "turned {action} as a precaution."
) )
) )
self.recovered_template = str( self.recovered_template = str(
raw.get("recovered_template") raw.get("recovered_template")
or "{source_name}: telemetry is back after {outage}. Rules resumed." or "{clock} {source_name}: telemetry is back after {outage}. Rules resumed."
) )
self.title = str(raw.get("title") or "{profile}") self.title = str(raw.get("title") or "{profile}")
self.throttle = parse_duration( self.throttle = parse_duration(
@@ -537,7 +537,7 @@ def validate(profile):
context_names = { context_names = {
"profile", "rule", "action", "action_word", "source_name", "source_model", "profile", "rule", "action", "action_word", "source_name", "source_model",
"source_serial", "target_name", "target_model", "target_host", "source_serial", "target_name", "target_model", "target_host",
"target_channel", "condition", "time", "reason", "event", "target_channel", "condition", "time", "clock", "reason", "event",
} }
for template in templates_to_check: for template in templates_to_check:
for field in notify_module.template_fields(template): for field in notify_module.template_fields(template):