unifi-network-maps 1.4.5__tar.gz → 1.4.7__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 (131) hide show
  1. unifi_network_maps-1.4.7/CHANGELOG.md +169 -0
  2. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/CONTRIBUTING.md +1 -1
  3. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/PKG-INFO +7 -2
  4. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/README.md +5 -0
  5. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/pyproject.toml +2 -2
  6. unifi_network_maps-1.4.7/src/unifi_network_maps/__init__.py +1 -0
  7. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/cli/main.py +5 -1
  8. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/svg.py +55 -3
  9. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps.egg-info/PKG-INFO +7 -2
  10. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps.egg-info/requires.txt +1 -1
  11. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_svg.py +13 -0
  12. unifi_network_maps-1.4.5/CHANGELOG.md +0 -93
  13. unifi_network_maps-1.4.5/src/unifi_network_maps/__init__.py +0 -1
  14. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/LICENSE +0 -0
  15. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/LICENSES.md +0 -0
  16. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/MANIFEST.in +0 -0
  17. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/RELEASING.md +0 -0
  18. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/SECURITY.md +0 -0
  19. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/setup.cfg +0 -0
  20. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/__main__.py +0 -0
  21. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/adapters/__init__.py +0 -0
  22. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/adapters/config.py +0 -0
  23. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/adapters/unifi.py +0 -0
  24. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/__init__.py +0 -0
  25. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/__init__.py +0 -0
  26. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/access-point.svg +0 -0
  27. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/ISOPACKS_LICENSE +0 -0
  28. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/block.svg +0 -0
  29. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/cache.svg +0 -0
  30. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/cardterminal.svg +0 -0
  31. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/cloud.svg +0 -0
  32. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/cronjob.svg +0 -0
  33. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/cube.svg +0 -0
  34. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/desktop.svg +0 -0
  35. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/diamond.svg +0 -0
  36. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/dns.svg +0 -0
  37. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/document.svg +0 -0
  38. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/firewall.svg +0 -0
  39. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/function-module.svg +0 -0
  40. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/image.svg +0 -0
  41. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/laptop.svg +0 -0
  42. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/loadbalancer.svg +0 -0
  43. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/lock.svg +0 -0
  44. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/mail.svg +0 -0
  45. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/mailmultiple.svg +0 -0
  46. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/mobiledevice.svg +0 -0
  47. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/office.svg +0 -0
  48. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/package-module.svg +0 -0
  49. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/paymentcard.svg +0 -0
  50. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/plane.svg +0 -0
  51. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/printer.svg +0 -0
  52. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/pyramid.svg +0 -0
  53. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/queue.svg +0 -0
  54. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/router.svg +0 -0
  55. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/server.svg +0 -0
  56. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/speech.svg +0 -0
  57. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/sphere.svg +0 -0
  58. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/storage.svg +0 -0
  59. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/switch-module.svg +0 -0
  60. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/tower.svg +0 -0
  61. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/truck-2.svg +0 -0
  62. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/truck.svg +0 -0
  63. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/user.svg +0 -0
  64. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/isometric/vm.svg +0 -0
  65. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/laptop.svg +0 -0
  66. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/router-network.svg +0 -0
  67. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/server-network.svg +0 -0
  68. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/icons/server.svg +0 -0
  69. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/themes/dark.yaml +0 -0
  70. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/assets/themes/default.yaml +0 -0
  71. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/cli/__init__.py +0 -0
  72. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/cli/__main__.py +0 -0
  73. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/cli/args.py +0 -0
  74. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/cli/render.py +0 -0
  75. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/cli/runtime.py +0 -0
  76. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/io/__init__.py +0 -0
  77. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/io/debug.py +0 -0
  78. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/io/export.py +0 -0
  79. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/io/mkdocs_assets.py +0 -0
  80. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/io/mock_data.py +0 -0
  81. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/io/mock_generate.py +0 -0
  82. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/model/__init__.py +0 -0
  83. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/model/labels.py +0 -0
  84. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/model/lldp.py +0 -0
  85. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/model/mock.py +0 -0
  86. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/model/ports.py +0 -0
  87. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/model/topology.py +0 -0
  88. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/__init__.py +0 -0
  89. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/device_ports_md.py +0 -0
  90. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/legend.py +0 -0
  91. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/lldp_md.py +0 -0
  92. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/markdown_tables.py +0 -0
  93. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/mermaid.py +0 -0
  94. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/mermaid_theme.py +0 -0
  95. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/mkdocs.py +0 -0
  96. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/svg_theme.py +0 -0
  97. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/templates/device_port_block.md.j2 +0 -0
  98. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/templates/legend_compact.html.j2 +0 -0
  99. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/templates/lldp_device_section.md.j2 +0 -0
  100. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/templates/markdown_section.md.j2 +0 -0
  101. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/templates/mermaid_legend.mmd.j2 +0 -0
  102. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/templates/mkdocs_document.md.j2 +0 -0
  103. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/templates/mkdocs_dual_theme_style.html.j2 +0 -0
  104. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/templates/mkdocs_html_block.html.j2 +0 -0
  105. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/templates/mkdocs_legend.css.j2 +0 -0
  106. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/templates/mkdocs_legend.js.j2 +0 -0
  107. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/templates/mkdocs_mermaid_block.md.j2 +0 -0
  108. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/templating.py +0 -0
  109. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps/render/theme.py +0 -0
  110. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps.egg-info/SOURCES.txt +0 -0
  111. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps.egg-info/dependency_links.txt +0 -0
  112. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps.egg-info/entry_points.txt +0 -0
  113. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/src/unifi_network_maps.egg-info/top_level.txt +0 -0
  114. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_cli.py +0 -0
  115. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_clients.py +0 -0
  116. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_config.py +0 -0
  117. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_contract_unifi.py +0 -0
  118. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_contract_unifi_live.py +0 -0
  119. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_debug.py +0 -0
  120. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_device_ports_md.py +0 -0
  121. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_export.py +0 -0
  122. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_groups.py +0 -0
  123. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_labels.py +0 -0
  124. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_lldp.py +0 -0
  125. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_lldp_md.py +0 -0
  126. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_mermaid.py +0 -0
  127. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_mock_generate.py +0 -0
  128. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_svg_iso.py +0 -0
  129. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_theme.py +0 -0
  130. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_topology.py +0 -0
  131. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.7}/tests/test_unifi.py +0 -0
@@ -0,0 +1,169 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.4.7] - 2026-01-15
9
+ ### Changed
10
+ - Merged PR #8: https://github.com/merlijntishauser/unifi-network-maps/pull/8
11
+
12
+ ## [1.4.6] - 2026-01-15
13
+ ### Added
14
+ - Home Assistant docs pointing to the standalone integration repo.
15
+
16
+ ### Changed
17
+ - Home Assistant integration work moved to `unifi-network-maps-ha`; core repo focuses on renderer + CLI.
18
+
19
+ ### Removed
20
+ - HA POC export module, CLI flag, BDD scenarios, and smoketest outputs (now in the HA repo).
21
+
22
+ ### Fixed
23
+ - BDD theme-file scenario
24
+ - SVG links render correctly for vertically stacked nodes.
25
+ - Publish workflow now checks out the tagged source before building.
26
+
27
+ ## [1.4.5] - 2026-01-11
28
+ ### Added
29
+ - Jinja2 templating for MkDocs output, Mermaid legend blocks, and Markdown sections.
30
+ - MkDocs sidebar assets and legend HTML blocks moved into reusable templates.
31
+ - BDD scenarios for module/console entrypoints plus additional CLI validation errors.
32
+
33
+ ### Changed
34
+ - Refactored CLI orchestration into focused CLI/render/runtime modules.
35
+ - Extracted MkDocs rendering and sidebar asset output into dedicated modules.
36
+ - Moved mock generation into the model layer with a thin IO facade.
37
+ - Centralized legend rendering helpers and shared markdown table utilities.
38
+ - Publish workflow now runs only after successful tagged CI.
39
+ - Added explicit workflow permissions to CI/CodeQL workflows.
40
+
41
+ ### Fixed
42
+ - CLI error handling for invalid theme file paths.
43
+
44
+ ### Security
45
+ - Enabled Jinja2 autoescaping for HTML templates and marked trusted HTML blocks safe.
46
+
47
+ ## [1.4.4] - 2026-01-11
48
+ ### Added
49
+ - Added smoke tests for dual-theme MkDocs sidebar legend output.
50
+
51
+ ### Changed
52
+ - Improved dark theme Mermaid readability (labels + link borders).
53
+
54
+ ### Fixed
55
+ - MkDocs sidebar legend duplication with dual-theme output.
56
+
57
+ ## [1.4.2] - 2026-01-10
58
+ ### Added
59
+ - Static code analysis and stricter type-checking.
60
+ - Contract tests for UniFi API wrapper with fixture-based validation.
61
+ - Optional live UniFi contract tests (gated by `UNIFI_CONTRACT_LIVE=1`).
62
+ - Split CI into dedicated jobs and added a contract-test job.
63
+ - Behave-based BDD tests covering CLI outputs, mkdocs assets, and error handling.
64
+ - Mkdocs timestamp (timezone configurable via `--mkdocs-timestamp-zone`).
65
+ - Optional dual Mermaid blocks for MkDocs Material theme switching (`--mkdocs-dual-theme`).
66
+ - `--no-cache` to bypass UniFi API cache reads/writes.
67
+ - File locking around cache read/write operations to avoid concurrent corruption.
68
+ - Optional UniFi API request timeouts via `UNIFI_REQUEST_TIMEOUT_SECONDS`.
69
+ - Made `--output` writes atomic to avoid partial files on interruption.
70
+
71
+ ### Changed
72
+ - Switched UniFi API cache payloads to JSON for safer local storage.
73
+ - Skips cache usage when the cache directory is group/world-writable.
74
+
75
+ ### Fixed
76
+ - Hardened Mermaid label escaping for newlines and backslashes.
77
+ - Device cache serialization to preserve LLDP data when caching.
78
+
79
+ ## [1.4.1] - 2026-01-06
80
+ ### Fixed
81
+ - Fixed pip install failure.
82
+
83
+ ## [1.4.0] - 2026-01-06
84
+ ### Added
85
+ - MkDocs output with gateway/switch details and per-port tables.
86
+ - Port tables show speed, PoE status, power, and wired clients per port.
87
+ - Compact legend with sidebar injection (`--mkdocs-sidebar-legend`).
88
+ - LLDP markdown includes the same device details and port tables when enabled.
89
+ - `--mock-data` for safe, offline rendering from fixtures.
90
+ - Faker-powered `--generate-mock` for deterministic mock fixtures (dev-only).
91
+ - Mock fixtures + SVG/Mermaid examples, with mock smoketest/CI steps.
92
+
93
+ ### Changed
94
+ - Improved uplink labeling (gateway shows Internet for WAN/unknown).
95
+ - Aggregated ports are combined into single LAG rows.
96
+ - Bumped minimum Python to 3.13 and aligned CI to 3.13.
97
+ - Pinned runtime/dev/build dependencies and added `requirements*.txt` + `constraints.txt`.
98
+
99
+ ## [1.3.1] - 2026-01-04
100
+ ### Added
101
+ - `lldp-md` output with per-device details tables and optional client sections.
102
+ - `--client-scope wired|wireless|all` and dashed wireless client links in Mermaid/SVG.
103
+ - Expanded smoketest outputs for wireless/all client scopes and LLDP markdown.
104
+
105
+ ### Fixed
106
+ - Fixed SVG icon loading paths after package reorg.
107
+
108
+ ### Changed
109
+ - Isometric port label placement on front tiles.
110
+
111
+ ## [1.3.0] - 2026-01-04
112
+ ### Added
113
+ - YAML-based theming with default + dark themes and `--theme-file`.
114
+
115
+ ### Changed
116
+ - Reorganized package into submodules (`adapters/`, `model/`, `render/`, `io/`, `cli/`).
117
+ - CLI help now grouped by category; CLI logic split into focused helpers.
118
+ - Isometric SVG layout constants centralized; extra viewBox padding to avoid clipping.
119
+ - LLDP port index fallback matches `port_table` `ifname`/`name`.
120
+ - Added PoE/edge/device count logging and improved label ordering helpers.
121
+ - Coverage excludes asset packages; docs updated (options/groups + AI disclosure).
122
+
123
+ ## [1.2.4] - 2026-01-03
124
+ ### Added
125
+ - Typed `UplinkInfo`/`PortInfo` and uplink fallback for LLDP gaps.
126
+ - CI publish workflow (trusted publishing) and release docs.
127
+ - Project metadata and packaging updated for OSS readiness.
128
+
129
+ ### Changed
130
+ - Deterministic edge ordering for repeatable output.
131
+
132
+ ## [1.1.0] - 2026-01-03
133
+ ### Added
134
+ - Isometric SVG output with grid-aligned links and isometric icon set.
135
+ - Smoketest target with multiple outputs (ports/clients/legend).
136
+ - UniFi API response caching with TTL.
137
+
138
+ ### Changed
139
+ - Improved port label placement and client labeling in SVG outputs.
140
+ - Refined visuals: link gradients, tile gradients, icon placement tweaks.
141
+
142
+ ### Fixed
143
+ - Mermaid legend/grouped output parsing errors.
144
+
145
+ ## [1.0.0] - 2025-12-30
146
+ ### Added
147
+ - Mermaid legend can render as a separate graph.
148
+ - Straight Mermaid links with node type coloring.
149
+ - Wired client leaf nodes and uplink port labels.
150
+ - CLI loads `.env` automatically.
151
+
152
+ ## [0.2.0] - 2026-01-02
153
+ ### Added
154
+ - Versioning workflow and bump tooling.
155
+ - Introduced SVG renderer and tree layout fixes.
156
+ - Increased test coverage and added coverage tooling.
157
+
158
+ [Unreleased]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.4.5...HEAD
159
+ [1.4.5]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.4.4...v1.4.5
160
+ [1.4.4]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.4.2...v1.4.4
161
+ [1.4.2]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.4.1...v1.4.2
162
+ [1.4.1]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.4.0...v1.4.1
163
+ [1.4.0]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.3.1...v1.4.0
164
+ [1.3.1]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.3.0...v1.3.1
165
+ [1.3.0]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.2.4...v1.3.0
166
+ [1.2.4]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.1.0...v1.2.4
167
+ [1.1.0]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.0.0...v1.1.0
168
+ [1.0.0]: https://github.com/merlijntishauser/unifi-network-maps/compare/v0.2.0...v1.0.0
169
+ [0.2.0]: https://github.com/merlijntishauser/unifi-network-maps/releases/tag/v0.2.0
@@ -83,7 +83,7 @@ See `LICENSES.md` for third-party license info.
83
83
 
84
84
  - Automated UniFi → Mermaid/SVG (incl. isometric) output, with ports/PoE/clients.
85
85
  - Deterministic CLI + cache + mock generator for CI and docs.
86
- - MkDocs/Markdown output plus planned HA‑friendly artifacts.
86
+ - MkDocs/Markdown output plus a separate HA integration repo for live updates.
87
87
  - A clean render pipeline that can power multiple export targets.
88
88
 
89
89
  So we’re not duplicating a packaged workflow; we’re combining data + topology modeling + diagram output + documentation/export in a way I haven’t seen in one place.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unifi-network-maps
3
- Version: 1.4.5
3
+ Version: 1.4.7
4
4
  Summary: Dynamic UniFi -> network maps in mermaid or svg
5
5
  Author: Merlijn
6
6
  License-Expression: MIT
@@ -25,7 +25,7 @@ Requires-Dist: python-dotenv==1.2.1
25
25
  Requires-Dist: PyYAML==6.0.3
26
26
  Requires-Dist: Jinja2==3.1.6
27
27
  Provides-Extra: dev
28
- Requires-Dist: Faker==40.1.0; extra == "dev"
28
+ Requires-Dist: Faker==40.1.2; extra == "dev"
29
29
  Requires-Dist: behave==1.3.3; extra == "dev"
30
30
  Requires-Dist: pre-commit==4.5.1; extra == "dev"
31
31
  Requires-Dist: pytest==9.0.2; extra == "dev"
@@ -144,6 +144,11 @@ Legend only:
144
144
  unifi-network-maps --legend-only --stdout
145
145
  ```
146
146
 
147
+ ## Home Assistant integration
148
+
149
+ The live Home Assistant integration (Config Flow + coordinator + custom card) lives in a separate repo:
150
+ - https://github.com/merlijntishauser/unifi-network-maps-ha
151
+
147
152
  ## Examples (mock data)
148
153
 
149
154
  These examples are generated from `examples/mock_data.json` (safe, anonymized fixture).
@@ -108,6 +108,11 @@ Legend only:
108
108
  unifi-network-maps --legend-only --stdout
109
109
  ```
110
110
 
111
+ ## Home Assistant integration
112
+
113
+ The live Home Assistant integration (Config Flow + coordinator + custom card) lives in a separate repo:
114
+ - https://github.com/merlijntishauser/unifi-network-maps-ha
115
+
111
116
  ## Examples (mock data)
112
117
 
113
118
  These examples are generated from `examples/mock_data.json` (safe, anonymized fixture).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "unifi-network-maps"
7
- version = "1.4.5"
7
+ version = "1.4.7"
8
8
  description = "Dynamic UniFi -> network maps in mermaid or svg"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -37,7 +37,7 @@ Changelog = "https://github.com/merlijntishauser/unifi-network-maps/blob/main/CH
37
37
 
38
38
  [project.optional-dependencies]
39
39
  dev = [
40
- "Faker==40.1.0",
40
+ "Faker==40.1.2",
41
41
  "behave==1.3.3",
42
42
  "pre-commit==4.5.1",
43
43
  "pytest==9.0.2",
@@ -0,0 +1 @@
1
+ __version__ = "1.4.7"
@@ -90,7 +90,11 @@ def main(argv: list[str] | None = None) -> int:
90
90
  except ValueError as exc:
91
91
  logging.error(str(exc))
92
92
  return 2
93
- mermaid_theme, svg_theme = resolve_themes(args.theme_file)
93
+ try:
94
+ mermaid_theme, svg_theme = resolve_themes(args.theme_file)
95
+ except Exception as exc: # noqa: BLE001
96
+ logging.error("Failed to load theme file: %s", exc)
97
+ return 2
94
98
 
95
99
  if args.legend_only:
96
100
  legend_style = resolve_legend_style(
@@ -6,6 +6,7 @@ import base64
6
6
  import math
7
7
  from collections.abc import Callable
8
8
  from dataclasses import dataclass
9
+ from html import escape as _escape_attr
9
10
  from pathlib import Path
10
11
 
11
12
  from ..model.topology import Edge
@@ -472,6 +473,7 @@ def render_svg(
472
473
  edges: list[Edge],
473
474
  *,
474
475
  node_types: dict[str, str],
476
+ node_data: dict[str, dict[str, str]] | None = None,
475
477
  options: SvgOptions | None = None,
476
478
  theme: SvgTheme = DEFAULT_THEME,
477
479
  ) -> str:
@@ -503,6 +505,7 @@ def render_svg(
503
505
  node_port_prefix,
504
506
  icons,
505
507
  options,
508
+ node_data,
506
509
  )
507
510
 
508
511
  lines.append("</svg>")
@@ -519,6 +522,8 @@ def _render_svg_edges(
519
522
  node_port_labels: dict[str, str] = {}
520
523
  node_port_prefix: dict[str, str] = {}
521
524
  for edge in edges:
525
+ _record_edge_labels(edge, node_types, node_port_labels, node_port_prefix)
526
+ for edge in sorted(edges, key=lambda item: item.poe):
522
527
  if edge.left not in positions or edge.right not in positions:
523
528
  continue
524
529
  src_x, src_y = positions[edge.left]
@@ -530,7 +535,17 @@ def _render_svg_edges(
530
535
  mid_y = (src_bottom + dst_top) / 2
531
536
  color = "url(#link-poe)" if edge.poe else "url(#link-standard)"
532
537
  width_px = 2 if edge.poe else 1
533
- path = f"M {src_cx} {src_bottom} L {src_cx} {mid_y} L {dst_cx} {mid_y} L {dst_cx} {dst_top}"
538
+ if math.isclose(src_cx, dst_cx, abs_tol=0.01):
539
+ elbow_x = src_cx + 0.5
540
+ path = (
541
+ f"M {src_cx} {src_bottom} L {src_cx} {mid_y} "
542
+ f"L {elbow_x} {mid_y} L {dst_cx} {mid_y} L {dst_cx} {dst_top}"
543
+ )
544
+ else:
545
+ path = (
546
+ f"M {src_cx} {src_bottom} L {src_cx} {mid_y} "
547
+ f"L {dst_cx} {mid_y} L {dst_cx} {dst_top}"
548
+ )
534
549
  dash = ' stroke-dasharray="6 4"' if edge.wireless else ""
535
550
  lines.append(
536
551
  f'<path d="{path}" stroke="{color}" stroke-width="{width_px}" fill="none"{dash}/>'
@@ -542,7 +557,6 @@ def _render_svg_edges(
542
557
  f'<text x="{icon_x}" y="{icon_y}" text-anchor="middle" fill="#1e88e5" '
543
558
  f'font-size="{max(options.font_size, 10)}">⚡</text>'
544
559
  )
545
- _record_edge_labels(edge, node_types, node_port_labels, node_port_prefix)
546
560
  return node_port_labels, node_port_prefix
547
561
 
548
562
 
@@ -589,11 +603,19 @@ def _render_svg_nodes(
589
603
  node_port_prefix: dict[str, str],
590
604
  icons: dict[str, str],
591
605
  options: SvgOptions,
606
+ node_data: dict[str, dict[str, str]] | None,
592
607
  ) -> None:
593
608
  for name, (x, y) in positions.items():
594
609
  node_type = node_types.get(name, "other")
595
610
  fill, stroke = _TYPE_COLORS.get(node_type, _TYPE_COLORS["other"])
596
611
  fill = f"url(#node-{node_type})"
612
+ group_attrs = _svg_node_group_attrs(node_data, name, node_type)
613
+ lines.append(f"<g{group_attrs}>")
614
+ lines.append(f"<title>{_escape_text(name)}</title>")
615
+ lines.append(
616
+ f'<rect x="{x}" y="{y}" width="{options.node_width}" height="{options.node_height}" '
617
+ 'fill="transparent" pointer-events="all" class="node-hitbox"/>'
618
+ )
597
619
  lines.append(
598
620
  f'<rect x="{x}" y="{y}" width="{options.node_width}" height="{options.node_height}" '
599
621
  f'rx="6" ry="6" fill="{fill}" stroke="{stroke}" stroke-width="1"/>'
@@ -631,6 +653,27 @@ def _render_svg_nodes(
631
653
  lines.append(
632
654
  f'<text x="{text_x}" y="{text_y}" fill="#1f1f1f" text-anchor="start">{safe_name}</text>'
633
655
  )
656
+ lines.append("</g>")
657
+
658
+
659
+ def _svg_node_group_attrs(
660
+ node_data: dict[str, dict[str, str]] | None,
661
+ name: str,
662
+ node_type: str,
663
+ ) -> str:
664
+ attrs: dict[str, str] = {
665
+ "class": "unm-node",
666
+ "data-node-id": name,
667
+ "data-node-type": node_type,
668
+ }
669
+ if node_data and (extra := node_data.get(name)):
670
+ for key, value in extra.items():
671
+ if key == "class":
672
+ attrs["class"] = f"{attrs['class']} {value}".strip()
673
+ else:
674
+ attrs[key] = value
675
+ rendered = [f' {key}="{_escape_attr(value, quote=True)}"' for key, value in attrs.items()]
676
+ return "".join(rendered)
634
677
 
635
678
 
636
679
  def _iso_project(layout: IsoLayout, gx: float, gy: float) -> tuple[float, float]:
@@ -759,6 +802,8 @@ def _render_iso_edges(
759
802
  node_port_prefix: dict[str, str],
760
803
  ) -> None:
761
804
  for edge in edges:
805
+ _record_iso_edge_label(edge, node_types, node_port_labels, node_port_prefix)
806
+ for edge in sorted(edges, key=lambda item: item.poe):
762
807
  if edge.left not in positions or edge.right not in positions:
763
808
  continue
764
809
  src_grid = grid_positions.get(edge.left)
@@ -800,7 +845,6 @@ def _render_iso_edges(
800
845
  f'<text x="{icon_x}" y="{icon_y}" text-anchor="middle" fill="#1e88e5" '
801
846
  f'font-size="{max(options.font_size, 10)}">⚡</text>'
802
847
  )
803
- _record_iso_edge_label(edge, node_types, node_port_labels, node_port_prefix)
804
848
 
805
849
 
806
850
  def _iso_edge_path(
@@ -1019,7 +1063,14 @@ def _render_iso_node(
1019
1063
  node_depth = 0.0
1020
1064
  tile_w = layout.tile_width
1021
1065
  tile_h = layout.tile_height
1066
+ group_attrs = _svg_node_group_attrs(None, name, node_type)
1067
+ lines.append(f"<g{group_attrs}>")
1068
+ lines.append(f"<title>{_escape_text(name)}</title>")
1022
1069
  top, left, right = _iso_node_polygons(x, y, tile_w, tile_h, node_depth)
1070
+ lines.append(
1071
+ f'<polygon points="{_points_to_svg(top)}" fill="transparent" '
1072
+ 'pointer-events="all" class="node-hitbox"/>'
1073
+ )
1023
1074
  left_fill = "#d0d0d0" if node_type == "other" else "#dcdcdc"
1024
1075
  right_fill = "#c2c2c2" if node_type == "other" else "#c8c8c8"
1025
1076
  _iso_render_faces(
@@ -1085,6 +1136,7 @@ def _render_iso_node(
1085
1136
  f'<text x="{name_x}" y="{name_y}" text-anchor="middle" fill="#1f1f1f" '
1086
1137
  f'font-size="{name_font_size}" transform="{name_transform}">{_escape_text(name)}</text>'
1087
1138
  )
1139
+ lines.append("</g>")
1088
1140
 
1089
1141
 
1090
1142
  def _render_iso_nodes(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unifi-network-maps
3
- Version: 1.4.5
3
+ Version: 1.4.7
4
4
  Summary: Dynamic UniFi -> network maps in mermaid or svg
5
5
  Author: Merlijn
6
6
  License-Expression: MIT
@@ -25,7 +25,7 @@ Requires-Dist: python-dotenv==1.2.1
25
25
  Requires-Dist: PyYAML==6.0.3
26
26
  Requires-Dist: Jinja2==3.1.6
27
27
  Provides-Extra: dev
28
- Requires-Dist: Faker==40.1.0; extra == "dev"
28
+ Requires-Dist: Faker==40.1.2; extra == "dev"
29
29
  Requires-Dist: behave==1.3.3; extra == "dev"
30
30
  Requires-Dist: pre-commit==4.5.1; extra == "dev"
31
31
  Requires-Dist: pytest==9.0.2; extra == "dev"
@@ -144,6 +144,11 @@ Legend only:
144
144
  unifi-network-maps --legend-only --stdout
145
145
  ```
146
146
 
147
+ ## Home Assistant integration
148
+
149
+ The live Home Assistant integration (Config Flow + coordinator + custom card) lives in a separate repo:
150
+ - https://github.com/merlijntishauser/unifi-network-maps-ha
151
+
147
152
  ## Examples (mock data)
148
153
 
149
154
  These examples are generated from `examples/mock_data.json` (safe, anonymized fixture).
@@ -4,7 +4,7 @@ PyYAML==6.0.3
4
4
  Jinja2==3.1.6
5
5
 
6
6
  [dev]
7
- Faker==40.1.0
7
+ Faker==40.1.2
8
8
  behave==1.3.3
9
9
  pre-commit==4.5.1
10
10
  pytest==9.0.2
@@ -1,3 +1,4 @@
1
+ import re
1
2
  from pathlib import Path
2
3
 
3
4
  import unifi_network_maps.render.svg as svg_module
@@ -42,6 +43,18 @@ def test_render_svg_dashes_wireless_links():
42
43
  assert 'stroke-dasharray="6 4"' in output
43
44
 
44
45
 
46
+ def test_render_svg_adds_elbow_for_vertical_links():
47
+ output = svg_module.render_svg(
48
+ [Edge("Root", "Child")],
49
+ node_types={"Root": "gateway", "Child": "switch"},
50
+ )
51
+ match = re.search(r'<path d="([^"]+)"', output)
52
+ assert match is not None
53
+ coords = [float(value) for value in re.findall(r"-?\d+(?:\.\d+)?", match.group(1))]
54
+ x_values = {round(x, 2) for x in coords[0::2]}
55
+ assert len(x_values) > 1
56
+
57
+
45
58
  def test_render_svg_compacts_device_labels():
46
59
  output = svg_module.render_svg(
47
60
  [Edge("A", "B", label="Switch A: Port 2 <-> Switch B: Port 5")],
@@ -1,93 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to this project will be documented in this file.
4
-
5
-
6
- ## 1.4.5 - (2026-01-11)
7
- - Refactored CLI orchestration into focused CLI/render/runtime modules.
8
- - Extracted MkDocs rendering and sidebar asset output into dedicated modules.
9
- - Moved mock generation into the model layer with a thin IO facade.
10
- - Centralized legend rendering helpers and shared markdown table utilities.
11
- - Added Jinja2 templating for MkDocs output, Mermaid legend blocks, and Markdown sections.
12
- - Moved MkDocs sidebar assets and legend HTML blocks into reusable templates.
13
- - Enabled Jinja2 autoescaping for HTML templates and marked trusted HTML blocks safe.
14
-
15
- ## v1.4.4 - (2026-01-10)
16
- - Improved dark theme Mermaid readability (labels + link borders).
17
- - Fixed MkDocs sidebar legend duplication with dual-theme output.
18
- - Added smoke tests for dual-theme MkDocs sidebar legend output.
19
-
20
- ## v1.4.2 - 2026-01-10
21
- - Added static code analysis and stricter type-checking
22
- - Added contract tests for UniFi API wrapper with fixture-based validation.
23
- - Added optional live UniFi contract tests (gated by `UNIFI_CONTRACT_LIVE=1`).
24
- - Split CI into dedicated jobs and added a contract-test job.
25
- - Added Behave-based BDD tests covering CLI outputs, mkdocs assets, and error handling.
26
- - Added mkdocs timestamp (timezone configurable via `--mkdocs-timestamp-zone`).
27
- - Added optional dual Mermaid blocks for MkDocs Material theme switching (`--mkdocs-dual-theme`).
28
- - Switched UniFi API cache payloads to JSON for safer local storage.
29
- - Skips cache usage when the cache directory is group/world-writable.
30
- - Added `--no-cache` to bypass UniFi API cache reads/writes.
31
- - Added file locking around cache read/write operations to avoid concurrent corruption.
32
- - Hardened Mermaid label escaping for newlines and backslashes.
33
- - Fixed device cache serialization to preserve LLDP data when caching.
34
- - Added optional UniFi API request timeouts via `UNIFI_REQUEST_TIMEOUT_SECONDS`.
35
- - Made `--output` writes atomic to avoid partial files on interruption.
36
-
37
- ## v1.4.1 - 2026-01-06
38
- - pip install was broken, fixed.
39
-
40
- ## v1.4.0 - 2026-01-06
41
- - Added MkDocs output, which includes gateway/switch details and per-port tables.
42
- - Port tables show speed, PoE status, power, and wired clients per port.
43
- - Added compact legend with sidebar injection (`--mkdocs-sidebar-legend`).
44
- - LLDP markdown includes the same device details and port tables when enabled.
45
- - Improved uplink labeling (gateway shows Internet for WAN/unknown).
46
- - Aggregated ports are combined into single LAG rows.
47
- - Bumped minimum Python to 3.13 and aligned CI to 3.13.
48
- - Pinned runtime/dev/build dependencies and added `requirements*.txt` + `constraints.txt`.
49
- - Added `--mock-data` for safe, offline rendering from fixtures.
50
- - Added Faker-powered `--generate-mock` for deterministic mock fixtures (dev-only).
51
- - Added mock fixtures + SVG/Mermaid examples, with mock smoketest/CI steps.
52
-
53
- ## v1.3.1 - 2026-01-04
54
- - Added `lldp-md` output with per-device details tables and optional client sections.
55
- - Added `--client-scope wired|wireless|all` and dashed wireless client links in Mermaid/SVG.
56
- - Expanded smoketest outputs for wireless/all client scopes and LLDP markdown.
57
- - Fixed SVG icon loading paths after package reorg.
58
- - Tuned isometric port label placement on front tiles.
59
-
60
- ## v1.3.0 - 2026-01-04
61
- - Reorganized package into submodules (`adapters/`, `model/`, `render/`, `io/`, `cli/`).
62
- - YAML-based theming with default + dark themes and `--theme-file`.
63
- - CLI help now grouped by category; CLI logic split into focused helpers.
64
- - Isometric SVG layout constants centralized; extra viewBox padding to avoid clipping.
65
- - LLDP port index fallback matches `port_table` `ifname`/`name`.
66
- - Added PoE/edge/device count logging and improved label ordering helpers.
67
- - Coverage excludes asset packages; docs updated (options/groups + AI disclosure).
68
-
69
- ## v1.2.4 - 2026-01-03
70
- - Added typed `UplinkInfo`/`PortInfo` and uplink fallback for LLDP gaps.
71
- - Deterministic edge ordering for repeatable output.
72
- - CI publish workflow (trusted publishing) and release docs.
73
- - Project metadata and packaging updated for OSS readiness.
74
-
75
- ## v1.1.0 - 2026-01-03
76
- - Added isometric SVG output with grid-aligned links and isometric icon set.
77
- - Improved port label placement and client labeling in SVG outputs.
78
- - Added smoketest target with multiple outputs (ports/clients/legend).
79
- - Added UniFi API response caching with TTL.
80
- - Fixed Mermaid legend/grouped output parsing errors.
81
- - Refined visuals: link gradients, tile gradients, icon placement tweaks.
82
-
83
- ## v1.0.0 - 2025-12-30
84
- - Mermaid legend can render as a separate graph.
85
- - Straight Mermaid links with node type coloring.
86
- - Added wired client leaf nodes and uplink port labels.
87
- - Expanded PoE detection tests and LLDP helpers.
88
- - CLI loads `.env` automatically.
89
-
90
- ## v0.2.0 - 2026-01-02 - Initial public release
91
- - Added versioning workflow and bump tooling.
92
- - Introduced SVG renderer and tree layout fixes.
93
- - Increased test coverage and added coverage tooling.
@@ -1 +0,0 @@
1
- __version__ = "1.4.5"