@switchbot/homebridge-switchbot 5.0.0-beta.98 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (282) hide show
  1. package/.changeset/config.json +14 -0
  2. package/.github/copilot-instructions.md +39 -0
  3. package/.github/workflows/ci.yml +4 -1
  4. package/.github/workflows/manual-e2e.yml +6 -3
  5. package/.github/workflows/release.yml +64 -15
  6. package/.github/workflows/stale.yml +2 -4
  7. package/.husky/pre-push +15 -0
  8. package/CHANGELOG.md +126 -134
  9. package/MIGRATION.md +16 -6
  10. package/README.md +84 -3
  11. package/TODO.md +263 -0
  12. package/config.schema.json +229 -36
  13. package/dist/SwitchBotHAPPlatform.d.ts +133 -0
  14. package/dist/SwitchBotHAPPlatform.d.ts.map +1 -0
  15. package/dist/SwitchBotHAPPlatform.js +555 -0
  16. package/dist/SwitchBotHAPPlatform.js.map +1 -0
  17. package/dist/SwitchBotMatterPlatform.d.ts +141 -0
  18. package/dist/SwitchBotMatterPlatform.d.ts.map +1 -0
  19. package/dist/SwitchBotMatterPlatform.js +536 -0
  20. package/dist/SwitchBotMatterPlatform.js.map +1 -0
  21. package/dist/device-types.d.ts +31 -0
  22. package/dist/device-types.d.ts.map +1 -0
  23. package/dist/device-types.js +246 -0
  24. package/dist/device-types.js.map +1 -0
  25. package/dist/deviceCommandMapper.d.ts +10 -0
  26. package/dist/deviceCommandMapper.d.ts.map +1 -0
  27. package/dist/deviceCommandMapper.js +319 -0
  28. package/dist/deviceCommandMapper.js.map +1 -0
  29. package/dist/deviceFactory.d.ts +3 -2
  30. package/dist/deviceFactory.d.ts.map +1 -1
  31. package/dist/deviceFactory.js +107 -29
  32. package/dist/deviceFactory.js.map +1 -1
  33. package/dist/devices/genericDevice.d.ts +59 -37
  34. package/dist/devices/genericDevice.d.ts.map +1 -1
  35. package/dist/devices/genericDevice.js +376 -78
  36. package/dist/devices/genericDevice.js.map +1 -1
  37. package/dist/errors.d.ts +38 -0
  38. package/dist/errors.d.ts.map +1 -0
  39. package/dist/errors.js +32 -0
  40. package/dist/errors.js.map +1 -0
  41. package/dist/homebridge-ui/device-types.js +246 -0
  42. package/dist/homebridge-ui/device-types.js.map +1 -0
  43. package/dist/homebridge-ui/deviceCommandMapper.js +319 -0
  44. package/dist/homebridge-ui/deviceCommandMapper.js.map +1 -0
  45. package/dist/homebridge-ui/endpoints/config.d.ts +3 -0
  46. package/dist/homebridge-ui/endpoints/config.d.ts.map +1 -0
  47. package/dist/homebridge-ui/endpoints/config.js +90 -0
  48. package/dist/homebridge-ui/endpoints/config.js.map +1 -0
  49. package/dist/homebridge-ui/endpoints/devices.d.ts +6 -0
  50. package/dist/homebridge-ui/endpoints/devices.d.ts.map +1 -0
  51. package/dist/homebridge-ui/endpoints/devices.js +144 -0
  52. package/dist/homebridge-ui/endpoints/devices.js.map +1 -0
  53. package/dist/homebridge-ui/endpoints/discovery.d.ts +7 -0
  54. package/dist/homebridge-ui/endpoints/discovery.d.ts.map +1 -0
  55. package/dist/homebridge-ui/endpoints/discovery.js +219 -0
  56. package/dist/homebridge-ui/endpoints/discovery.js.map +1 -0
  57. package/dist/homebridge-ui/errors.js +32 -0
  58. package/dist/homebridge-ui/errors.js.map +1 -0
  59. package/dist/homebridge-ui/homebridge-ui/endpoints/config.js +90 -0
  60. package/dist/homebridge-ui/homebridge-ui/endpoints/config.js.map +1 -0
  61. package/dist/homebridge-ui/homebridge-ui/endpoints/devices.js +144 -0
  62. package/dist/homebridge-ui/homebridge-ui/endpoints/devices.js.map +1 -0
  63. package/dist/homebridge-ui/homebridge-ui/endpoints/discovery.js +219 -0
  64. package/dist/homebridge-ui/homebridge-ui/endpoints/discovery.js.map +1 -0
  65. package/dist/homebridge-ui/homebridge-ui/server.js +11 -0
  66. package/dist/homebridge-ui/homebridge-ui/server.js.map +1 -0
  67. package/dist/homebridge-ui/homebridge-ui/utils/config-parser.js +108 -0
  68. package/dist/homebridge-ui/homebridge-ui/utils/config-parser.js.map +1 -0
  69. package/dist/homebridge-ui/homebridge-ui/utils/device-migration.js +111 -0
  70. package/dist/homebridge-ui/homebridge-ui/utils/device-migration.js.map +1 -0
  71. package/dist/homebridge-ui/homebridge-ui/utils/logger.js +17 -0
  72. package/dist/homebridge-ui/homebridge-ui/utils/logger.js.map +1 -0
  73. package/dist/homebridge-ui/public/css/styles.css +483 -0
  74. package/dist/homebridge-ui/public/index.html +197 -621
  75. package/dist/homebridge-ui/public/js/advanced-settings.d.ts +3 -0
  76. package/dist/homebridge-ui/public/js/advanced-settings.d.ts.map +1 -0
  77. package/dist/homebridge-ui/public/js/advanced-settings.js +95 -0
  78. package/dist/homebridge-ui/public/js/advanced-settings.js.map +1 -0
  79. package/dist/homebridge-ui/public/js/advanced-settings.ts +94 -0
  80. package/dist/homebridge-ui/public/js/api.d.ts +66 -0
  81. package/dist/homebridge-ui/public/js/api.d.ts.map +1 -0
  82. package/dist/homebridge-ui/public/js/api.js +295 -0
  83. package/dist/homebridge-ui/public/js/api.js.map +1 -0
  84. package/dist/homebridge-ui/public/js/api.ts +355 -0
  85. package/dist/homebridge-ui/public/js/app.d.ts +2 -0
  86. package/dist/homebridge-ui/public/js/app.d.ts.map +1 -0
  87. package/dist/homebridge-ui/public/js/app.js +3722 -0
  88. package/dist/homebridge-ui/public/js/app.js.map +7 -0
  89. package/dist/homebridge-ui/public/js/app.ts +22 -0
  90. package/dist/homebridge-ui/public/js/constants.d.ts +2 -0
  91. package/dist/homebridge-ui/public/js/constants.d.ts.map +1 -0
  92. package/dist/homebridge-ui/public/js/constants.js +2 -0
  93. package/dist/homebridge-ui/public/js/constants.js.map +1 -0
  94. package/dist/homebridge-ui/public/js/constants.ts +1 -0
  95. package/dist/homebridge-ui/public/js/credentials.d.ts +3 -0
  96. package/dist/homebridge-ui/public/js/credentials.d.ts.map +1 -0
  97. package/dist/homebridge-ui/public/js/credentials.js +99 -0
  98. package/dist/homebridge-ui/public/js/credentials.js.map +1 -0
  99. package/dist/homebridge-ui/public/js/credentials.ts +105 -0
  100. package/dist/homebridge-ui/public/js/devices-delete.d.ts +3 -0
  101. package/dist/homebridge-ui/public/js/devices-delete.d.ts.map +1 -0
  102. package/dist/homebridge-ui/public/js/devices-delete.js +199 -0
  103. package/dist/homebridge-ui/public/js/devices-delete.js.map +1 -0
  104. package/dist/homebridge-ui/public/js/devices-delete.ts +227 -0
  105. package/dist/homebridge-ui/public/js/devices.d.ts +9 -0
  106. package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -0
  107. package/dist/homebridge-ui/public/js/devices.js +98 -0
  108. package/dist/homebridge-ui/public/js/devices.js.map +1 -0
  109. package/dist/homebridge-ui/public/js/devices.ts +106 -0
  110. package/dist/homebridge-ui/public/js/discovery.d.ts +9 -0
  111. package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -0
  112. package/dist/homebridge-ui/public/js/discovery.js +1201 -0
  113. package/dist/homebridge-ui/public/js/discovery.js.map +1 -0
  114. package/dist/homebridge-ui/public/js/discovery.ts +1335 -0
  115. package/dist/homebridge-ui/public/js/logger.d.ts +7 -0
  116. package/dist/homebridge-ui/public/js/logger.d.ts.map +1 -0
  117. package/dist/homebridge-ui/public/js/logger.js +17 -0
  118. package/dist/homebridge-ui/public/js/logger.js.map +1 -0
  119. package/dist/homebridge-ui/public/js/logger.ts +17 -0
  120. package/dist/homebridge-ui/public/js/modal.d.ts +5 -0
  121. package/dist/homebridge-ui/public/js/modal.d.ts.map +1 -0
  122. package/dist/homebridge-ui/public/js/modal.js +35 -0
  123. package/dist/homebridge-ui/public/js/modal.js.map +1 -0
  124. package/dist/homebridge-ui/public/js/modal.ts +35 -0
  125. package/dist/homebridge-ui/public/js/modals.d.ts +15 -0
  126. package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -0
  127. package/dist/homebridge-ui/public/js/modals.js +675 -0
  128. package/dist/homebridge-ui/public/js/modals.js.map +1 -0
  129. package/dist/homebridge-ui/public/js/modals.ts +765 -0
  130. package/dist/homebridge-ui/public/js/render.d.ts +71 -0
  131. package/dist/homebridge-ui/public/js/render.d.ts.map +1 -0
  132. package/dist/homebridge-ui/public/js/render.js +960 -0
  133. package/dist/homebridge-ui/public/js/render.js.map +1 -0
  134. package/dist/homebridge-ui/public/js/render.ts +1084 -0
  135. package/dist/homebridge-ui/public/js/toast.d.ts +6 -0
  136. package/dist/homebridge-ui/public/js/toast.d.ts.map +1 -0
  137. package/dist/homebridge-ui/public/js/toast.js +38 -0
  138. package/dist/homebridge-ui/public/js/toast.js.map +1 -0
  139. package/dist/homebridge-ui/public/js/toast.ts +44 -0
  140. package/dist/homebridge-ui/public/js/types.d.ts +23 -0
  141. package/dist/homebridge-ui/public/js/types.d.ts.map +1 -0
  142. package/dist/homebridge-ui/public/js/types.js +2 -0
  143. package/dist/homebridge-ui/public/js/types.js.map +1 -0
  144. package/dist/homebridge-ui/public/js/types.ts +26 -0
  145. package/dist/homebridge-ui/server.d.ts +1 -3
  146. package/dist/homebridge-ui/server.d.ts.map +1 -1
  147. package/dist/homebridge-ui/server.js +8 -450
  148. package/dist/homebridge-ui/server.js.map +1 -1
  149. package/dist/homebridge-ui/settings.js +8 -0
  150. package/dist/homebridge-ui/settings.js.map +1 -0
  151. package/dist/homebridge-ui/switchbotClient.js +247 -0
  152. package/dist/homebridge-ui/switchbotClient.js.map +1 -0
  153. package/dist/homebridge-ui/utils/config-parser.d.ts +39 -0
  154. package/dist/homebridge-ui/utils/config-parser.d.ts.map +1 -0
  155. package/dist/homebridge-ui/utils/config-parser.js +108 -0
  156. package/dist/homebridge-ui/utils/config-parser.js.map +1 -0
  157. package/dist/homebridge-ui/utils/device-migration.d.ts +35 -0
  158. package/dist/homebridge-ui/utils/device-migration.d.ts.map +1 -0
  159. package/dist/homebridge-ui/utils/device-migration.js +111 -0
  160. package/dist/homebridge-ui/utils/device-migration.js.map +1 -0
  161. package/dist/homebridge-ui/utils/logger.d.ts +7 -0
  162. package/dist/homebridge-ui/utils/logger.d.ts.map +1 -0
  163. package/dist/homebridge-ui/utils/logger.js +17 -0
  164. package/dist/homebridge-ui/utils/logger.js.map +1 -0
  165. package/dist/index.d.ts +10 -0
  166. package/dist/index.d.ts.map +1 -1
  167. package/dist/index.js +12 -2
  168. package/dist/index.js.map +1 -1
  169. package/dist/settings.d.ts +1 -0
  170. package/dist/settings.d.ts.map +1 -1
  171. package/dist/settings.js +1 -0
  172. package/dist/settings.js.map +1 -1
  173. package/dist/switchbotClient.d.ts +12 -10
  174. package/dist/switchbotClient.d.ts.map +1 -1
  175. package/dist/switchbotClient.js +156 -103
  176. package/dist/switchbotClient.js.map +1 -1
  177. package/dist/utils.d.ts +76 -1
  178. package/dist/utils.d.ts.map +1 -1
  179. package/dist/utils.js +1121 -4
  180. package/dist/utils.js.map +1 -1
  181. package/docs/assets/highlight.css +16 -2
  182. package/docs/assets/main.js +1 -1
  183. package/docs/index.html +82 -5
  184. package/docs/variables/default.html +3 -1
  185. package/eslint.config.js +9 -5
  186. package/nodemon.json +2 -2
  187. package/package.json +34 -21
  188. package/scripts/build-ui.js +37 -0
  189. package/scripts/free-dev-ports.mjs +105 -0
  190. package/scripts/generate-matter-maps.js +34 -17
  191. package/scripts/sync-device-types.mjs +31 -0
  192. package/src/SwitchBotHAPPlatform.ts +558 -0
  193. package/src/SwitchBotMatterPlatform.ts +538 -0
  194. package/src/device-types.js +246 -0
  195. package/src/device-types.js.map +1 -0
  196. package/src/device-types.ts +261 -0
  197. package/src/deviceCommandMapper.js +319 -0
  198. package/src/deviceCommandMapper.js.map +1 -0
  199. package/src/deviceCommandMapper.ts +333 -0
  200. package/src/deviceFactory.ts +125 -45
  201. package/src/devices/genericDevice.ts +411 -69
  202. package/src/errors.js +32 -0
  203. package/src/errors.js.map +1 -0
  204. package/src/errors.ts +35 -0
  205. package/src/homebridge-ui/endpoints/config.ts +110 -0
  206. package/src/homebridge-ui/endpoints/devices.ts +153 -0
  207. package/src/homebridge-ui/endpoints/discovery.ts +240 -0
  208. package/src/homebridge-ui/public/css/styles.css +483 -0
  209. package/src/homebridge-ui/public/index.html +197 -621
  210. package/src/homebridge-ui/public/js/advanced-settings.ts +94 -0
  211. package/src/homebridge-ui/public/js/api.ts +355 -0
  212. package/src/homebridge-ui/public/js/app.ts +22 -0
  213. package/src/homebridge-ui/public/js/constants.ts +1 -0
  214. package/src/homebridge-ui/public/js/credentials.ts +105 -0
  215. package/src/homebridge-ui/public/js/devices-delete.ts +227 -0
  216. package/src/homebridge-ui/public/js/devices.ts +106 -0
  217. package/src/homebridge-ui/public/js/discovery.ts +1335 -0
  218. package/src/homebridge-ui/public/js/logger.ts +17 -0
  219. package/src/homebridge-ui/public/js/modal.ts +35 -0
  220. package/src/homebridge-ui/public/js/modals.ts +765 -0
  221. package/src/homebridge-ui/public/js/render.ts +1084 -0
  222. package/src/homebridge-ui/public/js/toast.ts +44 -0
  223. package/src/homebridge-ui/public/js/types.ts +26 -0
  224. package/src/homebridge-ui/server.ts +9 -526
  225. package/src/homebridge-ui/utils/config-parser.ts +125 -0
  226. package/src/homebridge-ui/utils/device-migration.ts +144 -0
  227. package/src/homebridge-ui/utils/logger.ts +17 -0
  228. package/src/index.ts +12 -2
  229. package/src/settings.js +8 -0
  230. package/src/settings.js.map +1 -0
  231. package/src/settings.ts +2 -0
  232. package/src/switchbotClient.js +247 -0
  233. package/src/switchbotClient.js.map +1 -0
  234. package/src/switchbotClient.ts +177 -114
  235. package/src/utils.ts +1133 -5
  236. package/test/client/switchbot-client-debounce.spec.ts +35 -0
  237. package/test/client/switchbot-client-openapi.spec.ts +19 -0
  238. package/test/client/switchbotClient.spec.ts +64 -0
  239. package/test/device/device-mapping.spec.ts +23 -0
  240. package/test/device/deviceBase.spec.ts +26 -0
  241. package/test/device/deviceFactory-edge.spec.ts +15 -0
  242. package/test/device/deviceFactory.spec.ts +33 -0
  243. package/test/device/fan-swing.spec.ts +34 -0
  244. package/test/device/genericDevice-blepoll.spec.ts +47 -0
  245. package/test/device/irdevice.spec.ts +9 -0
  246. package/test/device/lock-users.spec.ts +35 -0
  247. package/test/device/matter-descriptors.spec.ts +22 -0
  248. package/test/device/matter-device-state.spec.ts +37 -0
  249. package/test/e2e/run-e2e.spec.ts +18 -19
  250. package/test/errors/errors.spec.ts +10 -0
  251. package/test/helpers/matter-harness.ts +20 -9
  252. package/test/homebridge-ui/server.spec.ts +9 -0
  253. package/test/platform/accessory-restore.spec.ts +37 -0
  254. package/test/platform/matter-childbridge.spec.ts +34 -0
  255. package/test/platform/matter-integration.spec.ts +33 -0
  256. package/test/platform/platform-edge.spec.ts +73 -0
  257. package/test/platform/platform.integration.spec.ts +34 -0
  258. package/test/utils/utils-extra.spec.ts +10 -0
  259. package/test/utils/utils.spec.ts +53 -0
  260. package/todo/TODO.md +80 -0
  261. package/tsconfig.ui.json +11 -0
  262. package/.github/npm-version-script-esm.js +0 -97
  263. package/.github/workflows/beta-release.yml +0 -52
  264. package/dist/platform.d.ts +0 -35
  265. package/dist/platform.d.ts.map +0 -1
  266. package/dist/platform.js +0 -850
  267. package/dist/platform.js.map +0 -1
  268. package/src/platform.ts +0 -867
  269. package/test/accessory-restore.spec.ts +0 -73
  270. package/test/device-mapping.spec.ts +0 -37
  271. package/test/deviceFactory.spec.ts +0 -18
  272. package/test/fan-swing.spec.ts +0 -29
  273. package/test/lock-users.spec.ts +0 -44
  274. package/test/matter-childbridge.spec.ts +0 -55
  275. package/test/matter-descriptors.spec.ts +0 -97
  276. package/test/matter-device-state.spec.ts +0 -101
  277. package/test/matter-integration.spec.ts +0 -70
  278. package/test/platform.integration.spec.ts +0 -55
  279. package/test/switchbot-client-debounce.spec.ts +0 -131
  280. package/test/switchbot-client-openapi.spec.ts +0 -56
  281. package/test/switchbotClient.spec.ts +0 -10
  282. package/test/utils.spec.ts +0 -20
@@ -0,0 +1,483 @@
1
+ /* Device details panel contrast fix (class-based) */
2
+ .device-details-panel {
3
+ background: #f9fafb;
4
+ color: #222;
5
+ }
6
+ @media (prefers-color-scheme: dark) {
7
+ .device-details-panel {
8
+ background: #232323;
9
+ color: #fff;
10
+ }
11
+ }
12
+ /* Device item background and text color for both dark and light themes */
13
+ .device-item {
14
+ background: #232323;
15
+ color: #fff;
16
+ border: 1px solid #333;
17
+ }
18
+ @media (prefers-color-scheme: light) {
19
+ .device-item {
20
+ background: #f5f5f5;
21
+ color: #222;
22
+ border: 1px solid #ccc;
23
+ }
24
+ }
25
+
26
+ /* Device details panel contrast fix */
27
+ .device-item div[style*="background: #f9fafb"] {
28
+ background: #f9fafb !important;
29
+ color: #222 !important;
30
+ }
31
+ @media (prefers-color-scheme: dark) {
32
+ .device-item div[style*="background: #f9fafb"] {
33
+ background: #232323 !important;
34
+ color: #fff !important;
35
+ }
36
+ }
37
+ :root {
38
+ color-scheme: light dark;
39
+ --switchbot-red: #ef4444;
40
+ --switchbot-red-hover: #dc2626;
41
+ --switchbot-red-light: #fee2e2;
42
+ }
43
+
44
+ @media (prefers-color-scheme: light) {
45
+ body {
46
+ background: #f5f5f5;
47
+ color: #1a1a1a;
48
+ }
49
+ input {
50
+ background: #fff;
51
+ border-color: #ddd;
52
+ color: #1a1a1a;
53
+ }
54
+ input:focus {
55
+ border-color: #6366f1;
56
+ }
57
+ .card {
58
+ background: #fff;
59
+ border: 1px solid #ddd;
60
+ }
61
+ code {
62
+ background: #f0f0f0;
63
+ color: #1a1a1a;
64
+ }
65
+ h2 {
66
+ border-bottom-color: #ddd;
67
+ }
68
+ .status {
69
+ color: #666;
70
+ }
71
+ }
72
+
73
+ @media (prefers-color-scheme: dark) {
74
+ body {
75
+ background: #1a1a1a;
76
+ color: #fff;
77
+ }
78
+ input {
79
+ background: #2a2a2a;
80
+ border-color: #444;
81
+ color: #fff;
82
+ }
83
+ .card {
84
+ background: #2a2a2a;
85
+ }
86
+ code {
87
+ background: #333;
88
+ color: #fff;
89
+ }
90
+ h2 {
91
+ border-bottom-color: #444;
92
+ }
93
+ .status {
94
+ color: #888;
95
+ }
96
+ .device-item {
97
+ background: #333;
98
+ border: 1px solid #444;
99
+ }
100
+ }
101
+
102
+ @media (prefers-color-scheme: light) {
103
+ .device-item {
104
+ background: #f9f9f9;
105
+ border: 1px solid #e0e0e0;
106
+ }
107
+ }
108
+
109
+ body {
110
+ font-family:
111
+ system-ui,
112
+ -apple-system,
113
+ Arial;
114
+ padding: 10px;
115
+ }
116
+
117
+ h1 {
118
+ font-size: 18px;
119
+ margin-top: 0;
120
+ margin-bottom: 10px;
121
+ }
122
+
123
+ h2 {
124
+ font-size: 16px;
125
+ margin-top: 24px;
126
+ margin-bottom: 12px;
127
+ border-bottom: 1px solid;
128
+ padding-bottom: 8px;
129
+ }
130
+
131
+ ul {
132
+ padding-left: 0;
133
+ list-style: none;
134
+ }
135
+
136
+ li {
137
+ margin: 8px 0;
138
+ display: flex;
139
+ gap: 8px;
140
+ align-items: center;
141
+ }
142
+
143
+ button {
144
+ padding: 10px 20px;
145
+ background: var(--switchbot-red);
146
+ color: #fff;
147
+ border: none;
148
+ border-radius: 6px;
149
+ cursor: pointer;
150
+ font-weight: 500;
151
+ font-size: 14px;
152
+ transition: all 0.2s ease;
153
+ }
154
+
155
+ button:hover {
156
+ background: var(--switchbot-red-hover);
157
+ transform: translateY(-1px);
158
+ box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
159
+ }
160
+
161
+ button:active {
162
+ transform: translateY(0);
163
+ box-shadow: 0 2px 6px rgba(239, 68, 68, 0.2);
164
+ }
165
+
166
+ button.secondary {
167
+ background: #6b7280;
168
+ color: #fff;
169
+ }
170
+
171
+ button.secondary:hover {
172
+ background: #4b5563;
173
+ box-shadow: 0 4px 12px rgba(107, 114, 128, 0.3);
174
+ }
175
+
176
+ button.success {
177
+ background: #10b981;
178
+ }
179
+
180
+ button.success:hover {
181
+ background: #059669;
182
+ box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
183
+ }
184
+
185
+ code {
186
+ padding: 4px 6px;
187
+ border-radius: 4px;
188
+ font-family: monospace;
189
+ }
190
+
191
+ .form-group {
192
+ margin-bottom: 10px;
193
+ }
194
+
195
+ label {
196
+ display: block;
197
+ margin-bottom: 4px;
198
+ font-weight: 500;
199
+ font-size: 11px;
200
+ color: #6b7280;
201
+ }
202
+
203
+ input {
204
+ width: 100%;
205
+ max-width: 400px;
206
+ padding: 6px 8px;
207
+ border: 1px solid #d1d5db;
208
+ border-radius: 6px;
209
+ font-family: system-ui, -apple-system, monospace;
210
+ font-size: 12px;
211
+ transition: all 0.2s ease;
212
+ }
213
+
214
+ input:focus {
215
+ outline: none;
216
+ border-color: var(--switchbot-red, #ef4444);
217
+ box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
218
+ }
219
+
220
+ input:disabled,
221
+ input[readonly] {
222
+ background-color: #f9fafb;
223
+ cursor: not-allowed;
224
+ }
225
+
226
+ select {
227
+ padding: 8px 10px;
228
+ border: 1px solid #d1d5db;
229
+ border-radius: 6px;
230
+ font-family: system-ui, -apple-system, Arial;
231
+ font-size: 14px;
232
+ transition: all 0.2s ease;
233
+ cursor: pointer;
234
+ }
235
+
236
+ select:focus {
237
+ outline: none;
238
+ border-color: var(--switchbot-red, #ef4444);
239
+ box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
240
+ }
241
+
242
+ .status {
243
+ font-size: 11px;
244
+ margin-top: 3px;
245
+ }
246
+
247
+ .status.ok {
248
+ color: #10b981;
249
+ }
250
+
251
+ .error {
252
+ color: #ef4444;
253
+ }
254
+
255
+ .success-msg {
256
+ color: #10b981;
257
+ margin-top: 8px;
258
+ }
259
+
260
+ .card {
261
+ padding: 12px;
262
+ border-radius: 8px;
263
+ margin-bottom: 10px;
264
+ }
265
+
266
+ .card.compact {
267
+ padding: 10px;
268
+ margin-bottom: 10px;
269
+ }
270
+
271
+ .card.compact h2 {
272
+ margin-top: 0;
273
+ margin-bottom: 6px;
274
+ font-size: 13px;
275
+ font-weight: 600;
276
+ }
277
+
278
+ .card.compact .form-group {
279
+ margin-bottom: 6px;
280
+ }
281
+
282
+ .card.compact label {
283
+ font-size: 11px;
284
+ margin-bottom: 2px;
285
+ display: block;
286
+ }
287
+
288
+ .card.compact input {
289
+ padding: 5px 8px;
290
+ font-size: 12px;
291
+ }
292
+
293
+ .card.compact button {
294
+ padding: 6px 12px;
295
+ font-size: 12px;
296
+ }
297
+
298
+ .card.compact .status {
299
+ font-size: 11px;
300
+ margin-top: 4px;
301
+ }
302
+
303
+ .credentials-row {
304
+ display: flex;
305
+ flex-wrap: wrap;
306
+ gap: 8px;
307
+ }
308
+
309
+ .credentials-row .form-group {
310
+ flex: 1 1 100%;
311
+ min-width: 0;
312
+ }
313
+
314
+ @media (min-width: 700px) {
315
+ .credentials-row {
316
+ gap: 10px;
317
+ align-items: start;
318
+ }
319
+
320
+ .credentials-row .form-group {
321
+ flex: 1 1 calc(50% - 5px);
322
+ }
323
+ }
324
+
325
+ .device-item {
326
+ padding: 5px 8px;
327
+ border-radius: 5px;
328
+ margin-bottom: 0;
329
+ display: flex;
330
+ gap: 8px;
331
+ align-items: center;
332
+ justify-content: space-between;
333
+ transition: all 0.2s ease;
334
+ height: 100%;
335
+ min-width: 0;
336
+ }
337
+
338
+ .device-item:hover {
339
+ transform: translateY(-0.5px);
340
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
341
+ }
342
+
343
+ .device-actions {
344
+ display: flex;
345
+ flex-wrap: wrap;
346
+ gap: 6px;
347
+ }
348
+
349
+ .discover-phase-progress {
350
+ margin-bottom: 8px;
351
+ }
352
+
353
+ .discovery-settings-row {
354
+ display: flex;
355
+ flex-wrap: wrap;
356
+ gap: 8px;
357
+ align-items: center;
358
+ }
359
+
360
+ .discover-phase-track {
361
+ width: 100%;
362
+ height: 5px;
363
+ border-radius: 999px;
364
+ background: rgba(148, 163, 184, 0.35);
365
+ overflow: hidden;
366
+ }
367
+
368
+ .discover-phase-fill {
369
+ width: 0%;
370
+ height: 100%;
371
+ border-radius: 999px;
372
+ background: var(--switchbot-red, #ef4444);
373
+ transition: width 0.25s ease;
374
+ }
375
+
376
+ .discover-phase-label {
377
+ margin-top: 3px;
378
+ font-size: 10px;
379
+ opacity: 0.75;
380
+ }
381
+
382
+ .discovery-groups {
383
+ display: block;
384
+ max-height: 400px;
385
+ overflow-y: auto;
386
+ margin-top: 10px;
387
+ }
388
+
389
+ .discovery-group {
390
+ margin-top: 8px;
391
+ }
392
+
393
+ .discovery-group:first-child {
394
+ margin-top: 0;
395
+ }
396
+
397
+ .discovery-group-header {
398
+ margin: 0 0 4px;
399
+ font-size: 11px;
400
+ font-weight: 600;
401
+ opacity: 0.85;
402
+ }
403
+
404
+ .discovery-group-header-btn {
405
+ width: 100%;
406
+ text-align: left;
407
+ margin: 0 0 4px;
408
+ padding: 2px 0;
409
+ border: 0;
410
+ background: transparent;
411
+ color: inherit;
412
+ font-size: 11px;
413
+ font-weight: 600;
414
+ opacity: 0.9;
415
+ cursor: pointer;
416
+ border-radius: 0;
417
+ box-shadow: none;
418
+ transform: none;
419
+ }
420
+
421
+ .discovery-group-header-btn:hover,
422
+ .discovery-group-header-btn:active {
423
+ background: transparent;
424
+ color: inherit;
425
+ box-shadow: none;
426
+ transform: none;
427
+ }
428
+
429
+ .discovery-group-empty {
430
+ font-size: 11px;
431
+ opacity: 0.75;
432
+ padding: 4px 0;
433
+ }
434
+
435
+ #devices.device-grid,
436
+ #discoveredDevices.device-grid,
437
+ ul.device-grid {
438
+ display: grid;
439
+ grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
440
+ gap: 6px;
441
+ padding-left: 0;
442
+ list-style: none;
443
+ margin: 0;
444
+ }
445
+
446
+ .device-item code {
447
+ display: inline-block;
448
+ max-width: 100%;
449
+ white-space: normal;
450
+ overflow-wrap: anywhere;
451
+ word-break: break-word;
452
+ }
453
+
454
+ @media (max-width: 900px) {
455
+ .device-actions {
456
+ width: 100%;
457
+ justify-content: flex-start !important;
458
+ margin-left: 0 !important;
459
+ }
460
+ }
461
+
462
+ .card {
463
+ padding: 16px;
464
+ border-radius: 8px;
465
+ margin-bottom: 16px;
466
+ transition: box-shadow 0.2s ease;
467
+ }
468
+
469
+ .card:hover {
470
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
471
+ }
472
+ @keyframes pulse {
473
+ 0%, 100% {
474
+ opacity: 1;
475
+ }
476
+ 50% {
477
+ opacity: 0.5;
478
+ }
479
+ }
480
+
481
+ .discovery-scanning-pulse {
482
+ animation: pulse 1.5s ease-in-out infinite;
483
+ }