From 316cfa84f8a39c7a53fbfbafe9efac7d5b81e65e Mon Sep 17 00:00:00 2001 From: serversdwn Date: Wed, 24 Dec 2025 02:03:03 +0000 Subject: [PATCH] Add FTP credentials management and UI enhancements - Implement migration script to add ftp_username and ftp_password columns to nl43_config table. - Create set_ftp_credentials.py script for updating FTP credentials in the database. - Update requirements.txt to include aioftp for FTP functionality. - Enhance index.html with FTP controls including enable, disable, check status, and list files features. - Add JavaScript functions for handling FTP operations and displaying file lists. --- .gitignore | 1 + app/__pycache__/main.cpython-310.pyc | Bin 3275 -> 3351 bytes app/__pycache__/models.cpython-310.pyc | Bin 1424 -> 1508 bytes app/__pycache__/routers.cpython-310.pyc | Bin 6856 -> 14246 bytes app/__pycache__/services.cpython-310.pyc | Bin 6991 -> 13446 bytes app/main.py | 2 +- app/models.py | 2 + app/routers.py | 255 +++++++++++++++++++- app/services.py | 221 ++++++++++++++++- data/slmm.db | Bin 28672 -> 28672 bytes data/slmm.log | 288 +++++++++++++++++++++++ migrate_add_ftp_credentials.py | 59 +++++ requirements.txt | 1 + set_ftp_credentials.py | 65 +++++ templates/index.html | 208 ++++++++++++++++ 15 files changed, 1095 insertions(+), 7 deletions(-) create mode 100755 migrate_add_ftp_credentials.py create mode 100755 set_ftp_credentials.py diff --git a/.gitignore b/.gitignore index 9d67558..234ce26 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /manuals/ +/data/ \ No newline at end of file diff --git a/app/__pycache__/main.cpython-310.pyc b/app/__pycache__/main.cpython-310.pyc index 4a56d3c6dc6b374aeba724c2bc061257e7a5725c..6dabad060b7f8f84c44e1cfad1e6ed99ed8198c9 100644 GIT binary patch delta 571 zcmYjO&ubGw7@aTOpR>DZo3=@sY6Yb&wbj(02gyNb4~9w)dMJpcgnlVDG`n$jYOu^A z7_Sjz6c1L|y@|J;#G~lVvzJ^w`8RlRCZXWKd%W*^?;GaF&)N5tAPNFX)C%jH2j5Nu z$0%Q{K8f1RYTRVcnk>es8&>1PcDpKz#$R|X3+CMyFNKjhVl0D}ix%2_NNGkAOIRCZ zCroUS6L!l}@X=8mDCE)Yn~AQ*6-FLK5B(D}agUb}&^w$+sGzM$LDRxnCd;9NK|fa~ z=-nfabBUMu!rw}#G6F>S>2yX$77(oopOo=oO-472I)Z$LO?6Y zi#+>+(@M_>lb;i25BPa3{-49riSdvum*q#Rzc@Cq`9ZrG9r1y`&pW$E>Yc+zyE&>l zjX3T`4CPIGCBF>pvicdersqZ^V!MEnj27K=qfETTn6SS+;I?QAT9rFNE<>HGsBf565jb5{%*IPX03o_9FIoDAI#MvW*EU{>w-(pvg6 z@+=H^%6h;f9e8h+w?G3#WpWQu13jWxaqJ~)e+vBY+$OyWn_`U~E&bT9w_ diff --git a/app/__pycache__/models.cpython-310.pyc b/app/__pycache__/models.cpython-310.pyc index 4ef31d3412d3e8cbf858e14eed865f6cd6ac48f9..13b951991363fa19a187f2a5e2501e3436b0f0f9 100644 GIT binary patch delta 337 zcmbQh{e+u0pO=@50SM$hy)z$7IgqAIRoT z;YeWviVLK01v6-JPxfS-#=nyB7DrxbPEKM{PO6`#;N(Y)ExtTyB?a-N#i>PkiMgq_ zz`TOQ;^OlBqLf?Q<*7;Wsd*sfDJvO@6oD=P6TejSGxBp&^?|y|Qj3aH^2_t|i*s^w z^(Qwli3^AUm2ohzFtV`lG4U|+Fm7JV6v?PtBm@-p)08L@0huBSB0vg@#6g4vh+qN{ z%pgK?vH{B+Nw{?oi3Q~M&-%;tbWEIKEhoHg||3ta`RJ4b5iXXfhvkQ OfP@MI52FCHh#&xLCQQWu delta 271 zcmaFDJ%O7ypO=@50SJ_zdu0Yq6AKFTbMsSDbBgr}DvLm7vjTCk z5Rl+tU}0oo;bYqTk13MTM3eCrM_y@84#*5YO^G6qiXveUAp#;qL4+8HU;+`$AVPd{ z3(FiyxMdJ)Ktg1)C95W*{N!X-KVuLd;VOi}TO2k($Cc)!+A#uE6mtLx6$Tzg I0cH_?0HeY*vH$=8 diff --git a/app/__pycache__/routers.cpython-310.pyc b/app/__pycache__/routers.cpython-310.pyc index 4afed07120d422d0dc8dbe4f7aa3742de51169cb..97e9712a8e10d827836fbcbb0150249e6dc3d14d 100644 GIT binary patch literal 14246 zcmd5@TZ|jmd7c>#@0aD?SGFY2I+ktP)~>I)D2gM;@=B{66bKvIsf_3nREXCKmX;>Di(7JKHoj{FP9o)2*5IY;~4lGp)Jye09FPP+j11wzb$^sxGw;R1c_% zS=zbFyn9!z9yC>v6Zs3OD7>vz551s>qA1-^M9C~%PgD<^hj#7}Q@g6EnX}i`4ONs) zLrk054PDHL*&E60YIPYf_wIq2GY>OdO3aG|hWixbTAYY$NgQC9`!N1p;%%>ex&I2oJT?*Laq$Ghtnxgb zoCx!j_%y>j$}pe7JU$!jpvF7+xnLJl;+Xh6Lq3L;KQ6w2mHz^-{Np@>r^m3*nU4o6 z|3z_v`#m92XO+q`cZdrrNiX}%=~HK{o?}WcebVfj9btRL7tftL{oIv?*>##$$IG5K z*UwswOQz$^hL0zkcEjp)%!X4@y~6X&mU+gsyH>|GQBI#VZ5v(jm=Sfybee6mqIuab z)ot@-OPDQhYNKhkMD23D)dYYg*8rNjU^+Fi4#3>nOHV%Wtkv0QUhtpKI(4UKhb^t9 z*>Svb*OYeCc4~H~-nBO^$IY(RyUo>3>&YiPopN+Yb)~t{yyEFj{es<~myUmQ6iy)7 ze~!#ls;W?`+A9zP)da{u{gPrPg?60EDKj0E6I{-i*`Ta*ITx0bT+WC6Q(P{Dj+b#7 z-CEa@j+dt=vr}JhnZnC&I8mWMg`RE7PQ7hm(iZa8;SFu=^L1)6+>a5zWb{iztU6sr+_DZ+!WshGlJLZ+HeEK2uvWcR!Yljtg zWn`090!lY)^cpM2TUMjqvY%cFJG&3Rhz6tGL$+=jb;Ez@Q>Tr(5EATUSTbA!;@f67 zXz+5)ZY_L%wchHP&q--XFM);f67^Q=w&o=oE!$Hs^PBnEGA8O3N2Ya%Uf?cdnS`!t zT0zw_g`btRj5>p)eV}XF&)mB|-cRJ6xi5)7di3j`5gL?+D}5Pd)X-Qx#Pn0knLOG9%2! zUX^%c1&wwI8NKR^O0Snub#>p@qdt`xFH@@#3D#;}u2ySXqSvD5Lap{j4@AT*avoT{ zgzZRgs#dFaIu>LXWT9QF$pvm|b{x4#Px`uLwd4{WE+#&<&k;$Mhm>zHl0mN z$cL%=C}qZIMlUDlkWsP^BFiadHU3R$np#j(QyOX+(w$!YX0Ow9YE7}d+VI6}I;ik8 zu|Beow1*-0zlZ{{lmmXhs0|<(-&S`LyUMmU(Ck@owMKmXw)Unfk^^O+T}&dSkkUvQ zq^wBYN{RF>4SSneyRFJk1LK`Pz#hLhPeR}Dr(!eerOWmdjAIs18u^3Ck6?7(NX;!*iHaOA0$Wy57diENM}_V^fXA+lK4$=O@*FN z1G#-M%{7nH6Kc}<&2Y`<=?OI%{ARi4Bt4-fi{BjAyi8B1$>BH8HLubWYVw#zVVC5- zwo~-yYh5XDx$V^HZEeF1w zKq6~EFv6)mE$?B@DZsIim3a7g%qb6nC6ha;U8RHbG0vuV&LLq#FBXuBNF}5xq%zVp z*ti(7F>jar9L;xe%`%ve{mB~AY(Td#h5S5PdEagqc|pA_gd0>hNN{huZPx9cG~3YX zYShv6v{skSuQxos)viN(YqgslFWKsv^-ErHz3zZ<`?Z#N8LDKVYi)szZL236rZ;7a z8ZgzHoo44kt-bCQQGtCmYm&sUmzVXeTEKTc_00IxLx({`J-#eBjXIpPwgu8_*B$v) zf;mIkS<22)_6lXMQg)uQ-=yp{%Bqxo4Ov`r$Y-e52(w6X_zTuWi+w+`NOH^!<;M8$ zyO1NV1GIrO@*#0@0?GaxWWPkK9csD0R+|pBR9~yjgk@i=&4y)PtIdUFU#pF@UTC$@ znj@_@%jIGSpW|{VEa$mA6_yKB_O;!!EdGbu?kC-ZvZOO#u2S|WWxSb>(bMCUee7+G z?V>>lXe;G6ko^u4OaJkM*gur#RE8Q6`YCh?`S>qU_;^C!VIGD6h9KX`>?%9iUFD?m zdg<%hw!WPl_y@wH4utkVA0)3QpatKH5%PhkF8HFF#59TIo#HP2mXM~9%1F~lGf1;Y zb4c?@3rLGdOCkYTptI;s`l8zx&|krO_$+o{B8#1(_AD|l)pHshYfCb}<)BxYt(xey zyI!*Cm~Goj+c@+cM@o{c^2?Ncow85JkAdD0iF9I0QUYL-dP%6nP!bHn&l$65F%a-R zm;NytZPHvK0Y3-vj+6cxiE|R>q)krz64#pO>Xu_l7|=>*M6xFVl0*6l(z|!5uxQwp znW%Wp#T73|=cIiIHA#{3RWWBo0YiIEq~F%J6E1d@Xy6o_TQB0!b_zR1k%^A#^GfGs z7;~lmc~#lgRUAfufQ?EJA~(=+r0aB~V=UOHf!YVitNIp{d#BvLr_G)3W7Xa}dhXfNP$1fE zXbufl#EqtH2pD~29Ye>IhOj3bR_#|OsL{~dU@$K%+dM< zvl5(~q)m^XoQqUP5-p~Ig?g8lO+pG%v%1~=NbPV$KsMmUX+vHlJeMeI z5waLD0qF!{l5K+R5bPwH*{735ok%~KNFh9Z`39OlIa%oRb`e!bhX(6WcA2s*%C1mm zgxNBlyfXERZ}2}-`+%~m$i^hC1)VhR-ik|FdkKv3Yml@!F9S)N2_-Gd(ZDEzLthkb zY3CJopOyMw8p0lgH?O%{}ZR4A7ZZ%m!T1B9yW| zsV$r&)I1`~+h7EyHg{=+IWQ-l2gLL^TP#H~){?{(Az;J~%M(QC7;wl2pPbGMZC*u~ zI#|PxPw( z5ob-*V8*z`z2}VS$R>o76D$O-J98#^-Kmf8b8&Ku#80bqxCV;q0% z$T&1~$irE}0Uu*STJ1oSG2Ee!B2A|!JD6%S@ZS@=O*n7%9Wi3Gg-QM$-$MUzJtHqo zIQ-yd5b&UqzAnp@MS2JPxuV%{;F=vfYmN{c9XOT1SojQ8JWp94nWt0CNxD=v!i*22 zJS`o2F?u!#RIy1R-K%J%)Lf#hmDG|pN!=JKA=>-$I=YX!fuvb)ds&2QY>vu}oMvf& zmyoEN{d1gi-&SwJfyi3E%4d(dgCNXJHFC%ay@f#cHu!F!U5EAhj_Q8N-MOjkB%Ku8 zyy=^8=EBVjyAqDZg#8WRgBusM>05B=!Fpi4sLe&AoJTMS7*GQq&5pNqve5hU{j)wV zz6S5p7F^1hS0*+uSpTpt3-%xJc-jAh@o?(VcmMGqfyV4!k%OhAzoopTzNKv>=#;*Z z*-r9Yi_u&M`p(QC8Lb5@r>l6w+5X@ABQks`g2xOxXrFsW?PJxqQ&^*i05dP92u73# zsi7Qmr}*usorOUPLA06pienUi9e)~suk(06p>C(HraH&{UzU7%jPE z+h?pC?HRDF>@!+&INH{S@BI+ZnK&L^p|Rcn5c(P7a6SwNSp7>Bk@R&ol2{eZ*Stw;y2}yH;WS&8 zJ%TN?W%k5n3&8MwB>Tt6AdL{Ts3~rmf)_UxI_*KncY(IF{t?h8{0VN!N$kKcIS6GG z)3mm&UR67Y$J6f?B!+8%H}q1oPw7s z%N(ka6g-dXJVB_MbZr_n%cDXA8A`@C0W%z{l5~$f3&nT6-nfK4sl!sIs}hD;Z)_UF z`v*hfh*m4uWcQ&+NewP7M4d#BwNZazj~Q!~Y~Ua#>I8kYyT8QGMLuTtfoKGpS%m&_ zvu=bFI%XhP5*>te!1<>kSy^6|de$!5R)^hkvPR7x!O_M}ZcV;P;B+M3PWX`$9_R+u z8kF(zSmZH=EA|2%kmP>Vt|3a9z;UP^gzc?;py`M}BVIDwY>#tai z&f6%UK7!sobe8y&_&Vb9hy~neVPeb`Cu{d$w5$Q|(I)%cA6OV7=NA&qGrS7zjI)ABt_*_f~8E!26*1~H>=9)Gw#lna(j zNFhnhk7+&R2(P3i?-uVHj~OaFlAPKxedi_>c|#GMCE3Z_@BnS+@1jY&R!YMR(Ng%5 zi)8^DtE-t~8e;9ZR)A>xoR<0tYSi2ZC73UnC*mzje^=JZ2j|d#4o;W_Z1~|}uWM!K zOH?L&z;aFdIee~Z>K)W*ng5AOGn)J^rsK}XoHn=uv+7@t8Bv_#Zh&|H7M=bdxB-3( zqKO=@3y}+TuN%NA4{%G3gw`H|ecCmi<7+_1^XE)nNjts)o*r|F4QP9bqWwm(HrQA{`TDCT@LHEeFgMLtWg z1Bv526MRSV9bnuB*}iMw0ctvkagr^)z%qxS-Gwn?3Y^LNQj`2Wpsq~2%eXT~UguwN zGv)&{lXjy(yAjMs_}g(HTI!%-!hS4H1o~ArBp(d+@pt6+v0C55j>zxfK~iZC`}&6O z$bye}E&dvvehuu42w3FB+t$e|LDwBb?vge@4CGcK@ZS~tuY+;PJF&o?4Co=y>Wd@Z z2}D4^xVZ8*!no{5fZkem?|%VidyH%SSN7G@kIBBwsJJlGK_L1WTGJ(DUXHE;`dG)k z1w3rvTOiXu#<+JyghGCW2=Z5gOfYpE&t#gYQ?+(FXG6P<|6Ej^&GLjNnEFR~h^o#IX@ zjL3yn<48mTGM;GZju|JLaJ<9gN$rSyjiPx_m>cz8%Q?DqxLG|3oZc#S--w(}@=qz7MCLz0!;gu~@5KYiJWYof1+>bs zfL6?>MDPuykq^=K2_$;~S#)c}9gNvRlahfhJBzd`>B-T;nc|aUos4r7=hNk8Tcx{C5mUJ}Upw z)kKoI+q#p)B{Usz^7PxVLs3fa@Wr=Xm2OSgtKf$W?qqZ1nOhO{hMz~8AWQb9I`ry97I{ z6dFj%p6*xRc8Trp3B@41EOefi_@1dRG3gZWQvUx6_>#7l0EA@m z`2w{ix;;m(;fe43@f9*CCHT((-zMU_2^{U_2p>lcd}E&MU$$FVm1ZqRlKf{$D`^=g zNoFr|+`r!bG7&-72erhu9iQ`Ux}tsUWy)rqClHV``%C{MVko_ z3Hmhq=9@Qf-hA_Be(nA7c+#`1m;yhse>~sd%RfoZ_C6$vLKR9J#U(azRa!C5!u!p!!>L}wsmL1HEq+4+EMu(abk81_z@>=$Kk)>BwWk3WZ#67bW?W9P1|Yt zZ8{k@YiHe@og)g1?er$u)Go2}jL;~J)d`KisoDpgQ)q%(_Y`U|b6c|u?7+@6P3{t= zGHaV`5}IN&G|eKLI?d4Ry~sAP4+3ZQF;0#ZB+sCebV~B(q_6x#U%e4$2WUZ(iV(mw zoq+&mWB~Ips6+>Y_QvGc{GRiJbXJaAki5Bv&PRL~>Ad7DN=|8yb9Qt!3v^MEmSmJm zV;wEi70Fqqkr$Qn>OXNm%bHlwmPDX^K;C#KwJ6T(-+Ves$!Uc}sIj521XZ^Qv*-jh z?L-4GC=w^7gW&q}WVO{g!o7CDxWCrwgwc&UYqHxd zesV=LjR!>}aS5}~3azcG)1cLWRc?5GP!LBZ{_sQzDN6(1IC9J5L0Qb0>m)5MnJc|4 z`qKP>hp9@X+H873HE4KEzf$2j+0|$Ud=gvwbt4ZL_{FR+-mWoduPe*W7H>kBQr{ z1#(*aJhn>yB0h=zcuL)g?J7j+syp$nwo8Z-Bz7$EMtmt9rTTq?M((Syv&Kb!OnekC zo_ZQ1`8L7{oI1%*VjIKerx3ma5NcIQzhIH2#L81L+B3+;kok8JhUx7~q22Nse;D<% z!0^{aH!&|BB&NwZ@p0le#xWeWE?jHQdJ>x$*?Sg0Dt>1D=&TGb+Yc^|y@TNTxqJa^ zVYK0Un;v(o0Y8tN3kVkxE+Je75Eqlh!}}A=H?ZrGXk~(ZkXhTWi{B-WkgK9_bWZ#) z`Dx+H9Iuztyo~O?j_?fxNk4?G!wBOenx7udFI$*0ehuJNv6WsR*TlQ&zdj@LBe&~$ z?3Njs9-EQxVZQ+o8ttIg^ltGN#QT}j*D&q;`v_MMzHBLH%0^#=!)?Vxp?;Y|!w8)0 zHhVN}*4`%fGxwIbnmt8c67Ob@7*O$**NFHmF)c0^%Drv%u68HW)pnx0$~m}S&Vpcr z*iM{Ibd_Hyzf@7SSCr-j9mL!D0a5PiM7fgzhDA+e&}df&o-XCB(-@6Y{Y~|CLV@{; zev81kWM`9rSLxTHiJ{*>r&Wt4`vZWO>byQOL2AqdX}DWPCb*^0EX~nLd6uT&EMY#^ z5so7~DgK*#N^DI2r-%D>CwGBW{WfPVYX&7q4i6Z8PnF6GCtT+H)j9*|j5&FIabs=c z34}qR2_w=Ee-5o@0m9T7ui0cZtc=fc?(s0fI2RvJ6;}quM4rshs5uP?BqSsI_f9^a ze;@q(YYjksHLvPe@6-fIibMHD(iWHUYl~IrE00+&nE7>tn)p@zG+g}9WC)w$v;3-2 z$L0-jb2TrH9hmMl@bhJaaoO=&Ek^kdq5G?*ME3))HOvTa;uIc23*jaLSCqq#9rrX2 zthmEX`V&&O5N?ay!8POR82J|W%lEF{Rl0g57R0-Hmu##3s=Hs#AYA?BB#quzuP84n zQg;>59XzQ(*omDqjY-|byODjuhL_Jkcac{jQrMYJ&)-uv4oi+GJ4Mq2VP`vUk4%sm zJ3;ob338p^j*QDaI&K|~5WJyA2i{W3`Nz)zrf^Rff1X`2Wc4~B{yn=qqLJmffkrOR zIYWJvC%@X6h;w$c&HSK3;ii|Ww=jU=88Isr+NcHaaCk%;CFG$%pzLjoo)_+eIn@1T zwZ#LZ4QsW$+2T%Pi&gq1@9&Cut(b!&^V8y*(M5MF;%~=S{q)u=JQUoUX|VYCPDh4bJN3WB_zFd?DQf5*u;PpAQd%WLX1_Gztlz|TR8Uq!fv zHoXOJIQ|lTw-Ih5;2QTOWN>+f<_3h9Nzuog<>|CMjYJOBUy diff --git a/app/__pycache__/services.cpython-310.pyc b/app/__pycache__/services.cpython-310.pyc index b156234718a2681f68f94784c1c07142a0284e6c..3c8764f1fab1f58c9a46e566965458d14e7c666a 100644 GIT binary patch literal 13446 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 1: + snap.lp = parts[0] + if len(parts) >= 2: + snap.leq = parts[1] + if len(parts) >= 4: + snap.lmax = parts[3] + if len(parts) >= 5: + snap.lmin = parts[4] + if len(parts) >= 11: + snap.lpeak = parts[10] + except (IndexError, ValueError) as e: + logger.warning(f"Error parsing DRD data points: {e}") + + # Call the callback with the snapshot + await callback(snap) + + except asyncio.TimeoutError: + logger.warning(f"DRD stream timeout (no data for 30s) from {self.device_key}") + break + except asyncio.IncompleteReadError: + logger.info(f"DRD stream closed by device {self.device_key}") + break + + finally: + # Send SUB character to stop streaming + try: + writer.write(b"\x1A") + await writer.drain() + except Exception: + pass + + writer.close() + with contextlib.suppress(Exception): + await writer.wait_closed() + + logger.info(f"DRD stream ended for {self.device_key}") + + async def enable_ftp(self): + """Enable FTP server on the device. + + According to NL43 protocol: FTP,On enables the FTP server + """ + await self._send_command("FTP,On\r\n") + logger.info(f"FTP enabled on {self.device_key}") + + async def disable_ftp(self): + """Disable FTP server on the device. + + According to NL43 protocol: FTP,Off disables the FTP server + """ + await self._send_command("FTP,Off\r\n") + logger.info(f"FTP disabled on {self.device_key}") + + async def get_ftp_status(self) -> str: + """Query FTP server status on the device. + + Returns: "On" or "Off" + """ + resp = await self._send_command("FTP?\r\n") + logger.info(f"FTP status on {self.device_key}: {resp}") + return resp.strip() + + async def list_ftp_files(self, remote_path: str = "/") -> List[dict]: + """List files on the device via FTP. + + Args: + remote_path: Directory path on the device (default: root) + + Returns: + List of file info dicts with 'name', 'size', 'modified', 'is_dir' + """ + logger.info(f"Listing FTP files on {self.device_key} at {remote_path}") + + try: + # FTP uses standard port 21, not the TCP control port + async with aioftp.Client.context( + self.host, + port=21, + user=self.ftp_username, + password=self.ftp_password, + socket_timeout=10 + ) as client: + files = [] + async for path, info in client.list(remote_path): + file_info = { + "name": path.name, + "path": str(path), + "size": info.get("size", 0), + "modified": info.get("modify", ""), + "is_dir": info["type"] == "dir", + } + files.append(file_info) + logger.debug(f"Found file: {file_info}") + + logger.info(f"Found {len(files)} files/directories on {self.device_key}") + return files + + except Exception as e: + logger.error(f"Failed to list FTP files on {self.device_key}: {e}") + raise ConnectionError(f"FTP connection failed: {str(e)}") + + async def download_ftp_file(self, remote_path: str, local_path: str): + """Download a file from the device via FTP. + + Args: + remote_path: Full path to file on the device + local_path: Local path where file will be saved + """ + logger.info(f"Downloading {remote_path} from {self.device_key} to {local_path}") + + try: + # FTP uses standard port 21, not the TCP control port + async with aioftp.Client.context( + self.host, + port=21, + user=self.ftp_username, + password=self.ftp_password, + socket_timeout=10 + ) as client: + await client.download(remote_path, local_path, write_into=True) + logger.info(f"Successfully downloaded {remote_path} to {local_path}") + + except Exception as e: + logger.error(f"Failed to download {remote_path} from {self.device_key}: {e}") + raise ConnectionError(f"FTP download failed: {str(e)}") diff --git a/data/slmm.db b/data/slmm.db index 461e656d81229d0f3fc181bde0ad393ff94aab16..24c7a61c964981b7588c1c2e094bed2b6c53aa10 100644 GIT binary patch delta 262 zcmZp8z}WDBae|Z(M;ZeI13M7I0NX?zBLSeOUbq)8-(vYEQBh%#6Pkkaa!ibM4UG(p zOo61Xk%@wVp_PHTm8rR&rG=@vp`~wXVsU9vs-c;&iLstJ5Lik?4JM7i bqyo>T_D#~brLb8Vc!!niq= zr;drMF`AKGTv?g1sdjQU_wLC)Jd&Fa^1B$Y@W!Y b{8cBP_h-Y2Tm3a~%0cw$Q7F&3C_n)KTC88v diff --git a/data/slmm.log b/data/slmm.log index 0d5dc84..b365629 100644 --- a/data/slmm.log +++ b/data/slmm.log @@ -91,3 +91,291 @@ 2025-12-23 20:29:57,135 - app.routers - INFO - Started measurement on unit nl43-1 2025-12-23 20:30:46,229 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Stop 2025-12-23 20:30:46,455 - app.routers - INFO - Stopped measurement on unit nl43-1 +2025-12-23 20:35:33,744 - app.services - INFO - Sending command to 63.45.161.30:2255: DOD? +2025-12-23 20:35:33,993 - app.services - INFO - Parsed 64 data points from DOD response +2025-12-23 20:35:34,000 - app.routers - INFO - Retrieved live status for unit nl43-1 +2025-12-23 20:35:57,872 - app.services - INFO - Sending command to 63.45.161.30:2255: DOD? +2025-12-23 20:36:02,874 - app.services - ERROR - Connection timeout to 63.45.161.30:2255 +2025-12-23 20:36:02,874 - app.routers - ERROR - Failed to get live status for nl43-1: Failed to connect to device at 63.45.161.30:2255 +2025-12-23 20:37:56,046 - app.services - INFO - Sending command to 63.45.161.30:2255: DOD? +2025-12-23 20:37:56,234 - app.services - INFO - Parsed 64 data points from DOD response +2025-12-23 20:37:56,254 - app.routers - INFO - Retrieved live status for unit nl43-1 +2025-12-23 20:38:02,637 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Start +2025-12-23 20:38:02,774 - app.routers - INFO - Started measurement on unit nl43-1 +2025-12-23 20:38:09,492 - app.services - INFO - Sending command to 63.45.161.30:2255: DOD? +2025-12-23 20:38:09,665 - app.services - INFO - Parsed 64 data points from DOD response +2025-12-23 20:38:09,681 - app.routers - INFO - Retrieved live status for unit nl43-1 +2025-12-23 21:14:01,816 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Stop +2025-12-23 21:14:02,056 - app.routers - INFO - Stopped measurement on unit nl43-1 +2025-12-23 21:14:15,196 - app.services - INFO - Sending command to 63.45.161.30:2255: DOD? +2025-12-23 21:14:15,346 - app.services - INFO - Parsed 64 data points from DOD response +2025-12-23 21:14:15,357 - app.routers - INFO - Retrieved live status for unit nl43-1 +2025-12-23 21:44:51,888 - app.main - INFO - Database tables initialized +2025-12-23 21:44:51,888 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-23 21:45:29,201 - app.main - INFO - Database tables initialized +2025-12-23 21:45:29,201 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-23 21:45:46,360 - app.main - INFO - Database tables initialized +2025-12-23 21:45:46,360 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-23 22:18:54,486 - app.services - INFO - Sending command to 63.45.161.30:2255: DOD? +2025-12-23 22:18:54,657 - app.services - INFO - Parsed 64 data points from DOD response +2025-12-23 22:18:54,667 - app.routers - INFO - Retrieved live status for unit nl43-1 +2025-12-23 22:21:29,945 - app.routers - INFO - WebSocket connection accepted for unit nl43-1 +2025-12-23 22:21:29,945 - app.routers - INFO - Starting DRD stream for unit nl43-1 +2025-12-23 22:21:29,945 - app.services - INFO - Starting DRD stream for 63.45.161.30:2255 +2025-12-23 22:21:30,159 - app.services - INFO - DRD stream started successfully for 63.45.161.30:2255 +2025-12-23 22:22:08,492 - app.routers - ERROR - Failed to send snapshot via WebSocket: received 1005 (no status received [internal]); then sent 1005 (no status received [internal]) +2025-12-23 22:22:08,492 - app.services - INFO - DRD stream ended for 63.45.161.30:2255 +2025-12-23 22:22:08,492 - app.routers - ERROR - Unexpected error in WebSocket stream for nl43-1: received 1005 (no status received [internal]); then sent 1005 (no status received [internal]) +2025-12-23 22:22:08,492 - app.routers - INFO - WebSocket stream closed for unit nl43-1 +2025-12-23 23:04:34,743 - app.main - INFO - Database tables initialized +2025-12-23 23:04:34,743 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-23 23:04:40,114 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP,On +2025-12-23 23:04:40,280 - app.services - INFO - FTP enabled on 63.45.161.30:2255 +2025-12-23 23:04:40,280 - app.routers - INFO - Enabled FTP on unit nl43-1 +2025-12-23 23:04:46,040 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP,On +2025-12-23 23:04:46,200 - app.services - INFO - FTP enabled on 63.45.161.30:2255 +2025-12-23 23:04:46,200 - app.routers - INFO - Enabled FTP on unit nl43-1 +2025-12-23 23:04:48,486 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP? +2025-12-23 23:04:48,639 - app.services - INFO - FTP status on 63.45.161.30:2255: On +2025-12-23 23:04:59,824 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP,On +2025-12-23 23:05:00,080 - app.services - INFO - FTP enabled on 63.45.161.30:2255 +2025-12-23 23:05:00,080 - app.routers - INFO - Enabled FTP on unit nl43-1 +2025-12-23 23:05:07,794 - app.routers - INFO - WebSocket connection accepted for unit nl43-1 +2025-12-23 23:05:07,795 - app.routers - INFO - Starting DRD stream for unit nl43-1 +2025-12-23 23:05:07,795 - app.services - INFO - Starting DRD stream for 63.45.161.30:2255 +2025-12-23 23:05:07,959 - app.services - INFO - DRD stream started successfully for 63.45.161.30:2255 +2025-12-23 23:05:12,535 - app.routers - ERROR - Failed to send snapshot via WebSocket: received 1005 (no status received [internal]); then sent 1005 (no status received [internal]) +2025-12-23 23:05:12,535 - app.services - INFO - DRD stream ended for 63.45.161.30:2255 +2025-12-23 23:05:12,535 - app.routers - ERROR - Unexpected error in WebSocket stream for nl43-1: received 1005 (no status received [internal]); then sent 1005 (no status received [internal]) +2025-12-23 23:05:12,535 - app.routers - INFO - WebSocket stream closed for unit nl43-1 +2025-12-23 23:05:13,539 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Start +2025-12-23 23:05:13,680 - app.routers - INFO - Started measurement on unit nl43-1 +2025-12-23 23:05:26,426 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Stop +2025-12-23 23:05:26,719 - app.routers - INFO - Stopped measurement on unit nl43-1 +2025-12-23 23:05:29,480 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP,On +2025-12-23 23:05:29,639 - app.services - INFO - FTP enabled on 63.45.161.30:2255 +2025-12-23 23:05:29,639 - app.routers - INFO - Enabled FTP on unit nl43-1 +2025-12-23 23:06:10,213 - app.main - INFO - Database tables initialized +2025-12-23 23:06:10,213 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-23 23:06:17,453 - app.main - INFO - Database tables initialized +2025-12-23 23:06:17,454 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-23 23:06:28,863 - app.services - INFO - Sending command to 63.45.161.30:2255: DOD? +2025-12-23 23:06:29,072 - app.services - INFO - Parsed 64 data points from DOD response +2025-12-23 23:06:29,082 - app.routers - INFO - Retrieved live status for unit nl43-1 +2025-12-23 23:06:40,433 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Stop +2025-12-23 23:06:40,599 - app.services - ERROR - Communication error with 63.45.161.30:2255: Status error - device is in wrong state for this command +2025-12-23 23:06:40,599 - app.routers - ERROR - Unexpected error stopping measurement on nl43-1: Status error - device is in wrong state for this command +2025-12-23 23:06:51,860 - app.services - INFO - Listing FTP files on 63.45.161.30:2255 at / +2025-12-23 23:07:04,786 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP,On +2025-12-23 23:07:05,040 - app.services - INFO - FTP enabled on 63.45.161.30:2255 +2025-12-23 23:07:05,040 - app.routers - INFO - Enabled FTP on unit nl43-1 +2025-12-23 23:07:11,492 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Start +2025-12-23 23:07:12,118 - app.routers - INFO - Started measurement on unit nl43-1 +2025-12-23 23:07:15,845 - app.services - INFO - Listing FTP files on 63.45.161.30:2255 at / +2025-12-23 23:07:23,413 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Stop +2025-12-23 23:07:23,639 - app.routers - INFO - Stopped measurement on unit nl43-1 +2025-12-23 23:07:28,571 - app.services - INFO - Listing FTP files on 63.45.161.30:2255 at / +2025-12-23 23:07:29,502 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP? +2025-12-23 23:07:29,609 - app.services - INFO - FTP status on 63.45.161.30:2255: On +2025-12-23 23:07:33,406 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP,Off +2025-12-23 23:07:33,559 - app.services - INFO - FTP disabled on 63.45.161.30:2255 +2025-12-23 23:07:33,559 - app.routers - INFO - Disabled FTP on unit nl43-1 +2025-12-23 23:07:34,407 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP? +2025-12-23 23:07:34,565 - app.services - INFO - FTP status on 63.45.161.30:2255: Off +2025-12-23 23:07:35,926 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP,On +2025-12-23 23:07:36,129 - app.services - INFO - FTP enabled on 63.45.161.30:2255 +2025-12-23 23:07:36,129 - app.routers - INFO - Enabled FTP on unit nl43-1 +2025-12-23 23:07:37,244 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP? +2025-12-23 23:07:37,359 - app.services - INFO - FTP status on 63.45.161.30:2255: On +2025-12-23 23:07:45,658 - app.services - INFO - Listing FTP files on 63.45.161.30:2255 at / +2025-12-23 23:07:58,073 - app.services - INFO - Listing FTP files on 63.45.161.30:2255 at / +2025-12-23 23:09:02,738 - app.services - ERROR - Failed to list FTP files on 63.45.161.30:2255: [Errno 110] Connection timed out +2025-12-23 23:09:02,738 - app.routers - ERROR - Failed to list FTP files on nl43-1: FTP connection failed: [Errno 110] Connection timed out +2025-12-23 23:09:25,266 - app.services - ERROR - Failed to list FTP files on 63.45.161.30:2255: [Errno 110] Connection timed out +2025-12-23 23:09:25,267 - app.routers - ERROR - Failed to list FTP files on nl43-1: FTP connection failed: [Errno 110] Connection timed out +2025-12-23 23:09:39,602 - app.services - ERROR - Failed to list FTP files on 63.45.161.30:2255: [Errno 110] Connection timed out +2025-12-23 23:09:39,606 - app.routers - ERROR - Failed to list FTP files on nl43-1: FTP connection failed: [Errno 110] Connection timed out +2025-12-23 23:09:55,986 - app.services - ERROR - Failed to list FTP files on 63.45.161.30:2255: [Errno 110] Connection timed out +2025-12-23 23:09:55,987 - app.routers - ERROR - Failed to list FTP files on nl43-1: FTP connection failed: [Errno 110] Connection timed out +2025-12-23 23:10:08,274 - app.services - ERROR - Failed to list FTP files on 63.45.161.30:2255: [Errno 110] Connection timed out +2025-12-23 23:10:08,275 - app.routers - ERROR - Failed to list FTP files on nl43-1: FTP connection failed: [Errno 110] Connection timed out +2025-12-23 23:10:09,211 - app.main - INFO - Database tables initialized +2025-12-23 23:10:09,211 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-23 23:16:04,065 - app.routers - INFO - WebSocket connection accepted for unit nl43-1 +2025-12-23 23:16:04,066 - app.routers - INFO - Starting DRD stream for unit nl43-1 +2025-12-23 23:16:04,066 - app.services - INFO - Starting DRD stream for 63.45.161.30:2255 +2025-12-23 23:16:04,239 - app.services - INFO - DRD stream started successfully for 63.45.161.30:2255 +2025-12-23 23:17:19,332 - app.routers - ERROR - Failed to send snapshot via WebSocket: received 1005 (no status received [internal]); then sent 1005 (no status received [internal]) +2025-12-23 23:17:19,333 - app.services - INFO - DRD stream ended for 63.45.161.30:2255 +2025-12-23 23:17:19,333 - app.routers - ERROR - Unexpected error in WebSocket stream for nl43-1: received 1005 (no status received [internal]); then sent 1005 (no status received [internal]) +2025-12-23 23:17:19,333 - app.routers - INFO - WebSocket stream closed for unit nl43-1 +2025-12-23 23:17:21,479 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Start +2025-12-23 23:17:21,639 - app.routers - INFO - Started measurement on unit nl43-1 +2025-12-23 23:17:30,738 - app.services - INFO - Sending command to 63.45.161.30:2255: Manual Store +2025-12-23 23:17:30,887 - app.services - ERROR - Communication error with 63.45.161.30:2255: Command error - device did not recognize command +2025-12-23 23:17:30,888 - app.routers - ERROR - Unexpected error storing data on nl43-1: Command error - device did not recognize command +2025-12-23 23:17:37,891 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Stop +2025-12-23 23:17:38,127 - app.routers - INFO - Stopped measurement on unit nl43-1 +2025-12-23 23:17:41,793 - app.services - INFO - Sending command to 63.45.161.30:2255: Manual Store +2025-12-23 23:17:41,967 - app.services - ERROR - Communication error with 63.45.161.30:2255: Command error - device did not recognize command +2025-12-23 23:17:41,967 - app.routers - ERROR - Unexpected error storing data on nl43-1: Command error - device did not recognize command +2025-12-23 23:19:35,329 - app.routers - INFO - WebSocket connection accepted for unit nl43-1 +2025-12-23 23:19:35,330 - app.routers - INFO - Starting DRD stream for unit nl43-1 +2025-12-23 23:19:35,330 - app.services - INFO - Starting DRD stream for 63.45.161.30:2255 +2025-12-23 23:19:35,479 - app.services - INFO - DRD stream started successfully for 63.45.161.30:2255 +2025-12-23 23:20:27,813 - app.routers - ERROR - Failed to send snapshot via WebSocket: no close frame received or sent +2025-12-23 23:20:27,814 - app.services - INFO - DRD stream ended for 63.45.161.30:2255 +2025-12-23 23:20:27,814 - app.routers - ERROR - Unexpected error in WebSocket stream for nl43-1: no close frame received or sent +2025-12-23 23:20:27,814 - app.routers - INFO - WebSocket stream closed for unit nl43-1 +2025-12-23 23:30:30,336 - app.routers - INFO - WebSocket connection accepted for unit nl43-1 +2025-12-23 23:30:30,337 - app.routers - INFO - Starting DRD stream for unit nl43-1 +2025-12-23 23:30:30,337 - app.services - INFO - Starting DRD stream for 63.45.161.30:2255 +2025-12-23 23:30:30,479 - app.services - INFO - DRD stream started successfully for 63.45.161.30:2255 +2025-12-23 23:30:32,067 - app.routers - ERROR - Failed to send snapshot via WebSocket: received 1005 (no status received [internal]); then sent 1005 (no status received [internal]) +2025-12-23 23:30:32,067 - app.services - INFO - DRD stream ended for 63.45.161.30:2255 +2025-12-23 23:30:32,067 - app.routers - ERROR - Unexpected error in WebSocket stream for nl43-1: received 1005 (no status received [internal]); then sent 1005 (no status received [internal]) +2025-12-23 23:30:32,067 - app.routers - INFO - WebSocket stream closed for unit nl43-1 +2025-12-23 23:30:33,750 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP? +2025-12-23 23:30:33,920 - app.services - INFO - FTP status on 63.45.161.30:2255: On +2025-12-23 23:30:36,071 - app.services - INFO - Listing FTP files on 63.45.161.30:2255 at / +2025-12-23 23:30:48,848 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Start +2025-12-23 23:30:48,999 - app.routers - INFO - Started measurement on unit nl43-1 +2025-12-23 23:30:53,748 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Stop +2025-12-23 23:30:54,039 - app.routers - INFO - Stopped measurement on unit nl43-1 +2025-12-23 23:30:56,037 - app.services - INFO - Sending command to 63.45.161.30:2255: Manual Store +2025-12-23 23:30:56,199 - app.services - ERROR - Communication error with 63.45.161.30:2255: Command error - device did not recognize command +2025-12-23 23:30:56,200 - app.routers - ERROR - Unexpected error storing data on nl43-1: Command error - device did not recognize command +2025-12-23 23:30:58,439 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Start +2025-12-23 23:30:58,608 - app.routers - INFO - Started measurement on unit nl43-1 +2025-12-23 23:31:01,019 - app.services - INFO - Sending command to 63.45.161.30:2255: Manual Store +2025-12-23 23:31:01,159 - app.services - ERROR - Communication error with 63.45.161.30:2255: Command error - device did not recognize command +2025-12-23 23:31:01,160 - app.routers - ERROR - Unexpected error storing data on nl43-1: Command error - device did not recognize command +2025-12-23 23:32:33,823 - app.main - INFO - Database tables initialized +2025-12-23 23:32:33,823 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-23 23:32:44,473 - app.services - INFO - Sending command to 63.45.161.30:2255: ManualStore +2025-12-23 23:32:44,640 - app.services - ERROR - Communication error with 63.45.161.30:2255: Command error - device did not recognize command +2025-12-23 23:32:44,641 - app.routers - ERROR - Unexpected error storing data on nl43-1: Command error - device did not recognize command +2025-12-23 23:32:49,360 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Stop +2025-12-23 23:32:49,520 - app.routers - INFO - Stopped measurement on unit nl43-1 +2025-12-23 23:32:52,035 - app.services - INFO - Sending command to 63.45.161.30:2255: ManualStore +2025-12-23 23:32:52,359 - app.services - ERROR - Communication error with 63.45.161.30:2255: Command error - device did not recognize command +2025-12-23 23:32:52,359 - app.routers - ERROR - Unexpected error storing data on nl43-1: Command error - device did not recognize command +2025-12-23 23:32:55,975 - app.services - INFO - Sending command to 63.45.161.30:2255: DOD? +2025-12-23 23:32:56,217 - app.services - INFO - Parsed 64 data points from DOD response +2025-12-23 23:32:56,226 - app.routers - INFO - Retrieved live status for unit nl43-1 +2025-12-23 23:33:03,172 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Stop +2025-12-23 23:33:03,328 - app.services - ERROR - Communication error with 63.45.161.30:2255: Status error - device is in wrong state for this command +2025-12-23 23:33:03,328 - app.routers - ERROR - Unexpected error stopping measurement on nl43-1: Status error - device is in wrong state for this command +2025-12-23 23:36:10,824 - app.main - INFO - Database tables initialized +2025-12-23 23:36:10,824 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-23 23:36:27,467 - app.services - INFO - Sending command to 63.45.161.30:2255: Manual Store,Start +2025-12-23 23:36:27,640 - app.services - ERROR - Communication error with 63.45.161.30:2255: Status error - device is in wrong state for this command +2025-12-23 23:36:27,640 - app.routers - ERROR - Unexpected error storing data on nl43-1: Status error - device is in wrong state for this command +2025-12-23 23:36:42,238 - app.main - INFO - Database tables initialized +2025-12-23 23:36:42,238 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-23 23:36:50,276 - app.services - INFO - Sending command to 63.45.161.30:2255: DOD? +2025-12-23 23:36:50,449 - app.services - INFO - Parsed 64 data points from DOD response +2025-12-23 23:36:50,459 - app.routers - INFO - Retrieved live status for unit nl43-1 +2025-12-23 23:36:52,133 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Start +2025-12-23 23:36:52,399 - app.routers - INFO - Started measurement on unit nl43-1 +2025-12-23 23:36:58,920 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Stop +2025-12-23 23:36:59,238 - app.routers - INFO - Stopped measurement on unit nl43-1 +2025-12-23 23:37:01,738 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Start +2025-12-23 23:37:01,880 - app.routers - INFO - Started measurement on unit nl43-1 +2025-12-23 23:37:05,371 - app.services - INFO - Sending command to 63.45.161.30:2255: Manual Store,Start +2025-12-23 23:37:05,519 - app.services - ERROR - Communication error with 63.45.161.30:2255: Status error - device is in wrong state for this command +2025-12-23 23:37:05,520 - app.routers - ERROR - Unexpected error storing data on nl43-1: Status error - device is in wrong state for this command +2025-12-23 23:38:36,178 - app.main - INFO - Database tables initialized +2025-12-23 23:38:36,178 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-23 23:39:24,537 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP? +2025-12-23 23:39:24,679 - app.services - INFO - FTP status on 63.45.161.30:2255: On +2025-12-23 23:39:30,870 - app.services - INFO - Sending command to 63.45.161.30:2255: Manual Store,Start +2025-12-23 23:39:30,999 - app.services - ERROR - Communication error with 63.45.161.30:2255: Status error - device is in wrong state for this command +2025-12-23 23:39:30,999 - app.routers - ERROR - Unexpected error storing data on nl43-1: Status error - device is in wrong state for this command +2025-12-23 23:39:33,124 - app.services - INFO - Sending command to 63.45.161.30:2255: DOD? +2025-12-23 23:39:33,289 - app.services - INFO - Parsed 64 data points from DOD response +2025-12-23 23:39:33,309 - app.routers - INFO - Retrieved live status for unit nl43-1 +2025-12-23 23:41:09,171 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Stop +2025-12-23 23:41:09,519 - app.services - ERROR - Communication error with 63.45.161.30:2255: Status error - device is in wrong state for this command +2025-12-23 23:41:09,519 - app.routers - ERROR - Unexpected error stopping measurement on nl43-1: Status error - device is in wrong state for this command +2025-12-23 23:41:12,189 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Start +2025-12-23 23:41:12,359 - app.routers - INFO - Started measurement on unit nl43-1 +2025-12-23 23:41:25,761 - app.services - INFO - Sending command to 63.45.161.30:2255: Manual Store,Start +2025-12-23 23:41:26,218 - app.services - INFO - Manual store executed on 63.45.161.30:2255 +2025-12-23 23:41:26,218 - app.routers - INFO - Manual store executed on unit nl43-1 +2025-12-23 23:41:31,981 - app.services - INFO - Listing FTP files on 63.45.161.30:2255 at / +2025-12-23 23:43:41,458 - app.services - ERROR - Failed to list FTP files on 63.45.161.30:2255: [Errno 110] Connection timed out +2025-12-23 23:43:41,462 - app.routers - ERROR - Failed to list FTP files on nl43-1: FTP connection failed: [Errno 110] Connection timed out +2025-12-23 23:45:35,217 - app.main - INFO - Database tables initialized +2025-12-23 23:45:35,217 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-23 23:45:36,338 - app.main - INFO - Database tables initialized +2025-12-23 23:45:36,339 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-23 23:46:03,381 - app.services - INFO - Listing FTP files on 63.45.161.30:2255 at / +2025-12-24 00:18:40,457 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP,Off +2025-12-24 00:18:45,458 - app.services - ERROR - Connection timeout to 63.45.161.30:2255 +2025-12-24 00:18:45,459 - app.routers - ERROR - Failed to disable FTP on nl43-1: Failed to connect to device at 63.45.161.30:2255 +2025-12-24 00:18:48,372 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP,On +2025-12-24 00:18:53,373 - app.services - ERROR - Connection timeout to 63.45.161.30:2255 +2025-12-24 00:18:53,373 - app.routers - ERROR - Failed to enable FTP on nl43-1: Failed to connect to device at 63.45.161.30:2255 +2025-12-24 00:32:34,574 - app.main - INFO - Database tables initialized +2025-12-24 00:32:34,574 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-24 00:32:50,479 - app.routers - INFO - WebSocket connection accepted for unit nl43-1 +2025-12-24 00:32:50,480 - app.routers - INFO - Starting DRD stream for unit nl43-1 +2025-12-24 00:32:50,480 - app.services - INFO - Starting DRD stream for 63.45.161.30:2255 +2025-12-24 00:32:50,617 - app.services - INFO - DRD stream started successfully for 63.45.161.30:2255 +2025-12-24 00:33:14,274 - app.routers - ERROR - Failed to send snapshot via WebSocket: received 1005 (no status received [internal]); then sent 1005 (no status received [internal]) +2025-12-24 00:33:14,274 - app.services - INFO - DRD stream ended for 63.45.161.30:2255 +2025-12-24 00:33:14,274 - app.routers - ERROR - Unexpected error in WebSocket stream for nl43-1: received 1005 (no status received [internal]); then sent 1005 (no status received [internal]) +2025-12-24 00:33:14,274 - app.routers - INFO - WebSocket stream closed for unit nl43-1 +2025-12-24 00:33:15,714 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP,On +2025-12-24 00:33:15,937 - app.services - INFO - FTP enabled on 63.45.161.30:2255 +2025-12-24 00:33:15,937 - app.routers - INFO - Enabled FTP on unit nl43-1 +2025-12-24 00:33:20,974 - app.services - INFO - Sending command to 63.45.161.30:2255: Measure,Start +2025-12-24 00:33:21,220 - app.routers - INFO - Started measurement on unit nl43-1 +2025-12-24 00:33:29,089 - app.services - INFO - Sending command to 63.45.161.30:2255: Manual Store,Start +2025-12-24 00:33:29,218 - app.services - ERROR - Communication error with 63.45.161.30:2255: Status error - device is in wrong state for this command +2025-12-24 00:33:29,218 - app.routers - ERROR - Unexpected error storing data on nl43-1: Status error - device is in wrong state for this command +2025-12-24 00:33:38,733 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP? +2025-12-24 00:33:38,897 - app.services - INFO - FTP status on 63.45.161.30:2255: On +2025-12-24 00:33:41,454 - app.services - INFO - Listing FTP files on 63.45.161.30:2255 at / +2025-12-24 00:35:50,802 - app.services - ERROR - Failed to list FTP files on 63.45.161.30:2255: [Errno 110] Connection timed out +2025-12-24 00:35:50,803 - app.routers - ERROR - Failed to list FTP files on nl43-1: FTP connection failed: [Errno 110] Connection timed out +2025-12-24 01:07:41,525 - app.routers - INFO - WebSocket connection accepted for unit nl43-1 +2025-12-24 01:07:41,525 - app.routers - INFO - Starting DRD stream for unit nl43-1 +2025-12-24 01:07:41,526 - app.services - INFO - Starting DRD stream for 63.45.161.30:2255 +2025-12-24 01:07:41,696 - app.services - INFO - DRD stream started successfully for 63.45.161.30:2255 +2025-12-24 01:07:57,989 - app.routers - ERROR - Failed to send snapshot via WebSocket: received 1005 (no status received [internal]); then sent 1005 (no status received [internal]) +2025-12-24 01:07:57,989 - app.services - INFO - DRD stream ended for 63.45.161.30:2255 +2025-12-24 01:07:57,989 - app.routers - ERROR - Unexpected error in WebSocket stream for nl43-1: received 1005 (no status received [internal]); then sent 1005 (no status received [internal]) +2025-12-24 01:07:57,989 - app.routers - INFO - WebSocket stream closed for unit nl43-1 +2025-12-24 01:57:24,413 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP? +2025-12-24 01:57:24,738 - app.services - INFO - FTP status on 63.45.161.30:2255: On +2025-12-24 01:57:29,272 - app.services - INFO - Listing FTP files on 63.45.161.30:2255 at / +2025-12-24 01:57:29,578 - app.services - ERROR - Failed to list FTP files on 63.45.161.30:2255: Waiting for ('230', '33x') but got 530 [' Login Fail'] +2025-12-24 01:57:29,578 - app.routers - ERROR - Failed to list FTP files on nl43-1: FTP connection failed: Waiting for ('230', '33x') but got 530 [' Login Fail'] +2025-12-24 01:57:38,883 - app.services - INFO - Sending command to 63.45.161.30:2255: FTP,On +2025-12-24 01:57:39,018 - app.services - INFO - FTP enabled on 63.45.161.30:2255 +2025-12-24 01:57:39,018 - app.routers - INFO - Enabled FTP on unit nl43-1 +2025-12-24 01:57:40,971 - app.services - INFO - Listing FTP files on 63.45.161.30:2255 at / +2025-12-24 01:57:41,299 - app.services - ERROR - Failed to list FTP files on 63.45.161.30:2255: Waiting for ('230', '33x') but got 530 [' Login Fail'] +2025-12-24 01:57:41,299 - app.routers - ERROR - Failed to list FTP files on nl43-1: FTP connection failed: Waiting for ('230', '33x') but got 530 [' Login Fail'] +2025-12-24 01:59:59,951 - app.main - INFO - Database tables initialized +2025-12-24 01:59:59,951 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-24 02:00:23,252 - app.main - INFO - Database tables initialized +2025-12-24 02:00:23,252 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-24 02:00:38,817 - app.main - INFO - Database tables initialized +2025-12-24 02:00:38,817 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-24 02:00:52,583 - app.main - INFO - Database tables initialized +2025-12-24 02:00:52,583 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-24 02:01:10,756 - app.main - INFO - Database tables initialized +2025-12-24 02:01:10,756 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-24 02:01:19,001 - app.main - INFO - Database tables initialized +2025-12-24 02:01:19,001 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-24 02:01:25,547 - app.main - INFO - Database tables initialized +2025-12-24 02:01:25,547 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-24 02:01:51,685 - app.main - INFO - Database tables initialized +2025-12-24 02:01:51,685 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-24 02:02:08,074 - app.main - INFO - Database tables initialized +2025-12-24 02:02:08,074 - app.main - INFO - CORS allowed origins: ['*'] +2025-12-24 02:02:13,115 - app.main - INFO - Database tables initialized +2025-12-24 02:02:13,115 - app.main - INFO - CORS allowed origins: ['*'] diff --git a/migrate_add_ftp_credentials.py b/migrate_add_ftp_credentials.py new file mode 100755 index 0000000..93653c9 --- /dev/null +++ b/migrate_add_ftp_credentials.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +""" +Migration script to add FTP username and password columns to nl43_config table. +Run this once to update existing database schema. +""" + +import sqlite3 +import sys +from pathlib import Path + +DB_PATH = Path(__file__).parent / "data" / "slmm.db" + + +def migrate(): + """Add ftp_username and ftp_password columns to nl43_config table.""" + + if not DB_PATH.exists(): + print(f"Database not found at {DB_PATH}") + print("No migration needed - database will be created with new schema") + return + + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + try: + # Check if columns already exist + cursor.execute("PRAGMA table_info(nl43_config)") + columns = [row[1] for row in cursor.fetchall()] + + if "ftp_username" in columns and "ftp_password" in columns: + print("✓ FTP credential columns already exist, no migration needed") + return + + # Add ftp_username column if it doesn't exist + if "ftp_username" not in columns: + print("Adding ftp_username column...") + cursor.execute("ALTER TABLE nl43_config ADD COLUMN ftp_username TEXT") + print("✓ Added ftp_username column") + + # Add ftp_password column if it doesn't exist + if "ftp_password" not in columns: + print("Adding ftp_password column...") + cursor.execute("ALTER TABLE nl43_config ADD COLUMN ftp_password TEXT") + print("✓ Added ftp_password column") + + conn.commit() + print("\n✓ Migration completed successfully!") + print("\nYou can now set FTP credentials via the web UI or database.") + + except Exception as e: + conn.rollback() + print(f"✗ Migration failed: {e}", file=sys.stderr) + sys.exit(1) + finally: + conn.close() + + +if __name__ == "__main__": + migrate() diff --git a/requirements.txt b/requirements.txt index 0a4249c..f4d8c2b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ fastapi uvicorn sqlalchemy pydantic +aioftp diff --git a/set_ftp_credentials.py b/set_ftp_credentials.py new file mode 100755 index 0000000..ffcb7ca --- /dev/null +++ b/set_ftp_credentials.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +""" +Helper script to set FTP credentials for a device. +Usage: python3 set_ftp_credentials.py +""" + +import sys +import sqlite3 +from pathlib import Path + +DB_PATH = Path(__file__).parent / "data" / "slmm.db" + + +def set_credentials(unit_id: str, username: str, password: str): + """Set FTP credentials for a device.""" + + if not DB_PATH.exists(): + print(f"Error: Database not found at {DB_PATH}") + sys.exit(1) + + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + try: + # Check if unit exists + cursor.execute("SELECT unit_id FROM nl43_config WHERE unit_id = ?", (unit_id,)) + if not cursor.fetchone(): + print(f"Error: Unit '{unit_id}' not found in database") + print("\nAvailable units:") + cursor.execute("SELECT unit_id FROM nl43_config") + for row in cursor.fetchall(): + print(f" - {row[0]}") + sys.exit(1) + + # Update credentials + cursor.execute( + "UPDATE nl43_config SET ftp_username = ?, ftp_password = ? WHERE unit_id = ?", + (username, password, unit_id) + ) + conn.commit() + + print(f"✓ FTP credentials updated for unit '{unit_id}'") + print(f" Username: {username}") + print(f" Password: {'*' * len(password)}") + + except Exception as e: + conn.rollback() + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + finally: + conn.close() + + +if __name__ == "__main__": + if len(sys.argv) != 4: + print("Usage: python3 set_ftp_credentials.py ") + print("\nExample:") + print(" python3 set_ftp_credentials.py nl43-1 admin mypassword") + sys.exit(1) + + unit_id = sys.argv[1] + username = sys.argv[2] + password = sys.argv[3] + + set_credentials(unit_id, username, password) diff --git a/templates/index.html b/templates/index.html index 00d66b5..3b5a088 100644 --- a/templates/index.html +++ b/templates/index.html @@ -33,9 +33,25 @@ Controls + +
+ Live Stream (DRD) + + Not connected +
+ +
+ FTP File Download + + + + +
+
+
Status
No data yet.
@@ -49,9 +65,15 @@