Skip to content

Commit f00f895

Browse files
committed
feat: allow HASS to close its own page via POST /close-hass and make Chromium fullscreen
- Add HTTP listener on configurable port (default 8321) accepting POST /close-hass - Add public Slint function close-hass-remote() for Rust to trigger page navigation - Replace --window-size with --start-fullscreen for Chromium - Update HA page UI to show 'Home Assistant Opened!' status with troubleshooting hints - Add hass_api_port to Config - Add python3 to shell.nix for skia-bindings build
1 parent 83c4707 commit f00f895

6 files changed

Lines changed: 118 additions & 34 deletions

File tree

shell.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pkgs.mkShell {
1919
libXi
2020
openssl
2121
alsa-lib
22+
python3
2223
];
2324

2425
shellHook = ''

src/config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ pub enum ConfigError {
2020
pub struct Config {
2121
pub token: Option<String>,
2222
pub home_assistant_url: String,
23+
/// Port for the HTTP listener that accepts `POST /close-hass` from HASS.
24+
pub hass_api_port: u16,
2325
pub cashcode_serial_port: String,
2426
pub cctalk_serial_port: String,
2527
/// Override the value for specific coin positions (channels).
@@ -36,6 +38,7 @@ impl Default for Config {
3638
Self {
3739
token: None,
3840
home_assistant_url: "https://ha.hackem.cc/web-dramma/0?BrowserID=dramma".to_string(),
41+
hass_api_port: 8321,
3942
cashcode_serial_port:
4043
"/dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller_D-if00-port0"
4144
.to_string(),

src/home_assistant.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use log::{error, info};
2+
use std::io::{Read, Write};
3+
use std::net::TcpListener;
24
use std::process::{Child, Command};
5+
use std::sync::mpsc::Sender;
36
use std::sync::{Arc, Mutex};
47

58
/// Manages a Chromium subprocess for displaying Home Assistant
@@ -30,7 +33,7 @@ impl ChromiumManager {
3033
// Try chromium first, then chromium-browser as fallback (different Debian versions)
3134
let command_result = Command::new("chromium")
3235
.arg("--app=".to_string() + url)
33-
.arg("--window-size=1280,940")
36+
.arg("--start-fullscreen")
3437
.arg("--window-position=0,0")
3538
.arg("--disable-infobars")
3639
.arg("--noerrdialogs")
@@ -50,7 +53,7 @@ impl ChromiumManager {
5053
// Fallback to chromium-browser
5154
Command::new("chromium-browser")
5255
.arg("--app=".to_string() + url)
53-
.arg("--window-size=1280,940")
56+
.arg("--start-fullscreen")
5457
.arg("--window-position=0,0")
5558
.arg("--disable-infobars")
5659
.arg("--noerrdialogs")
@@ -108,3 +111,46 @@ impl Drop for ChromiumManager {
108111
self.close();
109112
}
110113
}
114+
115+
/// Starts a simple HTTP listener for remote control from Home Assistant.
116+
/// When a `POST /close-hass` request is received, sends a signal through `tx`.
117+
pub fn start_close_listener(port: u16, tx: Sender<()>) {
118+
let addr = format!("0.0.0.0:{}", port);
119+
let listener = match TcpListener::bind(&addr) {
120+
Ok(l) => l,
121+
Err(e) => {
122+
error!("Failed to bind HASS close listener on {}: {}", addr, e);
123+
return;
124+
}
125+
};
126+
info!("🏠 Home Assistant close listener on port {}", port);
127+
128+
for stream in listener.incoming() {
129+
let Ok(mut stream) = stream else {
130+
continue;
131+
};
132+
let mut buf = [0u8; 512];
133+
let Ok(n) = stream.read(&mut buf) else {
134+
continue;
135+
};
136+
let request = String::from_utf8_lossy(&buf[..n]);
137+
let first_line = request.lines().next().unwrap_or("");
138+
139+
if first_line.starts_with("POST /close-hass") {
140+
info!("🏠 Received remote close-hass request");
141+
let _ = tx.send(());
142+
let _ = stream.write_all(
143+
b"HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Length: 2\r\n\r\nOK",
144+
);
145+
} else if first_line.starts_with("OPTIONS") {
146+
// CORS preflight
147+
let _ = stream.write_all(
148+
b"HTTP/1.1 204 No Content\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: POST, OPTIONS\r\nAccess-Control-Allow-Headers: Content-Type\r\n\r\n",
149+
);
150+
} else {
151+
let _ = stream.write_all(
152+
b"HTTP/1.1 404 Not Found\r\nContent-Length: 9\r\n\r\nNot Found",
153+
);
154+
}
155+
}
156+
}

src/main.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,5 +623,27 @@ mod home_assistant_handler {
623623
info!("Hiding Home Assistant page, closing Chromium");
624624
chromium_hide.close();
625625
});
626+
627+
// HTTP listener so HASS can POST /close-hass to dismiss its own page
628+
let (tx, rx) = std::sync::mpsc::channel::<()>();
629+
let port = config.hass_api_port;
630+
thread::spawn(move || {
631+
home_assistant::start_close_listener(port, tx);
632+
});
633+
634+
let weak = app.as_weak();
635+
let timer = slint::Timer::default();
636+
timer.start(
637+
slint::TimerMode::Repeated,
638+
std::time::Duration::from_millis(200),
639+
move || {
640+
if rx.try_recv().is_ok() {
641+
if let Some(window) = weak.upgrade() {
642+
window.invoke_close_hass_remote();
643+
}
644+
}
645+
},
646+
);
647+
std::mem::forget(timer);
626648
}
627649
}

ui/main_window.slint

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ export component MainWindow inherits Window {
4747
callback fetch-usernames(); // fetches available-usernames for autocomplete
4848
callback confetti-started(); // tells rust to start confetti dismiss timer
4949

50+
/// Called from Rust when HASS sends a POST /close-hass request.
51+
public function close-hass-remote() {
52+
root.hide-home-assistant();
53+
root.current-page = Page.Main;
54+
}
55+
5056
title: "Donation Machine";
5157
width: 1280px;
5258
height: 1024px;

ui/pages/home_assistant.slint

Lines changed: 38 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,82 +2,88 @@ import { Button, Palette } from "std-widgets.slint";
22

33
export component HomeAssistant inherits Rectangle {
44
in-out property <string> home-assistant-url: "";
5-
5+
66
callback back-clicked();
7-
7+
88
background: Palette.background;
9-
9+
1010
VerticalLayout {
1111
alignment: start;
1212
padding: 16px;
1313
spacing: 16px;
14-
14+
1515
// back button
1616
HorizontalLayout {
1717
alignment: start;
18-
18+
1919
Button {
2020
text: "← Back";
2121
width: 150px;
2222
height: 60px;
23-
23+
2424
clicked => {
2525
root.back-clicked();
2626
}
2727
}
2828
}
29-
30-
// hass iframe placeholder
31-
// until slint supports html iframes
29+
30+
// status panel behind fullscreen Chromium
3231
Rectangle {
3332
background: #1a1a1a;
3433
border-width: 2px;
3534
border-color: #333333;
3635
border-radius: 8px;
3736
vertical-stretch: 1;
38-
37+
3938
VerticalLayout {
4039
alignment: center;
4140
padding: 32px;
42-
41+
spacing: 12px;
42+
43+
Text {
44+
text: "🏠";
45+
font-size: 64px;
46+
horizontal-alignment: center;
47+
}
48+
4349
Text {
44-
text: "Home Assistant";
50+
text: "Home Assistant Opened!";
4551
font-size: 28px;
4652
font-weight: 700;
47-
color: #ffffff;
53+
color: #4caf50;
4854
horizontal-alignment: center;
4955
}
50-
51-
Rectangle {
52-
height: 20px;
53-
}
54-
56+
5557
Text {
56-
text: root.home-assistant-url != "" ? "Connected to: " + root.home-assistant-url : "No URL configured";
58+
text: "Chromium is running in fullscreen";
5759
font-size: 16px;
5860
color: #cccccc;
5961
horizontal-alignment: center;
60-
wrap: word-wrap;
6162
}
62-
63-
Rectangle {
64-
height: 20px;
63+
64+
Rectangle { height: 8px; }
65+
66+
Text {
67+
text: root.home-assistant-url != "" ? root.home-assistant-url : "No URL configured";
68+
font-size: 13px;
69+
color: #888888;
70+
horizontal-alignment: center;
71+
wrap: word-wrap;
6572
}
66-
73+
74+
Rectangle { height: 24px; }
75+
6776
Text {
68-
text: "⚠️ Web view integration needed";
77+
text: "If you see this screen instead of Home Assistant:";
6978
font-size: 14px;
79+
font-weight: 600;
7080
color: #ff9800;
7181
horizontal-alignment: center;
7282
}
73-
74-
Rectangle {
75-
height: 10px;
76-
}
77-
83+
7884
Text {
79-
text: "if hass did not started,\nconfigure the iframe URL in .config/dramma.toml\nor install chromium huh";
80-
font-size: 12px;
85+
text: "• Make sure chromium is installed\n• Check .config/dramma.toml has the correct home_assistant_url\n• You can close this page with POST /close-hass";
86+
font-size: 13px;
8187
color: #999999;
8288
horizontal-alignment: center;
8389
wrap: word-wrap;

0 commit comments

Comments
 (0)