From 4aee9bb7737886309a821a2be254cd03ae0f40af Mon Sep 17 00:00:00 2001 From: sebseb7 Date: Wed, 24 Dec 2025 06:56:27 +0100 Subject: [PATCH] feat: add tapo usage logging, doc: add nginx proxy guide, chore: ignore sqlite files --- .gitignore | 5 ++ agents/tapo/src/main.rs | 5 ++ nginx_proxy.md | 120 +++++++++++++++++++++++++++++++++++++ server/data/sensors.db | Bin 45056 -> 0 bytes server/data/sensors.db-shm | Bin 32768 -> 0 bytes server/data/sensors.db-wal | Bin 1330792 -> 0 bytes 6 files changed, 130 insertions(+) create mode 100644 nginx_proxy.md delete mode 100644 server/data/sensors.db delete mode 100644 server/data/sensors.db-shm delete mode 100644 server/data/sensors.db-wal diff --git a/.gitignore b/.gitignore index fd8ffc4..9e491fe 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,8 @@ dist/ # Logs *.log + +# SQLite +*.db +*.db-shm +*.db-wal diff --git a/agents/tapo/src/main.rs b/agents/tapo/src/main.rs index 4cfad53..70a148c 100644 --- a/agents/tapo/src/main.rs +++ b/agents/tapo/src/main.rs @@ -389,6 +389,11 @@ async fn collect_device_data(device: &DeviceConfig) -> Vec { } Err(e) => debug!("get_schedule_rules failed for {}: {}", device.name, e), } + + match plug.get_device_usage().await { + Ok(usage) => info!("P100 Usage for {}: {:?}", device.name, usage), + Err(e) => warn!("Failed to get P100 usage for {}: {}", device.name, e), + } } Err(e) => error!("Failed to connect to P100 {}: {}", device.name, e), } diff --git a/nginx_proxy.md b/nginx_proxy.md new file mode 100644 index 0000000..eec07a0 --- /dev/null +++ b/nginx_proxy.md @@ -0,0 +1,120 @@ +# Setting up Nginx as a Reverse Proxy + +This guide explains how to configure Nginx to act as a reverse proxy for the TischlerCtrl server. This allows you to host the application on standard HTTP/HTTPS ports (80/443) and adds a layer of security. + +## Prerequisites + +- A Linux server (Debian/Ubuntu/Raspberry Pi OS). +- Root or sudo access. +- TischlerCtrl server running on localhost (default port: `8080`). + +## 1. Install Nginx + +If Nginx is not already installed: + +```bash +sudo apt update +sudo apt install nginx +``` + +## 2. Create Configuration File + +Create a new configuration file for the site in `/etc/nginx/sites-available/`. We'll name it `tischlerctrl`. + +```bash +sudo nano /etc/nginx/sites-available/tischlerctrl +``` + +Paste the following configuration using your actual domain name or IP address: + +```nginx +server { + listen 80; + server_name your-domain.com; # Replace with your domain or IP address + + # Access logs + access_log /var/log/nginx/tischlerctrl.access.log; + error_log /var/log/nginx/tischlerctrl.error.log; + + location /agentapi/ { + proxy_pass http://localhost:8080/; # Trailing slash strips /agentapi/ + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + + # Forwarding real client IP + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} +``` + +### Key Configuration Explained + +- **proxy_pass**: Forwards requests to your Node.js application running on port 8080. +- **WebSocket Support**: These lines are **critical** for TischlerCtrl as it relies on WebSockets for real-time sensor data: + ```nginx + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + ``` + +## 3. Enable the Site + +Create a symbolic link to the `sites-enabled` directory to activate the configuration: + +```bash +sudo ln -s /etc/nginx/sites-available/tischlerctrl /etc/nginx/sites-enabled/ +``` + +## 4. Test and Reload Nginx + +Test the configuration for syntax errors: + +```bash +sudo nginx -t +``` + +If the test is successful (returns `syntax is ok`), reload Nginx: + +```bash +sudo systemctl reload nginx +``` + +## 5. SSL Configuration (Recommended) + +To secure your connection with HTTPS (especially important for authentication), use Certbot to automatically configure a free specific Let's Encrypt SSL certificate. + +```bash +sudo apt install certbot python3-certbot-nginx +sudo certbot --nginx -d your-domain.com +``` + +Certbot will automatically modify your Nginx configuration to force HTTPS redirection and manage the SSL certificates. + +## 6. Update Client Configurations + +Since you are serving the API under `/agentapi/`, you must update your agents' configuration to point to the new URL path. + +### WebSocket URL Format + +- **Old (Direct):** `ws://server-ip:8080` +- **New (Proxy):** `ws://your-domain.com/agentapi/` (or `wss://` if using SSL) + +### Example for Tapo Agent (`config.toml`) + +```toml +server_url = "ws://your-domain.com/agentapi/" +# Or with SSL: +# server_url = "wss://your-domain.com/agentapi/" +``` + +### Example for Environment Variables + +For agents using `.env` files: + +```bash +SENSOR_SERVER="ws://your-domain.com/agentapi/" +``` diff --git a/server/data/sensors.db b/server/data/sensors.db deleted file mode 100644 index c269d1f1d22412650736fd7cd7b88263368106c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45056 zcmeI(-)T+YVHag+w+u~< zH?mE90w2T+?|c9g;|ur>K7f}V{tR1K*qAjjm3&Vi^Z(2_pWkl|83rDml-iyYt+NZ) z@kCa8Qws#NPleDlEuh}(>g|`XnpyJ~)KtGQzic+3efYx%vG`wFH1eKy_ow);cfX2V z#zJp=zy2ipTjXc8l7avN2q1s}0tnn5fx*3~k@zI34^GU6uZlYIVm#7HEQ`n76Cy)$?I*=nMb)>I3hm8`i_i0#SA+fCKXs`Z6c5yzF{ zVWDy=zO+t7p;kRAmQ@9ZR=Jv*>^p6iI8eQ;77r~^KB|gxtyJ=N88=r{t;cHp+_p&< z7H_rET$S3YW5a`0~0NECMTL2Yrk((a;orQOIs3fT1M zWf4oeuMF8e!$|Dx=+}*xLzZ@DVwQHp%Y&A7S1oG6kdfHi(gzO~Ms1u+H++nV&Eo57 zl6ng-s_nVUC?%%3ob28)5;@g~jfIUE?f2r!552Mp!<`rB$cy|)4&<7V*xJ&sH|Epf zMpy4FON0MI3XHGG<6vl*^{3-rKI^DkO*Mw9F|*r{z1hXJ+1}1>-0nKAOsON2&)?CF z#O9`c{oSZe=e%uyBfstW)4)tE{@T|9Q`L@#smVz0k2d}=H*C`4acrDu_W6ZuwXYWM z(zuY0C!4mq=bnzE2iE6>TB$1bYn6%`aqZ#Y`lwnsJoeAnaeAKJ@68@IhW;oJO6+gy z+9-b~vQFPS8!g+ugPrl`g6d;-ct5Yj)LW1LsHqnP0R#|0009ILKmY**5I_I{1pY&T zul2Ath`pc4YV}MmpLO!Ny7M3{vpH#I>P>UctbcqzQ#Z3_Bh!*CwOy*c%!X{4`AlO^ z&E#A8R<^a*N_*-n_q{9pKQ1cJ%%jenD<|Es;F2X|&yrQPBEzxvZZ1px#QKmY**5I_I{1Q0*~ z0R&c9VEFrg?*CUfz;q1(1Q0*~0R#|0009ILKmY+H!2LgIfB*srAb&4w|%&y1O2sK~WDn<-u9Qlk)xq8KW=pt7RSZKYKT zL!r=(N>RFrO76Grs1}{`;PK$!{Ly3iz2A?=_nhzVJkB|fbDqD?rr$md95X6fT-wR9djDqB!t)Qb}4_mFHjX@1EODzH{D_XXVt^GfN+5 z>g$Y}&Zy-Kztf-nopr$Jd7!g)ug%?0+vT8oI8_63IKLd6pEpGU5|DrdBp?9^NI(J- zkbndvAOQ(TKmrnwfCMBU0SQPz0uqpb1SB8<2}nQ!5|DrdBp?9^NI(J-kbndvAOQ(T zKmrnwfCMBU0SQPz0uqpb1SB8<6_y}6Vt=u0637|8VuVyM$K>}EzXn%fx91n#DkY23&CJj84s zl&o1_IfW!R6uN>tMsyK;{ z)ii$#vN~4R!YtAnSd7J46KiHIEXC5TrDa==wX+V^Nq=ZwR>kLfyEV@BKpiK6|Gj|Q zKT(l@cM7;2MimKor+^>9)F+BYB#=xdZD>yyE~XDx(w}P?%rHiAI}@40bY?P#CwQ6{ zc!g!W!CKzqBR=ITzGXKD`I%$6I}5B@-?cw=2%`aU zG$VzUVC(817&a z_wWD@^EeB5jtX951#5VljeNrAe8W!mafILa(`gxB%W7La^H{7kwIs{1)|O|TRsGfL zi`UgS0zuRzk{FuMf^@QJM<=?{i_0nC8j87z5sc+dCUY+j@(6QTNI5UDgq6I>JAA-q zzF<2)u%93Koxd>ivtX-lQP#*3EZH)xjkP!T4GM|`Bp?9^NI(J-kbndvAOQ(TKmrnw afCMBU0SQPz0uqpb1SB8<2}t0-75E2Qthp=z diff --git a/server/data/sensors.db-wal b/server/data/sensors.db-wal deleted file mode 100644 index c478dba92232e2119f6a4effded098738ba7ddca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1330792 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