From f558e9b6238a96744f329c6752b1383dcf2654d4 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Fri, 4 Sep 2026 00:39:16 -0400 Subject: [PATCH 1/2] docs(football-scoreboard): document all 177 settings, with real renders The README was long but incomplete: it documented perhaps half the settings, carried a "Version History" section still claiming v2.0.7 against a manifest at 3.4.1, and had no images. It now covers every schema leaf -- verified by a token audit -- with seven renders. Kept in full: dynamic team resolution and the AP_TOP_* warning, the rotation and resume semantics, the measured turnover example, the ranked/divisions reasoning including why broadcast was retired, the worked example, and the celebration banner table. Three things it did not say: - The mode toggles here are show_live / show_recent / show_upcoming, not live / recent / upcoming as in every other scoreboard. A display_modes block copied across silently sets nothing. - Five defaults differ between nfl and ncaa_fb -- enabled, live_game_duration, upcoming_games_to_show, other_upcoming_games_to_show and show_ranking. Documented as a table rather than duplicating 52 rows per league. - The live card's down-and-distance, red-zone colour, possession icon and timeout pips were undocumented, and are the plugin's most distinctive output. layout_mode: adaptive now has a side-by-side render against classic on a 128x64 panel, which is where the difference actually shows. Dropped the stale version-history section rather than correcting it; the manifest versions array is the real record. check_plugin.py passes 24/24 with no FAIL. Carries the docs-tooling changes from #423, which this render depends on. Co-Authored-By: Claude Opus 5 --- .../football-scoreboard/display-modes.png | Bin 0 -> 29817 bytes docs/assets/football-scoreboard/hero.png | Bin 0 -> 6470 bytes .../football-scoreboard/layout-mode.png | Bin 0 -> 28125 bytes .../football-scoreboard/live-detail.png | Bin 0 -> 21907 bytes .../football-scoreboard/panel-sizes.png | Bin 0 -> 34680 bytes .../football-scoreboard/period-states.png | Bin 0 -> 27030 bytes docs/assets/football-scoreboard/shots.json | 1501 +++++++++++++++++ .../football-scoreboard/show-records.png | Bin 0 -> 19541 bytes plugins.json | 4 +- plugins/football-scoreboard/README.md | 1215 +++++++------ plugins/football-scoreboard/manifest.json | 14 +- scripts/docs_render_support/sitecustomize.py | 55 +- scripts/render_docs_assets.py | 3 + 13 files changed, 2165 insertions(+), 627 deletions(-) create mode 100644 docs/assets/football-scoreboard/display-modes.png create mode 100644 docs/assets/football-scoreboard/hero.png create mode 100644 docs/assets/football-scoreboard/layout-mode.png create mode 100644 docs/assets/football-scoreboard/live-detail.png create mode 100644 docs/assets/football-scoreboard/panel-sizes.png create mode 100644 docs/assets/football-scoreboard/period-states.png create mode 100644 docs/assets/football-scoreboard/shots.json create mode 100644 docs/assets/football-scoreboard/show-records.png diff --git a/docs/assets/football-scoreboard/display-modes.png b/docs/assets/football-scoreboard/display-modes.png new file mode 100644 index 0000000000000000000000000000000000000000..7743f6c0360b7c96fcc817f3b008fe45a7bc46b3 GIT binary patch literal 29817 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 E1sT&5R{#J2 literal 0 HcmV?d00001 diff --git a/docs/assets/football-scoreboard/hero.png b/docs/assets/football-scoreboard/hero.png new file mode 100644 index 0000000000000000000000000000000000000000..b144dbb45e3771ddd09e4a794beaaed1251d3890 GIT binary patch literal 6470 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
    1ucYDD zrbKSWi=*bbHOTq{hp98iZt5RfYGZinW%13B$2Zf1bVHqqzO?CPnkhHqQ?Y<4T|`PS z!_z3X*i=zl@uq%p^SrN*Z|cml_FzogAf2i?;fcHgrBsZj(Us<_$oLO>m2dhN*L(n8_C}HLf1mI}opq4h5gT6*f;Y|EN=TbaaF-tw)RJ?>}(h zRQ@q8E-s8_VP=EF^OvMA%64|yI9W>VWejCXHP*!^BVN@;kv{6~si9l3Im;c4Pd&kk zuFG>!E>Acx(EE0j~ zmPHxvoDWnPVd5H{?Dg9ADl-}!GX^H6VY`HSZP?r*baOt3rq}t-Mc`8`i?$WBF#V?{ zJa`Pu$af^DREGd`M0C8YoFXExpRAvdNVsDiZ2+I#x+YcA)~p8Q5foHKNIDJ*Lcq}` z1DabU>J`k$DB;K6>B&gFGU)H$c1Pb|ioWnyF5MnG{MraK2iwJ2rFrHrDkRrj0R5?mI7^q$Ug~@gc z!q27mIV*+i)L)=ndoZxB3L8j>f3T@BC8eAtE8-ny!ry9vWwLdtyrPkxt-oy+)tLo* z;-jO-1YdfE<3Zxj#1?DINeA3ATOcTzf4k_6=?U*pCdYF4<*WLwtx(-Jx5ZLSr-z6{ zTtl+_(4XRZI^vx6lJUc`P`UV?68L4fKI0z(qDg)jO&mE}ydZ0%hFXWA+(>FT;70%Y z4Em~lnL}CB&Iz~ZzPh5itmk;bc7s}TROyCe-q_VXmaMvR)N{8N>cz|#Dwzj+&l(hX zM)T`a^0!L#bBDPjvI|I&$^1^AOfaq6iy9aPCWFQo?m^-UANaM>YBl_^wxR3+y8V0B zje9=-biRQ*etlWg<@-T>{wQmftRz;1l0hfFJHXnAC&x4L*a`P*Qs#mx$wzS5-Twa9 zj8A#;6ILwqJZvvUZ8>+jylA>|s<7dOn#zUVg1TTy`$Tk6rh|b+O?J*(kd#%*pPFB& z)ihRL74xrWO@x$}@txC&4y`*HrPUl>K32&v=SRjsO^Hbuh(da)9A@IGqh-3Rx&}` z@J}Scr(vtIa11@9U=p@wQO6o-7*{VZvp7GWtYlt^Z{RH|JZR;@MXr2GBWpj7ab$;6 zb#Fh#9qbG|hnBVfYWmk&T>@%29Jj%3%#7ynqmYI=kmHOlLFT&mSHrjRHtIvF8v~L> zXc9uhjGY0G$H3D2u3#+nzeMs4?S`?3A0+Kb^E`|PdF%tDu+L>v%EE@3tFFEoNYf4^ zdor<{JD+wIJ&>7*Q}aFr_PDoAZdT@Ls#)9>G+6tl7XREThfAP`>n$dHgg2d=L$pqC zEOvkC%XG1^*d$n(i#VftT~AzHwD{Q4yToYQm{e}*6G122Qn|N9PT21s55^u*GH>p$ zaqRBe_A?-q7(v5Gzj24><(mk@J_VK^H8wQT?!$|}ci)6z6!SEed4pCN!Ra1fE> zg{Uw$Frg_OB_PzL2vrdmSz z8wjt+ZnT<9AK|Rv`S^JN_&c5K=sx zY6ikz-eFZ#g~xTws?*4)?R0W9tvbYlz1H4$|9SRdCqtsiKIcuedr8AdgU@CT)t5EL zk+Z#5zW4}L#c=TPLXwsud!i0z$$pN|m#mr{j#Ux!_LxseaQ2WQ2g{BN+Si);y=8t{ z$hyAPdqvH>)56wzKTs=~q*3>GgP-^~KKaXh`Z)Rpa7P;pihvH5qQYvx5eoN-?sS3AfTsdpUZA9k;i5h@M zN<$zK3Kw3%jPnze6LK40*rhb~%}FSz|CzwKLl5_)2uaQ4yWzHjvuKNSaL;#^BJio! zg`4oH4{jAW!J~m^(GRUDOxdUd_yV9^|J>{aiu{virfqEjWv-ijRe`%#&&zBrB_a2E zGL1b}^`qxj-Bo}0LOS@;@vKXbq~3l7EwdQhIqQfs`A^Ai!d_cJBz=NvXF^k0Mr?$& zvxGV_vZv}xg_yF-uyZ(WEPaB6Z5C?xKMJYxH1}Hjrl4_|gm&**4oG4)Klez8>xH@d zl&G7WnYBm|JekCc7{BXMa)mAq7%y1IKa)fvW>mp~tZTxB^wQ@!wXpOinPr$c*3wfco|PYCCqMr<%RK_s+ds>gx!4A zz|>n15IE7`hoKM%4Y!i&qW6Vrv5L8SPUj`ukMX!QuGWNvF{0ZG@+n92#}t*c_1>Mj z&@Lw9U~sV7m^JO}Kw)4!F`apCxjp=XfrYZl(xfN1oF!tm;f|7d!UKeo&ggYa(olQe z9a4REuKc7`SDNPdm3NVKvX!Lk7ng1~UCOqCjC!IwX2`wXh2SWkCy9_`?&2QJ(pciV zrAV&fR#7z9zoAe%CF^-g{(*>Og8uAVp$Eh^%2DJ-vk~00UBzN0uu#q0Ry^q1i+T;Y z1!l31Zktcc@X|>`v+K((#wQd^3hFC#uhsXPFU@-hY4I12T1#H6d(aEKK*biFb49(mYR54O%{N3dx=y z+HlLurswtc(@UdQF^sU$$32a?bD)YCxb=P;B{sV&zzv(Nb068A~$NM-jz!u zb7JGd)yPwCT-d5eb`G#OTLQK(o`3*Uvop@P(U@uHKc2tEGoXrEf%C&3AG=X*L*!$t<#3 zX05E!oj2KIw$_9dyV!lD2|Ve+1AS$g;hqVyLMoSvjRZ8+TTd>cr@~V$&34F+$-9>z zM>I@2Z!3G9PqSD-q)I3xbs6H_!b4k35|h|x>X=k|MfNtA7|M=Msp)QPu?p*{%mh*D zSvb{ojs-($ml+Bo)kW|!Ewk0{dLRz%DeN^ytX(~Y>l)MwQd2`OaGIfUY*Nl&_+I4B z3cn~@Jr?unubUV#ipWDn@^)DT#5fAg;%gn`Sd{2c`aAC{x`8(mDkEE3e!HO z(HND_pSU-u{zU~hjXsF`eo>`6A`Qxz+)LmVnl_u z9Tq1E^(~tTU%skpL7(g`rh}Mw;Bs+M(usZ6{b4;AS74)$M3fbTZ+XoXb}4d8={H?E zc`S^-{$=mu_tv|;e2}g6`jw$4`wl3Q!PX{X0_v256C4t>v@mXnq_WV9cTLHsx;|sn{CfvoPjwuFc7=O2nyUN<&EKYpD^KzeW4gi zGis-81C?Us@@*x+kuIT*H){0t!MTa{uC8?+4vpq{IVEzAyZjln?yQ%Qp^0JU=8;#h z^=h$-%CC}e6K|vk3}MjUqUq;lP;Pu3e?m$h<2>z7ohp6UW&!V!R8d#4tDGposb+Q< zAfghZlaX*o;w8wA!@O`g5MafX$TN7N0#ewlYf}P_&z-*&}h!WJl1aVXc zqNm|C&dB(+m2)r7hSr&V`8KGgXijSyQZjG%7t{5yOHX*STFaQIgw3Kp#r69ZtLE() z3KLAwz!4cl%D+6sjE)%;%WzuxC{@aZ62ZQ>q4+7z_`71Xu*(ZkwXv(YQoC>3@H><# zX1M59ZKSzHNWZt@bJcqO=eEOvj$edJd9TgBMk)U|qPnp}-jf)AJAp_c ziBh}s(;E&{UbBwFl`jP(+%K^xOjxu!9i?9siy|WHGwLjs!dg_6$o&C9lrsb5eCV#K z`pIn52<2}4LKP-FF;OTKcnKSblT*j!>~;0@_#n>?pJG6-TXHg>8Hwq2T_a&@nD8)u zUh&O1^4Z&nXaP)w-4x6T=5-4)wT;Ft{4w z^6jPkP72O$(=)$pC%f~ts>(ieHM@I17X+y9I_6xdlZ4AgBC1Ui4j5yb6VB!@MVDh#k1}_gWwt2Dh@kQl&P`qX z!)3D?Dn|AAb1od`gBzQ$Zgdh5qa&eP0i?lHv8wCy$Qkmyn%HG6iLy+b&-z~t ze>&((7$cHUTlOq@LP|F(48h|aC87;`f4@vgj z3c|6eyb&+=lTYS{+^sPSS0dYFot@@AE<)_oMch*5RU$7AS79hKA@-Ej%{FmNctQdw z2#%3en?7Xcy+J!QeV?1XZE;^9At|v$L1v?ib4PGl&z@wKyK79xZkBn$Y#u zi4RhFW?_w{@tv-UA`s)&2!G_dbm9VSddwL%! z#u|E#v@~HTn`fjxEiQ3YC~^ZF;l;6g=1R>&Q|bxVxgc|WHD&Kb^7T&-l@Z-h@FnyC zrVhiLtu@JeW$#75>8c{%?v(v(?U59O7@-K z)WDiJ@pw-*6zUE+S4O=f@QZKld?&>x#2n4G9&^wj)cZd`VvK$@%9t{yarMOH(K3XTX8j;3N;^@?pud=$e|GbLa{~jX)Bz zX8kOBt;N|!J3Yib0GMq_Y7J6lJirfRjLL+|2OT~Iwo@h{Mz5b;w0K4O!s&OR_8Lvx00mkBKn7;Cl6V+67nsXDasm4`)BT~(M%RT@Vu zb1|C_g-uHMATAT6y80nM0r{ID$=8L!dC6Jn2%C0sG?RQI>4!M@;{g0uSpWF#hW7Wd z(Q@|%CiKY4GYnFgc7h+WqsnK=Al+nkw<$sD zG6WnoJ(_z}@C8%%=y<#{;iwB!A}4i6O;r1_-* z>p6zfp^FS40fi3@A2CUWCS*3cJbt;u?{hhQ<)`faYqtF?Zhk1kfn%wfutwU5J$$Mo z3y*8qm?~k;iI92~eYlVrjoNW}E(X(>%+F1@do=U7Jhff|*vG%(i=!%A82Pxd9x+u% zzmHoD8jX(t=4BX-Y#x$|=+YranRJP7Y(5_e4nA2`vkh#`7|K!&=S66}M3bsTdzcO4 zM{V#+O#RzYKzpMGR9HVxk=300w(N3lc-t|N09ekGdQ6@l&8w;(X5Jt*XvBot&-0|u zKZ&(Hq6;{gUWT?5r>5rvlQ|mRdT%#!X=zEcp8{5i`G*3HtwpyV47l1sN0CvwziH{Ue48rm5--1rF0mveH!e$JzA+^!Y%?!wPg z&>s)~dkd5(Eliu)0&)aVpN&V_(6&zib}Fsqz4f16E%%rNsJID6H6F%{+n}EKsdxYK zbo`3z8w1bBQZID?gXw>SA4VqFl<^g_q?cv>ki0j4x2pWs%fLCZe_FC$N~gf{#dK86 zJ5L$B%Q32=P*rV%^52^Kw}ajv&v38UfYRbnOjs0*sC&#cCkNCb56 z$FKGcS8=O40MrtV32qgOpHFP#fP-xSze`zKn(D$2u^@d~3T<^wn(~bAf`b##O`hJ~ z^laDPtnLHug&88y!B*FayEI_iSX*5DP8RG#3j(^PzzS|+GPq4$*5{<7BOMny0TX}l z`S~$Mp7*Ee=y1oq*R#MuAdqT-tGa5i!do}jNXn+2FQ^(5X;Ajq8X%S64=XAv*h772 zt6hMpT%YY3otQ|E1pHVc)OQnLE2ASLY#NVcTTMS)_p6wKF)nV5hRQ%*Uy#`R{)`^F zyX~Uv+xqSjV%5b$Mcs7aacl0?;@?At6cNQn|(iz;B5I#w9D+IVaWm zJis?dXaR6}W)YKqjM53;P=Mhih+8%RUlG7KL;+b?<~jqxHdri2BmMU1HCx|Q_Z*4G z7U@{ob<&jkRI53_Za`rU2tIJT^iiUTEjNB=pb~I?U+6faDZRBi zvksQb0oN{Lz|&ad_bh#JtfYQzYs+uYC{a(81}Cewt?vS=O|5@|i(}n|#l`N5ieJFt z!i8%NvmRzs%B!>a&A_iC34s9f@*a=U5F=`;yhnQiY^4)4Kd5?SVxkK?oPNFqc(@#Z zz5xnmeRZ`$XUp|pt?qhjQsD*6KeH`)R8G>qTTaRG2|GMjg5 zvPwOdfmeOjHc>?W;g5!|QtCx&zU=(~KF9);4_}Y+9o0%A}ZJlDfzStzQkRaufrkM#I?!e)be7^3$pX0YfonYVf1Cb62KtKy> zZ)#=gZ8Zc#LVmlupSUaC_rb1907vaO@8f7IPfe19MlDt0Zayf-vlFV z4e*@l-JJ_t09RGxX#@5E(}xcM$QJkJ&6)G(uUmaf0nG+Wv^bzJK0Q6uRrvCyj1ak0 zm*dQZ3!6a45C=S<2#Ok{Niq|Y1L&4|?ic{&@Fo~A)YOd)J}dyK;3O(V{4m`OY7x@G z!2#UASpZ*7!J}yPOI&lq!)KY9qoSgUoJPM2B=?8#2nq?=1I!U9Icuw{+OWnL(NVCc z0tg%@YTF48Z$Tf4&bh1{V%J;155Bnv36TKhn3tCa09_9(fEUqz2(Xa?zIRYH#YxO; zERFK>^Mj+*3NI0GQ8ypXYXDjVn3Y5KdoBnB#@_+xUknCgndHd>8e=fmRoMCM(T`9+ z5W*q139MQGi4rs}TWN?Dhh=Q8&d8KLwghDH~;X}X;@^JQ9!gxi8KI71Gv#YS5<-b&1{STz)c&>qC0nL(HibG^1!$( zE+%$MLE*rG1NGa_!S8Ilwf`m<$94eEC9o^O#dXteZJI;febv#)c;Ui@`GS>I_d5V+ zqbxKu0J!GSqxZ{M#elWpQHyH_7%oU(Av|^fRs-y=a56Y1FF>O1;7uw3rE6W>m#!8M z9+23PYrHj$RPP05E@x+FJv}{ur1llsrbmKr#cD$2*lOf&hp?4`XcN;{s7j2g+&Qfq zCj?xjHEklsWnboUFv+!HavR^Dh4fhn=wxqPo;U&AoAovF7=V_-GM+wt3T7s| ztjD*OhsF=`dw?E=iM(+AQA%`l7l3;+h%pI@csbO3J>4!DAnlZU{#vJrC& zoJMcQH@ZD1|)S*bu0J=fSLJI#~$+AT9vf5b`8A+R+v4b7r61O zvzOU&xh_j0+U5)%yEhK^$nNX)E$V>blya~I(Wowa|8Fd# z4M4tip{u~7Qw8P)5aH@ulBMj1DhHNg!2aSCyY$s7SIhwZKE3Pbn-(eUNioyWi6Rnr z!JUrKb8NS3X|V3BRfxmnA@PkxrqzL9ZRpI>bp#iJZE-A)R0n}{?*q(IGhKKQznc0m zpT`AnH}NQR-b*m_0>0@lXYi2_Vc{fMPYQbX8klEagIT)@!?Sep4!s4|0Ec4Vb$k3A zoA9rO*aDmpu!+D9s|iR!aCty+GSUX?z*Yx*vEb-wpqYCE7Sw%VFfl&f5P^8SxiSUL zxP#A&eqL&Uf`!7oJ_@`#jX=dKM@U%Gh6?_Vn^P%_{a~X{$Gc_*PqeWOQ_O@7DQw zc^(zOJwMm#_xj?sd6I1M9xmNmkg>hS#K>4R170~%vIU5#jEoGh>cq)<<_$PlPJwHV z{Lr~lAYI?MK%w-jl>YHs3PS;sImmqR+k=N+cO)R0I-Z;}0BECaZ$mbo76sW4r7tL$ zm)wk~n=558IeGGA_8+{H+gbOeh;mbn1-nlxG$=ZgVH*jxQ`EM*^~H58$|S2j}m9Lq2Xc{3!c7VoIVcz|=I_Ln1yseS7 zTtkA_dvwXK%C#SC{sG`kot)HdXa-g?EeZ_}+fv>=(1M16}2~YhB zs+?vX9uH4X5oFQ3$eJNn_ZrQ$J`9=NePBTJk3SY?d%kc7F^`RnMS2JW&?n{lpf6`| zaB#WD@GVo*r)6JRKr#{D1G5_ACFciewl{9v2gIaeXE01nP5;Ae4PacbY`dUW0ZsuR zuet5x7n2fqkQEiaW$PtG^#L!Q2~qi>b3SmavmUh`($-}Augc(1>E!hv=>e1w~zV1$o z4m`~Xot&ET9Qm@e+mQ4?tn`#OWeV^jEVTFE7vCP3>m6V{bKYaT!5WM34_Wu{GVs&e zpoLZWd#%hSzFFKV5|Tf1@E}-mD#0E@7thfNI(f)P!nf^fvU;K{oHQ@}xZJV}FB2G; z%{^ZWmTTy22Edtwj24Xn2$x70jE{@+n(Yp=W!F1<;1M4U?eh#SZvz6lA_d37Z>(bTe-nU#657hGh4~Z zs^eqIM=-+1Ge%E$2)?UC(z*Eab=%q5LxzpSgiAm6H0Toq3(Ozf1ZMZ}dng(Gn)lkXT%-;pSGP znQ2pK175cR9u)QSL8lL+sNlsexX9_&RTB}Bh40_LZ_UqCTDn`K`2UcX$9t{*8Bw=F z?EI3H;4>Keq_5EJv7{*1AAh9S6J+G%d3x4h>(ud+;Qau1p~>$e<#^IO zt!p9{2>`+Fb949dGMF|g9MVisQ(au9w;D)GOi!Qas0N))W|B1O$l3+3AbARw+UVSM zu%Go;Rs=4-ycH&ptP+d@1w<+$EUyegn%DwzU823g;sGiwg6qr&)X?CJ*T1@Zb3Xc? zX}Azg4k8NKpYkcRg3=p9u8&&r{m!MKQBPKhjuJA)L}qGYNgr=2tWf*TXlmuk${q4U< z8{vqxQNYSgfAyj2y^I1}aUm~s=Sv$noOdJ800c19NCTmyL-rDdY%LuC@AtV&+_PWb z>!(5B&u)um{{c#cQ{dPE)mG`#U+{oigziyaUf&$KfNV?42#a@>HFteZ=Wr^Ku%=nu zjUNP80F)P~9e$H#kOn z+D_=H_{OjCU`77VGXEZ|NCpSa5x#&xoZfT^aqoBX$30#cG)BzNbs8cl4J{Zk&v85^ z+*gt3RC7b`A46ddV)33DSzZ{1z9|QI>`uq`X3i`-BKMuc* z2k@yjWs@2P28NRYV4s2kG88WaNEMj82UUl8bFch&O*qrg#w*pNCEpdio#h8V7YNJm zuEgY7M5}HF$9btDG$Yn*{Bh>zD;g%Ju)_ozpp;HZ9h~p%U`gLeYewGRfXBma(=NLc~m2 zhdLbzg~DJmOhy>%WDkR1E3-uQol|CrEE!ZI%kSoVe^;))r|e*eus%=0{R-}m#m zKkxVZb!%()-9pKf%KRY#q_-jp4>TPmmQlTv{$fS1s)w$xIHBBLKagDwzfk}+O$G_O zzD%c|eto}-`sK?Pb+lR$a$Ph0+bCU|0{XHS*9Y3-5p)$PDRpnpa(e-C)@X19d=^Oa z^>8zko5~#_5r-sGICNbV>8pBvN2XGD+4lQF85FFmCX1MY5aS3}=&j^tQm=aNAFaV; zxrh=-a|TGh&){WbPYGt*mCJuc0I@8Yx(KeHd8o7?XNAOo9mfe&Cj07^NlckszZ=E| zlqbU4a0K?5b_Jxup+ma}{r!&Dmd4K1Qg9=)A8b1Q4zyLRhG~D0+5mB?Ue7kQc(VIpyRRXzmgZ64t4?k$rk8xo}5N=;>p_h7ei|5Or1ew-f^zfFUdhy%obCrizT7jv$7&!A^i6!#Mvr*{CwRf{r-qdw3;$&C*&-wLmn5#uGmprdXcMGE`3KK>>sbTY z1L~Hjt!1#gqTapJ$wOf>(AS}bN^05m0to8SB#O_?-b0_E6*va%p4_Vl#a-t|QLm2m zc6=-cAg>!#ou#=p3U*=6!y1mZ|671r{|+c#O=7LRQh*YicSGYPb6hQ$bkgEI=tCCS2V5^?{=;M;TAFiwalOQN5aS|l@~R+ zIEAO)MPTw%)Wa47ku$9&VFG)P>>_~jD3S?KhX&kc)w{b0fBv>3lDRnVg~+;c?i5n| z+>Tv@laud~$*Q#4(my%ZI-V=hkp3Y2(+tZNWhh^t;U-hm)z!>U5%#A*Qr4{LmJaHW z;*;ou{l8F*rt}j`mKyMl3uvEdUpX}oj{6SwD!xP)` zJz`*;ngkTrvFzs8uYHTiPCZU3pHVg%8LK2eWK|~17a279;W-5x+Whm*G{)phV-ql%TrPn{=`Gs}imhndgqdaE7l{+*D_OZ!;N>~YhIXQ+O}d>Bu{!v|;#Q>5F#-g&0Job~$ob>R zg8d_NMyqVD1U(jz$;rR2sySVH36Bi{c?zkRTUyUeBfqwe)N%~x?2~Xp5thNDnwU6&-k%`eLw8{T59s%f)kh_~w?`2J~ zis8vgWeDu%DT2H9xd+|~Z`^_gv^$DAfDOvxV*dnApSc(c&$pl-d-V!2YPOQWM%*ZT zb9n!I+e7Y~gS~?EO^Vo!DW$FlUQNc67BS2>f;Z7vR)r~Aq3OQDvv-cLLiPjs=4W|k zyGljv%M~uFpCIPAzRWAIhip9J7>P8j74buECRef^tu*@L^+Ft}#e+xClW92$*3`wG z1V>}vZHgY|MT-iirlv>O4_8~i@K?mS;PV;u-d}Dv8U!r`&l|`-wYePl_30zb$J%~Z zCa-8j8G-408;B%+9{B~CTN9i^y*pn7K_`AH!NN~DkGc=Z&@LiRNhOY!TR8WaMu9XC znK9I8LCnJny6v^~D`uzafHp9N*P02ush45Whu<6=wLq8klVmMSIysHP84eNb8MGFQ z-NY5gF1Nds8Kg7g`xMysh$p;ab+&<65L@+#>E;npR>7r;q?J+y<@0&vUH-aHU9lQ$ zk#Ql|Jkd?A@LiE^gH`IZ+e~qzElhvJLHCqv>zm96rtiB?atumIy~q+uturIHKxrf(k7Jh?foBc~9r;^{v78?x8kidV_iUxZp*x1Z$~cRN^lg)l zG9Gy;jASU^gGI9OM3+OIBO67N?*$Z#S7b~X%l)NZ7XA>MERvvE&*u0nDsjiMYL)Es zkS1_Q`D>M^UO%4ed6VLOTV z#R16WPFodEsmYDyI39J3SjYb>`IqO`_B+$0_dWBmP%B z@H^j?KH(y-WxyhM22Pv>s>x(XFgM7zCaGZCeEvS*g4kbQ8|}-p0{s+DbGB~ z>9@UU)-1*3!qWERa<}AS!_d5+_+@2xk7NmKPaxo{Fj?|v=T?3{PoVu(qv0QATz>A# zWZ{0Txu=S02HdvmaY^p?ld_reM+=~C(o>$v<~-w+1^RstgD#u8f>=ZHDAm0I7@FNv zgmK=_GOY}$qedoAdd@9EA94ewCl@4(UY9yaE7M-xmVUnv(jH+j9)DF~UiE)?oAgIt zn?~c|;_d;Z*s?TQuS>a$tpO4PZvP7FKSf^;F${GvyMF&&8EQUKYlSj5l~jb_ z)}E2k1NH@3yD?dIEGy~-<*|#Zo%ZI+Ayn>w5?s`KDubMKI*}+Gpd4~(XF|_&Iw%Rn z|4=`Q>jl1c(<6*wSf*La@iYaYoAo)&v&^dDRhz7`1FC?S%-K64<_S9+xk`ZR6W+E^Lk(4#mU%j#L%vod$v;rCss=dc`#D*!7s|j&B$BX8 zk@?H>RPBo`#9G_M@UhG|xM!eFFjOdc<=~}|E?z6-#L7ta#rL|O0O2j;%X{`&#r(bu zGe>2^kDaw?VmMjn@0HP0RDHqnGP$Ai%I*#+gOu$wdAghH&aB>9CqlR(J-wSCH#sdx z_*%yNpQpTQ)BUBlsifm|wx~_r^l~NFA`Ly9itGOQ=&++ceHDe&%Y$^Z)a;F)umW9$ z!|Mk2`U(vslJu<~a59}(a;AF}tzyQ6>I4a-n=HyZhkd16+)9&lx5o$R4^@{NfJZJL z9M0Mvse8Z_RdO>wl8MlRAru*AZd!uT91g7wt&YaYnYry0N^pHQd;uS;lYX}pn}Opj z8d#sDHe@Ag{@ivfhAJD{&$fSQ7(!0b*&a}&*g0}|e2c1AhM(ITj)!J{Y^vsmHfJg( zBbrkhF;7*6RwQ-pO#-V1UbLrI%VJ~mo>@fG@535WL&6vQNw5(xefPqFk;=*9?0Bin zP5{@1?&O$?5bj%ZEGmsp`=lv%tw-Ez$UGaJX^)@^DGdT3bA zct-wAw}9>TQc4K8lWy82eX+Pv#A~RomRjIcW3%2t|E;01_UR#H!Yc;;UJ4Ht3Nus4 zh-Ke6GW`_{?8f)VpSnx~bH{E8RE$&_6UW|9ofb(o3|M0k^A^ z2hAAVBVBMjd$4poXMlT6rW7Z^AIEFBphi5KnXPW zSf(^8HUn25RTX~`*-|W4WNLU!-lbU{|vhohbcY4&Vt7V;h@kgSFhf=>Qlmw@k;a!pnY;4Dm7G%@kG+R{J&Nz2@83 z;j5_l3QZT3r4Z(3tMUsARQiOB`^q(M30a5;3gw#5tg3r)bDr|hr@NsMirJ$Io?S)C zP`)NJ*SdarA)iWL0=nqCQ3)B4vQ)r5yxoLh4ErUURXL^poZE~;<-#b3{Y`C=d^i42 z$SqsrgIeFye`Q{(CRL3CL{P0+!m)N4KtEG89JalM1WrJdtFUJQ!{W#6B?D3GJ|K=*Y-n;!7R5s*#Dm71*! z+QvgA{~Xp{>yPSNkAsNsK3};k%BbJmRfmo|Ealxq&&qv*GFJ3$YmWpX3*8V5iojXy z-oIKjuv>Ps zE9*Dep+u`9rs}YHwr6j|Q4M}!5>qT{_s<>^^|o~F7lnD`hv*SEZQYRsQfT;%2uxtT zCQnJ7+h06NmU?s-8@oV1ycD`wKVa@X5}=|y)bl&+%@qPeT`&YpTV>|;SGu$PWCP5L zIU=Q1R@Ai@N2UcntEFfTc!2!vW;W50FLzwaHl z$C$y7Cuk{n)Wr|@nzxZ%zSdCb+M@x$H`d@pX|^G=InbOr_n1WNA(AD}&as1LT86Mjh8Tn)*X-x>ZDAxnv<#Q*k$ z6`BT5R)Ah=5^bSYH+a#IpM7d$(3z0>Mx1oJS$1F@KN&wDE&VB+s02{D@yq(t}Nbva$ zb76)(l|{j}$k8q%dME7lCPz#Jb`srb=RXM&OcUzqdmY*kvhTfu1f;t`VLlwZ(l(9Yjw$qU?u>hP9x&xu7a^U@-SC$i2f#0+ z0((0%H`o)AAN=JpJ1aXodv8&ObM9ll*u&Nq`48Sb2ZXp1TFfo{lyn= zQw}3F>$qleEkG^~m`Nka0;n;1vuqz=e$Yp7Eiby~3J>~12dokEE!IUa%(ZHum+ z@63TC$^@N8*IKIosD4l-Qt_bQU%j@m$X1FJBg`eJllO=UDL@i+k_RE0#WETd|Cua8 z*nM#Uq-&CbDC&>u*&=fC@-_FKo#iIUPp*92NWt)2u zk+bb})zr|EF+zQsUO|G?k#`|gQrn;XF=d{VS+{qg#+>|U%fkY2Xh*}xUdynxjZNsn zoEm-a2ipnt8u$nFnaSv@UB4H)a#t~KUxN6#mAGV*7yjLi#R8o`BlAD5D#nLY+PSKi z**{v;DZFloLyF^VL-@Z7q1k`x`4F^=n+kNeaKUVX)+&sS}m0e5M7=_>;BiuWhbK%z<7nlmn7r+4lT64YJuSuujR z4I~3?6t=so2ZI9!B1B3x=h@j=SHRH?GEj1?k_bW8C2Xxu5g~xO1p6R8n?{G%ye3{% zz9F%o$4&c!wlR0Do+tY(sVy9BBciLdEVq=#&3C+MqJ#;+1ge%dmQbVu8*eoWyMwUo zq!{7KvaS(oFm&355>HHXlB4mwk0m7kc+x=4V8ynHjJ#(QW3Rlr{ojd)pi}9!hNy)~ zK$ioj72IKN6Vkk6oN8{y0k6-}^uVx3@#5lI&xM;`oAr$;NYz@R>@Gb?Q?P<7(6(nL zdHc1+p1odqEW!VLu*}(+sw79;Bw9^PEy8v}f$az>zI5bw zMKZxjF|rUtOr`(v{7-Aft8b;|y76h<#4sG#xKn2zgtKK#WaQf4qt3<53li$-Fk{qU zU_c$!PPNg)s(Ie)5VW#atMTw3??!A|a<(4VC}xQV18FqKvk5iFkJk*NZ&-Zmh)X+u z%^(YS8<~gpgC?`dijwb0;G9>s#;i(OPffb#;&?u-Y;Hk#m1=29#D|T!XV@}5Th+cP zW$T)oYg=rpL^{5|@(P)H^*RVV3r*cm5Vv@7MPv=<$Bczhc&|-~v22qsMkru1D#(KM z6VJS?+DH{1?<`c*tJKi}eex#PJx-}?s*6UYcb^;toPlBl;F?_xa7Dk(A*6evR9KywoaBP8rtg5O7Y8BU1S>V10c-5I>{qq)f z?j_^Fl!Wk_zq^y)1tr!V$wMC+ClfAY+#H4fc!g*;H}lEzG%+#0QF-T{V`nQ8WMAxM z+}NOxxi;1+iwxotY@znujs07j{>Q)lHjKXi(Z1V5{=b#p|K=OM{qwa?|EPO@Ul07#RQT~d{CD4Q z;lDlADp7l9(gtd~|HR=ic3QyT)=_I4cVw7@N`4nhoG^3r*LAY-_tyku;~UjnTx`?9 zzkGZ;Y42#=F^krKJlc37WPA{8h2Kq@CF(kle52)NFmOj5w zEgW|4r~eSW0$vGWt9{nuTp^a{16TCxTzZv;G)pzcZUz^d+2!nyIW=%f1bt?m=i&Qb zVlASS%9T{?rJuZJV%kbi>#7RJQxRL-G7^M4$AyXm3>ZuNetKOD2K538Mhs~ zc_3OFm~diXf*5B!z?LLyR6UEX2q?K1y&E-qWJLW+;&)ehsfw;4q*L^CKxgD%T(<9OrXLTrwL$l7 iM6bKqA5WJe{&i;t!f*#d#1DMo1|x*ILD9)`H~$y6%^zt1 literal 0 HcmV?d00001 diff --git a/docs/assets/football-scoreboard/live-detail.png b/docs/assets/football-scoreboard/live-detail.png new file mode 100644 index 0000000000000000000000000000000000000000..74e89c979aa32322e9da3bb1e082272877de94d1 GIT binary patch literal 21907 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 literal 0 HcmV?d00001 diff --git a/docs/assets/football-scoreboard/period-states.png b/docs/assets/football-scoreboard/period-states.png new file mode 100644 index 0000000000000000000000000000000000000000..ffcc58a6f9ca807ed5fa34531232305df98d3e88 GIT binary patch literal 27030 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 B)Pw*4 literal 0 HcmV?d00001 diff --git a/docs/assets/football-scoreboard/shots.json b/docs/assets/football-scoreboard/shots.json new file mode 100644 index 00000000..f9d71460 --- /dev/null +++ b/docs/assets/football-scoreboard/shots.json @@ -0,0 +1,1501 @@ +{ + "plugin": "football-scoreboard", + "_comment": "Football keeps one manager per league and mode as a plain attribute -- nfl_live, nfl_recent, ncaa_fb_upcoming and so on -- and only builds them when that league's enabled flag is set, so the config below turns NFL on. Every mode's display() returns early unless games_list is populated, and the live path also reads live_games and active_celebration. display_mode picks which of the six screens is drawn. The card reads home_logo_path/away_logo_path off the game dict as Path objects, and period_text is what football.py's extractor builds for that state (Q1-Q4, OT1, HALF, Final, Final/OT). Teams, scores and records are invented. Live games also carry football's own fields: down_distance_text, is_redzone, possession_indicator, and home_timeouts/away_timeouts, which draw the pips along the bottom edge -- three per team, white for remaining and grey for used.", + "defaults": { + "width": 128, + "height": 32, + "scale": 6, + "freeze_time": "2026-12-14T21:25:00+00:00", + "skip_update": true, + "config": { + "enabled": true, + "nfl": { + "enabled": true + }, + "ncaa_fb": { + "enabled": false + } + } + }, + "shots": [ + { + "name": "hero", + "display_mode": "nfl_live", + "attrs": { + "nfl_live.games_list": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.current_game": { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + }, + "nfl_live.current_game_index": 0, + "nfl_live.last_game_switch": 9000000000.0, + "nfl_live.last_update": 9000000000.0, + "nfl_live.live_games": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.active_celebration": null + } + }, + { + "name": "m-live", + "display_mode": "nfl_live", + "standalone": false, + "attrs": { + "nfl_live.games_list": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.current_game": { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + }, + "nfl_live.current_game_index": 0, + "nfl_live.last_game_switch": 9000000000.0, + "nfl_live.last_update": 9000000000.0, + "nfl_live.live_games": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.active_celebration": null + } + }, + { + "name": "m-recent", + "display_mode": "nfl_recent", + "standalone": false, + "attrs": { + "nfl_recent.games_list": [ + { + "id": "2", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "27", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Final", + "status_text": "Final", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": false, + "is_final": true, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png" + } + ], + "nfl_recent.current_game": { + "id": "2", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "27", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Final", + "status_text": "Final", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": false, + "is_final": true, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png" + }, + "nfl_recent.current_game_index": 0, + "nfl_recent.last_game_switch": 9000000000.0, + "nfl_recent.last_update": 9000000000.0 + } + }, + { + "name": "m-upcoming", + "display_mode": "nfl_upcoming", + "standalone": false, + "attrs": { + "nfl_upcoming.games_list": [ + { + "id": "3", + "away_abbr": "DAL", + "home_abbr": "PHI", + "away_id": "1", + "home_id": "2", + "away_score": "", + "home_score": "", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "", + "status_text": "", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": false, + "is_final": false, + "is_upcoming": true, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/DAL.png", + "home_logo_path": "assets/sports/nfl_logos/PHI.png" + } + ], + "nfl_upcoming.current_game": { + "id": "3", + "away_abbr": "DAL", + "home_abbr": "PHI", + "away_id": "1", + "home_id": "2", + "away_score": "", + "home_score": "", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "", + "status_text": "", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": false, + "is_final": false, + "is_upcoming": true, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/DAL.png", + "home_logo_path": "assets/sports/nfl_logos/PHI.png" + }, + "nfl_upcoming.current_game_index": 0, + "nfl_upcoming.last_game_switch": 9000000000.0, + "nfl_upcoming.last_update": 9000000000.0 + } + }, + { + "name": "m-half", + "display_mode": "nfl_live", + "standalone": false, + "attrs": { + "nfl_live.games_list": [ + { + "id": "4", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "10", + "home_score": "14", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "HALF", + "status_text": "HALF", + "odds": null, + "is_halftime": true, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "", + "down_distance_text_long": "", + "is_redzone": false, + "possession": null, + "possession_indicator": null, + "scoring_event": null + } + ], + "nfl_live.current_game": { + "id": "4", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "10", + "home_score": "14", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "HALF", + "status_text": "HALF", + "odds": null, + "is_halftime": true, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "", + "down_distance_text_long": "", + "is_redzone": false, + "possession": null, + "possession_indicator": null, + "scoring_event": null + }, + "nfl_live.current_game_index": 0, + "nfl_live.last_game_switch": 9000000000.0, + "nfl_live.last_update": 9000000000.0, + "nfl_live.live_games": [ + { + "id": "4", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "10", + "home_score": "14", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "HALF", + "status_text": "HALF", + "odds": null, + "is_halftime": true, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "", + "down_distance_text_long": "", + "is_redzone": false, + "possession": null, + "possession_indicator": null, + "scoring_event": null + } + ], + "nfl_live.active_celebration": null + } + }, + { + "name": "rec-on", + "display_mode": "nfl_upcoming", + "standalone": false, + "attrs": { + "nfl_upcoming.games_list": [ + { + "id": "3", + "away_abbr": "DAL", + "home_abbr": "PHI", + "away_id": "1", + "home_id": "2", + "away_score": "", + "home_score": "", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "", + "status_text": "", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": false, + "is_final": false, + "is_upcoming": true, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/DAL.png", + "home_logo_path": "assets/sports/nfl_logos/PHI.png" + } + ], + "nfl_upcoming.current_game": { + "id": "3", + "away_abbr": "DAL", + "home_abbr": "PHI", + "away_id": "1", + "home_id": "2", + "away_score": "", + "home_score": "", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "", + "status_text": "", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": false, + "is_final": false, + "is_upcoming": true, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/DAL.png", + "home_logo_path": "assets/sports/nfl_logos/PHI.png" + }, + "nfl_upcoming.current_game_index": 0, + "nfl_upcoming.last_game_switch": 9000000000.0, + "nfl_upcoming.last_update": 9000000000.0 + }, + "config": { + "enabled": true, + "nfl": { + "enabled": true, + "display_options": { + "show_records": true + } + }, + "ncaa_fb": { + "enabled": false + } + } + }, + { + "name": "rec-off", + "display_mode": "nfl_upcoming", + "standalone": false, + "attrs": { + "nfl_upcoming.games_list": [ + { + "id": "3", + "away_abbr": "DAL", + "home_abbr": "PHI", + "away_id": "1", + "home_id": "2", + "away_score": "", + "home_score": "", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "", + "status_text": "", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": false, + "is_final": false, + "is_upcoming": true, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/DAL.png", + "home_logo_path": "assets/sports/nfl_logos/PHI.png" + } + ], + "nfl_upcoming.current_game": { + "id": "3", + "away_abbr": "DAL", + "home_abbr": "PHI", + "away_id": "1", + "home_id": "2", + "away_score": "", + "home_score": "", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "", + "status_text": "", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": false, + "is_final": false, + "is_upcoming": true, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/DAL.png", + "home_logo_path": "assets/sports/nfl_logos/PHI.png" + }, + "nfl_upcoming.current_game_index": 0, + "nfl_upcoming.last_game_switch": 9000000000.0, + "nfl_upcoming.last_update": 9000000000.0 + }, + "config": { + "enabled": true, + "nfl": { + "enabled": true, + "display_options": { + "show_records": false + } + }, + "ncaa_fb": { + "enabled": false + } + } + }, + { + "name": "size-64x32", + "width": 64, + "height": 32, + "display_mode": "nfl_live", + "standalone": false, + "attrs": { + "nfl_live.games_list": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.current_game": { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + }, + "nfl_live.current_game_index": 0, + "nfl_live.last_game_switch": 9000000000.0, + "nfl_live.last_update": 9000000000.0, + "nfl_live.live_games": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.active_celebration": null + } + }, + { + "name": "size-128x32", + "width": 128, + "height": 32, + "display_mode": "nfl_live", + "standalone": false, + "attrs": { + "nfl_live.games_list": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.current_game": { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + }, + "nfl_live.current_game_index": 0, + "nfl_live.last_game_switch": 9000000000.0, + "nfl_live.last_update": 9000000000.0, + "nfl_live.live_games": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.active_celebration": null + } + }, + { + "name": "size-128x64", + "width": 128, + "height": 64, + "display_mode": "nfl_live", + "standalone": false, + "attrs": { + "nfl_live.games_list": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.current_game": { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + }, + "nfl_live.current_game_index": 0, + "nfl_live.last_game_switch": 9000000000.0, + "nfl_live.last_update": 9000000000.0, + "nfl_live.live_games": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.active_celebration": null + } + }, + { + "name": "size-256x32", + "width": 256, + "height": 32, + "display_mode": "nfl_live", + "standalone": false, + "attrs": { + "nfl_live.games_list": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.current_game": { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + }, + "nfl_live.current_game_index": 0, + "nfl_live.last_game_switch": 9000000000.0, + "nfl_live.last_update": 9000000000.0, + "nfl_live.live_games": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.active_celebration": null + } + }, + { + "name": "m-redzone", + "display_mode": "nfl_live", + "standalone": false, + "attrs": { + "nfl_live.games_list": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "1st & Goal", + "down_distance_text_long": "1st & Goal", + "is_redzone": true, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.current_game": { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "1st & Goal", + "down_distance_text_long": "1st & Goal", + "is_redzone": true, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + }, + "nfl_live.current_game_index": 0, + "nfl_live.last_game_switch": 9000000000.0, + "nfl_live.last_update": 9000000000.0, + "nfl_live.live_games": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "1st & Goal", + "down_distance_text_long": "1st & Goal", + "is_redzone": true, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.active_celebration": null + } + }, + { + "name": "layout-classic", + "display_mode": "nfl_live", + "standalone": false, + "attrs": { + "nfl_live.games_list": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.current_game": { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + }, + "nfl_live.current_game_index": 0, + "nfl_live.last_game_switch": 9000000000.0, + "nfl_live.last_update": 9000000000.0, + "nfl_live.live_games": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.active_celebration": null + }, + "width": 128, + "height": 64, + "config": { + "enabled": true, + "nfl": { + "enabled": true + }, + "ncaa_fb": { + "enabled": false + }, + "layout_mode": "classic" + } + }, + { + "name": "layout-adaptive", + "display_mode": "nfl_live", + "standalone": false, + "attrs": { + "nfl_live.games_list": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.current_game": { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + }, + "nfl_live.current_game_index": 0, + "nfl_live.last_game_switch": 9000000000.0, + "nfl_live.last_update": 9000000000.0, + "nfl_live.live_games": [ + { + "id": "1", + "away_abbr": "BUF", + "home_abbr": "KC", + "away_id": "1", + "home_id": "2", + "away_score": "17", + "home_score": "24", + "away_record": "9-3", + "home_record": "11-1", + "period_text": "Q3", + "status_text": "LIVE", + "odds": null, + "is_halftime": false, + "is_period_break": false, + "is_live": true, + "is_final": false, + "is_upcoming": false, + "game_date": "12/14", + "game_time": "4:25pm", + "away_logo_path": "assets/sports/nfl_logos/BUF.png", + "home_logo_path": "assets/sports/nfl_logos/KC.png", + "home_timeouts": 3, + "away_timeouts": 2, + "down_distance_text": "2nd & 7", + "down_distance_text_long": "2nd & 7", + "is_redzone": false, + "possession": "2", + "possession_indicator": "home", + "scoring_event": null + } + ], + "nfl_live.active_celebration": null + }, + "width": 128, + "height": 64, + "config": { + "enabled": true, + "nfl": { + "enabled": true + }, + "ncaa_fb": { + "enabled": false + }, + "layout_mode": "adaptive" + } + } + ], + "composites": [ + { + "name": "display-modes", + "columns": 1, + "cells": [ + { + "shot": "m-live", + "label": "nfl_live", + "sublabel": "quarter and the running score" + }, + { + "shot": "m-recent", + "label": "nfl_recent", + "sublabel": "the finished game" + }, + { + "shot": "m-upcoming", + "label": "nfl_upcoming", + "sublabel": "kickoff date and time" + } + ] + }, + { + "name": "period-states", + "columns": 1, + "cells": [ + { + "shot": "m-live", + "label": "Q3", + "sublabel": "a quarter in progress" + }, + { + "shot": "m-half", + "label": "HALF", + "sublabel": "half-time" + }, + { + "shot": "m-recent", + "label": "Final", + "sublabel": "Final/OT past the fourth quarter" + } + ] + }, + { + "name": "live-detail", + "columns": 1, + "cells": [ + { + "shot": "m-live", + "label": "2nd & 7", + "sublabel": "down and distance in yellow, possession ball beside the team with the ball, timeout pips along the bottom" + }, + { + "shot": "m-redzone", + "label": "1st & Goal", + "sublabel": "the same line turns red inside the twenty" + } + ] + }, + { + "name": "show-records", + "columns": 1, + "cells": [ + { + "shot": "rec-on", + "label": "show_records: true" + }, + { + "shot": "rec-off", + "label": "show_records: false" + } + ] + }, + { + "name": "panel-sizes", + "columns": 2, + "cells": [ + { + "shot": "size-64x32", + "label": "64 x 32" + }, + { + "shot": "size-128x32", + "label": "128 x 32" + }, + { + "shot": "size-128x64", + "label": "128 x 64" + }, + { + "shot": "size-256x32", + "label": "256 x 32" + } + ] + }, + { + "name": "layout-mode", + "columns": 1, + "cells": [ + { + "shot": "layout-classic", + "label": "layout_mode: classic", + "sublabel": "the fixed layout, on a 128x64 panel" + }, + { + "shot": "layout-adaptive", + "label": "layout_mode: adaptive", + "sublabel": "fonts, logos and regions scaled to the panel" + } + ] + } + ] +} diff --git a/docs/assets/football-scoreboard/show-records.png b/docs/assets/football-scoreboard/show-records.png new file mode 100644 index 0000000000000000000000000000000000000000..5facd736704ce5f2963c5ddf8d4175c407378fe9 GIT binary patch literal 19541 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 q1 - -Recent Game (NCAA FB): - -led_matrix_1764889931266 - - - -### Core Functionality -- **Multiple League Support**: NFL and NCAA Football with independent configuration -- **Live Game Tracking**: Real-time scores, quarters, time remaining, down & distance -- **Recent Games**: Recently completed games with final scores and records -- **Upcoming Games**: Scheduled games with start times and odds -- **Dynamic Team Resolution**: Support for `AP_TOP_25`, `AP_TOP_10`, `AP_TOP_5` automatic team selection -- **Production-Ready**: Real ESPN API integration with caching and error handling -- **Favorite Team Result Colors**: Optionally show a finished game's score in green when your favorite team won and red when it lost - -### Professional Display -- **Team Logos**: Professional team logos with automatic download fallback -- **Scorebug Layout**: Broadcast-quality scoreboard display -- **Football-Specific Details**: Down & distance, possession indicators, timeout tracking -- **Color-Coded States**: Live (green), final (gray), upcoming (yellow), redzone (red) -- **Odds Integration**: Real-time betting odds display with spread and over/under -- **Rankings Display**: AP Top 25 rankings for NCAA Football teams - -### Advanced Features -- **Score/Win Celebrations**: Full-screen takeover when a favorite team scores or wins a live game (see below) -- **Background Data Service**: Non-blocking API calls with intelligent caching -- **Smart Filtering**: Show favorite teams only or all games -- **Granular Mode Control**: Enable/disable specific league/mode combinations independently -- **Dual Display Styles**: Switch mode (one game at a time) or scroll mode (all games scrolling) -- **High-FPS Scrolling**: Smooth 100+ FPS horizontal scrolling for scroll mode -- **Font Customization**: Customize fonts, sizes, and styles for all text elements -- **Layout Customization**: Adjust X/Y positioning offsets for all display elements -- **Error Recovery**: Graceful handling of API failures and missing data -- **Memory Optimized**: Efficient resource usage for Raspberry Pi deployment - -## 🎯 Dynamic Team Resolution - -The plugin supports automatic team selection using dynamic patterns: - -- **`AP_TOP_25`**: Automatically includes all 25 AP Top 25 ranked teams -- **`AP_TOP_10`**: Automatically includes top 10 ranked teams -- **`AP_TOP_5`**: Automatically includes top 5 ranked teams - -These patterns update automatically as rankings change throughout the season. You can mix them with specific teams: +[![ChuckBuilds](https://img.shields.io/badge/ChuckBuilds-LEDMatrix-blue)](https://github.com/ChuckBuilds/LEDMatrix) + +# Football Scoreboard + +Live, recent, and upcoming **NFL** and **NCAA Football** games on your LEDMatrix +display, from ESPN's public API. No API key required. + +![NFL live scorebug](../../docs/assets/football-scoreboard/hero.png) + +## Contents + +- [Quick start](#quick-start) +- [Display modes](#display-modes) +- [The live card](#the-live-card) +- [How games are chosen](#how-games-are-chosen) +- [Dynamic team resolution](#dynamic-team-resolution) +- [Rotation, resume, and durations](#rotation-resume-and-durations) +- [Score and win celebrations](#score-and-win-celebrations) +- [Adaptive layout](#adaptive-layout) +- [Panel sizes](#panel-sizes) +- [Settings reference](#settings-reference) +- [Per-league settings](#per-league-settings) +- [Matchup separator and the upcoming card middle](#matchup-separator-and-the-upcoming-card-middle) +- [Text colours and layout offsets](#text-colours-and-layout-offsets) +- [Favorite team result colours](#favorite-team-result-colours) +- [Vegas ticker: seeing live games more often](#vegas-ticker-seeing-live-games-more-often) +- [Team abbreviations](#team-abbreviations) +- [Data sources and architecture](#data-sources-and-architecture) +- [Installation](#installation) +- [Troubleshooting](#troubleshooting) + +## Quick start + +1. Install **Football Scoreboard** from the LEDMatrix Plugin Store. +2. Turn on `enabled`, then the league you want — `nfl` is on by default, + `ncaa_fb` is off. +3. Add your teams under that league's **Favorite Teams**. ```json -"favorite_teams": ["AP_TOP_25", "UGA", "ALA"] +{ + "football-scoreboard": { + "enabled": true, + "nfl": { + "enabled": true, + "favorite_teams": ["KC", "BUF"], + "filtering": { "show_favorite_teams_only": false }, + "game_limits": { + "upcoming_games_to_show": 3, + "other_upcoming_games_to_show": 3 + } + }, + "ncaa_fb": { + "enabled": true, + "favorite_teams": ["UGA", "AUB"] + } + } +} ``` -This will show games for all AP Top 25 teams plus Georgia and Alabama (duplicates are automatically removed). - -> **These expand into real teams, and that has consequences.** Adding `AP_TOP_10` alongside two teams of your own makes yours 2 of up to 12 favorites (fewer when your teams are already ranked — duplicates are removed), all competing for the same slots — so your teams can stop appearing. See [Which Games Get Shown](#-which-games-get-shown) before mixing them. +## Display modes -## 📺 Display Modes +Six modes — three per league — that the LEDMatrix host rotation cycles through +independently. -### Granular Mode Control +![The three NFL display modes](../../docs/assets/football-scoreboard/display-modes.png) -The plugin supports **granular display modes** that give you precise control over what's shown: - -- **NFL Modes**: `nfl_live`, `nfl_recent`, `nfl_upcoming` -- **NCAA FB Modes**: `ncaa_fb_live`, `ncaa_fb_recent`, `ncaa_fb_upcoming` - -Each league and game type can be independently enabled or disabled. This allows you to: -- Show only NFL live games -- Show only NCAA FB recent games -- Mix and match any combination of modes -- Control exactly which content appears on your display - -### Display Style Options - -The plugin supports two display styles for each game type: - -1. **Switch Mode** (Default): Display one game at a time with timed transitions - - Shows each game for a configurable duration - - Smooth transitions between games - - Best for focused viewing of individual games - -2. **Scroll Mode**: High-FPS horizontal scrolling of all games - - All games scroll horizontally in a continuous stream - - League separator icons between different leagues - - Dynamic duration based on total content width - - Supports 100+ FPS smooth scrolling - - Best for seeing all games at once - -You can configure the display mode separately for live, recent, and upcoming games in each league. - -### How Rotation Works - -The plugin registers granular display modes directly in `manifest.json`. The display controller rotates through these modes automatically in the order they appear. Each mode can have its own `display_duration` configured in the plugin config. - -**Default Rotation Order:** -1. `nfl_recent` -2. `nfl_upcoming` -3. `nfl_live` -4. `ncaa_fb_recent` -5. `ncaa_fb_upcoming` -6. `ncaa_fb_live` - -**Customizing Rotation Order:** -You can reorder modes in `manifest.json` to change the rotation sequence. For example, to show all Recent games before Upcoming: - -```json -"display_modes": [ - "nfl_recent", - "ncaa_fb_recent", - "nfl_upcoming", - "ncaa_fb_upcoming", - "nfl_live", - "ncaa_fb_live" -] -``` - -**Disabled Leagues/Modes:** -If a league or mode is disabled in the config, the plugin returns `False` for that mode, and the display controller automatically skips it. This allows you to: -- Disable entire leagues (e.g., disable NCAA FB to show only NFL) -- Disable specific modes per league (e.g., disable `nfl_upcoming` but keep `nfl_recent` and `nfl_live`) -- Mix and match enabled/disabled modes as needed - -### Mode Durations - -Each granular mode respects its own mode duration settings: -- `nfl_recent` uses `nfl.mode_durations.recent_mode_duration` or top-level `recent_mode_duration` -- `ncaa_fb_upcoming` uses `ncaa_fb.mode_durations.upcoming_mode_duration` or top-level `upcoming_mode_duration` -- Each mode can have independent duration configuration +| Mode | Shows | Top line | +|---|---|---| +| `nfl_live` | NFL games in progress | `Q1`–`Q4`, `OT1` past the fourth, or `HALF` | +| `nfl_recent` | Finished NFL games | `Final`, or `Final/OT` | +| `nfl_upcoming` | Scheduled NFL games | `Next Game`, then the date and kickoff time | +| `ncaa_fb_live` | NCAA games in progress | As above | +| `ncaa_fb_recent` | Finished NCAA games | As above | +| `ncaa_fb_upcoming` | Scheduled NCAA games | As above | -### Live Priority +The three period states: -When live games are available, the display controller prioritizes live modes (`nfl_live`, `ncaa_fb_live`) based on the `has_live_content()` and `get_live_modes()` methods. The plugin returns only the granular live modes that actually have live content. +![Q3, HALF and Final](../../docs/assets/football-scoreboard/period-states.png) -## ⏱️ Duration Configuration +Each mode renders as **switch** (one game at a time, timed) or **scroll** (all +games scroll horizontally at high FPS), set per league and per mode with +`.display_modes._display_mode`. -The plugin offers flexible duration control at multiple levels to fine-tune your display experience: +> **The mode toggles are named `show_live`, `show_recent`, `show_upcoming`** in +> this plugin, not `live` / `recent` / `upcoming` as in the other scoreboards. +> Copying a `display_modes` block over from another scoreboard's config will +> silently leave every mode at its default. -### Per-Game Duration +## The live card -Controls how long each individual game displays before rotating to the next game **within the same mode**. +A live football card carries more than the score. Down and distance sit under +the score in yellow, turning **red inside the red zone**; a small football icon +marks the team with possession; and three timeout pips per team run along the +bottom edge — white for remaining, grey for used. -**Configuration:** -- `live_game_duration`: Seconds per live game (default: 30s) -- `non_favorite_live_game_duration`: Seconds per live game with **no** favorite team (default: 0 = off) -- `recent_game_duration`: Seconds per recent game (default: 15s) -- `upcoming_game_duration`: Seconds per upcoming game (default: 15s) +![Down, distance, possession and timeouts](../../docs/assets/football-scoreboard/live-detail.png) -**Example:** With `recent_game_duration: 15`, each recent game shows for 15 seconds before moving to the next. +All four come straight from ESPN's `situation` block and need no configuration. +Their positions are nudgeable through `customization.layout.down_distance`, +`customization.layout.possession`, and `customization.layout.timeouts`. -#### Shorter dwell for non-favorite live games +## How games are chosen -Set `non_favorite_live_game_duration` to give live games that don't involve one of your favorite teams a shorter turn than your favorites. For example, `live_game_duration: 30` and `non_favorite_live_game_duration: 5` shows your teams for 30s each while everyone else's games flash by in 5s. +**`upcoming_games_to_show` is not "how many cards you see".** It is the size of +a *pool*. The panel cycles through that pool one card at a time +(`upcoming_game_duration`, 15s by default) and keeps its place between visits, +so a pool of 3 means the board rotates through the same 3 games until the +schedule moves on. A bigger number gives you a *longer lap*, so any one game +comes round **less** often. -This **only takes effect when both** of the following are true: +### The three regimes -- one or more `favorite_teams` are configured for the league, **and** -- non-favorite live games are actually shown — `filtering.show_favorite_teams_only` is **off**, or `filtering.show_all_live` is **on** (otherwise non-favorite games never appear in the first place). +Which one you are in depends on `favorite_teams` and +`filtering.show_favorite_teams_only`: -| Favorite teams set? | Non-favorite games shown? | Live game has a favorite? | Duration used | -|---|---|---|---| -| No | — | — | `live_game_duration` (unchanged) | -| Yes | No (`show_favorite_teams_only` on, `show_all_live` off) | favorite | `live_game_duration` | -| Yes | Yes (`show_favorite_teams_only` off, or `show_all_live` on) | favorite | `live_game_duration` | -| Yes | Yes (`show_favorite_teams_only` off, or `show_all_live` on) | none | `non_favorite_live_game_duration` (when > 0) | +| `favorite_teams` | `show_favorite_teams_only` | What you get | +|---|---|---| +| empty | either | The next N games league-wide, chronologically. Every game is a non-favorite game, so both filters below apply to all of them. | +| set | **on** (default) | Only your teams. The limit is a budget **per team**. | +| set | **off** | **Your teams first, then other games to fill.** Both limits are **totals**. | -Leave it at `0` to display every live game for `live_game_duration` (the previous behavior). +The third row is what most people want. Before v2.26.0 it did not exist — with +the flag off, favorites were ignored *entirely* and you got the next N games +league-wide. On a college slate that is roughly 950 upcoming games, so your team +appeared about as often as chance allowed. -### Per-Mode Duration +### The settings -Controls the **total time** a mode displays before rotating to the next mode, regardless of how many games are available. +Per league, under `game_limits`: -**Configuration:** -- `recent_mode_duration`: Total seconds for Recent mode (default: dynamic) -- `upcoming_mode_duration`: Total seconds for Upcoming mode (default: dynamic) -- `live_mode_duration`: Total seconds for Live mode (default: dynamic) +| Option | Default (NFL / NCAA) | Description | +|---|---|---| +| `upcoming_games_to_show` | `1` / `5` | How many **favorite** upcoming games to pool. | +| `recent_games_to_show` | `5` / `5` | The same, for finished games. | +| `other_upcoming_games_to_show` | `1` / `5` | How many **non-favorite** upcoming games to add. `0` gives favorites only. | +| `other_recent_games_to_show` | `5` / `5` | The same, for finished games. | +| `other_rotation_interval_seconds` | `1800` | How often the non-favorite slice advances. `0` pins it. | +| `other_games_min_quality` | `ranked` | Which non-favorite games qualify: `ranked` or `any`. | +| `other_games_divisions` | `["fbs"]` | Which divisions non-favorite games may come from: `fbs`, `fcs`, `other`. | -**Example:** With `recent_mode_duration: 60` and `recent_game_duration: 15`, Recent mode shows 4 games (60s ÷ 15s = 4) before rotating to Upcoming mode. +**Your favorite teams are never filtered by the last two.** Follow a Division II +school and its games always appear, whatever the quality bar or division boxes +say. Those settings only decide what fills the *remaining* slots. -### How They Work Together +Within the other-games pool, **the better matchup leads**, and each team appears +once. The pool is each team's *next* game ordered by the best poll position of +either side, so a top-five matchup sits in the first window rather than whichever +kicks off soonest — and the #1 team's whole season does not sort above everyone +else's opener. Ties fall back to kickoff order, and a league with no poll keeps +chronological order. Your favorite teams are ordered by when they play, not by +rank: for your own team the next game is the point. -**Per-game duration** + **Per-mode duration**: -``` -Recent Mode (60s total): - ├─ Game 1: 15s - ├─ Game 2: 15s - ├─ Game 3: 15s - └─ Game 4: 15s - → Rotate to Upcoming Mode - -Upcoming Mode (60s total): - ├─ Game 1: 15s - └─ ... (continues) -``` +### Variety comes from turnover, not from a bigger pool -### Resume Functionality +Rather than widening the pool, the non-favorite slice **moves**. The window +advances by its own width every `other_rotation_interval_seconds`, so +consecutive windows do not overlap and the board works through the schedule +instead of resampling the front of it. -When a mode times out before showing all games, it **resumes from where it left off** on the next cycle: +Measured on a real board — favorites `UGA` + `AUB`, 3 others, rotating every 30 +minutes: +```text + + 0 min: UNC@TCU, SJSU@USC, NCSU@UVA + + 30 min: JVST@NDSU, SAC@EMU, HAW@STAN + + 60 min: NMSU@FSU, MEM@UNLV, MASS@RUTG + + 90 min: BCU@UCF, AKR@WAKE, MRMK@DEL ``` -Cycle 1: Recent Mode (60s, 10 games available) - ├─ Game 1-4 shown ✓ - └─ Time expires → Rotate -Cycle 2: Recent Mode resumes - ├─ Game 5-8 shown ✓ (continues from Game 4, no repetition) - └─ Time expires → Rotate +18 different matchups over three hours, while the pool stays at 6 cards and a +full lap still takes about 90 seconds of airtime. -Cycle 3: Recent Mode resumes - ├─ Game 9-10 shown ✓ - └─ All games shown → Full cycle complete → Reset progress -``` - -### Dynamic Duration (Fallback) +Your favorites are **not** rotated. For upcoming games the soonest ones are the +point — rotating them would show a week-8 fixture instead of Saturday's. -If per-mode durations are **not** configured, the plugin uses **dynamic calculation**: -- **Formula**: `total_duration = number_of_games × per_game_duration` -- **Example**: 24 games @ 15s each = 360 seconds for the mode +### Keeping the filler out -This ensures all games are shown but may result in very long mode durations if you have many games. +Selection is otherwise purely chronological, and on a college slate most of what +that returns is filler. Of roughly 950 upcoming games, about 250 involve a +nationally ranked team; the rest are matchups most viewers have never heard of. +Rotating harder just serves more of them, which is why `other_games_min_quality` +defaults to `ranked`. + +`ranked` means ranked in the **top division's** poll — the AP Top 25 for college +football. South Dakota State is a perennial FCS number one, but South Dakota +State at Northwestern is not a ranked matchup and does not qualify: the ranked +side is FCS and the FBS side is unranked. The poll is matched on team id rather +than abbreviation, so two schools sharing an abbreviation across divisions +cannot promote each other. + +> **Retired:** `broadcast` no longer exists. ESPN lists a broadcaster for almost +> every game now — ESPN+ included — so on a real Week 1/Week 2 slate it passed +> **174 of 175** games: a quality bar in the dropdown that behaved as `any`. +> Boards still holding it are read as `ranked` and say so once in the log. + +`other_games_divisions` needs **one** team in a checked division, not both. With +only `fbs` checked you still get #12 Texas Tech hosting Abilene Christian — a +game involving a team you asked for — while Abilene Christian vs Furman stays +out. Check `fcs` or `other` to bring the smaller-division matchups in as well. + +Both filters **fail open**: if rankings cannot be fetched, or the division +rosters do not resolve, the game is allowed through. A board showing filler is a +poor board; a board showing nothing is a broken one. + +They fail open a second time, as a set: if the filters between them leave +**nothing at all**, the unfiltered list is used instead. Setting +`other_upcoming_games_to_show` or `other_recent_games_to_show` to `0` is the one +way to ask for an empty slate, and that is honoured. + +> **Both settings only mean something for `ncaa_fb`.** The NFL has no poll and +> no divisions, so both are inert there and cost nothing — no poll is requested +> and no division lookup is made. -### Per-League Overrides +### A worked example -You can set different durations per league using the `mode_durations` section: +Follow Georgia and Auburn, and want their next games plus some variety: ```json { - "nfl": { - "mode_durations": { - "recent_mode_duration": 45, - "upcoming_mode_duration": 30 - } - }, "ncaa_fb": { - "mode_durations": { - "recent_mode_duration": 60 + "favorite_teams": ["UGA", "AUB"], + "filtering": { "show_favorite_teams_only": false }, + "game_limits": { + "upcoming_games_to_show": 3, + "other_upcoming_games_to_show": 3 } } } ``` -When multiple leagues are enabled with different durations, the system uses the **maximum** to ensure all leagues get their time. +That gives 6 cards: the 3 soonest UGA/AUB games, plus 3 ranked FBS matchups that +turn over every half hour. + +### Live rotation -### Integration with Dynamic Duration Caps +When several games are live at once the rotation is weighted: a game involving +one of your teams gets `filtering.favorite_live_boost` turns for every one turn +other live games get, and is queued first whenever the rotation refreshes. Set +it to `1` for even rotation. It never interrupts a game already on screen — it +just gets more and sooner turns, and it is independent of `live_priority`, which +controls whether live games preempt the recent/upcoming rotation at all. -If you have dynamic duration caps configured (e.g., `max_duration_seconds: 120`), the system uses the **minimum** of: -- Per-mode duration (e.g., 180s) -- Dynamic duration cap (e.g., 120s) -- **Result**: 120s (ensures cap is respected) +A live game the API stops reporting for `stale_game_timeout` seconds is dropped, +so an abandoned game does not sit on the board forever. -## 🎨 Visual Features +## Dynamic team resolution -### Professional Scorebug Display -- **Team Logos**: High-quality team logos positioned on left and right sides -- **Scores**: Centered score display with outlined text for visibility -- **Game Status**: Quarter/time display at top center -- **Date Display**: Recent games show date underneath score -- **Down & Distance**: Live game situation information (NFL only) -- **Possession Indicator**: Visual indicators for ball possession -- **Odds Display**: Spread and over/under betting lines -- **Rankings**: AP Top 25 rankings for NCAA Football -- **Customizable Layout**: Adjust positioning of all elements via X/Y offsets -- **Customizable Fonts**: Configure font family and size for each text element +Instead of listing schools by hand, put one of these tokens in `favorite_teams` +and it expands to the current AP poll, updating as the rankings move: -### Adaptive Layout (beta) +| Token | Expands to | +|---|---| +| `AP_TOP_5` | The AP Top 5 | +| `AP_TOP_10` | The AP Top 10 | +| `AP_TOP_25` | The full AP Top 25 | -Set `"layout_mode": "adaptive"` to scale fonts, logos, and element regions -to your panel size — the score renders at up to 32px on a 256x128 instead of -the fixed 10px, and layouts degrade gracefully on small panels. +Tokens mix with literal abbreviations, and duplicates are removed: ```json -{ - "layout_mode": "adaptive" -} +"favorite_teams": ["AP_TOP_25", "UGA", "ALA"] ``` -- The default is `"classic"`: rendering is completely unchanged unless you - opt in. **To revert at any time, set it back to `"classic"`** — no - reinstall needed. -- Your font and X/Y offset customizations still apply in adaptive mode: - an explicitly configured font wins over the adaptive sizing, and offsets - shift elements from their computed positions. -- Requires a LEDMatrix core with the adaptive layout system - (`docs/ADAPTIVE_LAYOUT.md`); older cores silently keep the classic layout. +> **These expand into real teams, and that has consequences.** Adding +> `AP_TOP_10` alongside two teams of your own makes yours 2 of up to 12 +> favorites, all competing for the same slots, and your own teams then queue +> behind every top-10 game that kicks off earlier. On one real schedule UGA's +> next game was favorite-game #5 and Auburn's was #8, so with +> `upcoming_games_to_show: 3` neither appeared. If you want your teams +> guaranteed, keep the favorites list to your teams and let +> `other_upcoming_games_to_show` supply the variety. -### Layout Customization +## Rotation, resume, and durations -The plugin supports fine-tuning element positioning for custom display sizes. All offsets are relative to the default calculated positions, allowing you to adjust elements without breaking the layout. +The plugin registers its six granular modes in `manifest.json`, and the display +controller rotates through them in the order they appear: -#### Accessing Layout Settings +1. `nfl_recent` +2. `nfl_upcoming` +3. `nfl_live` +4. `ncaa_fb_recent` +5. `ncaa_fb_upcoming` +6. `ncaa_fb_live` -Layout customization is available in the web UI under the plugin configuration section: -1. Open the **Football Scoreboard** tab (second nav row) -2. Expand the **Customization** section -3. Find the **Layout Positioning** subsection +Reorder them in `manifest.json` to change the sequence — for instance to show +both leagues' Recent screens before either Upcoming. -#### Offset Values +A league or mode disabled in the config makes the plugin return `False` for that +mode, and the controller skips it. So you can disable a whole league, or a +single mode within one, and the rotation closes up around it. -- **Positive values**: Move element right (x_offset) or down (y_offset) -- **Negative values**: Move element left (x_offset) or up (y_offset) -- **Default (0)**: No change from calculated position +### Resume -#### Available Elements +When a mode's time runs out before it has shown every game in its pool, it +**resumes where it left off** on the next visit rather than restarting: -- **home_logo**: Home team logo position (x_offset, y_offset) -- **away_logo**: Away team logo position (x_offset, y_offset) -- **score**: Game score position (x_offset, y_offset) -- **status_text**: Status/period text position (x_offset, y_offset) -- **date**: Game date position (x_offset, y_offset) -- **time**: Game time position (x_offset, y_offset) -- **records**: Team records/rankings position (away_x_offset, home_x_offset, y_offset) +```text +Cycle 1: Recent mode (60s, 10 games in the pool) + games 1-4 shown, time expires, rotate -#### Example Adjustments +Cycle 2: Recent mode resumes + games 5-8 shown, time expires, rotate -**Move logos inward for smaller displays:** -```json -{ - "customization": { - "layout": { - "home_logo": { "x_offset": -5 }, - "away_logo": { "x_offset": 5 } - } - } -} +Cycle 3: Recent mode resumes + games 9-10 shown, full lap complete, progress resets ``` -**Adjust score position:** -```json -{ - "customization": { - "layout": { - "score": { "x_offset": 0, "y_offset": -2 } - } - } -} -``` +### Dynamic duration -**Shift records upward:** -```json -{ - "customization": { - "layout": { - "records": { "y_offset": -3 } - } - } -} -``` +With no per-mode duration configured, the mode's total is calculated as +`number_of_games x per_game_duration` — 24 games at 15s each is a 360-second +mode. That shows everything but can make a mode very long on a full slate, which +is what `dynamic_duration.max_duration_seconds` is for. -#### Display Size Compatibility +## Score and win celebrations -Layout offsets work across different display sizes. The plugin calculates default positions based on your display dimensions, and offsets are applied relative to those defaults. This ensures compatibility with various LED matrix configurations. - -### Color Coding -- **Live Games**: Green text for active status -- **Redzone**: Red highlighting when teams are in scoring position -- **Final Games**: Gray text for completed games -- **Upcoming Games**: Yellow text for scheduled games -- **Odds**: Green text for betting information - -### Score & Win Celebrations When a favorite team scores or wins a **live** game, the scorebug briefly gives -way to a full-screen celebration: the involved team logos at the edges, the new -score centered with the scoring side's digit pulsing, and a banner at the top. +way to a full-screen celebration: the two logos at the edges, the new score +centered with the scoring side's digits pulsing, and a banner at the top. -The banner is chosen from the **points scored between two updates** (not from any -feed text), so it works for every league the same way: +The banner is chosen from the **points scored between two updates**, not from +any feed text, so it works the same way in both leagues: | Points gained | Banner | |---|---| | 6 or more | `TOUCHDOWN!` / ` TD!` | | 3 | ` FIELD GOAL!` | | 2 | ` SAFETY!` | -| other (e.g. lone extra point) | ` SCORES!` | -| game goes final, favorite ahead | ` WINS!` | +| Anything else, e.g. a lone extra point | ` SCORES!` | +| Game goes final with the favorite ahead | ` WINS!` | -A touchdown that arrives as `+6` then a `+1` extra point a few seconds later -shows a **single** celebration — the extra point is folded in while the first is -still on screen. Note that a 2-point conversion and a safety are both `+2`; the -banner reads `SAFETY!` for either. +A touchdown arriving as `+6` and then `+1` a few seconds later shows a +**single** celebration — the extra point folds into the one already on screen. A +two-point conversion and a safety are both `+2`, so the banner reads `SAFETY!` +for either. -Configure per league (under the `nfl` / `ncaa_fb` config sections): -- `celebration_enabled` (boolean, default `true`) -- `celebration_duration` (seconds on screen, default `8`) -- `celebrate_opponent_scores` (also celebrate the opponent, default `false`; when - no favorite teams are configured, any team's score celebrates) +Configured per league: `celebration_enabled` (default `true`), +`celebration_duration` (default `8` seconds), and `celebrate_opponent_scores` +(default `false`; with no favorites configured, any team's score celebrates). -## Favorite Team Result Colors +## Adaptive layout -A run of games against the same opponent is hard to read at a glance: in scroll -and Vegas mode the same two logos go past several times and only the digits -change. Turn on **Customization -> Favorite Team Result Colors** to color a -finished game's score by how your favorite team did - green for a win, red for -a loss. +`layout_mode` chooses the layout engine. `classic` is the original fixed layout. +`adaptive` (beta) scales fonts, logos, and element regions to the panel, so +content grows on a large panel instead of sitting in a 128x32-shaped island. + +![classic against adaptive on a 128x64 panel](../../docs/assets/football-scoreboard/layout-mode.png) + +The difference is clearest on a tall panel: adaptive sizes the crests to the +space and keeps the score clear of them, where classic draws both at their fixed +size and lets the score overlap. ```json -{ - "customization": { - "favorite_result_colors": { - "enabled": true, - "win_color": [0, 255, 0], - "loss_color": [255, 0, 0], - "tie_color": [255, 200, 0] - } - } -} +{ "layout_mode": "adaptive" } ``` -- Off by default. Until you enable it the score keeps exactly the color it has - today. -- Only finished games are colored. Live and upcoming cards are untouched. -- A game needs exactly one favorite team. If neither side is a favorite, or both - are, the score keeps its normal color. -- Applies to both the one-game-at-a-time switch view and the scroll/Vegas - ticker. -- The three colors are Advanced settings; leave them alone for the defaults - above. +It is plugin-wide, not per league, and defaults to `classic` so nothing changes +until you ask for it. -## 🏷️ Team Abbreviations +## Panel sizes -### NFL Teams -Common abbreviations: TB, DAL, GB, KC, BUF, SF, PHI, NE, MIA, NYJ, LAC, DEN, LV, CIN, BAL, CLE, PIT, IND, HOU, TEN, JAX, ARI, LAR, SEA, WAS, NYG, MIN, DET, CHI, ATL, CAR, NO +![Live card at four panel sizes](../../docs/assets/football-scoreboard/panel-sizes.png) -### NCAA Football Teams -Common abbreviations: UGA (Georgia), AUB (Auburn), BAMA (Alabama), CLEM (Clemson), OSU (Ohio State), MICH (Michigan), FSU (Florida State), LSU (LSU), OU (Oklahoma), TEX (Texas), ORE (Oregon), MISS (Mississippi), GT (Georgia Tech), VAN (Vanderbilt), BYU (BYU) +The plugin passes the render-safety harness on all eight supported sizes. At +64x32 the two crests and the centre column share very little room; 128x32 or +wider is a much better fit, and `layout_mode: adaptive` is worth trying on +anything larger than 128x32. -## 🔧 Technical Details +## Settings reference -### Architecture -This plugin reuses the proven code from the main LEDMatrix project: -- **SportsCore**: Base class for all sports functionality -- **Football**: Football-specific game detail extraction -- **NFL Managers**: Live, Recent, and Upcoming managers for NFL -- **NCAA FB Managers**: Live, Recent, and Upcoming managers for NCAA Football -- **BaseOddsManager**: Production-ready odds fetching from ESPN API -- **DynamicTeamResolver**: Automatic team resolution for rankings +Settings marked **Advanced** sit behind the *Advanced* toggle in the web UI. +Defaults are the schema defaults, which is what the web UI writes. -### Data Sources -- **ESPN API**: Primary data source for games, scores, and rankings -- **Real-time Updates**: Live game data updates every 30 seconds -- **Intelligent Caching**: 1-hour cache for rankings, 30-minute cache for odds -- **Error Recovery**: Graceful handling of API failures +### Plugin level -### Performance -- **Background Processing**: Non-blocking data fetching -- **Memory Optimized**: Efficient resource usage for Raspberry Pi -- **Smart Caching**: Reduces API calls while maintaining data freshness -- **Configurable Intervals**: Adjustable update frequencies per league +| Key | Type | Default | What it does | +|---|---|---|---| +| `enabled` | boolean | `true` | Master on/off switch for the whole plugin. | +| `display_duration` | 5–300 s | `30` | How long the display controller shows this plugin's mode before rotating to the next plugin. | +| `game_display_duration` | 3–60 s | `15` | **Advanced.** Per-game time within a mode, where the league does not override it. | +| `update_interval` | 30–86400 s | `3600` | **Advanced.** Base data refresh cadence. | +| `layout_mode` | `classic` \| `adaptive` | `classic` | **Advanced.** Layout engine — see [Adaptive layout](#adaptive-layout). | +| `timezone` | string | `""` | **Advanced.** IANA zone for kickoff times, e.g. `America/Chicago`. Blank follows the LEDMatrix global timezone, then the host system's, then UTC. | +| `schedule_lookback_days` | 1–60 | `14` | **Advanced.** How far back to fetch for the Recent screens. | +| `schedule_lookahead_days` | 1–60 | `7` | **Advanced.** How far ahead to fetch for Upcoming. A game beyond this horizon is never fetched, so it cannot reach the board even though the date is known. | +| `no_data_interval_seconds` | 5–86400 s | `300` | **Advanced.** Wait between live checks when nothing is live. Backs off further the longer nothing is found. | +| `live_idle_max_interval_seconds` | 5–86400 s | `900` | **Advanced.** Ceiling for that back-off. Useful out of season. | + +## Per-league settings + +Every table below exists twice, once under `nfl` and once under `ncaa_fb`, with +the same keys. `` stands for either. **Five defaults differ between the +two leagues** — everything else is identical: + +| Key | NFL | NCAA FB | +|---|---|---| +| `.enabled` | `true` | `false` | +| `.live_game_duration` | `30` | `20` | +| `.game_limits.upcoming_games_to_show` | `1` | `5` | +| `.game_limits.other_upcoming_games_to_show` | `1` | `5` | +| `.display_options.show_ranking` | `false` | `true` | -## 📦 Installation +The NCAA defaults are larger because a college Saturday has far more games than +an NFL Sunday, and `show_ranking` is on there because college football has a +poll to rank against. -### From the Plugin Store (recommended) -1. Open the LEDMatrix web interface (`http://your-pi-ip:5000`) -2. Open the **Plugin Manager** tab -3. Find **Football Scoreboard** in the **Plugin Store** section and click - **Install** -4. Open the **Football Scoreboard** tab in the second nav row to configure - your favorite teams and per-league preferences +### Teams and priority +| Key | Type | Default | What it does | +|---|---|---|---| +| `.enabled` | boolean | see above | Build this league's managers at all. | +| `.favorite_teams` | array | `[]` | Teams to prioritise. Abbreviations, or an `AP_TOP_*` token. | +| `.exclude_teams` | array | `[]` | **Advanced.** Teams to always hide, from the live rotation and from finals alike (spoiler protection). Takes precedence over `favorite_teams` and `show_all_live`. | +| `.live_priority` | boolean | `true` | **Advanced.** Let this league's live games interrupt the rotation and display immediately. | -## ⚙️ Configuration +### Display modes -### Display Mode Settings +| Key | Type | Default | +|---|---|---| +| `.display_modes.show_live` | boolean | `true` | +| `.display_modes.show_recent` | boolean | `true` | +| `.display_modes.show_upcoming` | boolean | `true` | +| `.display_modes.live_display_mode` | `switch` \| `scroll` | `switch` (**Advanced**) | +| `.display_modes.recent_display_mode` | `switch` \| `scroll` | `switch` (**Advanced**) | +| `.display_modes.upcoming_display_mode` | `switch` \| `scroll` | `switch` (**Advanced**) | -Each league (NFL, NCAA FB) can be configured with: -- **Enable/Disable**: Turn entire leagues on or off -- **Mode Toggles**: Enable/disable live, recent, or upcoming games independently -- **Display Style**: Choose "switch" (one game at a time) or "scroll" (all games scrolling) for each game type -- **Scroll Settings**: Configure scroll speed, frame delay, gap between games, and league separators +### Filtering -### Filtering & Favorites +| Key | Type | Default | What it does | +|---|---|---|---| +| `.filtering.show_favorite_teams_only` | boolean | `true` | Show only your teams' games. | +| `.filtering.show_all_live` | boolean | `false` | **Advanced.** Show every live game regardless of favorites. `exclude_teams` still applies. | +| `.filtering.favorite_live_boost` | 1–5 | `2` | **Advanced.** Turns a favorite's live game gets per one turn for other live games. `1` is even rotation. | -Per league (`nfl`, `ncaa_fb`), under `filtering`: +### Game limits -| Option | Default | Description | -|--------|---------|-------------| -| `favorite_teams` | `[]` | Teams to follow — see [Dynamic Team Resolution](#-dynamic-team-resolution) | -| `exclude_teams` | `[]` | Teams to always hide from live rotation **and** recent/final scores (e.g. to avoid spoilers if you're watching delayed). Wins over every other setting below — an excluded team never shows up even if `show_all_live` is on. | -| `filtering.show_favorite_teams_only` | `true` | Only show games from favorite teams | -| `filtering.show_all_live` | `false` | Show all live games, not just favorites | -| `filtering.favorite_live_boost` | `2` | How many turns your favorite's live game gets in the rotation for every 1 turn other live games get. Your favorite's game is also always queued first whenever the live rotation refreshes. Set to `1` for perfectly even rotation. Only has a visible effect when more than one game is live at once and `favorite_teams` is configured. | +All **Advanced**. Defaults given as NFL / NCAA where they differ; see +[The settings](#the-settings) for what each one means. -With both `show_favorite_teams_only` and `show_all_live` off, all live games rotate evenly — `favorite_live_boost` is what gives your favorite's game precedence in that mode without hiding everyone else's scores. +| Key | Type | Default | +|---|---|---| +| `.game_limits.recent_games_to_show` | 1–20 | `5` | +| `.game_limits.upcoming_games_to_show` | 1–20 | `1` / `5` | +| `.game_limits.other_recent_games_to_show` | 0–20 | `5` | +| `.game_limits.other_upcoming_games_to_show` | 0–20 | `1` / `5` | +| `.game_limits.other_rotation_interval_seconds` | 0–86400 s | `1800` | +| `.game_limits.other_games_min_quality` | `any` \| `ranked` | `ranked` | +| `.game_limits.other_games_divisions` | array | `["fbs"]` | -## 🎯 Which Games Get Shown +### Durations -This trips people up, so it is worth being precise: **`upcoming_games_to_show` is not "how many cards you see".** It is the size of a *pool*. The panel cycles through that pool one card at a time (`upcoming_game_duration`, 15s by default), and it keeps its place between visits. So a pool of 3 means the board rotates through the same 3 games until the schedule moves on. +All **Advanced**. -That is why making the number bigger does not help you see a particular team more often — a bigger pool means a *longer lap*, so any one game comes round **less** often. +| Key | Type | Default | What it does | +|---|---|---|---| +| `.live_game_duration` | 10–120 s | `30` / `20` | Per-game time for live games. Applies to games with a favorite when a non-favorite duration is set. | +| `.non_favorite_live_game_duration` | 0–120 s | `0` | Shorter turn for live games with no favorite. `0` means use `live_game_duration` for everything. | +| `.recent_game_duration` | number | `15` | Per-game time on the Recent screen. Falls back to the top-level `game_display_duration` when unset. | +| `.upcoming_game_duration` | number | `15` | The same for Upcoming. | -### The three modes +`non_favorite_live_game_duration` **only takes effect** when favorite teams are +configured **and** non-favorite live games are being shown — +`show_favorite_teams_only` off, or `show_all_live` on. Otherwise non-favorite +games are never on screen to shorten: -Which mode you are in depends on two things: whether `favorite_teams` is set, and whether `filtering.show_favorite_teams_only` is on. +| Favorites set? | Non-favorite games shown? | Game has a favorite? | Duration used | +|---|---|---|---| +| No | — | — | `live_game_duration` | +| Yes | No | favorite | `live_game_duration` | +| Yes | Yes | favorite | `live_game_duration` | +| Yes | Yes | none | `non_favorite_live_game_duration`, when above `0` | -| `favorite_teams` | `show_favorite_teams_only` | What you get | -|---|---|---| -| empty | either | The next N games league-wide, chronologically. Every game shown is a non-favorite game, so the two filters below apply to all of them. | -| set | **on** | Only your teams. `upcoming_games_to_show` is a budget **per team**. | -| set | **off** | **Your teams first, then other games to fill.** Both limits are **totals**. | +### Update intervals -The third row is the one most people actually want, and before v2.26.0 it did not exist — with the flag off, favorites were ignored *entirely* and you got the next N games league-wide. On a college slate that is ~950 upcoming games, so your team showed up about as often as chance allowed. +All **Advanced**. -### The settings +| Key | Type | Default | What it does | +|---|---|---|---| +| `.live_update_interval` | 5–300 s | `30` | How often live game data refreshes. | +| `.recent_update_interval` | 60–86400 s | `3600` | How often the finished-games list is rebuilt. This also sets how soon a game that has just ended can appear — lower it if you want results sooner. | +| `.upcoming_update_interval` | 60–86400 s | `3600` | How often the upcoming-games list is rebuilt. Selection and the non-favorite rotation both run on the display side, so this governs only the fetch. | +| `.stale_game_timeout` | 60–3600 s | `300` | Drop a live game the API has stopped updating. | -Per league, under `game_limits`: +### Display options -| Option | Default | Description | -|---|---|---| -| `upcoming_games_to_show` | `5` (ncaa_fb) | How many **favorite** upcoming games to show (a total, not per team, when `show_favorite_teams_only` is off). | -| `recent_games_to_show` | varies | The same, for finished games. | -| `other_upcoming_games_to_show` | matches `upcoming_games_to_show` | How many **non-favorite** upcoming games to add. `0` gives you favorites only. | -| `other_recent_games_to_show` | matches `recent_games_to_show` | The same, for finished games. | -| `other_rotation_interval_seconds` | `1800` | How often the non-favorite slice advances. `0` pins it. | -| `other_games_min_quality` | `ranked` | Which non-favorite games qualify: `ranked` or `any`. | -| `other_games_divisions` | `["fbs"]` | Which divisions non-favorite games may come from: `fbs`, `fcs`, `other`. | +All **Advanced**. -**Your favorite teams are never filtered by the last two.** Follow a Division II school and its games always appear, whatever the quality bar or division boxes say. Those settings only decide what fills the *remaining* slots. +| Key | Type | Default | What it does | +|---|---|---|---| +| `.display_options.show_records` | boolean | `false` | Draw win-loss records in the bottom corners. | +| `.display_options.show_ranking` | boolean | `false` / `true` | Draw poll rank badges. Unranked teams show no badge, by design. | +| `.display_options.show_odds` | boolean | `true` | Draw betting odds. | -Within the other-games pool, **the better matchup leads**, and each team appears once. The pool is each team's *next* game ordered by the best poll position of either side, so a top-five matchup sits in the first window rather than whichever kicks off soonest — and the #1 team's whole season does not sort above everyone else's opener. Ties fall back to kickoff order, and a league with no poll keeps chronological order. Your favorite teams are ordered by when they play, not by rank -- for your own team the next game is the point. +![show_records on and off](../../docs/assets/football-scoreboard/show-records.png) -### Variety comes from turnover, not from a bigger pool +### Celebrations -Rather than widening the pool, the non-favorite slice **moves**. The window advances by its own width every `other_rotation_interval_seconds`, so consecutive windows do not overlap and the board works through the schedule instead of resampling the front of it. +| Key | Type | Default | +|---|---|---| +| `.celebration_enabled` | boolean | `true` | +| `.celebration_duration` | 3–30 s | `8` (**Advanced**) | +| `.celebrate_opponent_scores` | boolean | `false` (**Advanced**) | -Measured on a real board — favorites `UGA` + `AUB`, 3 others, rotating every 30 minutes: +### Dynamic duration -```text - + 0 min: UNC@TCU, SJSU@USC, NCSU@UVA - + 30 min: JVST@NDSU, SAC@EMU, HAW@STAN - + 60 min: NMSU@FSU, MEM@UNLV, MASS@RUTG - + 90 min: BCU@UCF, AKR@WAKE, MRMK@DEL -``` +Sizes each mode's total time from how much there is to show. All **Advanced**. -18 different matchups over three hours, while the pool stays at 6 cards and a full lap still takes about 90 seconds of airtime. +| Key | Type | Default | +|---|---|---| +| `.dynamic_duration.enabled` | boolean | `false` | +| `.dynamic_duration.min_duration_seconds` | 10–300 s | `30` | +| `.dynamic_duration.max_duration_seconds` | 60–600 s | — | +| `.dynamic_duration.modes.live.enabled` | boolean | `false` | +| `.dynamic_duration.modes.live.min_duration_seconds` | 10–300 s | — | +| `.dynamic_duration.modes.live.max_duration_seconds` | 60–600 s | — | +| `.dynamic_duration.modes.recent.enabled` | boolean | `false` | +| `.dynamic_duration.modes.recent.min_duration_seconds` | 10–300 s | — | +| `.dynamic_duration.modes.recent.max_duration_seconds` | 60–600 s | — | +| `.dynamic_duration.modes.upcoming.enabled` | boolean | `false` | +| `.dynamic_duration.modes.upcoming.min_duration_seconds` | 10–300 s | — | +| `.dynamic_duration.modes.upcoming.max_duration_seconds` | 60–600 s | — | + +### Scroll settings + +All **Advanced**, and per league. + +| Key | Type | Default | What it does | +|---|---|---|---| +| `.scroll_settings.scroll_speed` | 1.0–200.0 px/s | `50.0` | Higher scrolls faster. | +| `.scroll_settings.scroll_delay` | 0.001–0.1 s | `0.01` | Frame delay; `0.01` is 100 FPS. Lower is smoother. | +| `.scroll_settings.gap_between_games` | 8–128 px | `48` | Gap between game cards. | +| `.scroll_settings.show_league_separators` | boolean | `true` | Draw league icons between leagues. | +| `.scroll_settings.dynamic_duration` | boolean | `true` | Size the scroll duration from the content width. | +| `.scroll_settings.game_card_width` | 32–512 px | `128` | Card width. Lower it on a multi-panel chain to fit more games on screen at once. | + +## Matchup separator and the upcoming card middle -Your favorites are **not** rotated. For upcoming games the soonest ones are the point — rotating them would show you a week-8 fixture instead of Saturday's. +The **Matchup Card Layout** section (`scroll_card`) controls what sits between +the two crests before a game starts, and how the date and time are written. +Plugin-wide, not per league. -### Keeping the filler out +| Setting | Key | Default | What it does | +|---|---|---|---| +| Matchup Separator | `scroll_card.vs_text` | `VS` | Text between the teams: `VS`, `@`, `at`, `v`. The away side is always on the left, so `@` and `at` read as "away at home". Blank draws nothing. | +| Middle of an Upcoming Card | `scroll_card.upcoming_center` | `vs` | Scroll and Vegas cards: `vs`, `date_time`, or `none`. | +| Middle of a Full-Screen Upcoming Scoreboard | `scroll_card.switch_upcoming_center` | `date_time` | The same choice for the full-screen scoreboard, plus `inherit` to follow the row above. | +| Date Format | `scroll_card.date_format` | `abbrev` | Scroll and Vegas cards: `Sep 19`, `9/19`, `19 Sep`, `19/9`, or `Fri Sep 19`. | +| Full-Screen Date Format | `scroll_card.switch_date_format` | `numeric` | **Advanced.** The same for the full-screen scoreboard, plus `inherit`. It has its own default because the two displays disagree about what is normal. | +| Time Format | `scroll_card.time_format` | `12h` | 12- or 24-hour clock. | +| Show Date / Show Time | `scroll_card.show_date`, `scroll_card.show_time` | `true` | Drop either line from the scroll and Vegas cards. | +| Full-Screen Show Date / Show Time | `scroll_card.switch_show_date`, `scroll_card.switch_show_time` | `true` | The same for the full-screen scoreboard. Separate switches because the originals predate this display reading the block, and sharing them would have changed what existing boards draw. | +| Swap Date and Time | `scroll_card.swap_date_time` | `false` | Flip the two lines. Each display starts from its own order, so this flips rather than forces. | + +The centre-gap settings size the scroll and Vegas card's middle strip only — the +full-screen scoreboard pins its crests to the panel edges and is unaffected. + +| Key | Type | Default | What it does | +|---|---|---|---| +| `scroll_card.center_gap` | 0–64 px | unset | Pixels kept clear down the middle. Unset scales with card width; `0` restores edge-to-edge logos. | +| `scroll_card.center_gap_ratio` | 0.0–0.6 | `0.28` | **Advanced.** Fraction of card width used when the gap is not pinned. | +| `scroll_card.center_gap_min` | 0–64 px | `22` | **Advanced.** Floor for the scaled gap. | +| `scroll_card.center_gap_max` | 0–96 px | `40` | **Advanced.** Ceiling for the scaled gap. | + +## Text colours and layout offsets -Selection is otherwise purely chronological, and on a college slate most of what that returns is filler. Of ~950 upcoming games, roughly 250 involve a nationally ranked team; the rest are matchups most viewers have never heard of. Rotating harder just serves more of them, which is why `other_games_min_quality` defaults to `ranked`. +Seven text elements, each with `font`, `font_size`, and `text_color`, under +`customization.`. All **Advanced**. Faces: +`PressStart2P-Regular.ttf`, `4x6-font.ttf`, `5by7.regular.ttf`, `5x7.bdf`, +`4x6.bdf`, `cozette.bdf`. -`ranked` means ranked in the **top division's** poll — the AP Top 25 for college football. South Dakota State is a perennial FCS number one, but South Dakota State at Northwestern is not a ranked matchup and does not qualify: the ranked side is FCS and the FBS side is unranked. The poll is matched on team id rather than abbreviation, so two schools sharing an abbreviation across divisions cannot promote each other. +| Element | Default font | Default size | Draws | +|---|---|---|---| +| `score_text` | `PressStart2P-Regular.ttf` | `10` | The score, and the matchup separator on an upcoming card | +| `period_text` | `PressStart2P-Regular.ttf` | `8` | The quarter and clock, and the date/time on an upcoming scoreboard | +| `team_name` | `PressStart2P-Regular.ttf` | `8` | Team names and abbreviations | +| `status_text` | `4x6-font.ttf` | `6` | Status lines such as "Next Game" | +| `detail_text` | `4x6-font.ttf` | `6` | Small detail lines, including down and distance | +| `rank_text` | `PressStart2P-Regular.ttf` | `10` | Poll rank badges | +| `odds_text` | `4x6-font.ttf` | `6` | Betting odds (defaults to green, `[0, 255, 0]`) | + +The `.bdf` faces are bitmap fonts that exist at exactly one pixel size; sizes +snap to that grid rather than being scaled. Odds sizes snap too: `4x6-font.ttf` +to 7, 14, 21; press_start to 8, 16. Every `customization.` object sets +`additionalProperties: false`. -> **Retired:** `broadcast` no longer exists. ESPN lists a broadcaster for almost every game now — ESPN+ included — so on a real Week 1/Week 2 slate it passed **174 of 175** games: a quality bar in the dropdown that behaved as `any`. Boards still holding it are read as `ranked` and say so once in the log; change the setting to clear the schema warning. +```json +{ + "customization": { + "score_text": { "text_color": [255, 200, 0] }, + "status_text": { "text_color": "#00A0FF" } + } +} +``` -`other_games_divisions` needs **one** team in a checked division, not both. With only `fbs` checked you still get #12 Texas Tech hosting Abilene Christian — a game involving a team you asked for — while Abilene Christian vs Furman stays out. Check `fcs` or `other` to bring the smaller-division matchups in as well. +Two things keep their own colours on purpose: the odds figures, tinted by which +side is favoured, and down-and-distance, which is yellow normally and red in the +red zone. -Both filters **fail open**: if rankings cannot be fetched, or the division rosters do not resolve, the game is allowed through. A board showing filler is a poor board; a board showing nothing is a broken one. +### Layout offsets -They fail open a second time, as a set: if the filters between them leave **nothing at all** — your teams idle and every other game rejected — the unfiltered list is used instead. Setting `other_upcoming_games_to_show` or `other_recent_games_to_show` to `0` is the one way to ask for an empty slate, and that is honoured. +Nudge any element in pixels. All default to `0`, all **Advanced**, all under +`customization.layout.`, and all set `additionalProperties: false`. -> These two settings only mean something for `ncaa_fb`. The NFL has no poll and no divisions, so both are inert there and cost nothing — no poll is requested and no division lookup is made. College football is also the only league ESPN publishes FBS/FCS group rosters for at all, so `other_games_divisions` does nothing in the other sports plugins either. +| Element | Keys | Measured from | +|---|---|---| +| `home_logo`, `away_logo` | `x_offset`, `y_offset` | Default logo position | +| `score` | `x_offset`, `y_offset` | Panel centre | +| `status_text` | `x_offset`, `y_offset` | Centre horizontally, top vertically | +| `date` | `x_offset`, `y_offset` | Centre horizontally, default position vertically | +| `time` | `x_offset`, `y_offset` | Centre horizontally, the date's position vertically | +| `down_distance` | `x_offset`, `y_offset` | Default down-and-distance position | +| `possession` | `x_offset`, `y_offset` | Default possession-icon position | +| `timeouts` | `x_offset`, `y_offset` | Default timeout-pip position | +| `records` | `away_x_offset`, `home_x_offset`, `y_offset` | Away from the left, home from the right, both from the bottom | +| `odds` | `x_offset`, `y_offset` | Default odds position | + +## Favorite team result colours -### A worked example +A run of games against the same opponent is hard to read at a glance: in scroll +and Vegas mode the same two crests go past several times and only the digits +change. Turn this on to colour a finished game's score by how your team did. -Say you follow Georgia and Auburn and want to see their next games plus some variety: +| Key | Type | Default | +|---|---|---| +| `customization.favorite_result_colors.enabled` | boolean | `false` | +| `customization.favorite_result_colors.win_color` | `[r, g, b]` | `[0, 255, 0]` | +| `customization.favorite_result_colors.loss_color` | `[r, g, b]` | `[255, 0, 0]` | +| `customization.favorite_result_colors.tie_color` | `[r, g, b]` | `[255, 200, 0]` | ```json -"ncaa_fb": { - "favorite_teams": ["UGA", "AUB"], - "filtering": { "show_favorite_teams_only": false }, - "game_limits": { - "upcoming_games_to_show": 3, - "other_upcoming_games_to_show": 3 +{ + "customization": { + "favorite_result_colors": { + "enabled": true, + "win_color": [0, 255, 0], + "loss_color": [255, 0, 0], + "tie_color": [255, 200, 0] + } } } ``` -That gives 6 cards: the 3 soonest UGA/AUB games, plus 3 ranked FBS matchups that turn over every half hour. - -> **Careful with `AP_TOP_25` / `AP_TOP_10` in `favorite_teams`.** They expand into real teams, so adding `AP_TOP_10` makes UGA and AUB 2 of 11 favorites — and your own teams then queue behind every top-10 game that kicks off earlier. On one real schedule, UGA's next game was favorite-game #5 and Auburn's was #8, so with `upcoming_games_to_show: 3` neither appeared. If you want your teams guaranteed, keep the favorites list to your teams and let `other_upcoming_games_to_show` supply the variety. - - -### Customization Options - -- **Font Customization**: Adjust font family and size for: - - Score text - - Period/time text - - Team names - - Status text - - Detail text (down/distance, etc.) - - Ranking text - -- **Layout Customization**: Fine-tune positioning with X/Y offsets for: - - Team logos (home/away) - - Score display - - Status/period text - - Date and time - - Down & distance - - Timeouts - - Possession indicator - - Records/rankings - - Betting odds - -### Timezone - -- `timezone` (Advanced): IANA name used to display event start times, e.g. - `America/Chicago`. Leave blank (the default) to follow the LEDMatrix global - timezone; if that isn't set, the host system's timezone is used, and only if - neither is available do times fall back to UTC. - - **Leftover `"UTC"` from an older version?** Before the write-back fix this - plugin could persist `"timezone": "UTC"` into your saved config, where it - then shadowed your real global timezone. That stale value is now detected - and ignored automatically whenever your global or system timezone disagrees - — no manual edit needed. If you genuinely want UTC here, set `Etc/UTC`, - which is always honored. - - -## 🐛 Troubleshooting - -### Common Issues -- **Start times look like UTC** (a 6:45pm Central start showing as 11:45PM): - the plugin couldn't read your global timezone. Set `timezone` under the - plugin's Advanced Settings to your IANA zone, e.g. `America/Chicago`. -- **No games showing**: Check if leagues are enabled and favorite teams are configured -- **Missing team logos**: Logos are automatically downloaded from ESPN API -- **Slow updates**: Adjust the `live_update_interval` in league configuration -- **API errors**: Check your internet connection and ESPN API availability -- **Dynamic teams not working**: Ensure you're using exact patterns like `AP_TOP_25` -- **Scroll mode not working**: Verify `scroll_display_mode` is set to "scroll" in config -- **Modes not appearing**: Check that specific modes (e.g., `nfl_live`) are enabled in display_modes settings - - -## 📊 Version History - -### v2.0.7 (Current) -- ✅ **Granular Display Modes**: Independent control of NFL/NCAA FB live/recent/upcoming modes -- ✅ **Scroll Display Mode**: High-FPS horizontal scrolling of all games with league separators -- ✅ **Switch Display Mode**: One game at a time with timed transitions (default) -- ✅ **Font Customization**: Customize fonts and sizes for all text elements -- ✅ **Layout Customization**: Adjust X/Y positioning offsets for all display elements -- ✅ **Date Display**: Recent games show date underneath score -- ✅ Production-ready with real ESPN API integration -- ✅ Dynamic team resolution (AP_TOP_25, AP_TOP_10, AP_TOP_5) -- ✅ Real-time odds display with spread and over/under -- ✅ Nested configuration structure for better organization -- ✅ Full compatibility with LEDMatrix web UI -- ✅ Comprehensive error handling and caching -- ✅ Memory-optimized for Raspberry Pi deployment - -### Previous Versions -- v2.0.6: Bug fixes and improvements -- v2.0.5: Production-ready release with ESPN API integration -- v2.0.4: Initial refactoring to reuse LEDMatrix core code -- v1.x: Original modular implementation - -## 🤝 Contributing - -This plugin is built on the proven LEDMatrix core codebase. For issues or feature requests, please use the GitHub issue tracker. - -## 📄 License - -This plugin follows the same license as the main LEDMatrix project. +- Only finished games are coloured; live and upcoming cards are untouched. +- A game needs **exactly one** favorite team. Neither side or both, and the score + keeps its normal colour. +- The three colours are Advanced settings. + +This tint is applied by the LEDMatrix core rather than by the plugin, which is +why the keys do not appear in this plugin's source. ## Vegas ticker: seeing live games more often By default a live game **takes over** the display: the Vegas ticker stops and -this scoreboard shows full screen until the game ends. If you would rather keep -the marquee scrolling and still see scores, set this in the core config: +this scoreboard shows full screen until the game ends. To keep the marquee +scrolling and still see scores, set this in the **core** config — not in this +plugin's settings: ```json { @@ -689,103 +667,102 @@ the marquee scrolling and still see scores, set this in the core config: ``` The ticker is otherwise a strict round robin — every plugin appears once per -cycle — so with a dozen plugins enabled a score comes round once a lap. These -weights let this plugin claim several slots per cycle, spaced evenly through -it rather than bunched together. - -`live_weight` applies whenever this scoreboard has a live game. -`favorite_live_weight` applies when one of your `favorite_teams` is playing, so -your team's game comes round more often than other live games. That distinction -has to be made here rather than in the core, which can tell *that* a game is -live but not *whose*. - -Two things to keep in mind: +cycle. These weights let this plugin claim several slots per cycle, spaced +evenly through it. `live_weight` applies whenever this scoreboard has a live +game; `favorite_live_weight` applies when one of your teams is playing. That +distinction has to be made here rather than in the core, which can tell *that* a +game is live but not *whose*. - The weight is per **plugin**, not per game. With four games live this scoreboard still occupies one slot at a time and picks between its own games - using `favorite_live_boost`; these weights control how often the scoreboard - itself comes round. -- More slots make the cycle **longer**, not faster — everything else appears - proportionally less often. And appearing more often only helps if the data is - fresh, which is governed by this plugin's own live update interval. + using `favorite_live_boost`. +- More slots make the cycle **longer**, not faster. -## Matchup separator and the upcoming card middle +## Team abbreviations -The **Matchup Card Layout** section (advanced) controls what sits between the -two team logos before a game starts, and how the date and time are written. -These settings now apply to every display mode -- the scroll ticker, the Vegas -ticker, and the full-screen scoreboard -- rather than only the tickers. +**NFL:** `TB`, `DAL`, `GB`, `KC`, `BUF`, `SF`, `PHI`, `NE`, `MIA`, `NYJ`, `LAC`, +`DEN`, `LV`, `CIN`, `BAL`, `CLE`, `PIT`, `IND`, `HOU`, `TEN`, `JAX`, `ARI`, +`LAR`, `SEA`, `WAS`, `NYG`, `MIN`, `DET`, `CHI`, `ATL`, `CAR`, `NO`. -| Setting | Key | Default | What it does | -|---|---|---|---| -| Matchup Separator | `vs_text` | `VS` | Text drawn between the teams: `VS`, `@`, `at`, `v`. The away team is always on the left, so `@` and `at` read as "away at home". Blank draws nothing. | -| Middle of an Upcoming Card | `upcoming_center` | `vs` | Scroll and Vegas cards: the separator, the date and time stacked, or nothing. | -| Middle of a Full-Screen Upcoming Scoreboard | `switch_upcoming_center` | `date_time` | The same choice for the full-screen scoreboard, plus `inherit` to follow the setting above. It defaults to the stacked date and time, which is what this display has always shown, so nothing changes until you pick something else. | -| Date Format | `date_format` | `abbrev` | How the scroll and Vegas cards write the date: `Sep 19`, `9/19`, `19 Sep`, `19/9`, or `Fri Sep 19`. | -| Full-Screen Date Format | `switch_date_format` | `numeric` | The same choice for the full-screen scoreboard, plus `inherit` to follow the row above. It has its own default because the two displays disagree about what is normal: the cards have always written `Sep 19` and the full-screen scoreboard `9/19`, so a single shared default would restyle one of them. | -| Time Format | `time_format` | `12h` | 12- or 24-hour clock. | -| Show Date / Show Time | `show_date`, `show_time` | `true` | Drop either line. | -| Swap Date and Time | `swap_date_time` | `false` | Swap the two lines over. Each display starts from its own order, so this flips them rather than forcing one: the scroll and Vegas cards put the time on top, the full-screen date/time stack puts the date on top. | +**NCAA Football:** `UGA` (Georgia), `AUB` (Auburn), `BAMA` (Alabama), `CLEM` +(Clemson), `OSU` (Ohio State), `MICH` (Michigan), `FSU` (Florida State), `LSU`, +`OU` (Oklahoma), `TEX` (Texas), `ORE` (Oregon), `MISS` (Mississippi), `GT` +(Georgia Tech), `VAN` (Vanderbilt), `BYU`. -Choosing the separator for the full-screen scoreboard moves the date and time -out of the middle and onto the top and bottom rows, the same way the scroll -card lays them out; the "Next Game" header gives up the top row to them. +To check any team, read `events[].competitions[].competitors[].team.abbreviation` +from the scoreboard endpoint below. -The center-gap settings in the same section size the scroll and Vegas card's -middle strip only -- the full-screen scoreboard pins its logos to the panel -edges and is unaffected. +## Data sources and architecture -Example: +ESPN's public site API, no key required: -```json -{ - "scroll_card": { - "vs_text": "@", - "switch_upcoming_center": "vs", - "date_format": "weekday" - } -} -``` +- NFL: `https://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard` +- NCAA FB: `https://site.api.espn.com/apis/site/v2/sports/football/college-football/scoreboard` +- AP poll: the same host's `/rankings` path for college football -### Text Colours +Crests are downloaded on first sight and cached under `assets/sports/nfl_logos/` +and `assets/sports/ncaa_logos/`. Rankings are cached for an hour, and the two +ranking groups share one fetch. -Each text element in the **Customization** section carries a colour, and it now -applies to the text drawn in that element's face — on the full-screen scoreboard -and on the scroll and Vegas cards alike. Until this version the picker changed -only which font was loaded; every string was drawn white. +Each league keeps three managers — live, recent, upcoming — built only when that +league's `enabled` flag is set. The plugin exposes each as its own display mode +so the host controller can schedule and skip them individually. -| Element | Key | Colours | -|---|---|---| -| Score | `score_text` | The score, and the matchup separator on an upcoming card | -| Period / clock | `period_text` | The clock, period, and the date and time on an upcoming scoreboard | -| Team name | `team_name` | Team names and abbreviations | -| Status | `status_text` | Status lines such as "Next Game" | -| Detail | `detail_text` | Small detail lines | -| Ranking | `rank_text` | Team rankings drawn in the ranking face | - -Colours are `[r, g, b]` or `"#RRGGBB"`, and every default is white, so a display -nobody has recoloured looks exactly as it did. - -The adaptive layout (`layout_mode: "adaptive"`) honours the same colours, by -the same rule — the colour follows the face the text is set in. The clock, -a recent card's "Final" and an upcoming card's kickoff time are set in the -period face and take `period_text`'s colour; the stacked date+time centre -stands in for the score and takes `score_text`'s; the bottom date line is set -in the detail face and takes `detail_text`'s. Corner records, timeout bars and -the semantic fills (scoring events, down & distance, the favourite-result -tint) keep their own colours, exactly as in classic. +## Installation -```json -{ - "customization": { - "score_text": { "text_color": [255, 200, 0] }, - "status_text": { "text_color": "#00A0FF" } - } -} +From the Plugin Store in the LEDMatrix web UI: open `http://your-pi-ip:5000`, go +to **Plugin Manager**, find **Football Scoreboard** under **Plugin Store**, and +click **Install**. Then open the plugin's tab to pick your teams. + +Manual install from source: + +```bash +cd /path/to/LEDMatrix +cp -r /path/to/ledmatrix-plugins/plugins/football-scoreboard plugin-repos/ +sudo systemctl restart ledmatrix ``` -Two things keep their own colours on purpose: the betting-odds figures, which -are coloured by which side is favoured, and a finished game's score when -**Favorite Team Result Colors** is on — that tint wins, and your score colour -shows on every other game. Records and rankings drawn in the small fixed face -stay white; no element in the schema owns that face. +The documentation images come from `docs/assets/football-scoreboard/shots.json` +and re-render with `python scripts/render_docs_assets.py --plugin +football-scoreboard --check`. + +## Troubleshooting + +**Nothing appears.** Check that `enabled` is on, and that the league's own +`enabled` is on — `ncaa_fb` is off by default. With +`filtering.show_favorite_teams_only` at its default of `true` and no +`favorite_teams` set, there is nothing to select from. + +**I disabled a mode and it still shows.** The keys here are `show_live`, +`show_recent`, and `show_upcoming` — not `live`, `recent`, `upcoming`. A +`display_modes` block copied from another scoreboard sets nothing. + +**My team almost never appears.** You probably have an `AP_TOP_*` token in +`favorite_teams` alongside it. The token expands into real teams that compete +for the same slots — see [Dynamic team resolution](#dynamic-team-resolution). + +**The same few games keep repeating.** That is the pool cycling. Lower +`other_rotation_interval_seconds` for faster turnover rather than raising the +pool size — a larger pool makes the lap longer, so each game appears less often, +not more. + +**Non-favorite games are all obscure matchups.** Set +`game_limits.other_games_min_quality` to `ranked` (its default) for college +football. In the NFL there is no poll, so the setting is inert and every game +qualifies. + +**A finished game disappeared too soon.** Raise `schedule_lookback_days` +(default 14), or lower `recent_update_interval` if results are slow to appear. + +**Start times look like UTC.** The plugin could not read your global timezone. +Set `timezone` under Advanced Settings to your IANA zone. + +**A game I know about never appears.** It may be beyond +`schedule_lookahead_days` (default 7). A game outside that horizon is never +fetched. + +## Contributing and license + +Issues and pull requests are welcome at +[ChuckBuilds/ledmatrix-plugins](https://github.com/ChuckBuilds/ledmatrix-plugins). +See `LICENSE`. diff --git a/plugins/football-scoreboard/manifest.json b/plugins/football-scoreboard/manifest.json index 2d928979..6999be34 100644 --- a/plugins/football-scoreboard/manifest.json +++ b/plugins/football-scoreboard/manifest.json @@ -1,7 +1,7 @@ { "id": "football-scoreboard", "name": "Football Scoreboard", - "version": "3.4.1", + "version": "3.4.2", "update_interval": 60, "author": "ChuckBuilds", "class_name": "FootballScoreboardPlugin", @@ -25,6 +25,16 @@ "ncaa_fb_live" ], "versions": [ + { + "version": "3.4.2", + "released": "2026-09-02", + "ledmatrix_min_version": "3.3.0", + "notes": "Fix every grid-snapped font rendering a pixel narrow. The shared code reads this plugin's config_schema.json to tell a default font size from one the user chose; a default gets snapped to the font's pixel grid, a choice is left alone. It located the schema by inspecting loaded modules, which fails under the real plugin loader -- the loader renames a plugin's modules and removes their original names, so nothing was left to inspect. The lookup returned nothing, every size then looked user-chosen, and the snap was skipped: 4x6-font.ttf drew at 6 instead of 7, which is 3-pixel-wide glyphs instead of 4. On a 256x64 panel the betting odds, team records and the date row were hard to read. The plugin now tells the shared code where it lives instead of leaving it to guess.", + "changelog": "Retry a team logo whose previous download failed, instead of showing a grey box forever. A failed download is cached by the core as a placeholder wearing the real logo's filename; the logo loader scans filename variations, found that stub, and so never called the downloader again. The loader now skips a placeholder that is stale enough to be worth retrying and lets the download run, which also picks up stubs already on disk. The retry is rate-limited by the core (6h), so this does not trade a permanent grey box for a request every frame. Needs a core carrying src.logo_downloader.is_placeholder_logo; against an older core the check is skipped and behaviour is unchanged. Ported byte-identically across every sports lineage.", + "changes": [ + "Rewrote the README as a complete settings reference covering all 177 settings, with rendered examples of every display mode, the live-card detail, and the adaptive layout." + ] + }, { "version": "3.4.1", "released": "2026-09-02", @@ -424,7 +434,7 @@ "released": "2026-07-10", "version": "2.8.0", "ledmatrix_min_version": "2.0.0", - "notes": "Adaptive layout (beta, opt-in): set layout_mode: \"adaptive\" to scale fonts/logos/regions to any panel size. Default stays \"classic\" — rendering is unchanged unless you opt in; switch back to classic in config to revert without reinstalling. Adaptive mode also applies customization.layout x/y offsets in scroll mode (classic scroll never did). User-configured fonts win over adaptive sizing." + "notes": "Adaptive layout (beta, opt-in): set layout_mode: \"adaptive\" to scale fonts/logos/regions to any panel size. Default stays \"classic\" \u2014 rendering is unchanged unless you opt in; switch back to classic in config to revert without reinstalling. Adaptive mode also applies customization.layout x/y offsets in scroll mode (classic scroll never did). User-configured fonts win over adaptive sizing." }, { "released": "2026-07-08", diff --git a/scripts/docs_render_support/sitecustomize.py b/scripts/docs_render_support/sitecustomize.py index 44eafa3a..64fbb438 100644 --- a/scripts/docs_render_support/sitecustomize.py +++ b/scripts/docs_render_support/sitecustomize.py @@ -89,12 +89,59 @@ def advance(seconds): _WANTED = _json.loads(_ATTRS) _TARGET = "src.plugin_system.plugin_loader" + def _resolve(instance, path): + """Walk a dotted path to the object that owns the final attribute. + + The sports scoreboards keep their per-mode state on sub-managers rather + than on the plugin -- self._managers["live"].current_game -- so a shot + that can only set top-level attributes cannot reach the game it wants + to draw. A segment is tried as a mapping key first, then as an + attribute, so both self._managers["live"] and self.live_manager work. + """ + parts = path.split(".") + target = instance + for part in parts[:-1]: + if hasattr(target, "get") and not hasattr(target, part): + nxt = target.get(part) + else: + nxt = getattr(target, part, None) + if nxt is None and hasattr(target, "get"): + nxt = target.get(part) + if nxt is None: + return None, None + target = nxt + return target, parts[-1] + + def _coerce(name, value): + """Turn JSON into the shapes the plugins actually hold. + + A shots file can only carry JSON, but plugin state is not all strings + and lists: colours are tuples throughout the core, and a logo field is + a pathlib.Path -- the sports renderers call logo_path.parent, so a + string there raises AttributeError and the card silently fails to draw. + """ + if isinstance(value, list) and len(value) == 3 and all( + isinstance(v, int) for v in value): + return tuple(value) # colours are tuples everywhere in the core + if isinstance(value, str) and name.endswith("_path") and value: + import pathlib as _pathlib + return _pathlib.Path(value) + if isinstance(value, dict): + return {k: _coerce(k, v) for k, v in value.items()} + if isinstance(value, list): + return [_coerce(name, v) for v in value] + return value + def _apply(instance): for name, value in _WANTED.items(): - if isinstance(value, list) and len(value) == 3 and all( - isinstance(v, int) for v in value): - value = tuple(value) # colours are tuples everywhere in the core - setattr(instance, name, value) + value = _coerce(name.rsplit(".", 1)[-1], value) + owner, attr = _resolve(instance, name) + if owner is None: + continue # the path does not exist on this plugin; leave it alone + if hasattr(owner, "__setitem__") and not hasattr(owner, attr): + owner[attr] = value + else: + setattr(owner, attr, value) return instance class _PatchingLoader: diff --git a/scripts/render_docs_assets.py b/scripts/render_docs_assets.py index fa38f5f9..2ca204e6 100644 --- a/scripts/render_docs_assets.py +++ b/scripts/render_docs_assets.py @@ -268,6 +268,9 @@ def render_shot( mock_path = resolve_mock_data(mock_spec, shot_list_dir, tmpdir, name) if mock_path: cmd += ["--mock-data", str(mock_path)] + display_mode = shot.get("display_mode", defaults.get("display_mode")) + if display_mode: + cmd += ["--display-mode", str(display_mode)] if shot.get("skip_update", defaults.get("skip_update", False)): cmd.append("--skip-update") From 9b93a35bd2bade868173b0dce5e9e95650dad7ce Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Fri, 4 Sep 2026 01:11:30 -0400 Subject: [PATCH 2/2] docs(football-scoreboard): keep the ChuckBuilds links block The rewrite collapsed the YouTube, Instagram, Discord and sponsorship links into a shields badge. They are the project owner support links, not decoration, so restore them verbatim at the top. Co-Authored-By: Claude Opus 5 --- plugins/football-scoreboard/README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/plugins/football-scoreboard/README.md b/plugins/football-scoreboard/README.md index c7c6561b..f1b3282f 100644 --- a/plugins/football-scoreboard/README.md +++ b/plugins/football-scoreboard/README.md @@ -1,4 +1,14 @@ -[![ChuckBuilds](https://img.shields.io/badge/ChuckBuilds-LEDMatrix-blue)](https://github.com/ChuckBuilds/LEDMatrix) +### Connect with ChuckBuilds + +- Show support on YouTube: https://www.youtube.com/@ChuckBuilds +- Stay in touch on Instagram: https://www.instagram.com/ChuckBuilds/ +- Want to chat or need support? Reach out on the ChuckBuilds Discord: https://discord.com/invite/uW36dVAtcT +- Feeling generous? Support the project: + - GitHub Sponsors: https://github.com/sponsors/ChuckBuilds + - Buy Me a Coffee: https://buymeacoffee.com/chuckbuilds + - Ko-fi: https://ko-fi.com/chuckbuilds/ + +--- # Football Scoreboard