diff --git a/.env.example b/.env.example
index 7ddadcc..8048f3c 100644
--- a/.env.example
+++ b/.env.example
@@ -10,7 +10,7 @@ DATA_DIR="./data"
REPORT_DIR="./reports"
WEB_DIR="./web"
SITE_BASE_PATH="/"
-SITE_URL="http://localhost"
+SITE_URL="https://hapi.novosel.dk"
# Extra owner directions appended to the 05:00 AI prompt
PROMPT_FILE="./llm_instructions.md"
diff --git a/README.md b/README.md
index b7b1f65..83d6e1f 100644
--- a/README.md
+++ b/README.md
@@ -105,7 +105,7 @@ Run the 05:00-style analysis/publishing step:
Open the blog served by nginx:
```text
-http://localhost/
+https://hapi.novosel.dk/
```
This instance publishes to the web root with:
@@ -113,7 +113,7 @@ This instance publishes to the web root with:
```bash
WEB_DIR="/var/www/html"
SITE_BASE_PATH="/"
-SITE_URL="http://piagent"
+SITE_URL="https://hapi.novosel.dk"
```
For a subdirectory install, use for example:
@@ -121,10 +121,10 @@ For a subdirectory install, use for example:
```bash
WEB_DIR="/var/www/html/haobserver"
SITE_BASE_PATH="/haobserver"
-SITE_URL="http://piagent"
+SITE_URL="https://hapi.novosel.dk"
```
-Daily articles are written under `articles/YYYY-MM-DD.html` inside `WEB_DIR`, and `index.html` links to the archive. An RSS feed is published at `rss.xml`, and a sci-fi favicon is published at `favicon.svg`. New articles include context from previous reports from the last `ARTICLE_CONTEXT_DAYS` days.
+Daily articles are written under `articles/YYYY-MM-DD.html` inside `WEB_DIR`, and `index.html` links to the archive. A clean RSS feed is published at `rss.xml` using `SITE_URL` for absolute links, and a sci-fi favicon is published at `favicon.svg`. New articles include context from previous reports from the last `ARTICLE_CONTEXT_DAYS` days.
## Install cron jobs
diff --git a/ha_observer.py b/ha_observer.py
index 823f480..cdcd3ec 100755
--- a/ha_observer.py
+++ b/ha_observer.py
@@ -267,8 +267,9 @@ Use these only for trend/context awareness. Do not claim something happened toda
return f"""You are writing today's Home Assistant smart-home blog article for the owner.
-Write a funny but useful morning briefing in a blog/article style. Use light humor, emojis,
-and playful headings, but remain factual and privacy-aware. Include:
+Write a funny but useful morning briefing in a clean blog/article style. Use light humor,
+but keep emojis/smileys rare: at most one in the whole article. Prefer clear headings,
+short paragraphs, and readable bullet lists. Remain factual and privacy-aware. Include:
- A short comedy headline for the day
- What seemed to happen at home today
- Behavioral patterns that can reasonably be inferred
@@ -340,22 +341,67 @@ def get_llm_conclusions(input_summary: str, previous_articles: str = "") -> str:
return f"Unknown LLM_MODE={LLM_MODE!r}. Use none, pi, ollama, or openai."
-def markdownish_to_html(text: str) -> str:
- safe = html.escape(text)
- safe = re.sub(r"^### (.*)$", r"
", content, flags=re.DOTALL | re.IGNORECASE)
+ title = "Smart Home Briefing"
+ if title_match:
+ title = re.sub(r"<[^>]+>", " ", title_match.group(1) or title_match.group(2) or "")
+ title = re.sub(r"\s+", " ", html.unescape(title)).strip() or "Smart Home Briefing"
+ text = re.sub(r" \s*", "\n", content)
+ text = re.sub(r"(p|li|h1|h2|h3)>", "\n", text, flags=re.IGNORECASE)
+ text = re.sub(r"<[^>]+>", " ", text)
+ text = html.unescape(text)
+ text = re.sub(r"[`*_#]", "", text)
+ text = re.sub(r"^[\s\-•]+", "", text, flags=re.MULTILINE)
+ text = re.sub(r"[ \t]+", " ", text)
+ text = re.sub(r"\n\s*\n+", "\n\n", text).strip()
+ return title, text
+
+
def write_rss_feed() -> Path:
articles_dir = WEB_DIR / "articles"
items = []
for path in sorted(articles_dir.glob("*.html"), reverse=True)[:20]:
- title = path.stem
+ fallback_title = path.stem
try:
- title = datetime.strptime(path.stem, "%Y-%m-%d").strftime("Smart Home Briefing - %A, %B %-d, %Y")
+ fallback_title = datetime.strptime(path.stem, "%Y-%m-%d").strftime("Smart Home Briefing - %A, %B %-d, %Y")
except ValueError:
- title = f"Smart Home Briefing - {path.stem}"
+ fallback_title = f"Smart Home Briefing - {path.stem}"
content = path.read_text(encoding="utf-8", errors="ignore")
- description = re.sub(r"<[^>]+>", " ", content)
- description = re.sub(r"\s+", " ", html.unescape(description)).strip()[:500]
+ article_title, article_text = clean_rss_text(content)
+ title = article_title if article_title != "Smart Home Briefing" else fallback_title
+ description = article_text[:600]
pub_dt = datetime.fromtimestamp(path.stat().st_mtime, timezone.utc)
url = site_url(f"articles/{path.name}")
items.append(f"""
@@ -488,12 +558,14 @@ def write_rss_feed() -> Path:
{html.escape(description)}
""")
now = format_datetime(datetime.now(timezone.utc), usegmt=True)
+ feed_url = site_url("rss.xml")
feed = f"""
-
+Smart Home Gossip Gazette
{html.escape(site_url())}
- Daily Home Assistant smart-home briefings from the orbital raccoon telemetry desk.
+
+ Daily Home Assistant smart-home briefings.en{now}
{''.join(items)}
@@ -512,7 +584,8 @@ def blog_shell(title: str, subtitle: str, main_content: str, archive_links: str)
{html.escape(title)}
-
+
+
diff --git a/llm_instructions.md b/llm_instructions.md
index de3d95a..289ad37 100644
--- a/llm_instructions.md
+++ b/llm_instructions.md
@@ -3,10 +3,8 @@
Edit this file whenever you want to change how the 05:00 AI report is written.
The contents are appended to the AI prompt before the Home Assistant data.
-Suggested directions:
-
- Keep the tone funny, sarcastic, and playful, but still useful.
-- Use clear confidence labels: **strong evidence**, **possible**, **wild guess**.
+- Use clear confidence labels: strong evidence, possible, wild guess.
- Focus on patterns in occupancy, sleep/wake timing, lights, heating, doors, motion, media, and unusual sensor changes.
- Point out privacy leaks: what could a nosy neighbor, burglar, or raccoon detective infer?
- Recommend practical Home Assistant automations.
@@ -14,7 +12,7 @@ Suggested directions:
- Avoid being creepy about personal habits; summarize respectfully.
- Prefer concise bullet points over long paragraphs.
- entities marked smb_ are located in different house in Samobor, Croatia, others are in Sonderborg Denmark
-- people FJR and Megane are my motorcycle and car not persons at home
+- people: FJR is my motorcycle and Megane is my car not persons at home
Optional custom questions to answer: