settings.html 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379
  1. {% extends "base.html" %} {% block title %}Settings - {{ app_name or 'Dune Weaver' }}{%
  2. endblock %}
  3. {% block additional_styles %}
  4. /* Dark mode styles for settings page */
  5. .dark .bg-white {
  6. background-color: #262626;
  7. }
  8. .dark .text-slate-900 {
  9. color: #ffffff;
  10. }
  11. .dark .text-slate-800 {
  12. color: #f8fafc;
  13. }
  14. .dark .text-slate-700 {
  15. color: #f1f5f9;
  16. }
  17. .dark .text-slate-600 {
  18. color: #e2e8f0;
  19. }
  20. .dark .text-slate-500 {
  21. color: #e2e8f0;
  22. }
  23. /* Label overrides for better visibility */
  24. .dark label {
  25. color: #f1f5f9;
  26. }
  27. .dark .border-slate-200 {
  28. border-color: #404040;
  29. }
  30. .dark .border-slate-300 {
  31. border-color: #404040;
  32. }
  33. .dark .divide-slate-100 {
  34. border-color: #333333;
  35. }
  36. .dark .bg-slate-50 {
  37. background-color: #262626;
  38. }
  39. .dark .hover\:bg-slate-50:hover {
  40. background-color: #404040;
  41. }
  42. .dark .bg-slate-100 {
  43. background-color: #404040;
  44. }
  45. .dark .form-input,
  46. .dark input[type="number"],
  47. .dark input[type="text"],
  48. .dark input[type="time"] {
  49. background-color: #1f1f1f;
  50. border-color: #404040;
  51. color: #e5e5e5;
  52. }
  53. .dark .form-input::placeholder {
  54. color: #9ca3af;
  55. }
  56. .dark .form-input:focus {
  57. border-color: #0c7ff2;
  58. ring-color: #0c7ff2;
  59. }
  60. .dark input[type="time"]::-webkit-calendar-picker-indicator {
  61. filter: invert(1);
  62. }
  63. .dark .form-select {
  64. background-color: #1f1f1f;
  65. border-color: #404040;
  66. color: #e5e5e5;
  67. }
  68. .dark .form-select:focus {
  69. border-color: #0c7ff2;
  70. ring-color: #0c7ff2;
  71. }
  72. .dark .focus\:ring-sky-500:focus {
  73. ring-color: #0c7ff2;
  74. }
  75. .dark .focus\:border-sky-500:focus {
  76. border-color: #0c7ff2;
  77. }
  78. .dark .hover\:text-gray-700:hover {
  79. color: #e5e5e5;
  80. }
  81. .dark .text-gray-400 {
  82. color: #9ca3af;
  83. }
  84. /* Autocomplete suggestions dark mode */
  85. .dark #clearFromInSuggestions,
  86. .dark #clearFromOutSuggestions {
  87. background-color: #262626;
  88. border-color: #404040;
  89. }
  90. .dark .suggestion-item {
  91. color: #e5e5e5;
  92. }
  93. .dark .suggestion-item:hover {
  94. background-color: #404040;
  95. }
  96. .dark .suggestion-item.selected {
  97. background-color: #0c7ff2;
  98. color: white;
  99. }
  100. /* Light mode autocomplete styles */
  101. .suggestion-item {
  102. padding: 8px 12px;
  103. cursor: pointer;
  104. color: #1f2937;
  105. transition: background-color 0.15s;
  106. }
  107. .suggestion-item:hover {
  108. background-color: #f3f4f6;
  109. }
  110. .suggestion-item.selected {
  111. background-color: #0c7ff2;
  112. color: white;
  113. }
  114. .suggestion-item mark {
  115. background-color: #fef3c7;
  116. font-weight: 600;
  117. }
  118. .dark .suggestion-item mark {
  119. background-color: #92400e;
  120. color: #fef3c7;
  121. }
  122. /* Toggle switch styles */
  123. .switch {
  124. position: relative;
  125. display: inline-block;
  126. width: 60px;
  127. height: 34px;
  128. }
  129. .switch input {
  130. opacity: 0;
  131. width: 0;
  132. height: 0;
  133. }
  134. .slider {
  135. position: absolute;
  136. cursor: pointer;
  137. top: 0;
  138. left: 0;
  139. right: 0;
  140. bottom: 0;
  141. background-color: #ccc;
  142. transition: .4s;
  143. }
  144. .slider:before {
  145. position: absolute;
  146. content: "";
  147. height: 26px;
  148. width: 26px;
  149. left: 4px;
  150. bottom: 4px;
  151. background-color: white;
  152. transition: .4s;
  153. }
  154. input:checked + .slider {
  155. background-color: #0c7ff2;
  156. }
  157. input:focus + .slider {
  158. box-shadow: 0 0 1px #0c7ff2;
  159. }
  160. input:checked + .slider:before {
  161. transform: translateX(26px);
  162. }
  163. .slider.round {
  164. border-radius: 34px;
  165. }
  166. .slider.round:before {
  167. border-radius: 50%;
  168. }
  169. /* Dark mode for switches */
  170. .dark .slider {
  171. background-color: #404040;
  172. }
  173. .dark input:checked + .slider {
  174. background-color: #0c7ff2;
  175. }
  176. /* Spin animation for loading states */
  177. @keyframes spin {
  178. from {
  179. transform: rotate(0deg);
  180. }
  181. to {
  182. transform: rotate(360deg);
  183. }
  184. }
  185. .animate-spin {
  186. animation: spin 1s linear infinite;
  187. }
  188. /* Time slot specific styles */
  189. .time-slot-item {
  190. background-color: #f8fafc;
  191. border: 1px solid #e2e8f0;
  192. border-radius: 8px;
  193. padding: 16px;
  194. transition: all 0.15s;
  195. }
  196. .dark .time-slot-item {
  197. background-color: #1e293b;
  198. border-color: #475569;
  199. }
  200. .time-slot-item:hover {
  201. border-color: #cbd5e1;
  202. }
  203. .dark .time-slot-item:hover {
  204. border-color: #64748b;
  205. }
  206. /* Info box dark mode - grey theme */
  207. .dark .bg-blue-50 {
  208. background-color: #1f1f1f;
  209. }
  210. .dark .border-blue-200 {
  211. border-color: #404040;
  212. }
  213. .dark .text-blue-600 {
  214. color: #e2e8f0;
  215. }
  216. .dark .text-blue-800 {
  217. color: #f1f5f9;
  218. }
  219. .dark .text-blue-700 {
  220. color: #e2e8f0;
  221. }
  222. /* Amber box dark mode - grey theme */
  223. .dark .bg-amber-50 {
  224. background-color: #1f1f1f;
  225. }
  226. .dark .border-amber-200 {
  227. border-color: #404040;
  228. }
  229. .dark .text-amber-600 {
  230. color: #f1f5f9;
  231. }
  232. /* Sky box dark mode - grey theme (Still Sands options) */
  233. .dark .bg-sky-50 {
  234. background-color: #1f1f1f;
  235. }
  236. .dark .border-sky-200 {
  237. border-color: #404040;
  238. }
  239. /* Select dropdown dark mode */
  240. .dark select {
  241. background-color: #1f1f1f;
  242. border-color: #404040;
  243. color: #e5e5e5;
  244. }
  245. .dark select:focus {
  246. border-color: #0c7ff2;
  247. }
  248. .dark select option {
  249. background-color: #1f1f1f;
  250. color: #e5e5e5;
  251. }
  252. .dark select optgroup {
  253. background-color: #262626;
  254. color: #9ca3af;
  255. }
  256. {% endblock %}
  257. {% block content %}
  258. <div class="layout-content-container flex flex-col w-full max-w-4xl gap-8 pt-2 pb-[75px]">
  259. <div
  260. class="flex flex-wrap justify-between items-center p-4 bg-white rounded-xl shadow-sm mt-2 sm:mt-8"
  261. >
  262. <h1
  263. class="text-slate-900 tracking-tight text-2xl sm:text-3xl font-bold leading-tight"
  264. >
  265. Settings
  266. </h1>
  267. </div>
  268. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  269. <h2
  270. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  271. >
  272. Device Connection
  273. </h2>
  274. <div>
  275. <div
  276. class="flex items-center gap-4 px-6 py-5 hover:bg-slate-50 transition-colors"
  277. >
  278. <div
  279. class="text-slate-600 flex items-center justify-center rounded-lg bg-slate-100 shrink-0 size-12"
  280. >
  281. <span class="material-icons text-3xl">usb_off</span>
  282. </div>
  283. <div class="flex-1">
  284. <p class="text-slate-800 text-base font-medium leading-normal">
  285. Status
  286. </p>
  287. <p
  288. id="serialStatus"
  289. class="text-red-500 text-sm font-medium leading-normal"
  290. >
  291. Disconnected
  292. </p>
  293. </div>
  294. <button
  295. id="disconnectButton"
  296. class="text-xs font-medium bg-red-100 hover:bg-red-200 text-red-700 dark:bg-red-900 dark:hover:bg-red-800 dark:text-red-200 px-3 py-1.5 rounded-md transition-colors"
  297. hidden
  298. >
  299. Disconnect
  300. </button>
  301. </div>
  302. <div id="portSelectionDiv" class="px-6 py-5 space-y-4">
  303. <label class="flex flex-col gap-1.5">
  304. <span class="text-slate-700 text-sm font-medium leading-normal"
  305. >Available Serial Ports</span
  306. >
  307. <div class="flex gap-3 items-center">
  308. <select
  309. id="portSelect"
  310. class="form-select flex-1 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-medium leading-normal transition-colors "
  311. >
  312. <option value="">Select a port...</option>
  313. </select>
  314. <button
  315. id="connectButton"
  316. class="flex items-center justify-center gap-2 min-w-[100px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors flex-shrink-0"
  317. >
  318. <span class="material-icons text-lg">cable</span>
  319. <span class="truncate">Connect</span>
  320. </button>
  321. </div>
  322. <p class="text-xs text-slate-500 mt-2">
  323. Select a port and click 'Connect' to establish a connection.
  324. </p>
  325. </label>
  326. </div>
  327. <!-- Preferred Port Configuration -->
  328. <div class="px-6 py-5">
  329. <label class="flex flex-col gap-1.5">
  330. <span class="text-slate-700 text-sm font-medium leading-normal flex items-center gap-2">
  331. <span class="material-icons text-slate-600 text-base">star</span>
  332. Preferred Port for Auto-Connect
  333. </span>
  334. <div class="flex gap-3 items-center">
  335. <select
  336. id="preferredPortSelect"
  337. class="form-select flex-1 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-medium leading-normal transition-colors"
  338. >
  339. <option value="">No preference (auto-detect)</option>
  340. </select>
  341. <button
  342. id="savePreferredPort"
  343. class="flex items-center justify-center gap-2 min-w-[100px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors flex-shrink-0"
  344. >
  345. <span class="material-icons text-lg">save</span>
  346. <span class="truncate">Save</span>
  347. </button>
  348. </div>
  349. <p class="text-xs text-slate-500 mt-2">
  350. When multiple ports are available, this port will be used automatically on startup. If set to "No preference", the system will try the last connected port or the first available port.
  351. </p>
  352. <p id="currentPreferredPort" class="text-xs text-sky-600 mt-1 hidden">
  353. <span class="material-icons text-xs align-middle">check_circle</span>
  354. <span id="preferredPortDisplay"></span>
  355. </p>
  356. </label>
  357. </div>
  358. </div>
  359. </section>
  360. <!-- Homing Configuration Section -->
  361. <section id="homingSection" class="bg-white rounded-xl shadow-sm overflow-hidden">
  362. <h2
  363. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  364. >
  365. Homing Configuration
  366. </h2>
  367. <div class="px-6 py-5 space-y-6">
  368. <!-- Homing Mode Selection -->
  369. <div class="space-y-3">
  370. <label class="text-sm font-medium text-slate-700 flex items-center gap-2">
  371. <span class="material-icons text-slate-600 text-base">home</span>
  372. Homing Mode
  373. </label>
  374. <div class="space-y-3">
  375. <!-- Crash Homing Option -->
  376. <label class="flex items-start gap-3 p-3 border border-slate-300 rounded-lg cursor-pointer hover:bg-slate-50 transition-colors">
  377. <input
  378. type="radio"
  379. name="homingMode"
  380. value="0"
  381. id="homingModeCrash"
  382. class="mt-0.5 w-4 h-4 text-sky-600 focus:ring-sky-500"
  383. />
  384. <div class="flex-1">
  385. <div class="text-sm font-medium text-slate-700">Crash Homing</div>
  386. <div class="text-xs text-slate-500 mt-1">
  387. Y axis moves until physical stop, then theta and rho set to 0 (no x0 y0 command)
  388. </div>
  389. </div>
  390. </label>
  391. <!-- Sensor Homing Option -->
  392. <label class="flex items-start gap-3 p-3 border border-slate-300 rounded-lg cursor-pointer hover:bg-slate-50 transition-colors">
  393. <input
  394. type="radio"
  395. name="homingMode"
  396. value="1"
  397. id="homingModeSensor"
  398. class="mt-0.5 w-4 h-4 text-sky-600 focus:ring-sky-500"
  399. />
  400. <div class="flex-1">
  401. <div class="text-sm font-medium text-slate-700">Sensor Homing</div>
  402. <div class="text-xs text-slate-500 mt-1">
  403. Homes both X and Y axes using sensors
  404. </div>
  405. </div>
  406. </label>
  407. </div>
  408. </div>
  409. <!-- Compass Reference Point (Sensor mode only) -->
  410. <div id="compassOffsetContainer" class="space-y-2">
  411. <label for="angularOffsetInput" class="text-sm font-medium text-slate-700 flex items-center gap-2">
  412. <span class="material-icons text-slate-600 text-base">explore</span>
  413. Sensor offset (degrees) <span class="text-xs text-slate-400">(Sensor mode only)</span>
  414. </label>
  415. <input
  416. type="number"
  417. id="angularOffsetInput"
  418. min="0"
  419. max="360"
  420. step="0.1"
  421. value="0"
  422. class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-sky-500 focus:border-sky-500 text-sm"
  423. placeholder="0.0"
  424. />
  425. <p class="text-xs text-slate-500">
  426. Set the angle (in degrees) where your radial arm to be offset by. You want to choose a value here so that the radial arm will point East.
  427. </p>
  428. </div>
  429. <!-- Homing Info Box -->
  430. <div id="homingInfoBox" class="text-xs text-slate-600 bg-blue-50 border border-blue-200 rounded-lg p-3">
  431. <div class="flex items-start gap-2">
  432. <span class="material-icons text-blue-600 text-base">info</span>
  433. <div id="homingInfoContent">
  434. <p class="font-medium text-blue-800">Crash Homing Mode:</p>
  435. <ul class="mt-1 space-y-1 text-blue-700">
  436. <li>• Y axis moves -22mm (or -30mm for mini) until physical stop</li>
  437. <li>• Theta set to 0, rho set to 0</li>
  438. <li>• No x0 y0 command sent</li>
  439. <li>• No hardware sensors required</li>
  440. </ul>
  441. </div>
  442. </div>
  443. </div>
  444. <!-- Auto-Home During Playlists -->
  445. <div class="bg-slate-50 rounded-lg p-4 space-y-4">
  446. <div class="flex items-center justify-between">
  447. <div class="flex-1">
  448. <h3 class="text-slate-800 text-base font-semibold flex items-center gap-2">
  449. <span class="material-icons text-slate-600 text-base">autorenew</span>
  450. Auto-Home During Playlists
  451. </h3>
  452. <p class="text-xs text-slate-500 mt-1">
  453. Automatically perform homing after a certain number of patterns during playlist playback to maintain accuracy.
  454. </p>
  455. </div>
  456. <label class="switch">
  457. <input type="checkbox" id="autoHomeEnabledToggle">
  458. <span class="slider round"></span>
  459. </label>
  460. </div>
  461. <div id="autoHomeSettings" style="display: none;">
  462. <label class="flex flex-col gap-1.5">
  463. <span class="text-slate-700 text-sm font-medium leading-normal">Home after every X patterns</span>
  464. <input
  465. type="number"
  466. id="autoHomeAfterPatternsInput"
  467. min="1"
  468. max="100"
  469. step="1"
  470. value="5"
  471. class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-sky-500 focus:border-sky-500 text-sm"
  472. placeholder="5"
  473. />
  474. <p class="text-xs text-slate-500">
  475. Homing will occur right after the clear pattern completes, before the next actual pattern begins.
  476. </p>
  477. </label>
  478. </div>
  479. </div>
  480. <div class="flex justify-end">
  481. <button
  482. id="saveHomingConfig"
  483. class="flex items-center justify-center gap-2 min-w-[140px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  484. >
  485. <span class="material-icons text-lg">save</span>
  486. <span class="truncate">Save Configuration</span>
  487. </button>
  488. </div>
  489. </div>
  490. </section>
  491. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  492. <h2
  493. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  494. >
  495. Application Settings
  496. </h2>
  497. <div class="px-6 py-5 space-y-6">
  498. <label class="flex flex-col gap-1.5">
  499. <span class="text-slate-700 text-sm font-medium leading-normal"
  500. >Application Name</span
  501. >
  502. <div class="flex gap-3 items-center">
  503. <div class="relative flex-1">
  504. <input
  505. id="appNameInput"
  506. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 pr-10 text-base font-normal leading-normal transition-colors"
  507. placeholder="e.g., Dune Weaver"
  508. value="Dune Weaver"
  509. />
  510. <button
  511. type="button"
  512. onclick="document.getElementById('appNameInput').value='Dune Weaver';"
  513. class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700"
  514. aria-label="Reset to default"
  515. title="Reset to default"
  516. >
  517. <span class="material-icons">restart_alt</span>
  518. </button>
  519. </div>
  520. <button
  521. id="saveAppName"
  522. class="flex items-center justify-center gap-2 min-w-[140px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors flex-shrink-0"
  523. >
  524. <span class="material-icons text-lg">save</span>
  525. <span class="truncate">Save Name</span>
  526. </button>
  527. </div>
  528. <p class="text-xs text-slate-500 mt-2">
  529. This name will appear in the browser tab and at the top of every page.
  530. </p>
  531. </label>
  532. </div>
  533. </section>
  534. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  535. <h2
  536. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  537. >
  538. Pattern Clearing
  539. </h2>
  540. <div class="px-6 py-5 space-y-6">
  541. <p class="text-sm text-slate-600">
  542. Customize the clearing behavior used when transitioning between patterns. Set custom patterns and speed to control how sand is distributed.
  543. </p>
  544. <!-- Clearing Speed Section -->
  545. <div class="bg-slate-50 rounded-lg p-4 space-y-4">
  546. <h3 class="text-slate-800 text-base font-semibold">Clearing Speed</h3>
  547. <p class="text-sm text-slate-600">
  548. Set a custom speed for clearing patterns. Leave empty to use the default pattern speed. This allows clearing patterns to run at a different speed than regular patterns.
  549. </p>
  550. <div class="flex flex-col gap-1.5">
  551. <label for="clearPatternSpeedInput" class="text-slate-700 text-sm font-medium leading-normal">
  552. Speed (steps per minute)
  553. </label>
  554. <div class="flex gap-3 items-center">
  555. <input
  556. id="clearPatternSpeedInput"
  557. type="number"
  558. min="50"
  559. max="2000"
  560. step="50"
  561. class="form-input flex-1 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  562. placeholder="Default (use pattern speed)"
  563. value=""
  564. />
  565. <button
  566. id="saveClearSpeed"
  567. class="flex items-center justify-center gap-2 min-w-[120px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  568. >
  569. <span class="material-icons text-lg">save</span>
  570. <span class="truncate">Save Speed</span>
  571. </button>
  572. </div>
  573. <div id="effectiveClearSpeed" class="text-xs text-slate-500 mt-1"></div>
  574. </div>
  575. </div>
  576. <!-- Custom Patterns Section -->
  577. <div class="bg-slate-50 rounded-lg p-4 space-y-4">
  578. <h3 class="text-slate-800 text-base font-semibold">Custom Clear Patterns</h3>
  579. <p class="text-sm text-slate-600">
  580. Choose specific patterns to use when clearing. Leave empty to use the default clearing behavior.
  581. </p>
  582. <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
  583. <div class="flex flex-col gap-1.5">
  584. <label for="customClearFromInInput" class="text-slate-700 text-sm font-medium leading-normal">Clear From Center Pattern</label>
  585. <div class="relative">
  586. <input
  587. id="customClearFromInInput"
  588. type="text"
  589. class="form-input w-full resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 pr-10 text-base font-normal leading-normal transition-colors"
  590. placeholder="Type to search patterns or leave empty for default"
  591. autocomplete="off"
  592. />
  593. <button
  594. type="button"
  595. id="clearFromInClear"
  596. class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hidden"
  597. aria-label="Clear selection"
  598. title="Clear selection"
  599. >
  600. <span class="material-icons text-xl">close</span>
  601. </button>
  602. <div id="clearFromInSuggestions" class="absolute z-10 w-full mt-1 bg-white border border-slate-300 rounded-lg shadow-lg max-h-60 overflow-y-auto hidden"></div>
  603. </div>
  604. <p class="text-xs text-slate-500 mt-1">
  605. Pattern to use when clearing from the center outward.
  606. </p>
  607. </div>
  608. <div class="flex flex-col gap-1.5">
  609. <label for="customClearFromOutInput" class="text-slate-700 text-sm font-medium leading-normal">Clear From Perimeter Pattern</label>
  610. <div class="relative">
  611. <input
  612. id="customClearFromOutInput"
  613. type="text"
  614. class="form-input w-full resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 pr-10 text-base font-normal leading-normal transition-colors"
  615. placeholder="Type to search patterns or leave empty for default"
  616. autocomplete="off"
  617. />
  618. <button
  619. type="button"
  620. id="clearFromOutClear"
  621. class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hidden"
  622. aria-label="Clear selection"
  623. title="Clear selection"
  624. >
  625. <span class="material-icons text-xl">close</span>
  626. </button>
  627. <div id="clearFromOutSuggestions" class="absolute z-10 w-full mt-1 bg-white border border-slate-300 rounded-lg shadow-lg max-h-60 overflow-y-auto hidden"></div>
  628. </div>
  629. <p class="text-xs text-slate-500 mt-1">
  630. Pattern to use when clearing from the perimeter inward.
  631. </p>
  632. </div>
  633. </div>
  634. <div class="flex justify-end">
  635. <button
  636. id="saveClearPatterns"
  637. class="flex items-center justify-center gap-2 min-w-[140px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  638. >
  639. <span class="material-icons text-lg">save</span>
  640. <span class="truncate">Save Patterns</span>
  641. </button>
  642. </div>
  643. </div>
  644. </div>
  645. </section>
  646. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  647. <h2
  648. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  649. >
  650. LED Controller Configuration
  651. </h2>
  652. <div class="px-6 py-5 space-y-6">
  653. <!-- LED Provider Selection -->
  654. <div class="flex flex-col gap-2">
  655. <span class="text-slate-700 text-sm font-medium leading-normal">LED Provider</span>
  656. <div class="flex gap-3">
  657. <label class="flex items-center gap-2 cursor-pointer">
  658. <input type="radio" name="ledProvider" value="none" id="ledProviderNone" class="w-4 h-4 text-sky-600 border-slate-300 focus:ring-sky-500">
  659. <span class="text-sm text-slate-700">None</span>
  660. </label>
  661. <label class="flex items-center gap-2 cursor-pointer">
  662. <input type="radio" name="ledProvider" value="wled" id="ledProviderWled" class="w-4 h-4 text-sky-600 border-slate-300 focus:ring-sky-500">
  663. <span class="text-sm text-slate-700">WLED</span>
  664. </label>
  665. <label class="flex items-center gap-2 cursor-pointer">
  666. <input type="radio" name="ledProvider" value="dw_leds" id="ledProviderDwLeds" class="w-4 h-4 text-sky-600 border-slate-300 focus:ring-sky-500">
  667. <span class="text-sm text-slate-700">DW LEDs (Local GPIO)</span>
  668. </label>
  669. </div>
  670. <p class="text-xs text-slate-500">
  671. Select your LED control system (settings are mutually exclusive)
  672. </p>
  673. </div>
  674. <!-- WLED Configuration (shown when WLED is selected) -->
  675. <div id="wledConfig" class="flex flex-col gap-4 hidden">
  676. <label class="flex flex-col gap-1.5">
  677. <span class="text-slate-700 text-sm font-medium leading-normal">WLED IP Address</span>
  678. <div class="relative flex-1">
  679. <input
  680. id="wledIpInput"
  681. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 pr-10 text-base font-normal leading-normal transition-colors"
  682. placeholder="e.g., 192.168.1.100"
  683. value=""
  684. />
  685. <button
  686. type="button"
  687. onclick="document.getElementById('wledIpInput').value='';"
  688. class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700"
  689. aria-label="Clear WLED IP"
  690. >
  691. <span class="material-icons">close</span>
  692. </button>
  693. </div>
  694. <p class="text-xs text-slate-500">
  695. Enter the IP address of your WLED controller
  696. </p>
  697. </label>
  698. </div>
  699. <!-- DW LEDs Configuration (shown when DW LEDs is selected) -->
  700. <div id="dwLedsConfig" class="flex flex-col gap-4 hidden">
  701. <div class="bg-blue-50 border border-blue-200 rounded-lg p-3">
  702. <div class="flex items-start gap-2">
  703. <span class="material-icons text-blue-600 text-base">info</span>
  704. <div class="text-xs text-blue-700">
  705. <p class="font-medium text-blue-800">Supported LED Strips</p>
  706. <p class="mt-1"><strong>RGB (3-channel):</strong> WS2811, WS2812, WS2812B, WS2813, WS2815 and other WS281x strips.</p>
  707. <p class="mt-1"><strong>RGBW (4-channel):</strong> SK6812, SK6812W and other RGBW strips with dedicated white channel.</p>
  708. </div>
  709. </div>
  710. </div>
  711. <label class="flex flex-col gap-1.5">
  712. <span class="text-slate-700 text-sm font-medium leading-normal">Number of LEDs</span>
  713. <input
  714. id="dwLedNumLeds"
  715. type="number"
  716. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  717. placeholder="60"
  718. value="60"
  719. min="1"
  720. max="1000"
  721. />
  722. <p class="text-xs text-slate-500">
  723. Total number of LEDs in your WS281x strip
  724. </p>
  725. </label>
  726. <label class="flex flex-col gap-1.5">
  727. <span class="text-slate-700 text-sm font-medium leading-normal">GPIO Pin</span>
  728. <select
  729. id="dwLedGpioPin"
  730. class="form-select flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 px-4 text-base font-normal leading-normal transition-colors"
  731. >
  732. <option value="12">GPIO 12 (PWM0)</option>
  733. <option value="13">GPIO 13 (PWM1)</option>
  734. <option value="18">GPIO 18 (PWM0)</option>
  735. <option value="19">GPIO 19 (PWM1)</option>
  736. </select>
  737. <p class="text-xs text-slate-500">
  738. Select a PWM-capable GPIO pin for WS281x timing
  739. </p>
  740. </label>
  741. <label class="flex flex-col gap-1.5">
  742. <span class="text-slate-700 text-sm font-medium leading-normal">Pixel Color Order</span>
  743. <select
  744. id="dwLedPixelOrder"
  745. class="form-select flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 px-4 text-base font-normal leading-normal transition-colors"
  746. >
  747. <optgroup label="RGB Strips (3-channel)">
  748. <option value="GRB" selected>GRB - WS2812/WS2812B (most common)</option>
  749. <option value="RGB">RGB - WS2815/WS2811 and some variants</option>
  750. <option value="BGR">BGR - Some WS2811 variants</option>
  751. <option value="RBG">RBG - Rare variant</option>
  752. <option value="GBR">GBR - Rare variant</option>
  753. <option value="BRG">BRG - Rare variant</option>
  754. </optgroup>
  755. <optgroup label="RGBW Strips (4-channel)">
  756. <option value="GRBW">GRBW - SK6812 RGBW (most common)</option>
  757. <option value="RGBW">RGBW - SK6812 RGBW variant</option>
  758. </optgroup>
  759. </select>
  760. <p class="text-xs text-slate-500">
  761. Most WS2812B strips use GRB. SK6812 RGBW strips typically use GRBW. If colors appear wrong, try different orders.
  762. </p>
  763. </label>
  764. </div>
  765. <!-- Save Button -->
  766. <button
  767. id="saveLedConfig"
  768. class="flex items-center justify-center gap-2 w-full sm:w-auto cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  769. >
  770. <span class="material-icons text-lg">save</span>
  771. <span class="truncate">Save LED Configuration</span>
  772. </button>
  773. </div>
  774. </section>
  775. <!-- MQTT Configuration Section -->
  776. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  777. <h2
  778. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  779. >
  780. MQTT / Home Assistant Integration
  781. </h2>
  782. <div class="px-6 py-5 space-y-6">
  783. <!-- MQTT Enable Toggle -->
  784. <div class="flex items-center justify-between">
  785. <div class="flex-1">
  786. <h3 class="text-slate-700 text-base font-medium leading-normal">Enable MQTT</h3>
  787. <p class="text-xs text-slate-500 mt-1">
  788. Connect to an MQTT broker for Home Assistant integration and remote control.
  789. </p>
  790. </div>
  791. <label class="switch">
  792. <input type="checkbox" id="mqttEnableToggle">
  793. <span class="slider round"></span>
  794. </label>
  795. </div>
  796. <!-- Connection Status -->
  797. <div id="mqttStatusBanner" class="hidden">
  798. <div id="mqttConnectedBanner" class="bg-green-50 border border-green-200 rounded-lg p-3 hidden">
  799. <div class="flex items-center gap-2">
  800. <span class="material-icons text-green-600 text-base">check_circle</span>
  801. <span class="text-sm text-green-700 font-medium">Connected to MQTT broker</span>
  802. </div>
  803. </div>
  804. <div id="mqttDisconnectedBanner" class="bg-amber-50 border border-amber-200 rounded-lg p-3 hidden">
  805. <div class="flex items-center gap-2">
  806. <span class="material-icons text-amber-600 text-base">warning</span>
  807. <span class="text-sm text-amber-700 font-medium">MQTT is enabled but not connected. Check your settings or restart the application.</span>
  808. </div>
  809. </div>
  810. </div>
  811. <!-- MQTT Settings (shown when enabled) -->
  812. <div id="mqttSettings" class="space-y-4" style="display: none;">
  813. <!-- Broker Settings -->
  814. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  815. <label class="flex flex-col gap-1.5">
  816. <span class="text-slate-700 text-sm font-medium leading-normal">Broker Address <span class="text-red-500">*</span></span>
  817. <input
  818. id="mqttBrokerInput"
  819. type="text"
  820. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  821. placeholder="e.g., 192.168.1.100 or mqtt.local"
  822. />
  823. <p class="text-xs text-slate-500">IP address or hostname of your MQTT broker</p>
  824. </label>
  825. <label class="flex flex-col gap-1.5">
  826. <span class="text-slate-700 text-sm font-medium leading-normal">Port</span>
  827. <input
  828. id="mqttPortInput"
  829. type="number"
  830. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  831. placeholder="1883"
  832. value="1883"
  833. />
  834. <p class="text-xs text-slate-500">Default: 1883 (or 8883 for TLS)</p>
  835. </label>
  836. </div>
  837. <!-- Authentication -->
  838. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  839. <label class="flex flex-col gap-1.5">
  840. <span class="text-slate-700 text-sm font-medium leading-normal">Username</span>
  841. <input
  842. id="mqttUsernameInput"
  843. type="text"
  844. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  845. placeholder="Optional"
  846. />
  847. <p class="text-xs text-slate-500">Leave empty if no authentication required</p>
  848. </label>
  849. <label class="flex flex-col gap-1.5">
  850. <span class="text-slate-700 text-sm font-medium leading-normal">Password</span>
  851. <div class="relative">
  852. <input
  853. id="mqttPasswordInput"
  854. type="password"
  855. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 pr-10 text-base font-normal leading-normal transition-colors"
  856. placeholder="Optional"
  857. />
  858. <button
  859. type="button"
  860. onclick="togglePasswordVisibility('mqttPasswordInput', this)"
  861. class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700"
  862. aria-label="Toggle password visibility"
  863. >
  864. <span class="material-icons text-xl">visibility_off</span>
  865. </button>
  866. </div>
  867. <p class="text-xs text-slate-500">Leave empty if no authentication required</p>
  868. </label>
  869. </div>
  870. <!-- Home Assistant Discovery Settings -->
  871. <div class="border-t border-slate-200 pt-4">
  872. <h4 class="text-slate-700 text-sm font-medium mb-3">Home Assistant Discovery</h4>
  873. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  874. <label class="flex flex-col gap-1.5">
  875. <span class="text-slate-700 text-sm font-medium leading-normal">Device Name</span>
  876. <input
  877. id="mqttDeviceNameInput"
  878. type="text"
  879. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  880. placeholder="Dune Weaver"
  881. value="Dune Weaver"
  882. />
  883. <p class="text-xs text-slate-500">Display name in Home Assistant</p>
  884. </label>
  885. <label class="flex flex-col gap-1.5">
  886. <span class="text-slate-700 text-sm font-medium leading-normal">Device ID</span>
  887. <input
  888. id="mqttDeviceIdInput"
  889. type="text"
  890. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  891. placeholder="dune_weaver"
  892. value="dune_weaver"
  893. />
  894. <p class="text-xs text-slate-500">Unique identifier (no spaces)</p>
  895. </label>
  896. </div>
  897. </div>
  898. <!-- Advanced Settings (Collapsible) -->
  899. <details class="border-t border-slate-200 pt-4">
  900. <summary class="text-slate-700 text-sm font-medium cursor-pointer hover:text-slate-900">
  901. Advanced Settings
  902. </summary>
  903. <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
  904. <label class="flex flex-col gap-1.5">
  905. <span class="text-slate-700 text-sm font-medium leading-normal">Client ID</span>
  906. <input
  907. id="mqttClientIdInput"
  908. type="text"
  909. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  910. placeholder="dune_weaver"
  911. value="dune_weaver"
  912. />
  913. <p class="text-xs text-slate-500">MQTT client identifier</p>
  914. </label>
  915. <label class="flex flex-col gap-1.5">
  916. <span class="text-slate-700 text-sm font-medium leading-normal">Discovery Prefix</span>
  917. <input
  918. id="mqttDiscoveryPrefixInput"
  919. type="text"
  920. class="form-input flex w-full min-w-0 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  921. placeholder="homeassistant"
  922. value="homeassistant"
  923. />
  924. <p class="text-xs text-slate-500">Home Assistant discovery topic prefix</p>
  925. </label>
  926. </div>
  927. </details>
  928. <!-- Test Connection Button -->
  929. <div class="flex flex-wrap gap-3 pt-2">
  930. <button
  931. id="testMqttConnection"
  932. class="flex items-center justify-center gap-2 cursor-pointer rounded-lg h-10 px-4 bg-slate-100 hover:bg-slate-200 text-slate-700 text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  933. >
  934. <span class="material-icons text-lg">wifi_tethering</span>
  935. <span class="truncate">Test Connection</span>
  936. </button>
  937. <span id="mqttTestResult" class="flex items-center text-sm"></span>
  938. </div>
  939. </div>
  940. <!-- Save Button -->
  941. <button
  942. id="saveMqttConfig"
  943. class="flex items-center justify-center gap-2 w-full sm:w-auto cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  944. >
  945. <span class="material-icons text-lg">save</span>
  946. <span class="truncate">Save MQTT Configuration</span>
  947. </button>
  948. <!-- Restart Notice -->
  949. <div id="mqttRestartNotice" class="bg-blue-50 border border-blue-200 rounded-lg p-3 hidden">
  950. <div class="flex items-start gap-2">
  951. <span class="material-icons text-blue-600 text-base">info</span>
  952. <div class="text-xs text-blue-700">
  953. <p class="font-medium text-blue-800">Restart Required</p>
  954. <p class="mt-1">MQTT configuration changes require a restart to take effect. Use the restart button in the header to apply changes.</p>
  955. </div>
  956. </div>
  957. </div>
  958. </div>
  959. </section>
  960. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  961. <h2
  962. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  963. >
  964. Auto-play on Boot
  965. </h2>
  966. <div class="px-6 py-5 space-y-6">
  967. <div class="flex items-center justify-between">
  968. <div class="flex-1">
  969. <h3 class="text-slate-700 text-base font-medium leading-normal">Enable Auto-play on Boot</h3>
  970. <p class="text-xs text-slate-500 mt-1">
  971. Automatically start playing a selected playlist when the system boots up.
  972. </p>
  973. </div>
  974. <label class="switch">
  975. <input type="checkbox" id="auto_playModeToggle">
  976. <span class="slider round"></span>
  977. </label>
  978. </div>
  979. <div id="auto_playSettings" class="space-y-4" style="display: none;">
  980. <label class="flex flex-col gap-1.5">
  981. <span class="text-slate-700 text-sm font-medium leading-normal">Startup Playlist</span>
  982. <select
  983. id="auto_playPlaylistSelect"
  984. class="form-select flex-1 resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-medium leading-normal transition-colors"
  985. >
  986. <option value="">Select a playlist...</option>
  987. </select>
  988. <p class="text-xs text-slate-500 mt-1">
  989. Choose which playlist to automatically play when the system starts.
  990. </p>
  991. </label>
  992. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  993. <label class="flex flex-col gap-1.5">
  994. <span class="text-slate-700 text-sm font-medium leading-normal">Run Mode</span>
  995. <select
  996. id="auto_playRunModeSelect"
  997. class="form-select resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-medium leading-normal transition-colors"
  998. >
  999. <option value="single">Single (play once)</option>
  1000. <option value="loop">Loop (repeat forever)</option>
  1001. </select>
  1002. <p class="text-xs text-slate-500 mt-1">
  1003. How to run the playlist when it finishes.
  1004. </p>
  1005. </label>
  1006. <label class="flex flex-col gap-1.5">
  1007. <span class="text-slate-700 text-sm font-medium leading-normal">Pause Between Patterns (seconds)</span>
  1008. <input
  1009. id="auto_playPauseTimeInput"
  1010. type="number"
  1011. min="0"
  1012. step="0.5"
  1013. class="form-input resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-normal leading-normal transition-colors"
  1014. placeholder="5.0"
  1015. />
  1016. <p class="text-xs text-slate-500 mt-1">
  1017. Time to wait between each pattern (0 or more seconds).
  1018. </p>
  1019. </label>
  1020. </div>
  1021. <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
  1022. <label class="flex flex-col gap-1.5">
  1023. <span class="text-slate-700 text-sm font-medium leading-normal">Clear Pattern</span>
  1024. <select
  1025. id="auto_playClearPatternSelect"
  1026. class="form-select resize-none overflow-hidden rounded-lg text-slate-900 focus:outline-0 focus:ring-2 focus:ring-sky-500 border border-slate-300 bg-white focus:border-sky-500 h-10 placeholder:text-slate-400 px-4 text-base font-medium leading-normal transition-colors"
  1027. >
  1028. <option value="none">None</option>
  1029. <option value="adaptive">Adaptive</option>
  1030. <option value="clear_from_in">Clear From Center</option>
  1031. <option value="clear_from_out">Clear From Perimeter</option>
  1032. <option value="clear_sideway">Clear Sideway</option>
  1033. <option value="random">Random</option>
  1034. </select>
  1035. <p class="text-xs text-slate-500 mt-1">
  1036. Pattern to run before each main pattern.
  1037. </p>
  1038. </label>
  1039. <div class="flex items-center justify-between">
  1040. <div class="flex-1">
  1041. <h4 class="text-slate-700 text-sm font-medium leading-normal">Shuffle Playlist</h4>
  1042. <p class="text-xs text-slate-500 mt-1">
  1043. Randomize the order of patterns in the playlist.
  1044. </p>
  1045. </div>
  1046. <label class="switch">
  1047. <input type="checkbox" id="auto_playShuffleToggle">
  1048. <span class="slider round"></span>
  1049. </label>
  1050. </div>
  1051. </div>
  1052. <div class="flex justify-end">
  1053. <button
  1054. id="saveAutoPlaySettings"
  1055. class="flex items-center justify-center gap-2 min-w-[140px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  1056. >
  1057. <span class="material-icons text-lg">save</span>
  1058. <span class="truncate">Save Auto-play</span>
  1059. </button>
  1060. </div>
  1061. </div>
  1062. </div>
  1063. </section>
  1064. <section class="bg-white rounded-xl shadow-sm overflow-hidden">
  1065. <h2
  1066. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  1067. >
  1068. Still Sands
  1069. </h2>
  1070. <div class="px-6 py-5 space-y-6">
  1071. <div class="flex items-center justify-between">
  1072. <div class="flex-1">
  1073. <h3 class="text-slate-700 text-base font-medium leading-normal">Enable Still Sands</h3>
  1074. <p class="text-xs text-slate-500 mt-1">
  1075. Automatically bring the sands to rest during specified time periods.
  1076. </p>
  1077. </div>
  1078. <label class="switch">
  1079. <input type="checkbox" id="scheduledPauseToggle">
  1080. <span class="slider round"></span>
  1081. </label>
  1082. </div>
  1083. <div id="scheduledPauseSettings" class="space-y-4" style="display: none;">
  1084. <!-- Finish Current Pattern Option -->
  1085. <div class="bg-sky-50 rounded-lg p-4 border border-sky-200">
  1086. <div class="flex items-center justify-between">
  1087. <div class="flex-1">
  1088. <h4 class="text-slate-800 text-sm font-medium flex items-center gap-2">
  1089. <span class="material-icons text-slate-800 dark:text-slate-200 text-base">hourglass_bottom</span>
  1090. Finish Current Pattern
  1091. </h4>
  1092. <p class="text-xs text-slate-600 mt-1">
  1093. Let the current pattern complete before entering still mode
  1094. </p>
  1095. </div>
  1096. <label class="switch">
  1097. <input type="checkbox" id="stillSandsFinishPattern">
  1098. <span class="slider round"></span>
  1099. </label>
  1100. </div>
  1101. </div>
  1102. <!-- WLED Control Option -->
  1103. <div class="bg-sky-50 rounded-lg p-4 border border-sky-200">
  1104. <div class="flex items-center justify-between">
  1105. <div class="flex-1">
  1106. <h4 class="text-slate-800 text-sm font-medium flex items-center gap-2">
  1107. <span class="material-icons text-slate-800 dark:text-slate-200 text-base">lightbulb</span>
  1108. Control WLED Lights
  1109. </h4>
  1110. <p class="text-xs text-slate-600 mt-1">
  1111. Turn off WLED lights during still periods for complete rest
  1112. </p>
  1113. </div>
  1114. <label class="switch">
  1115. <input type="checkbox" id="stillSandsWledControl">
  1116. <span class="slider round"></span>
  1117. </label>
  1118. </div>
  1119. </div>
  1120. <!-- Timezone Selection -->
  1121. <div class="bg-sky-50 rounded-lg p-4 border border-sky-200">
  1122. <div class="flex items-center justify-between">
  1123. <div class="flex-1">
  1124. <h4 class="text-slate-800 text-sm font-medium flex items-center gap-2">
  1125. <span class="material-icons text-slate-800 dark:text-slate-200 text-base">schedule</span>
  1126. Timezone
  1127. </h4>
  1128. <p class="text-xs text-slate-600 mt-1">
  1129. Select a timezone for still periods (defaults to system timezone)
  1130. </p>
  1131. </div>
  1132. <select id="stillSandsTimezone" class="h-10 px-3 rounded-lg border border-slate-300 bg-white text-slate-800 text-sm min-w-[200px]">
  1133. <option value="">System Default</option>
  1134. <optgroup label="Americas">
  1135. <option value="America/New_York">Eastern Time (New York)</option>
  1136. <option value="America/Chicago">Central Time (Chicago)</option>
  1137. <option value="America/Denver">Mountain Time (Denver)</option>
  1138. <option value="America/Los_Angeles">Pacific Time (Los Angeles)</option>
  1139. <option value="America/Anchorage">Alaska (Anchorage)</option>
  1140. <option value="Pacific/Honolulu">Hawaii (Honolulu)</option>
  1141. <option value="America/Toronto">Toronto</option>
  1142. <option value="America/Vancouver">Vancouver</option>
  1143. <option value="America/Mexico_City">Mexico City</option>
  1144. <option value="America/Sao_Paulo">São Paulo</option>
  1145. <option value="America/Buenos_Aires">Buenos Aires</option>
  1146. </optgroup>
  1147. <optgroup label="Europe">
  1148. <option value="Europe/London">London</option>
  1149. <option value="Europe/Paris">Paris</option>
  1150. <option value="Europe/Berlin">Berlin</option>
  1151. <option value="Europe/Amsterdam">Amsterdam</option>
  1152. <option value="Europe/Rome">Rome</option>
  1153. <option value="Europe/Madrid">Madrid</option>
  1154. <option value="Europe/Zurich">Zurich</option>
  1155. <option value="Europe/Stockholm">Stockholm</option>
  1156. <option value="Europe/Moscow">Moscow</option>
  1157. </optgroup>
  1158. <optgroup label="Asia & Pacific">
  1159. <option value="Asia/Tokyo">Tokyo</option>
  1160. <option value="Asia/Shanghai">Shanghai</option>
  1161. <option value="Asia/Hong_Kong">Hong Kong</option>
  1162. <option value="Asia/Singapore">Singapore</option>
  1163. <option value="Asia/Seoul">Seoul</option>
  1164. <option value="Asia/Dubai">Dubai</option>
  1165. <option value="Asia/Kolkata">India (Kolkata)</option>
  1166. <option value="Asia/Bangkok">Bangkok</option>
  1167. <option value="Australia/Sydney">Sydney</option>
  1168. <option value="Australia/Melbourne">Melbourne</option>
  1169. <option value="Australia/Perth">Perth</option>
  1170. <option value="Pacific/Auckland">Auckland</option>
  1171. </optgroup>
  1172. <optgroup label="Africa">
  1173. <option value="Africa/Cairo">Cairo</option>
  1174. <option value="Africa/Johannesburg">Johannesburg</option>
  1175. <option value="Africa/Lagos">Lagos</option>
  1176. </optgroup>
  1177. <optgroup label="GMT Offsets">
  1178. <option value="Etc/GMT+12">GMT-12</option>
  1179. <option value="Etc/GMT+11">GMT-11</option>
  1180. <option value="Etc/GMT+10">GMT-10</option>
  1181. <option value="Etc/GMT+9">GMT-9</option>
  1182. <option value="Etc/GMT+8">GMT-8</option>
  1183. <option value="Etc/GMT+7">GMT-7</option>
  1184. <option value="Etc/GMT+6">GMT-6</option>
  1185. <option value="Etc/GMT+5">GMT-5</option>
  1186. <option value="Etc/GMT+4">GMT-4</option>
  1187. <option value="Etc/GMT+3">GMT-3</option>
  1188. <option value="Etc/GMT+2">GMT-2</option>
  1189. <option value="Etc/GMT+1">GMT-1</option>
  1190. <option value="Etc/GMT">GMT / UTC</option>
  1191. <option value="Etc/GMT-1">GMT+1</option>
  1192. <option value="Etc/GMT-2">GMT+2</option>
  1193. <option value="Etc/GMT-3">GMT+3</option>
  1194. <option value="Etc/GMT-4">GMT+4</option>
  1195. <option value="Etc/GMT-5">GMT+5</option>
  1196. <option value="Etc/GMT-6">GMT+6</option>
  1197. <option value="Etc/GMT-7">GMT+7</option>
  1198. <option value="Etc/GMT-8">GMT+8</option>
  1199. <option value="Etc/GMT-9">GMT+9</option>
  1200. <option value="Etc/GMT-10">GMT+10</option>
  1201. <option value="Etc/GMT-11">GMT+11</option>
  1202. <option value="Etc/GMT-12">GMT+12</option>
  1203. <option value="Etc/GMT-13">GMT+13</option>
  1204. <option value="Etc/GMT-14">GMT+14</option>
  1205. </optgroup>
  1206. </select>
  1207. </div>
  1208. </div>
  1209. <div class="bg-slate-50 rounded-lg p-4 space-y-4">
  1210. <div class="flex items-center justify-between">
  1211. <h4 class="text-slate-800 text-base font-semibold">Still Periods</h4>
  1212. <button
  1213. id="addTimeSlotButton"
  1214. class="flex items-center justify-center gap-2 cursor-pointer rounded-lg h-9 px-3 bg-sky-600 hover:bg-sky-700 text-white text-xs font-medium leading-normal tracking-[0.015em] transition-colors"
  1215. >
  1216. <span class="material-icons text-base">add</span>
  1217. <span>Add Still Period</span>
  1218. </button>
  1219. </div>
  1220. <p class="text-sm text-slate-600">
  1221. Define time periods when the sands should rest in stillness. Patterns will resume automatically when still periods end.
  1222. </p>
  1223. <div id="timeSlotsContainer" class="space-y-3">
  1224. <!-- Time slots will be dynamically added here -->
  1225. </div>
  1226. <div class="text-xs text-slate-500 bg-blue-50 border border-blue-200 rounded-lg p-3">
  1227. <div class="flex items-start gap-2">
  1228. <span class="material-icons text-blue-600 text-base">info</span>
  1229. <div>
  1230. <p class="font-medium text-blue-800">Important Notes:</p>
  1231. <ul class="mt-1 space-y-1 text-blue-700">
  1232. <li>• Times are based on the selected timezone (or system default if not set)</li>
  1233. <li>• By default, patterns pause immediately when entering a still period</li>
  1234. <li>• Enable "Finish Current Pattern" to let patterns complete first</li>
  1235. <li>• Patterns will resume automatically when exiting a still period</li>
  1236. <li>• Still periods that span midnight (e.g., 22:00 to 06:00) are supported</li>
  1237. </ul>
  1238. </div>
  1239. </div>
  1240. </div>
  1241. </div>
  1242. <div class="flex justify-end">
  1243. <button
  1244. id="savePauseSettings"
  1245. class="flex items-center justify-center gap-2 min-w-[140px] cursor-pointer rounded-lg h-10 px-4 bg-sky-600 hover:bg-sky-700 text-white text-sm font-medium leading-normal tracking-[0.015em] transition-colors"
  1246. >
  1247. <span class="material-icons text-lg">save</span>
  1248. <span class="truncate">Save Still Sands</span>
  1249. </button>
  1250. </div>
  1251. </div>
  1252. </div>
  1253. </section>
  1254. <section id="software-version-section" class="bg-white rounded-xl shadow-sm overflow-hidden">
  1255. <h2
  1256. class="text-slate-800 text-xl sm:text-2xl font-semibold leading-tight tracking-[-0.01em] px-6 py-4 border-b border-slate-200"
  1257. >
  1258. Software Version
  1259. </h2>
  1260. <div>
  1261. <div class="flex items-center gap-4 px-6 py-5">
  1262. <div
  1263. class="text-slate-600 flex items-center justify-center rounded-lg bg-slate-100 shrink-0 size-12"
  1264. >
  1265. <span class="material-icons text-3xl">terminal</span>
  1266. </div>
  1267. <div class="flex-1">
  1268. <p class="text-slate-800 text-base font-medium leading-normal">
  1269. Current Version
  1270. </p>
  1271. <p id="currentVersionText" class="text-slate-500 text-sm font-normal leading-normal">Loading...</p>
  1272. </div>
  1273. </div>
  1274. <div class="flex items-center gap-4 px-6 py-5">
  1275. <div
  1276. class="text-slate-600 flex items-center justify-center rounded-lg bg-slate-100 shrink-0 size-12"
  1277. >
  1278. <span class="material-icons text-3xl">system_update</span>
  1279. </div>
  1280. <div class="flex-1">
  1281. <p class="text-slate-800 text-base font-medium leading-normal">
  1282. Latest Version
  1283. </p>
  1284. <p id="latestVersionText" class="text-slate-500 text-sm font-normal leading-normal">Checking...</p>
  1285. </div>
  1286. <button
  1287. id="updateSoftware"
  1288. class="flex items-center justify-center gap-1.5 min-w-[84px] cursor-pointer rounded-lg h-9 px-3 bg-gray-400 text-white text-xs font-medium leading-normal tracking-[0.015em] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
  1289. disabled
  1290. >
  1291. <span id="updateIcon" class="material-icons text-base">download</span>
  1292. <span id="updateText" class="truncate">Update</span>
  1293. </button>
  1294. </div>
  1295. </div>
  1296. </section>
  1297. </div>
  1298. {% endblock %} {% block scripts %}
  1299. <script src="/static/js/settings.js"></script>
  1300. {% endblock %}