From 5333a3770a65ddea5a62db0d0476cdbdf762c42c Mon Sep 17 00:00:00 2001 From: sebseb7 Date: Sun, 18 Jan 2026 04:57:26 -0500 Subject: [PATCH] feat: Add a real-time Shelly device status dashboard with WebSocket updates and associated model images. --- modelPics/S3PL-00112EU.png | Bin 0 -> 10798 bytes modelPics/S3SW-001P16EU.png | Bin 0 -> 9703 bytes modelPics/S3SW-001X8EU.png | Bin 0 -> 6931 bytes modelPics/S3SW-002P16EU.png | Bin 0 -> 7147 bytes modelPics/SNDC-0D4P10WW.png | Bin 0 -> 5310 bytes modelPics/SNDM-00100WW.png | Bin 0 -> 4609 bytes server.js | 18 +- status_server.js | 583 ++++++++++++++++++++++++++++++++++++ 8 files changed, 598 insertions(+), 3 deletions(-) create mode 100644 modelPics/S3PL-00112EU.png create mode 100644 modelPics/S3SW-001P16EU.png create mode 100644 modelPics/S3SW-001X8EU.png create mode 100644 modelPics/S3SW-002P16EU.png create mode 100644 modelPics/SNDC-0D4P10WW.png create mode 100644 modelPics/SNDM-00100WW.png create mode 100644 status_server.js diff --git a/modelPics/S3PL-00112EU.png b/modelPics/S3PL-00112EU.png new file mode 100644 index 0000000000000000000000000000000000000000..99a31b650cd62cc8b7c9fdfdfeec6650ddaef10f GIT binary patch literal 10798 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 literal 0 HcmV?d00001 diff --git a/modelPics/S3SW-001P16EU.png b/modelPics/S3SW-001P16EU.png new file mode 100644 index 0000000000000000000000000000000000000000..4158d5b5f8a46ce53cc1b35b495f31a6501a5793 GIT binary patch literal 9703 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 MD61w@FJ&I~e+=3Vk^lez literal 0 HcmV?d00001 diff --git a/modelPics/S3SW-001X8EU.png b/modelPics/S3SW-001X8EU.png new file mode 100644 index 0000000000000000000000000000000000000000..d6c1b4cddcbdb3d557b5d2e449d6db8870e71f9e GIT binary patch literal 6931 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 OQc=*9uaUL*^8Wz(QuEjV literal 0 HcmV?d00001 diff --git a/modelPics/S3SW-002P16EU.png b/modelPics/S3SW-002P16EU.png new file mode 100644 index 0000000000000000000000000000000000000000..195c3c92e7b3205a8eb70343f63187b98a925d8e GIT binary patch literal 7147 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 L=qfiUT7UU}SCTUw literal 0 HcmV?d00001 diff --git a/modelPics/SNDC-0D4P10WW.png b/modelPics/SNDC-0D4P10WW.png new file mode 100644 index 0000000000000000000000000000000000000000..f6266f044e5e59cc736186e85ec7a66dfaf3e51f GIT binary patch literal 5310 zcmb_g={FP()TS)ie}$w8N%%>KtTQBA$dU+IhDwMS+n6!7v4kvH1fUX3_q#-TEmFldFQ z>bna#rL`$;p}>zbJ>03$9HPm?LVl$=Nt|;7+(S)V8S$LLi2B75ZjqP{`V7xGX?1KB z&jruSf9#`Tf;qU=Ecr~l*e=VUv$&2Ic`ge{=p*3nNPYJRpZYINwuu!{Z?hAMYluW5 z2iG|T&&UT>f?-kDxD3Ba{~eMaYHex5;yYVJmcU0=m`IW&$VtEYU_vFpUs zyK5fRx-;n}uxszg|G2)+1B6Sq`W`AJMcg)=c&hyFdRJ4=tD}@o{u+;_m#4xQ!O_cI z8VC0zOm_^z#d15HsMljtXVNHu{jk}!TQ5%ZF@ia{lSZP=o9e&o%s1jLeU5CHL6n-D z)qR9i?BtHMqwgjagb8ov1HJwV?urYBhAi42g_kKMf#pi8 zl%lujNTH6(jE&^xy4Q;<75TJzZffaVo3@B{UzP49@S&4*n4|ZiBqN9y|@&oI?tfA?go* z$n{rgAYuqid~l^m5@=h43gQ^z9w0z8yQj5Oz}U(rh@pyZ{FC zJ>-4=ug?COjhv)zPI!oL=*yOTM*c3++iqb5&>Jri_d4?eQV?p+OaWk&eXO|;fpMuQEm~t(Krxjve^@Py1Dlm8Q!BFuVk8|ut)*`Vt(~JPa)PnX8djj~m zZOYe!qrcWkH-}=&4S`bNC|YI_6WI9RIwCDY0I^xa$?N;`tZWW zA%58De!_*vmsd7PeB(dGyRiv5gfNeY>wr}e1 zjSNQUVQiN89YhSkI?Hq;6Aya}k;w#+jx?)ny#;+37uY+n=(}b0Whc#)p0`CNPt2uF zI})V0v{Kv&{g@4rxP=7iqmGjiVTw)SF_WiVySn$;!q+3d=CQG`7xRY(ns(HPd^}g8 z(w#GVK7NXpd$d4Rv4GGQfklp#rDYu%zrK?2i%updFhc++4e)zI1$v+81vM^{d&?7o zTdY-tJ%apftDNw^06-JT48z<@3cji%@<9vz+Vd+2$x!sXR|E$OluBwB83?^^q@67ojAGaJ*_T$3QI2kS3X+)KOy zPiA*jhJ5wR(~5r(d9+`3v}x;fv{C6c?%tJu=>H59Z2R1frIP`AbUS1EBF^|7%>f0f zo>{UiR#tTUR>3|i$>01^bak59KtI|05X`prX_G`Da+S!AR`p!AioEaK8XZ(5g|snegW3fT}7d}KdJ{&g&)E>I(5+F)nRHF zlQ>p(YCH1P2dxc_1~?^T37sy5NacXr-Dd212#4}U@KHMG0*XTIJvbhieXg@9W?0Q{ zmPphLk6GrU#qShU*_hcv2AD#3p!cF#BVhgG;DPU5aYp14IV-qxJW$g{Ydc6D`GL*$~CVSkaT)ZV)a#t9gW6=)LG;ACT=v`R}{hiG}TsS8DrX3Tkd=TkSb*dB-Kq zY<;}|epGRcXO5Eqxl1a*>-!#kci3DvIkjuBcH7}i&3|rvdmvNxfeH_99?W%CpQ4pc zPYV$&T6YDT!=gt(qi_rgQ5)zc!Hj~Jd1(D?^v5{XO;@X(jvsPnlzaQxY>LeU1h(fh~I8b zoh{lV-@FmkGI!G)&bbDel^fKTZ4%v6znFStm}Rc>hQ{r^XF6fvetqxno4r20L#+mU zmTadYK3O&*>hElEVO4)%Wu`pZlkM4?-Y69BX>@PuAqc&zj0)>L;XCPz{g+Z*T4zUL zsFric5`sRi;c=N!hwm>6>mUjxt2EFkQ$4u!(t8<^K?|DNm1F}`J-@mX&@l7u_ev`Q z?PJ&Zh+aP0!`L1JgYNmSVpk6W0sGgrMf1# z9oXjRjW1rR$B{VCI6m3P^ydsREE0$)#YS|6##ce`jlJI^f6(uv%6e0^zo;Kx^xCue759hn0Gk5V>C=lv$B7sAp~rlUuyAwp z!DZPkj}wpkHd`6$4B?#ld4p`?6aRS_`Ha|&7F|(g2#LMRZ?I^ySK21wt7~m*d_?JV zgcjN^?J+g8&MR9Q3-1fJJSy;@G-PNngpFhg8~C2*v}l_NNTi-#;k>P{Bb#hAmgBw-z!c*t^2X?R4O+vT=Skn4dCse>K7!sWvtF{9Pyd_s$ z_dysSPf=XSIlUo22)os5#UtI{!!$QQLhj?0u@e#0x;JoF|0~ z&lH~v0v2{!DA#eg`AkO~bMe~Tn33Pd`ef1y8)*0NRT)z*vSsK1{yHc*i8jBZZ7{~G zDDzjq4fZF0yX08U`e@xOX|ZJo|1zr|t$kXwJQGyakn*50&)@t&!-cmt{|b=!Hhn=u zA*ix%zLwD>gTn$7{xUb(zA%gL4_<+ZP@;7WM@qHsmdw0tyW||lzNIGboBUQpJ@C39 zfk1i%8c6eL)oxM{J__F?L|R&e=7?bdE!pY8W8>h>7UM4Yy9)Ep`j8VbKA@}4TmEwd z%@%(MZiG8g(Zbg#fycj)=2?Uno`dAtYnvHEpZG>$5BZ7E@bwC)~AP?yex*23jAuv3CJX zu0T2c9Q-w>Ux7E~$;)3AiIKpy*VLoX3YW0|0Xq}*wro#K;@w(RFz$=5KpOSI_>~W# z4;jNJkl<~Zdk3dWGlMM!Yl@Y%#dZ5baZ_7|39EJf8^p#?r%#RTry*VzeBox5rJuCrh%-ED`dOFVfp% znCFDc^^Tqe0^fKaS`-Lt%tbwN{mrg5D=P)RT2*{kme7)3lcyWsG|XFjj<|mlGQxbc zq%}6flnT$Vg5IR$=Mjm$uuG>uEI+#zm z(#{y@b-r(I3aS7iwo0JSC&et2DoHB($0_g`_sfR;t5M1Fq2NEwh6ppkI)7;!8eFW^ ztZ*gioCadEYr=jjlq<5TcWLIl&cAvR7a;D#u=2Pf*^mW5sn2IQO2>#rkSH+FOr)$u zEf3ZiqR*i3d%L!`(SV&AW*5X81@ME;#QNY~DHcy(yG}g0-xyFs8>zIK<6C@#0@0g( zal`LHiZtV^W8fV*PH1dnzgo^TxCAKYs}T`ATrmLl?D_%^7*e(zLQHh5!s|^0M(NLk zg^s?x=t-sfwl@43t0X?hxOBKT1kb~c*``TdL)0AF8whfgf4$|fbn|u&hZFeA@vY{2 zyGOs`xk*t{$3Uq+;Hq{97pYGSqI54oS zX!q3qfZmBBi_iA5>|G$cxvyW2!P@@aM~dH+QYZ&WE}jWRv!o9|rS_LuI>*Hg&A%C~ z(OKFL9Xw;*Nn)%?v(l&N-jWgzDb=tooOLqcpuKhqcNwsN?d^Rf4wVS23 zbs`hy-FZN2YxsW8EjOD`G?C!g@8k#rOM&Kk)=u1;k{LsY3eDQmB=-FYXC|6q}>6-Dt`t}JFj+^5Hc%_5dj*^Yl4rU#$XNZc+_N>z0W zjVEW|tS)&+(xIoJ{;PEbwiM8k>_gE*`=B+l77|^&?TP~lxXvrxqx%J{P|Wr}xZ?KQ za5kkW2lRk7iW+VYztPFAMD{y6{e5cc`+t4bR4>0dW%&`wo zTSlGjoLu;L{|n#m^YM6%$Lsae^YQ%Q^?bZuSUxlcunVy>Ffag2?i*O2)uaD{mGLZh zDt+KSD|kyY8$+{aft6KlA3lD5UxuovY-;@Uy{x>cv1zchtoCCqzO<|jjTy&aC-MuL zYU_I2&|^i#E!H8Vhe4AM1BflHBiQz-r@5z=(T8Ofo!1mXb8f-z0}H0c617b-`yU31|$g@fN$C7mQ~=VqNU#%O~b_WWHG ziJb)ncrSmzsUs#qGxuRPy>CW!mtgZ#$J*80)U?{M zQ$7vS`ZL$GDxa<~5wrdW@Uh=R@8j+fz_>5RwIl|d(s&8-+ zjw{i?bA4(bl?RnC$w>Rb~QOJT%$1aNAt8vG~}4e%FBlj@jcu>Z|9`v zFfU+Ly%6f*-Fy`6PQ_+z9!T^wenSk_)_3Etz_xk zP?HfpL#f}%cZ?39j&OJig1OjKem!^xTr}eu`6*>V&h*G}_~}cDog!tRxhX@j(vizu ztaPJ=C;CBCjH)kVq=gj|dg}Rrzqi&GVW%?N*8INo*qQe(Gn6{~!@eLqiyMqFs=kVP z7hUs#8pXI5>xqypb21ZObx{L#!#zG>Sv4fc@EmcFrhPq*Uup;R6ugK0CJ8*)6X>e}WR}X0_S@XayP52Nn!a-L;Bj zXH`!xF?8C@p!PwH3_&*snfFt@OM=`pkF2e?LwN`Z_cm@E88~H!-Vc7|S{4=bV2!(z zJ~s`qI-s7mZuF zpv-OA2a8vKeI_*TctshL6Yy0jC%kE!*!<6Jxv19X-00xaJgoetDo0N2>PY=9WsN-A0XDbkU0rLm}tOn-^!yIF8%#Mah|M`PS=K z$(J@oJR>)Vx%6J>>$%P5#TMT@NTpw^1ac z;fhfc3f2sxcf_&CdQH zB}}L&h-(9NqaPaZ3-rCw$v+pSQI@p;V+mIodGGemJ11!mYdtTE#>s?OfjnzaWiH(a z@Clbeww$&kzmxgJ(Y|h;Ya7hov_qP?AugwmOB7jcb!ptmVFK48jjk2S(Y85TiUh-1 z|GI;Oi+^^leGTLC%W&XVkbM%Zh2&*HEKMOipcD*Fa1)?-@_5VyXQ=tz_P6Kzu8M~3Y5*I%>3PhuXA{W%UWHSKBs zde7PKu8L$RIz=U7&F(gX^W%;Sr@VP0522^~YV1*w`Kmd@rg@Cp-CY_$X$&L?f`Ocd zJR7W2k`nP{ddttN#Z`PI7Lt5g9LK#|>JZ*D&A^sZsggxfc>TFtS-qOoFfamQZWX1O ziKyVr`Ql^a=9{Qs>QQ~-79D+K2hDg$sy0Ud^t81FF=oDW?+~#56%^qrUyw?#Bkd8V z3gA3N9IkDQy`{_uk89~5_GjrJ$Ph%Bmx*(yA6NTQ`7E1U`dS6m^N4hXNM8T4)* zL+*DYzQzjpyV=hIJnj|~cIZa7p*}OYyz&CP-EF;GVY|04&r))?PEhnR<`Tx%Uvgay z=?Xf+@5UsgTd6{?|2AL<4YU}jl>?W;cM}8>qbAlm;9WLctumu0*TP}=mc1sFAzds; z`Eoc*{bmk)@@U);%+gyrp%o${yjya0&6`NiHbt|kU2)fHO8aeof$@%vf4g$)0RLxP ze1)^wj^Zb=P}ECTsKV;PYw!WhICg{|CmE(Jj1^MNF)4#JA#d_VKNlWesF8a9uB5+@ zq;yHNO#AqL?bt%R`|X3gcK#~?hOC;re{njG&3fuQ)ei7P#`>==GH%7f&Po-0*(&Qn zE8l!|Z^o1LB-@cT1gG6BH8GiwsZ+j^nS%(I0Zw7!dn6zo4DOU5>*AuDZp*bUUzdME zG$}^{+}L`7StUs5%`ezQ|KXC}#>p^ouFX16Z07}-K+Hf?I}4J3$2-@A-Qs4d;HyQ1 z;&+osWU5!UQkqD__>^&~iqYIfq0Vmmd7FCuu$c6JDZkG1%a$V{iRL4Z?{%i2Jh)*@ zinK&7NS$^(GjM1qDwz6eA&p}@bSAPh3S!TJ{!;^}x-3XCUgOUW z=Li-#@f;~0vyH`4^I}bewKz5M|5@{S8f-;zI6hGx(+=n6x|tY1%03;z3khh%u|?_I zHG6k*#|t#iQWC#wmPeX{E>rcc?Oe6*!QUaSuOtMCwo*!cuK0@dhx=X^yD4BSPSBw# z+gOd=XjOjutr51y$d~v64S{I5ip(gAPjXvKy-oG|syftA9?W5O~g zN0}Ygf8xuy{gd<43k{pT3Sh@gS8k2lM}qnm#0|la;%PzErG}IqxgR>&7rizSobsBl zYF*p;Q{%iQM zYShP=5bO&wS(f8(fLE}?mTy~|B&7^_9oX56GS}qLbbd4MCp9!iI#TvW2NzI-g>93yYMF37(-Vxpc|x zy+9hEU5vU}B45u^d3H8&&T(tMryQYb#1Xfiu%di){&}4qAcSlK6uUHo*K;w??=p7) z$}#o-z&!Fne`hJYi^?51hTEN3k*Bu*FquT$EAF;~S^wNYUw!7|I%(kd=M~{*@SJOWg?q!3quMCt-Fb#$k&8XaUc#GnzQb#DcQf?Xj{=p1;C3MD<*Lt^#Nkl?t6S{j@y`sFsK%1^=-l)uCL3ySWT zVn*zf_+rc>K|`JVwYg?I&m#QNQMlapC}njxp005!gdC-K5x1UA?QetqnQ#oE9GZJ) zrULQ}E>tMl@f5zYCCf>&D2dJdv^E!OnVacc^Ou`lQ*P6Et<5*;c&D>uuO;aDNutCd z_~7;tL2f(GqKbnU3y;?k>5xQ}%>4nW$%f&S4o{aPia>isk?@kz|2vMv)^*yo%hGPA T+;cd4crch4J~XJ)b4~akLJDF& literal 0 HcmV?d00001 diff --git a/server.js b/server.js index a5eb5d2..01840be 100644 --- a/server.js +++ b/server.js @@ -4,6 +4,7 @@ import path from 'path'; import sqlite3 from 'sqlite3'; import { fileURLToPath } from 'url'; import { initRuleEngine, loadRules, runRules, watchRules } from './rule_engine.js'; +import { broadcastEvent, startStatusServer } from './status_server.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -143,6 +144,7 @@ function checkAndLogEvent(mac, component, field, type, event, connectionId = nul if (connectionId) console.log(`[ID: ${connectionId}] Event logged: ${event} on ${component} (${field})`); db.run("INSERT INTO events (channel_id, event, timestamp) VALUES (?, ?, ?)", [channelId, String(event), new Date().toISOString()]); forwardToUpstream(); + broadcastEvent(mac, component, field, type, event); runRules(mac, component, field, type, event); return; } @@ -160,6 +162,7 @@ function checkAndLogEvent(mac, component, field, type, event, connectionId = nul if (connectionId) console.log(`[ID: ${connectionId}] Status change logged: ${event} on ${component} (${field})`); db.run("INSERT INTO events (channel_id, event, timestamp) VALUES (?, ?, ?)", [channelId, currentEventStr, new Date().toISOString()]); forwardToUpstream(); + broadcastEvent(mac, component, field, type, currentEventStr); runRules(mac, component, field, type, currentEventStr); } }); @@ -179,6 +182,9 @@ loadRules().then(() => { console.error('Error loading rules:', err); }); +// Start status dashboard server +startStatusServer(); + // Global counter for connection IDs let connectionIdCounter = 0; @@ -349,11 +355,17 @@ wss.on('connection', (ws, req) => { if (data.method === 'NotifyEvent') { if (data.params && data.params.events) { const mac = connectionDeviceMap.get(connectionId); - // Even if we don't have MAC from map yet (unlikely for identified device), we can try data.src or skip + // Known button event types to store + const knownButtonEvents = ['single_push', 'double_push', 'triple_push', 'long_push', 'btn_down', 'btn_up']; + if (mac) { data.params.events.forEach(evt => { - // Pass the button event (btn_down/up/etc) as values - checkAndLogEvent(mac, evt.component, 'button', 'enum', evt.event, connectionId); + // Only store known input button events + if (evt.component.startsWith('input') && knownButtonEvents.includes(evt.event)) { + checkAndLogEvent(mac, evt.component, 'button', 'enum', evt.event, connectionId); + } else { + console.log(`[ID: ${connectionId}] Skipped unknown event: ${evt.component} -> ${evt.event}`); + } }); } } diff --git a/status_server.js b/status_server.js new file mode 100644 index 0000000..249eddb --- /dev/null +++ b/status_server.js @@ -0,0 +1,583 @@ +import http from 'http'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { WebSocketServer } from 'ws'; +import sqlite3 from 'sqlite3'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const PORT = 8081; +const MODEL_PICS_DIR = path.join(__dirname, 'modelPics'); + +// SQLite database connection (read-only for status queries) +const db = new sqlite3.Database('devices.db', sqlite3.OPEN_READONLY); + +// WebSocket clients for broadcasting +const wsClients = new Set(); + +// Query initial status data +function getStatusData() { + return new Promise((resolve, reject) => { + const sql = ` + SELECT d.mac, d.model, d.connected, d.last_seen, + c.id as channel_id, c.component, c.field, c.type, + (SELECT e.event FROM events e WHERE e.channel_id = c.id ORDER BY e.id DESC LIMIT 1) as last_event, + (SELECT e.timestamp FROM events e WHERE e.channel_id = c.id ORDER BY e.id DESC LIMIT 1) as last_ts + FROM devices d + LEFT JOIN channels c ON c.mac = d.mac + ORDER BY d.mac, c.component, c.field + `; + db.all(sql, [], (err, rows) => { + if (err) reject(err); + else { + // Group by device + const devices = {}; + for (const row of rows) { + if (!devices[row.mac]) { + devices[row.mac] = { + mac: row.mac, + model: row.model, + connected: row.connected, + last_seen: row.last_seen, + channels: [] + }; + } + if (row.channel_id) { + devices[row.mac].channels.push({ + id: row.channel_id, + component: row.component, + field: row.field, + type: row.type, + event: row.last_event, + timestamp: row.last_ts + }); + } + } + resolve(Object.values(devices)); + } + }); + }); +} + +// Broadcast event to all connected WebSocket clients +export function broadcastEvent(mac, component, field, type, event) { + const message = JSON.stringify({ + type: 'event', + mac, + component, + field, + eventType: type, + event, + timestamp: new Date().toISOString() + }); + for (const client of wsClients) { + if (client.readyState === 1) { // OPEN + client.send(message); + } + } +} + +// HTML Dashboard +const dashboardHTML = ` + + + + + Shelly Status Dashboard + + + + +
+

Shelly Status Dashboard

+

+ + Connecting... +

+
+ +
+
+ + + +

Loading devices...

+
+
+ + + +`; + +// Create HTTP server +const server = http.createServer(async (req, res) => { + const url = new URL(req.url, `http://${req.headers.host}`); + + // Serve model images + if (url.pathname.startsWith('/modelPics/')) { + const filename = path.basename(url.pathname); + const filepath = path.join(MODEL_PICS_DIR, filename); + + if (fs.existsSync(filepath)) { + res.writeHead(200, { 'Content-Type': 'image/png' }); + fs.createReadStream(filepath).pipe(res); + } else { + res.writeHead(404); + res.end('Not found'); + } + return; + } + + // Serve dashboard + if (url.pathname === '/' || url.pathname === '/index.html') { + res.writeHead(200, { 'Content-Type': 'text/html' }); + res.end(dashboardHTML); + return; + } + + res.writeHead(404); + res.end('Not found'); +}); + +// Create WebSocket server attached to HTTP server +const wss = new WebSocketServer({ server }); + +wss.on('connection', async (ws) => { + console.log('[Status] Browser client connected'); + wsClients.add(ws); + + // Send initial status data + try { + const devices = await getStatusData(); + ws.send(JSON.stringify({ type: 'init', devices })); + } catch (err) { + console.error('[Status] Error fetching initial data:', err); + } + + ws.on('close', () => { + console.log('[Status] Browser client disconnected'); + wsClients.delete(ws); + }); +}); + +// Start server function +export function startStatusServer() { + server.listen(PORT, () => { + console.log(`[Status] Dashboard server running at http://localhost:${PORT}`); + }); +} + +// Allow running standalone +if (process.argv[1] === fileURLToPath(import.meta.url)) { + startStatusServer(); +}