nanoleaf-kde-sync 1.9.3__tar.gz

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 (277) hide show
  1. nanoleaf_kde_sync-1.9.3/LICENSE +27 -0
  2. nanoleaf_kde_sync-1.9.3/MANIFEST.in +3 -0
  3. nanoleaf_kde_sync-1.9.3/PKG-INFO +167 -0
  4. nanoleaf_kde_sync-1.9.3/README.md +139 -0
  5. nanoleaf_kde_sync-1.9.3/VERSION +1 -0
  6. nanoleaf_kde_sync-1.9.3/assets/icons/hicolor/128x128/apps/nanoleaf-kde-sync.png +0 -0
  7. nanoleaf_kde_sync-1.9.3/assets/icons/hicolor/48x48/apps/nanoleaf-kde-sync.png +0 -0
  8. nanoleaf_kde_sync-1.9.3/assets/icons/hicolor/scalable/apps/nanoleaf-kde-sync.svg +21 -0
  9. nanoleaf_kde_sync-1.9.3/assets/udev/60-nanoleaf-kde-sync.rules +7 -0
  10. nanoleaf_kde_sync-1.9.3/pyproject.toml +129 -0
  11. nanoleaf_kde_sync-1.9.3/setup.cfg +4 -0
  12. nanoleaf_kde_sync-1.9.3/src/nanoleaf_kde_sync.egg-info/PKG-INFO +167 -0
  13. nanoleaf_kde_sync-1.9.3/src/nanoleaf_kde_sync.egg-info/SOURCES.txt +275 -0
  14. nanoleaf_kde_sync-1.9.3/src/nanoleaf_kde_sync.egg-info/dependency_links.txt +1 -0
  15. nanoleaf_kde_sync-1.9.3/src/nanoleaf_kde_sync.egg-info/entry_points.txt +8 -0
  16. nanoleaf_kde_sync-1.9.3/src/nanoleaf_kde_sync.egg-info/requires.txt +21 -0
  17. nanoleaf_kde_sync-1.9.3/src/nanoleaf_kde_sync.egg-info/top_level.txt +1 -0
  18. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/VERSION +1 -0
  19. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/__init__.py +20 -0
  20. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/assets/icons/hicolor/128x128/apps/nanoleaf-kde-sync.png +0 -0
  21. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/assets/icons/hicolor/48x48/apps/nanoleaf-kde-sync.png +0 -0
  22. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/assets/icons/hicolor/scalable/apps/nanoleaf-kde-sync.svg +21 -0
  23. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/assets/udev/60-nanoleaf-kde-sync.rules +7 -0
  24. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/__init__.py +27 -0
  25. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/_drm_helper_bridge.py +249 -0
  26. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/_drm_master_fd.py +58 -0
  27. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/_drm_zone_sampler.py +1356 -0
  28. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/_utils.py +69 -0
  29. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/auto_probe.py +253 -0
  30. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/backend_normalization.py +14 -0
  31. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/backend_selection.py +34 -0
  32. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/dimensions.py +137 -0
  33. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/errors.py +14 -0
  34. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/factory.py +625 -0
  35. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/interfaces.py +33 -0
  36. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/kmsgrab.py +426 -0
  37. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/kwin_dbus.py +1138 -0
  38. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/latency_probe.py +209 -0
  39. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/mock_capture.py +48 -0
  40. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/nanoleaf_drm_helper +0 -0
  41. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/portal_helpers.py +15 -0
  42. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/probe_models.py +64 -0
  43. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/probe_timing.py +56 -0
  44. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/replay_capture.py +72 -0
  45. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/source_context.py +239 -0
  46. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/source_identity.py +142 -0
  47. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/capture/xdg_portal.py +1020 -0
  48. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/color/__init__.py +22 -0
  49. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/color/_types.py +6 -0
  50. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/color/capture_metadata.py +332 -0
  51. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/color/hdr.py +335 -0
  52. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/color/metadata_hysteresis.py +57 -0
  53. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/color/primaries.py +480 -0
  54. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/color/zone_mapper.py +61 -0
  55. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/compat/__init__.py +61 -0
  56. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/compat/kde_version.py +157 -0
  57. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/compat/kwin_probe.py +182 -0
  58. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/compat/portal_probe.py +146 -0
  59. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/compat/update_checker.py +344 -0
  60. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/compat/version_snapshot.py +115 -0
  61. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/config/__init__.py +4 -0
  62. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/config/model.py +305 -0
  63. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/config/normalize.py +819 -0
  64. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/config/presets.py +364 -0
  65. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/config/serialization.py +42 -0
  66. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/config/store.py +267 -0
  67. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/desktop_entry.py +261 -0
  68. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/device/__init__.py +9 -0
  69. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/device/hid_transport.py +674 -0
  70. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/device/interfaces.py +31 -0
  71. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/device/protocol.py +128 -0
  72. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/device/send_policy.py +139 -0
  73. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/device/transport_profiler.py +84 -0
  74. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/device/usb_driver.py +650 -0
  75. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/doc_paths.py +36 -0
  76. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/__init__.py +0 -0
  77. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/anchor_calibration.py +143 -0
  78. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/blending.py +695 -0
  79. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/calibration_resolver.py +267 -0
  80. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/color_accuracy_diagnostics.py +115 -0
  81. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/color_context.py +111 -0
  82. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/color_domain.py +44 -0
  83. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/color_pipeline.py +737 -0
  84. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/color_processing.py +646 -0
  85. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/colour_path_diagnostics.py +416 -0
  86. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/compositor.py +119 -0
  87. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/content_bounds.py +162 -0
  88. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/diagnostics_exports.py +782 -0
  89. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/edge_locality_diagnostics.py +78 -0
  90. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/engine.py +1968 -0
  91. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/errors.py +219 -0
  92. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/fps_governor.py +145 -0
  93. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/frame_context.py +140 -0
  94. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/output_session.py +66 -0
  95. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/palette_adaptive.py +434 -0
  96. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/palette_temporal.py +361 -0
  97. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/predictive_sync.py +248 -0
  98. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/processing.py +79 -0
  99. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/readiness_check.py +363 -0
  100. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/ring_buf.py +197 -0
  101. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/srgb.py +64 -0
  102. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/startup.py +370 -0
  103. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/state.py +572 -0
  104. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/status_warnings.py +45 -0
  105. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/zone_derivation.py +205 -0
  106. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/zone_presets.py +149 -0
  107. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/runtime/zones.py +1015 -0
  108. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/service.py +1327 -0
  109. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/tools/__init__.py +3 -0
  110. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/tools/autostart.py +83 -0
  111. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/tools/color_kmeans.py +76 -0
  112. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/tools/colour_path_probe.py +44 -0
  113. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/tools/config_init.py +61 -0
  114. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/tools/diagnostic_bundle.py +188 -0
  115. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/tools/doctor.py +715 -0
  116. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/tools/flicker_lab.py +205 -0
  117. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/tools/output_format.py +34 -0
  118. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/tools/portal_tools.py +116 -0
  119. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/tools/reset.py +60 -0
  120. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/tools/setcap_helper.py +133 -0
  121. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/tools/smoke_test.py +165 -0
  122. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/__init__.py +11 -0
  123. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/calibration_flow.py +78 -0
  124. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/calibration_preview.py +186 -0
  125. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/calibration_state.py +482 -0
  126. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/calibration_widget.py +151 -0
  127. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/command_results_dialog.py +68 -0
  128. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/diagnostic_hub_dialog.py +493 -0
  129. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/display_configurator.py +1564 -0
  130. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/layout_helpers.py +99 -0
  131. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/led_color_calibration_dialog.py +114 -0
  132. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/live_diagnostics.py +437 -0
  133. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/preset_ui.py +92 -0
  134. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/qt_lazy.py +69 -0
  135. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/settings_dialog.py +2932 -0
  136. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/style.qss +209 -0
  137. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/tray_app.py +1570 -0
  138. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/zone_calibration.py +189 -0
  139. nanoleaf_kde_sync-1.9.3/src/nanoleaf_sync/ui/zone_presets.py +15 -0
  140. nanoleaf_kde_sync-1.9.3/tests/test_4d_speed_path.py +248 -0
  141. nanoleaf_kde_sync-1.9.3/tests/test_adaptive_smoothing.py +156 -0
  142. nanoleaf_kde_sync-1.9.3/tests/test_anchor_calibration.py +122 -0
  143. nanoleaf_kde_sync-1.9.3/tests/test_audit_fixes.py +137 -0
  144. nanoleaf_kde_sync-1.9.3/tests/test_audit_gaps_extended.py +70 -0
  145. nanoleaf_kde_sync-1.9.3/tests/test_audit_stage1.py +304 -0
  146. nanoleaf_kde_sync-1.9.3/tests/test_auto_probe.py +173 -0
  147. nanoleaf_kde_sync-1.9.3/tests/test_auto_probe_extended.py +188 -0
  148. nanoleaf_kde_sync-1.9.3/tests/test_autostart_tool.py +29 -0
  149. nanoleaf_kde_sync-1.9.3/tests/test_backend_diagnostics_reporting.py +35 -0
  150. nanoleaf_kde_sync-1.9.3/tests/test_calibration_flow.py +22 -0
  151. nanoleaf_kde_sync-1.9.3/tests/test_calibration_state.py +46 -0
  152. nanoleaf_kde_sync-1.9.3/tests/test_calibration_surface_consistency.py +7 -0
  153. nanoleaf_kde_sync-1.9.3/tests/test_calibration_widget.py +87 -0
  154. nanoleaf_kde_sync-1.9.3/tests/test_capture_factory_threading.py +218 -0
  155. nanoleaf_kde_sync-1.9.3/tests/test_capture_metadata.py +48 -0
  156. nanoleaf_kde_sync-1.9.3/tests/test_capture_utils.py +114 -0
  157. nanoleaf_kde_sync-1.9.3/tests/test_check_calibration_guardrails.py +87 -0
  158. nanoleaf_kde_sync-1.9.3/tests/test_check_release_versions.py +147 -0
  159. nanoleaf_kde_sync-1.9.3/tests/test_cleanup_qol_pass.py +133 -0
  160. nanoleaf_kde_sync-1.9.3/tests/test_color_accuracy_diagnostics.py +104 -0
  161. nanoleaf_kde_sync-1.9.3/tests/test_color_accuracy_pipeline.py +75 -0
  162. nanoleaf_kde_sync-1.9.3/tests/test_color_analyzer.py +213 -0
  163. nanoleaf_kde_sync-1.9.3/tests/test_color_golden_matrix.py +135 -0
  164. nanoleaf_kde_sync-1.9.3/tests/test_color_pipeline_overhaul.py +291 -0
  165. nanoleaf_kde_sync-1.9.3/tests/test_color_processing_extended.py +264 -0
  166. nanoleaf_kde_sync-1.9.3/tests/test_colour_path_diagnostics.py +164 -0
  167. nanoleaf_kde_sync-1.9.3/tests/test_compat_doctor.py +17 -0
  168. nanoleaf_kde_sync-1.9.3/tests/test_compat_errors.py +50 -0
  169. nanoleaf_kde_sync-1.9.3/tests/test_compat_kde_version.py +32 -0
  170. nanoleaf_kde_sync-1.9.3/tests/test_compat_kwin_probe.py +94 -0
  171. nanoleaf_kde_sync-1.9.3/tests/test_compat_portal_probe.py +103 -0
  172. nanoleaf_kde_sync-1.9.3/tests/test_compat_report_upstream.py +122 -0
  173. nanoleaf_kde_sync-1.9.3/tests/test_compat_update_checker.py +215 -0
  174. nanoleaf_kde_sync-1.9.3/tests/test_compat_version_snapshot.py +125 -0
  175. nanoleaf_kde_sync-1.9.3/tests/test_compositor.py +105 -0
  176. nanoleaf_kde_sync-1.9.3/tests/test_config.py +217 -0
  177. nanoleaf_kde_sync-1.9.3/tests/test_config_init.py +101 -0
  178. nanoleaf_kde_sync-1.9.3/tests/test_config_store.py +282 -0
  179. nanoleaf_kde_sync-1.9.3/tests/test_corner_anchor_calibration.py +51 -0
  180. nanoleaf_kde_sync-1.9.3/tests/test_dark_output_stability.py +313 -0
  181. nanoleaf_kde_sync-1.9.3/tests/test_desktop_entry.py +160 -0
  182. nanoleaf_kde_sync-1.9.3/tests/test_desktop_entry_extended.py +242 -0
  183. nanoleaf_kde_sync-1.9.3/tests/test_diagnostic_hub_service.py +35 -0
  184. nanoleaf_kde_sync-1.9.3/tests/test_diagnostic_hub_tools.py +37 -0
  185. nanoleaf_kde_sync-1.9.3/tests/test_diagnostics_exports_extended.py +436 -0
  186. nanoleaf_kde_sync-1.9.3/tests/test_dimensions.py +90 -0
  187. nanoleaf_kde_sync-1.9.3/tests/test_display_configurator.py +46 -0
  188. nanoleaf_kde_sync-1.9.3/tests/test_doc_paths.py +36 -0
  189. nanoleaf_kde_sync-1.9.3/tests/test_doctor.py +305 -0
  190. nanoleaf_kde_sync-1.9.3/tests/test_doctor_extended.py +596 -0
  191. nanoleaf_kde_sync-1.9.3/tests/test_drm_helper_bridge.py +90 -0
  192. nanoleaf_kde_sync-1.9.3/tests/test_errors_extended.py +106 -0
  193. nanoleaf_kde_sync-1.9.3/tests/test_factory_extended.py +324 -0
  194. nanoleaf_kde_sync-1.9.3/tests/test_first_run_onboarding.py +16 -0
  195. nanoleaf_kde_sync-1.9.3/tests/test_fps_governor.py +213 -0
  196. nanoleaf_kde_sync-1.9.3/tests/test_guided_calibration_preview.py +20 -0
  197. nanoleaf_kde_sync-1.9.3/tests/test_guided_led_calibration_workflow.py +129 -0
  198. nanoleaf_kde_sync-1.9.3/tests/test_hdr.py +169 -0
  199. nanoleaf_kde_sync-1.9.3/tests/test_hid_transport_extended.py +161 -0
  200. nanoleaf_kde_sync-1.9.3/tests/test_import_smoke_exports.py +83 -0
  201. nanoleaf_kde_sync-1.9.3/tests/test_install_helpers.py +74 -0
  202. nanoleaf_kde_sync-1.9.3/tests/test_kwin_capture_bounds.py +42 -0
  203. nanoleaf_kde_sync-1.9.3/tests/test_kwin_dbus_capture.py +751 -0
  204. nanoleaf_kde_sync-1.9.3/tests/test_kwin_probe_cache.py +63 -0
  205. nanoleaf_kde_sync-1.9.3/tests/test_latency_probe.py +72 -0
  206. nanoleaf_kde_sync-1.9.3/tests/test_led_calibration_and_spread.py +94 -0
  207. nanoleaf_kde_sync-1.9.3/tests/test_live_fps_diagnostics.py +330 -0
  208. nanoleaf_kde_sync-1.9.3/tests/test_live_sampling_mode.py +78 -0
  209. nanoleaf_kde_sync-1.9.3/tests/test_misc_coverage.py +270 -0
  210. nanoleaf_kde_sync-1.9.3/tests/test_mixed_content_sampling.py +146 -0
  211. nanoleaf_kde_sync-1.9.3/tests/test_neighbor_blend.py +46 -0
  212. nanoleaf_kde_sync-1.9.3/tests/test_normalize.py +230 -0
  213. nanoleaf_kde_sync-1.9.3/tests/test_normalize_extended.py +251 -0
  214. nanoleaf_kde_sync-1.9.3/tests/test_output_session.py +13 -0
  215. nanoleaf_kde_sync-1.9.3/tests/test_palette_adaptive_sampling.py +137 -0
  216. nanoleaf_kde_sync-1.9.3/tests/test_palette_temporal_stability.py +186 -0
  217. nanoleaf_kde_sync-1.9.3/tests/test_pipeline_integration.py +51 -0
  218. nanoleaf_kde_sync-1.9.3/tests/test_pixel_sampling_pipeline.py +212 -0
  219. nanoleaf_kde_sync-1.9.3/tests/test_predictive_sync.py +255 -0
  220. nanoleaf_kde_sync-1.9.3/tests/test_predictive_sync_pipeline.py +158 -0
  221. nanoleaf_kde_sync-1.9.3/tests/test_primaries.py +257 -0
  222. nanoleaf_kde_sync-1.9.3/tests/test_primaries_extended.py +494 -0
  223. nanoleaf_kde_sync-1.9.3/tests/test_priority_sampling_and_hid_diagnostics.py +425 -0
  224. nanoleaf_kde_sync-1.9.3/tests/test_probe_timing.py +37 -0
  225. nanoleaf_kde_sync-1.9.3/tests/test_processing.py +23 -0
  226. nanoleaf_kde_sync-1.9.3/tests/test_protocol_extended.py +121 -0
  227. nanoleaf_kde_sync-1.9.3/tests/test_readiness_check.py +476 -0
  228. nanoleaf_kde_sync-1.9.3/tests/test_readiness_check_extended.py +181 -0
  229. nanoleaf_kde_sync-1.9.3/tests/test_reference_ambient_neutral_model.py +160 -0
  230. nanoleaf_kde_sync-1.9.3/tests/test_reset_tool.py +67 -0
  231. nanoleaf_kde_sync-1.9.3/tests/test_runtime_engine.py +515 -0
  232. nanoleaf_kde_sync-1.9.3/tests/test_runtime_error_translation.py +56 -0
  233. nanoleaf_kde_sync-1.9.3/tests/test_runtime_real_driver_flow.py +158 -0
  234. nanoleaf_kde_sync-1.9.3/tests/test_runtime_startup.py +170 -0
  235. nanoleaf_kde_sync-1.9.3/tests/test_runtime_state.py +34 -0
  236. nanoleaf_kde_sync-1.9.3/tests/test_runtime_zone_presets.py +23 -0
  237. nanoleaf_kde_sync-1.9.3/tests/test_runtime_zones.py +68 -0
  238. nanoleaf_kde_sync-1.9.3/tests/test_sampling_diagnostics.py +303 -0
  239. nanoleaf_kde_sync-1.9.3/tests/test_screen_capture.py +571 -0
  240. nanoleaf_kde_sync-1.9.3/tests/test_secondary_audit_completion.py +222 -0
  241. nanoleaf_kde_sync-1.9.3/tests/test_security_regressions.py +36 -0
  242. nanoleaf_kde_sync-1.9.3/tests/test_security_regressions_extended.py +51 -0
  243. nanoleaf_kde_sync-1.9.3/tests/test_send_policy_periodic_ack.py +19 -0
  244. nanoleaf_kde_sync-1.9.3/tests/test_serialization.py +100 -0
  245. nanoleaf_kde_sync-1.9.3/tests/test_service_capture_dims.py +117 -0
  246. nanoleaf_kde_sync-1.9.3/tests/test_service_robustness.py +351 -0
  247. nanoleaf_kde_sync-1.9.3/tests/test_service_status.py +244 -0
  248. nanoleaf_kde_sync-1.9.3/tests/test_service_status_modes.py +648 -0
  249. nanoleaf_kde_sync-1.9.3/tests/test_settings_dialog.py +203 -0
  250. nanoleaf_kde_sync-1.9.3/tests/test_smoke_test.py +115 -0
  251. nanoleaf_kde_sync-1.9.3/tests/test_srgb.py +137 -0
  252. nanoleaf_kde_sync-1.9.3/tests/test_stage2_frame_context.py +75 -0
  253. nanoleaf_kde_sync-1.9.3/tests/test_stage3_color_context.py +75 -0
  254. nanoleaf_kde_sync-1.9.3/tests/test_stage4_send_policy.py +97 -0
  255. nanoleaf_kde_sync-1.9.3/tests/test_stage5_architecture.py +116 -0
  256. nanoleaf_kde_sync-1.9.3/tests/test_stage5_bundle.py +40 -0
  257. nanoleaf_kde_sync-1.9.3/tests/test_stages_3_5_completion.py +158 -0
  258. nanoleaf_kde_sync-1.9.3/tests/test_synthetic_clips.py +78 -0
  259. nanoleaf_kde_sync-1.9.3/tests/test_tools_autostart.py +22 -0
  260. nanoleaf_kde_sync-1.9.3/tests/test_tray_async_tools.py +26 -0
  261. nanoleaf_kde_sync-1.9.3/tests/test_tray_menu_structure.py +63 -0
  262. nanoleaf_kde_sync-1.9.3/tests/test_tray_mode_labels.py +24 -0
  263. nanoleaf_kde_sync-1.9.3/tests/test_tray_preview_recovery.py +215 -0
  264. nanoleaf_kde_sync-1.9.3/tests/test_tray_preview_resume.py +87 -0
  265. nanoleaf_kde_sync-1.9.3/tests/test_tray_quit_async.py +308 -0
  266. nanoleaf_kde_sync-1.9.3/tests/test_tray_restart_output_guard.py +126 -0
  267. nanoleaf_kde_sync-1.9.3/tests/test_tray_self_check.py +52 -0
  268. nanoleaf_kde_sync-1.9.3/tests/test_tray_settings_apply.py +394 -0
  269. nanoleaf_kde_sync-1.9.3/tests/test_tray_start_robustness.py +182 -0
  270. nanoleaf_kde_sync-1.9.3/tests/test_ui_headless.py +131 -0
  271. nanoleaf_kde_sync-1.9.3/tests/test_wizard_and_settings_structure.py +78 -0
  272. nanoleaf_kde_sync-1.9.3/tests/test_xdg_portal_colour_path.py +263 -0
  273. nanoleaf_kde_sync-1.9.3/tests/test_xdg_portal_robustness.py +426 -0
  274. nanoleaf_kde_sync-1.9.3/tests/test_zone_calibration.py +27 -0
  275. nanoleaf_kde_sync-1.9.3/tests/test_zone_derivation.py +87 -0
  276. nanoleaf_kde_sync-1.9.3/tests/test_zone_mapper.py +37 -0
  277. nanoleaf_kde_sync-1.9.3/tests/test_zone_presets.py +49 -0
@@ -0,0 +1,27 @@
1
+ Nanoleaf Source-Available Non-Commercial License
2
+ Copyright (c) 2026 SpinGiantCRM
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to use,
6
+ copy, modify, merge, and distribute the Software, subject to the following
7
+ conditions:
8
+
9
+ 1. The above copyright notice and this permission notice shall be included in all
10
+ copies or substantial portions of the Software.
11
+
12
+ 2. Redistribution of the Software must be at no charge.
13
+
14
+ 3. Any use, distribution, sublicensing, hosting, bundling, sale, or other
15
+ activity involving the Software that is intended for or directed toward
16
+ commercial advantage or monetary compensation is prohibited without prior
17
+ written permission from the copyright holder.
18
+
19
+ 4. All commercial rights are reserved to the copyright holder.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ include VERSION
2
+ graft assets
3
+ recursive-include src/nanoleaf_sync/ui *.qss
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.4
2
+ Name: nanoleaf-kde-sync
3
+ Version: 1.9.3
4
+ Summary: Nanoleaf screen mirroring for KDE Plasma on Linux.
5
+ License-Expression: LicenseRef-Nanoleaf-Source-Available-Non-Commercial
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: numpy<3,>=1.21
10
+ Requires-Dist: PyQt6<7,>=6.0
11
+ Requires-Dist: dbus-next<1,>=0.2.0
12
+ Requires-Dist: hidapi<1,>=0.14.0
13
+ Requires-Dist: dacite<2,>=1.8.1
14
+ Requires-Dist: tomli-w<2,>=1.0.0
15
+ Requires-Dist: packaging<26,>=24.0
16
+ Provides-Extra: test
17
+ Requires-Dist: pytest>=8; extra == "test"
18
+ Requires-Dist: pytest-cov>=5; extra == "test"
19
+ Requires-Dist: pytest-timeout>=2.3; extra == "test"
20
+ Requires-Dist: ruff>=0.6; extra == "test"
21
+ Requires-Dist: mypy>=1.0; extra == "test"
22
+ Requires-Dist: pre-commit>=4.0; extra == "test"
23
+ Provides-Extra: portal
24
+ Requires-Dist: pipewire; extra == "portal"
25
+ Provides-Extra: gstreamer
26
+ Requires-Dist: pygobject; extra == "gstreamer"
27
+ Dynamic: license-file
28
+
29
+ # Nanoleaf Screen Mirror for KDE
30
+
31
+ Public Nanoleaf USB screen mirroring app for KDE Plasma 6 on Linux (Arch / CachyOS and other distros via source install).
32
+
33
+ Mirrors your display edge colors to supported Nanoleaf USB strips in real time, with a tray app, setup wizard, calibration, and diagnostics.
34
+
35
+ ## What you need
36
+
37
+ - Linux with KDE Plasma 6 (Wayland recommended)
38
+ - A supported Nanoleaf USB strip: `NL82K1` (`0x37fa:0x8201`) or `NL82K2` (`0x37fa:0x8202`)
39
+ - USB permissions via udev (see [Hardware setup](docs/HARDWARE_SETUP.md))
40
+
41
+ ## Install (Arch / CachyOS)
42
+
43
+ **When published on AUR:**
44
+
45
+ ```bash
46
+ paru -S --needed python-dacite nanoleaf-kde-sync
47
+ paru -Syu
48
+ ```
49
+
50
+ **Until AUR account is available — local build from this repo:**
51
+
52
+ ```bash
53
+ paru -S --needed python-dacite
54
+ ./scripts/build_arch_package.sh
55
+ ```
56
+
57
+ Install udev rules after package install:
58
+
59
+ ```bash
60
+ ./scripts/setup_udev.sh
61
+ ```
62
+
63
+ ## Quick start
64
+
65
+ ```bash
66
+ nanoleaf-kde-sync-init-config
67
+ nanoleaf-kde-sync-doctor
68
+ nanoleaf-kde-sync-smoke-test
69
+ nanoleaf-kde-sync
70
+ ```
71
+
72
+ Service-only mode:
73
+
74
+ ```bash
75
+ nanoleaf-kde-sync-service
76
+ ```
77
+
78
+ ## Core commands
79
+
80
+ - `nanoleaf-kde-sync` — tray app (recommended)
81
+ - `nanoleaf-kde-sync-service` — headless runtime service
82
+ - `nanoleaf-kde-sync-init-config` — generate default config
83
+ - `nanoleaf-kde-sync-doctor` — environment/device diagnostics
84
+ - `nanoleaf-kde-sync-smoke-test` — quick functional sanity check
85
+ - `nanoleaf-kde-sync-autostart` — manage KDE autostart integration
86
+ - `nanoleaf-kde-sync-reset` — reset config/calibration/diagnostic cache safely
87
+
88
+ ## First-run setup
89
+
90
+ 1. Start the tray app (`nanoleaf-kde-sync`).
91
+ 2. Complete the setup wizard.
92
+ 3. Keep **manual strip zone count** set to your real hardware value.
93
+ 4. Run a calibration test pattern and assign TL/TR/BR/BL anchors.
94
+
95
+ Manual strip count is authoritative for runtime, mapping, and calibration. Device-reported count is diagnostics-only unless you explicitly apply a new value.
96
+
97
+ See the [User guide](docs/USER_GUIDE.md) for a full walkthrough.
98
+
99
+ ## HDR / SDR notes
100
+
101
+ - `Display preset = SDR`: SDR-safe defaults.
102
+ - `Display preset = HDR`: HDR-first defaults.
103
+ - `Display preset = Auto`: follows compositor capability.
104
+
105
+ On Plasma HDR desktops, verify SDR white reference and compositor HDR settings in app diagnostics before tuning brightness.
106
+
107
+ ## Fresh install / reinstall / uninstall helpers
108
+
109
+ Pacman-managed reinstall from a local checkout:
110
+
111
+ ```bash
112
+ ./scripts/reinstall_local.sh
113
+ ./scripts/uninstall_local.sh
114
+ # optional full purge:
115
+ ./scripts/uninstall_local.sh --purge-config
116
+ ```
117
+
118
+ See [Arch / AUR packaging](docs/PACKAGING_AUR.md) for maintainer and AUR publish steps.
119
+
120
+ ## Reset commands
121
+
122
+ ```bash
123
+ nanoleaf-kde-sync-reset app-config --stop-runtime
124
+ nanoleaf-kde-sync-reset calibration --stop-runtime
125
+ nanoleaf-kde-sync-reset diagnostics --stop-runtime
126
+ ```
127
+
128
+ ## Documentation
129
+
130
+ - [User guide](docs/USER_GUIDE.md)
131
+ - [Hardware setup](docs/HARDWARE_SETUP.md)
132
+ - [Troubleshooting](docs/TROUBLESHOOTING.md)
133
+ - [Smoke test](docs/SMOKE_TEST.md)
134
+ - [Security](docs/SECURITY.md)
135
+ - [Contributing](CONTRIBUTING.md)
136
+
137
+ Installed packages also ship docs under `/usr/share/doc/nanoleaf-kde-sync/`.
138
+
139
+ ## Release gate
140
+
141
+ Run `./scripts/release_gate.sh` before tagging a release. CI must be green on `main`.
142
+
143
+ ## Developer setup
144
+
145
+ ```bash
146
+ python -m venv .venv
147
+ source .venv/bin/activate
148
+ pip install -e .[test]
149
+ pre-commit install
150
+ pre-commit run --all-files
151
+ ```
152
+
153
+ ## Supported environment
154
+
155
+ - Linux
156
+ - KDE Plasma 6 (Wayland recommended)
157
+ - Supported Nanoleaf USB strips: `NL82K1` (`0x37fa:0x8201`), `NL82K2` (`0x37fa:0x8202`)
158
+
159
+ ## Known limitations
160
+
161
+ - Single-monitor flow only (no multi-monitor support).
162
+ - Device strip count auto-detection is diagnostics-only; not auto-applied.
163
+ - Desktop-entry launch context is still preferred for reliable KWin authorization.
164
+
165
+ ## License
166
+
167
+ Source-available non-commercial license — see [LICENSE](LICENSE). Redistribution terms apply; read before packaging or mirroring.
@@ -0,0 +1,139 @@
1
+ # Nanoleaf Screen Mirror for KDE
2
+
3
+ Public Nanoleaf USB screen mirroring app for KDE Plasma 6 on Linux (Arch / CachyOS and other distros via source install).
4
+
5
+ Mirrors your display edge colors to supported Nanoleaf USB strips in real time, with a tray app, setup wizard, calibration, and diagnostics.
6
+
7
+ ## What you need
8
+
9
+ - Linux with KDE Plasma 6 (Wayland recommended)
10
+ - A supported Nanoleaf USB strip: `NL82K1` (`0x37fa:0x8201`) or `NL82K2` (`0x37fa:0x8202`)
11
+ - USB permissions via udev (see [Hardware setup](docs/HARDWARE_SETUP.md))
12
+
13
+ ## Install (Arch / CachyOS)
14
+
15
+ **When published on AUR:**
16
+
17
+ ```bash
18
+ paru -S --needed python-dacite nanoleaf-kde-sync
19
+ paru -Syu
20
+ ```
21
+
22
+ **Until AUR account is available — local build from this repo:**
23
+
24
+ ```bash
25
+ paru -S --needed python-dacite
26
+ ./scripts/build_arch_package.sh
27
+ ```
28
+
29
+ Install udev rules after package install:
30
+
31
+ ```bash
32
+ ./scripts/setup_udev.sh
33
+ ```
34
+
35
+ ## Quick start
36
+
37
+ ```bash
38
+ nanoleaf-kde-sync-init-config
39
+ nanoleaf-kde-sync-doctor
40
+ nanoleaf-kde-sync-smoke-test
41
+ nanoleaf-kde-sync
42
+ ```
43
+
44
+ Service-only mode:
45
+
46
+ ```bash
47
+ nanoleaf-kde-sync-service
48
+ ```
49
+
50
+ ## Core commands
51
+
52
+ - `nanoleaf-kde-sync` — tray app (recommended)
53
+ - `nanoleaf-kde-sync-service` — headless runtime service
54
+ - `nanoleaf-kde-sync-init-config` — generate default config
55
+ - `nanoleaf-kde-sync-doctor` — environment/device diagnostics
56
+ - `nanoleaf-kde-sync-smoke-test` — quick functional sanity check
57
+ - `nanoleaf-kde-sync-autostart` — manage KDE autostart integration
58
+ - `nanoleaf-kde-sync-reset` — reset config/calibration/diagnostic cache safely
59
+
60
+ ## First-run setup
61
+
62
+ 1. Start the tray app (`nanoleaf-kde-sync`).
63
+ 2. Complete the setup wizard.
64
+ 3. Keep **manual strip zone count** set to your real hardware value.
65
+ 4. Run a calibration test pattern and assign TL/TR/BR/BL anchors.
66
+
67
+ Manual strip count is authoritative for runtime, mapping, and calibration. Device-reported count is diagnostics-only unless you explicitly apply a new value.
68
+
69
+ See the [User guide](docs/USER_GUIDE.md) for a full walkthrough.
70
+
71
+ ## HDR / SDR notes
72
+
73
+ - `Display preset = SDR`: SDR-safe defaults.
74
+ - `Display preset = HDR`: HDR-first defaults.
75
+ - `Display preset = Auto`: follows compositor capability.
76
+
77
+ On Plasma HDR desktops, verify SDR white reference and compositor HDR settings in app diagnostics before tuning brightness.
78
+
79
+ ## Fresh install / reinstall / uninstall helpers
80
+
81
+ Pacman-managed reinstall from a local checkout:
82
+
83
+ ```bash
84
+ ./scripts/reinstall_local.sh
85
+ ./scripts/uninstall_local.sh
86
+ # optional full purge:
87
+ ./scripts/uninstall_local.sh --purge-config
88
+ ```
89
+
90
+ See [Arch / AUR packaging](docs/PACKAGING_AUR.md) for maintainer and AUR publish steps.
91
+
92
+ ## Reset commands
93
+
94
+ ```bash
95
+ nanoleaf-kde-sync-reset app-config --stop-runtime
96
+ nanoleaf-kde-sync-reset calibration --stop-runtime
97
+ nanoleaf-kde-sync-reset diagnostics --stop-runtime
98
+ ```
99
+
100
+ ## Documentation
101
+
102
+ - [User guide](docs/USER_GUIDE.md)
103
+ - [Hardware setup](docs/HARDWARE_SETUP.md)
104
+ - [Troubleshooting](docs/TROUBLESHOOTING.md)
105
+ - [Smoke test](docs/SMOKE_TEST.md)
106
+ - [Security](docs/SECURITY.md)
107
+ - [Contributing](CONTRIBUTING.md)
108
+
109
+ Installed packages also ship docs under `/usr/share/doc/nanoleaf-kde-sync/`.
110
+
111
+ ## Release gate
112
+
113
+ Run `./scripts/release_gate.sh` before tagging a release. CI must be green on `main`.
114
+
115
+ ## Developer setup
116
+
117
+ ```bash
118
+ python -m venv .venv
119
+ source .venv/bin/activate
120
+ pip install -e .[test]
121
+ pre-commit install
122
+ pre-commit run --all-files
123
+ ```
124
+
125
+ ## Supported environment
126
+
127
+ - Linux
128
+ - KDE Plasma 6 (Wayland recommended)
129
+ - Supported Nanoleaf USB strips: `NL82K1` (`0x37fa:0x8201`), `NL82K2` (`0x37fa:0x8202`)
130
+
131
+ ## Known limitations
132
+
133
+ - Single-monitor flow only (no multi-monitor support).
134
+ - Device strip count auto-detection is diagnostics-only; not auto-applied.
135
+ - Desktop-entry launch context is still preferred for reliable KWin authorization.
136
+
137
+ ## License
138
+
139
+ Source-available non-commercial license — see [LICENSE](LICENSE). Redistribution terms apply; read before packaging or mirroring.
@@ -0,0 +1 @@
1
+ 1.9.3
@@ -0,0 +1,21 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" role="img" aria-label="nanoleaf-kde-sync">
2
+ <defs>
3
+ <linearGradient id="brand" x1="0" y1="0" x2="1" y2="1">
4
+ <stop offset="0" stop-color="#3ddc97"/>
5
+ <stop offset="1" stop-color="#30a7ff"/>
6
+ </linearGradient>
7
+ <linearGradient id="screen" x1="0" y1="0" x2="1" y2="0">
8
+ <stop offset="0" stop-color="#e05c5c" stop-opacity="0.82"/>
9
+ <stop offset="0.5" stop-color="#3ddc97" stop-opacity="0.82"/>
10
+ <stop offset="1" stop-color="#30a7ff" stop-opacity="0.82"/>
11
+ </linearGradient>
12
+ </defs>
13
+ <rect x="4" y="4" width="120" height="120" rx="22" fill="#1c202e"/>
14
+ <rect x="12" y="14" width="104" height="70" rx="9" fill="none" stroke="url(#brand)" stroke-width="4.5"/>
15
+ <rect x="19" y="21" width="90" height="56" rx="5" fill="url(#screen)"/>
16
+ <rect x="55" y="84" width="18" height="9" rx="4" fill="#3ddc97" opacity="0.75"/>
17
+ <rect x="40" y="93" width="48" height="7" rx="3.5" fill="#3ddc97" opacity="0.6"/>
18
+ <rect x="12" y="109" width="28" height="14" rx="7" fill="#e05c5c" opacity="0.92"/>
19
+ <rect x="50" y="109" width="28" height="14" rx="7" fill="#3ddc97" opacity="0.92"/>
20
+ <rect x="88" y="109" width="28" height="14" rx="7" fill="#30a7ff" opacity="0.92"/>
21
+ </svg>
@@ -0,0 +1,7 @@
1
+ # Nanoleaf USB Screen Mirror Light Strip and Pegboard Desk Dock
2
+ # Gives local users access via the `plugdev` group.
3
+ SUBSYSTEM=="hidraw", ATTRS{idVendor}=="37fa", ATTRS{idProduct}=="8201", MODE="0660", GROUP="plugdev", TAG+="uaccess"
4
+ SUBSYSTEM=="hidraw", ATTRS{idVendor}=="37fa", ATTRS{idProduct}=="8202", MODE="0660", GROUP="plugdev", TAG+="uaccess"
5
+
6
+ # Disable USB autosuspend so HID resume latency does not drop frames at 60 FPS.
7
+ ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="37fa", ATTR{idProduct}=="8201|8202", ATTR{power/control}="on"
@@ -0,0 +1,129 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "nanoleaf-kde-sync"
7
+ dynamic = ["version"]
8
+ description = "Nanoleaf screen mirroring for KDE Plasma on Linux."
9
+ readme = "README.md"
10
+ license = "LicenseRef-Nanoleaf-Source-Available-Non-Commercial"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.11"
13
+ dependencies = [
14
+ "numpy>=1.21,<3",
15
+ "PyQt6>=6.0,<7",
16
+ "dbus-next>=0.2.0,<1",
17
+ "hidapi>=0.14.0,<1",
18
+ "dacite>=1.8.1,<2",
19
+ "tomli-w>=1.0.0,<2",
20
+ "packaging>=24.0,<26",
21
+ ]
22
+
23
+ [project.optional-dependencies]
24
+ test = [
25
+ "pytest>=8",
26
+ "pytest-cov>=5",
27
+ "pytest-timeout>=2.3",
28
+ "ruff>=0.6",
29
+ "mypy>=1.0",
30
+ "pre-commit>=4.0",
31
+ ]
32
+ portal = [
33
+ "pipewire",
34
+ ]
35
+ gstreamer = [
36
+ "pygobject",
37
+ ]
38
+
39
+ [project.scripts]
40
+ nanoleaf-kde-sync = "nanoleaf_sync.ui.tray_app:main"
41
+ nanoleaf-kde-sync-service = "nanoleaf_sync.service:main"
42
+ nanoleaf-kde-sync-doctor = "nanoleaf_sync.tools.doctor:main"
43
+ nanoleaf-kde-sync-smoke-test = "nanoleaf_sync.tools.smoke_test:main"
44
+ nanoleaf-kde-sync-init-config = "nanoleaf_sync.tools.config_init:main"
45
+ nanoleaf-kde-sync-autostart = "nanoleaf_sync.tools.autostart:main"
46
+ nanoleaf-kde-sync-reset = "nanoleaf_sync.tools.reset:main"
47
+
48
+
49
+ [tool.setuptools.dynamic]
50
+ version = {file = ["VERSION"]}
51
+ [tool.setuptools]
52
+ package-dir = {"" = "src"}
53
+
54
+ [tool.setuptools.packages.find]
55
+ where = ["src"]
56
+ include = ["nanoleaf_sync*"]
57
+
58
+ [tool.setuptools.package-data]
59
+ nanoleaf_sync = [
60
+ "VERSION",
61
+ "ui/style.qss",
62
+ "assets/icons/**/*",
63
+ "assets/udev/60-nanoleaf-kde-sync.rules",
64
+ "capture/nanoleaf_drm_helper",
65
+ ]
66
+
67
+ [tool.pytest.ini_options]
68
+ testpaths = ["tests"]
69
+ pythonpath = ["src"]
70
+
71
+ [tool.coverage.run]
72
+ omit = [
73
+ "src/nanoleaf_sync/ui/settings_dialog.py",
74
+ "src/nanoleaf_sync/ui/display_configurator.py",
75
+ "src/nanoleaf_sync/ui/live_diagnostics.py",
76
+ "src/nanoleaf_sync/ui/led_color_calibration_dialog.py",
77
+ ]
78
+
79
+ [tool.ruff]
80
+ line-length = 100
81
+ target-version = "py311"
82
+
83
+ [tool.ruff.lint]
84
+ select = ["E", "F", "I", "N", "W", "UP", "B", "SIM"]
85
+
86
+ [tool.ruff.lint.per-file-ignores]
87
+ "tests/**" = ["N802", "N803", "N806"]
88
+ "src/nanoleaf_sync/tools/doctor.py" = ["E402"]
89
+ "src/nanoleaf_sync/tools/smoke_test.py" = ["E402"]
90
+ "src/nanoleaf_sync/tools/autostart.py" = ["E402"]
91
+ "src/nanoleaf_sync/capture/kwin_dbus.py" = ["E402"]
92
+ "src/nanoleaf_sync/config/store.py" = ["E402"]
93
+ "src/nanoleaf_sync/device/hid_transport.py" = ["E402"]
94
+ "src/nanoleaf_sync/ui/settings_dialog.py" = ["E402", "N802", "N803", "N806"]
95
+ "src/nanoleaf_sync/ui/display_configurator.py" = ["N802", "N803", "N806"]
96
+ "src/nanoleaf_sync/ui/calibration_widget.py" = ["N806"]
97
+ "src/nanoleaf_sync/ui/led_color_calibration_dialog.py" = ["N806"]
98
+ "src/nanoleaf_sync/capture/_drm_zone_sampler.py" = ["N802", "N806"]
99
+ "src/nanoleaf_sync/color/hdr.py" = ["N806"]
100
+ "src/nanoleaf_sync/color/primaries.py" = ["N806"]
101
+
102
+ [tool.ruff.format]
103
+ line-ending = "lf"
104
+
105
+ [tool.mypy]
106
+ python_version = "3.12"
107
+ ignore_missing_imports = true
108
+ warn_redundant_casts = true
109
+ warn_unused_ignores = true
110
+ exclude = ["src/nanoleaf_sync/ui/"]
111
+ files = ["src/nanoleaf_sync"]
112
+
113
+ [[tool.mypy.overrides]]
114
+ module = [
115
+ "nanoleaf_sync.capture.*",
116
+ "nanoleaf_sync.runtime.*",
117
+ "nanoleaf_sync.device.*",
118
+ "nanoleaf_sync.service",
119
+ "nanoleaf_sync.desktop_entry",
120
+ "nanoleaf_sync.color.hdr",
121
+ "nanoleaf_sync.color.zone_mapper",
122
+ "nanoleaf_sync.tools.portal_tools",
123
+ "nanoleaf_sync.tools.color_kmeans",
124
+ ]
125
+ ignore_errors = true
126
+
127
+ [tool.bandit]
128
+ exclude_dirs = ["tests", "build", "dist", ".venv"]
129
+ skips = ["B101", "B108", "B110", "B311", "B324", "B404", "B603", "B607", "B608"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.4
2
+ Name: nanoleaf-kde-sync
3
+ Version: 1.9.3
4
+ Summary: Nanoleaf screen mirroring for KDE Plasma on Linux.
5
+ License-Expression: LicenseRef-Nanoleaf-Source-Available-Non-Commercial
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: numpy<3,>=1.21
10
+ Requires-Dist: PyQt6<7,>=6.0
11
+ Requires-Dist: dbus-next<1,>=0.2.0
12
+ Requires-Dist: hidapi<1,>=0.14.0
13
+ Requires-Dist: dacite<2,>=1.8.1
14
+ Requires-Dist: tomli-w<2,>=1.0.0
15
+ Requires-Dist: packaging<26,>=24.0
16
+ Provides-Extra: test
17
+ Requires-Dist: pytest>=8; extra == "test"
18
+ Requires-Dist: pytest-cov>=5; extra == "test"
19
+ Requires-Dist: pytest-timeout>=2.3; extra == "test"
20
+ Requires-Dist: ruff>=0.6; extra == "test"
21
+ Requires-Dist: mypy>=1.0; extra == "test"
22
+ Requires-Dist: pre-commit>=4.0; extra == "test"
23
+ Provides-Extra: portal
24
+ Requires-Dist: pipewire; extra == "portal"
25
+ Provides-Extra: gstreamer
26
+ Requires-Dist: pygobject; extra == "gstreamer"
27
+ Dynamic: license-file
28
+
29
+ # Nanoleaf Screen Mirror for KDE
30
+
31
+ Public Nanoleaf USB screen mirroring app for KDE Plasma 6 on Linux (Arch / CachyOS and other distros via source install).
32
+
33
+ Mirrors your display edge colors to supported Nanoleaf USB strips in real time, with a tray app, setup wizard, calibration, and diagnostics.
34
+
35
+ ## What you need
36
+
37
+ - Linux with KDE Plasma 6 (Wayland recommended)
38
+ - A supported Nanoleaf USB strip: `NL82K1` (`0x37fa:0x8201`) or `NL82K2` (`0x37fa:0x8202`)
39
+ - USB permissions via udev (see [Hardware setup](docs/HARDWARE_SETUP.md))
40
+
41
+ ## Install (Arch / CachyOS)
42
+
43
+ **When published on AUR:**
44
+
45
+ ```bash
46
+ paru -S --needed python-dacite nanoleaf-kde-sync
47
+ paru -Syu
48
+ ```
49
+
50
+ **Until AUR account is available — local build from this repo:**
51
+
52
+ ```bash
53
+ paru -S --needed python-dacite
54
+ ./scripts/build_arch_package.sh
55
+ ```
56
+
57
+ Install udev rules after package install:
58
+
59
+ ```bash
60
+ ./scripts/setup_udev.sh
61
+ ```
62
+
63
+ ## Quick start
64
+
65
+ ```bash
66
+ nanoleaf-kde-sync-init-config
67
+ nanoleaf-kde-sync-doctor
68
+ nanoleaf-kde-sync-smoke-test
69
+ nanoleaf-kde-sync
70
+ ```
71
+
72
+ Service-only mode:
73
+
74
+ ```bash
75
+ nanoleaf-kde-sync-service
76
+ ```
77
+
78
+ ## Core commands
79
+
80
+ - `nanoleaf-kde-sync` — tray app (recommended)
81
+ - `nanoleaf-kde-sync-service` — headless runtime service
82
+ - `nanoleaf-kde-sync-init-config` — generate default config
83
+ - `nanoleaf-kde-sync-doctor` — environment/device diagnostics
84
+ - `nanoleaf-kde-sync-smoke-test` — quick functional sanity check
85
+ - `nanoleaf-kde-sync-autostart` — manage KDE autostart integration
86
+ - `nanoleaf-kde-sync-reset` — reset config/calibration/diagnostic cache safely
87
+
88
+ ## First-run setup
89
+
90
+ 1. Start the tray app (`nanoleaf-kde-sync`).
91
+ 2. Complete the setup wizard.
92
+ 3. Keep **manual strip zone count** set to your real hardware value.
93
+ 4. Run a calibration test pattern and assign TL/TR/BR/BL anchors.
94
+
95
+ Manual strip count is authoritative for runtime, mapping, and calibration. Device-reported count is diagnostics-only unless you explicitly apply a new value.
96
+
97
+ See the [User guide](docs/USER_GUIDE.md) for a full walkthrough.
98
+
99
+ ## HDR / SDR notes
100
+
101
+ - `Display preset = SDR`: SDR-safe defaults.
102
+ - `Display preset = HDR`: HDR-first defaults.
103
+ - `Display preset = Auto`: follows compositor capability.
104
+
105
+ On Plasma HDR desktops, verify SDR white reference and compositor HDR settings in app diagnostics before tuning brightness.
106
+
107
+ ## Fresh install / reinstall / uninstall helpers
108
+
109
+ Pacman-managed reinstall from a local checkout:
110
+
111
+ ```bash
112
+ ./scripts/reinstall_local.sh
113
+ ./scripts/uninstall_local.sh
114
+ # optional full purge:
115
+ ./scripts/uninstall_local.sh --purge-config
116
+ ```
117
+
118
+ See [Arch / AUR packaging](docs/PACKAGING_AUR.md) for maintainer and AUR publish steps.
119
+
120
+ ## Reset commands
121
+
122
+ ```bash
123
+ nanoleaf-kde-sync-reset app-config --stop-runtime
124
+ nanoleaf-kde-sync-reset calibration --stop-runtime
125
+ nanoleaf-kde-sync-reset diagnostics --stop-runtime
126
+ ```
127
+
128
+ ## Documentation
129
+
130
+ - [User guide](docs/USER_GUIDE.md)
131
+ - [Hardware setup](docs/HARDWARE_SETUP.md)
132
+ - [Troubleshooting](docs/TROUBLESHOOTING.md)
133
+ - [Smoke test](docs/SMOKE_TEST.md)
134
+ - [Security](docs/SECURITY.md)
135
+ - [Contributing](CONTRIBUTING.md)
136
+
137
+ Installed packages also ship docs under `/usr/share/doc/nanoleaf-kde-sync/`.
138
+
139
+ ## Release gate
140
+
141
+ Run `./scripts/release_gate.sh` before tagging a release. CI must be green on `main`.
142
+
143
+ ## Developer setup
144
+
145
+ ```bash
146
+ python -m venv .venv
147
+ source .venv/bin/activate
148
+ pip install -e .[test]
149
+ pre-commit install
150
+ pre-commit run --all-files
151
+ ```
152
+
153
+ ## Supported environment
154
+
155
+ - Linux
156
+ - KDE Plasma 6 (Wayland recommended)
157
+ - Supported Nanoleaf USB strips: `NL82K1` (`0x37fa:0x8201`), `NL82K2` (`0x37fa:0x8202`)
158
+
159
+ ## Known limitations
160
+
161
+ - Single-monitor flow only (no multi-monitor support).
162
+ - Device strip count auto-detection is diagnostics-only; not auto-applied.
163
+ - Desktop-entry launch context is still preferred for reliable KWin authorization.
164
+
165
+ ## License
166
+
167
+ Source-available non-commercial license — see [LICENSE](LICENSE). Redistribution terms apply; read before packaging or mirroring.