install as service
This commit is contained in:
@@ -55,6 +55,13 @@ Run the service; host, port, and URL prefix all come from `config.yaml`:
|
|||||||
.venv/bin/python wsgi.py
|
.venv/bin/python wsgi.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For production, use the Waitress entry point. It reads host, port, thread count,
|
||||||
|
and all application settings from `config.yaml`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
.venv/bin/python serve.py
|
||||||
|
```
|
||||||
|
|
||||||
With the example settings, open <http://localhost:30225/w/>. Keep one application worker because the polling
|
With the example settings, open <http://localhost:30225/w/>. Keep one application worker because the polling
|
||||||
scheduler runs inside the service process. Alternatively set
|
scheduler runs inside the service process. Alternatively set
|
||||||
`collector.enabled: false` in web workers and invoke this from a system timer:
|
`collector.enabled: false` in web workers and invoke this from a system timer:
|
||||||
@@ -78,6 +85,42 @@ The service account must have write permission on the log directory. Console
|
|||||||
logging remains enabled by default so `python wsgi.py` displays its listening
|
logging remains enabled by default so `python wsgi.py` displays its listening
|
||||||
address; set `logging.console: false` to use only the logfile.
|
address; set `logging.console: false` to use only the logfile.
|
||||||
|
|
||||||
|
## systemd installation
|
||||||
|
|
||||||
|
The included `deploy/netatmo.service` runs Waitress from `/srv/service_netatmo`
|
||||||
|
under a dedicated, non-login `netatmo` account. As root, create the account and
|
||||||
|
writable data directory:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
useradd --system --home-dir /srv/service_netatmo --shell /usr/sbin/nologin netatmo
|
||||||
|
install -d -o netatmo -g netatmo -m 0750 /var/lib/rrd
|
||||||
|
chown netatmo:netatmo /srv/service_netatmo/config.yaml
|
||||||
|
chmod 0600 /srv/service_netatmo/config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
Create the virtual environment and install dependencies as that account:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo -u netatmo python3 -m venv /srv/service_netatmo/.venv
|
||||||
|
sudo -u netatmo /srv/service_netatmo/.venv/bin/pip install -r /srv/service_netatmo/requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Install and start the unit:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
install -o root -g root -m 0644 /srv/service_netatmo/deploy/netatmo.service /etc/systemd/system/netatmo.service
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable --now netatmo.service
|
||||||
|
systemctl status netatmo.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Follow service output and the rotating application log with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
journalctl -u netatmo.service -f
|
||||||
|
tail -f /var/lib/rrd/netatmo_service.log
|
||||||
|
```
|
||||||
|
|
||||||
## HTTP API
|
## HTTP API
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|||||||
+3
-2
@@ -2,6 +2,7 @@
|
|||||||
server:
|
server:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 30225
|
port: 30225
|
||||||
|
threads: 4
|
||||||
url_prefix: /w
|
url_prefix: /w
|
||||||
|
|
||||||
storage:
|
storage:
|
||||||
@@ -23,8 +24,8 @@ graphs:
|
|||||||
height: 240
|
height: 240
|
||||||
|
|
||||||
netatmo:
|
netatmo:
|
||||||
client_id: 6a93db3d7cdb0e40ea0c2655
|
client_id: REPLACE_ME
|
||||||
client_secret: c6uRbAzZ9e8STPtXas6PYKaZZiD68Y6QHFaKq2GEIa8
|
client_secret: REPLACE_ME
|
||||||
redirect_uri: http://www.example.com/w
|
redirect_uri: http://www.example.com/w
|
||||||
token_file: /var/lib/rrd/netatmo_tokens.json
|
token_file: /var/lib/rrd/netatmo_tokens.json
|
||||||
device_id: ""
|
device_id: ""
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Netatmo RRD Flask service (Waitress)
|
||||||
|
Documentation=file:///srv/service_netatmo/README.md
|
||||||
|
Wants=network-online.target
|
||||||
|
After=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=netatmo
|
||||||
|
Group=netatmo
|
||||||
|
WorkingDirectory=/srv/service_netatmo
|
||||||
|
ExecStart=/srv/service_netatmo/.venv/bin/python /srv/service_netatmo/serve.py
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
TimeoutStopSec=30
|
||||||
|
UMask=0077
|
||||||
|
|
||||||
|
NoNewPrivileges=true
|
||||||
|
PrivateTmp=true
|
||||||
|
ProtectHome=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
ReadWritePaths=/var/lib/rrd
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -20,7 +20,7 @@ def default_config(base: Path | None = None) -> dict[str, Any]:
|
|||||||
base = (base or Path.cwd()).resolve()
|
base = (base or Path.cwd()).resolve()
|
||||||
rrd = base / "rrd"
|
rrd = base / "rrd"
|
||||||
return {
|
return {
|
||||||
"HOST": "0.0.0.0", "PORT": 5000, "URL_PREFIX": "", "RRD_FOLDER": rrd,
|
"HOST": "0.0.0.0", "PORT": 5000, "THREADS": 4, "URL_PREFIX": "", "RRD_FOLDER": rrd,
|
||||||
"LOG_FILE": rrd / "netatmo_service.log", "LOG_LEVEL": "INFO",
|
"LOG_FILE": rrd / "netatmo_service.log", "LOG_LEVEL": "INFO",
|
||||||
"LOG_MAX_BYTES": 5242880, "LOG_BACKUP_COUNT": 5, "LOG_CONSOLE": True,
|
"LOG_MAX_BYTES": 5242880, "LOG_BACKUP_COUNT": 5, "LOG_CONSOLE": True,
|
||||||
"NETATMO_TOKEN_FILE": rrd / "netatmo_tokens.json",
|
"NETATMO_TOKEN_FILE": rrd / "netatmo_tokens.json",
|
||||||
@@ -63,6 +63,7 @@ def load_config(filename: str | Path | None = None) -> dict[str, Any]:
|
|||||||
result.update({
|
result.update({
|
||||||
"HOST": str(server.get("host", result["HOST"])),
|
"HOST": str(server.get("host", result["HOST"])),
|
||||||
"PORT": int(server.get("port", result["PORT"])),
|
"PORT": int(server.get("port", result["PORT"])),
|
||||||
|
"THREADS": int(server.get("threads", result["THREADS"])),
|
||||||
"URL_PREFIX": _prefix(server.get("url_prefix", result["URL_PREFIX"])),
|
"URL_PREFIX": _prefix(server.get("url_prefix", result["URL_PREFIX"])),
|
||||||
"GRAPH_WIDTH": int(graphs.get("width", result["GRAPH_WIDTH"])),
|
"GRAPH_WIDTH": int(graphs.get("width", result["GRAPH_WIDTH"])),
|
||||||
"GRAPH_HEIGHT": int(graphs.get("height", result["GRAPH_HEIGHT"])),
|
"GRAPH_HEIGHT": int(graphs.get("height", result["GRAPH_HEIGHT"])),
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# run as root
|
||||||
|
|
||||||
|
useradd --system --home-dir /srv/service_netatmo --shell /usr/sbin/nologin netatmo
|
||||||
|
install -d -o netatmo -g netatmo -m 0750 /var/lib/rrd
|
||||||
|
|
||||||
|
chown netatmo:netatmo /srv/service_netatmo/config.yaml
|
||||||
|
chmod 0600 /srv/service_netatmo/config.yaml
|
||||||
|
|
||||||
|
# sudo -u netatmo python3 -m venv /srv/service_netatmo/.venv
|
||||||
|
# sudo -u netatmo /srv/service_netatmo/.venv/bin/pip install \
|
||||||
|
-r /srv/service_netatmo/requirements.txt
|
||||||
|
|
||||||
|
install -o root -g root -m 0644 \
|
||||||
|
/srv/service_netatmo/deploy/netatmo.service \
|
||||||
|
/etc/systemd/system/netatmo.service
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable --now netatmo.service
|
||||||
|
systemctl status netatmo.service
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
Flask>=3.1,<4
|
Flask>=3.1,<4
|
||||||
PyYAML>=6.0,<7
|
PyYAML>=6.0,<7
|
||||||
|
waitress>=3.0,<4
|
||||||
rrdtool-bindings # if python 3.14
|
rrdtool-bindings # if python 3.14
|
||||||
rrdtool # if python 3.12
|
rrdtool # if python 3.12
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
"""Production entry point using Waitress and config.yaml."""
|
||||||
|
|
||||||
|
from waitress import serve
|
||||||
|
|
||||||
|
from wsgi import app
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
serve(
|
||||||
|
app,
|
||||||
|
host=app.config["HOST"],
|
||||||
|
port=app.config["PORT"],
|
||||||
|
threads=app.config["THREADS"],
|
||||||
|
)
|
||||||
@@ -8,6 +8,7 @@ def test_yaml_config_is_loaded_and_relative_paths_are_resolved(tmp_path):
|
|||||||
server:
|
server:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 12345
|
port: 12345
|
||||||
|
threads: 6
|
||||||
url_prefix: weather/
|
url_prefix: weather/
|
||||||
storage:
|
storage:
|
||||||
rrd_folder: data
|
rrd_folder: data
|
||||||
@@ -27,6 +28,7 @@ modules:
|
|||||||
config = load_config(config_file)
|
config = load_config(config_file)
|
||||||
assert config["HOST"] == "127.0.0.1"
|
assert config["HOST"] == "127.0.0.1"
|
||||||
assert config["PORT"] == 12345
|
assert config["PORT"] == 12345
|
||||||
|
assert config["THREADS"] == 6
|
||||||
assert config["URL_PREFIX"] == "/weather"
|
assert config["URL_PREFIX"] == "/weather"
|
||||||
assert config["RRD_FOLDER"] == tmp_path / "data"
|
assert config["RRD_FOLDER"] == tmp_path / "data"
|
||||||
assert config["LOG_FILE"] == tmp_path / "logs/service.log"
|
assert config["LOG_FILE"] == tmp_path / "logs/service.log"
|
||||||
|
|||||||
Reference in New Issue
Block a user