unifi-network-maps 1.4.5__tar.gz → 1.4.6__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.6/CHANGELOG.md +165 -0
  2. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/CONTRIBUTING.md +1 -1
  3. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/PKG-INFO +6 -1
  4. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/README.md +5 -0
  5. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/pyproject.toml +1 -1
  6. unifi_network_maps-1.4.6/src/unifi_network_maps/__init__.py +1 -0
  7. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/cli/main.py +5 -1
  8. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/svg.py +34 -3
  9. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps.egg-info/PKG-INFO +6 -1
  10. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_svg.py +13 -0
  11. unifi_network_maps-1.4.5/CHANGELOG.md +0 -93
  12. unifi_network_maps-1.4.5/src/unifi_network_maps/__init__.py +0 -1
  13. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/LICENSE +0 -0
  14. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/LICENSES.md +0 -0
  15. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/MANIFEST.in +0 -0
  16. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/RELEASING.md +0 -0
  17. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/SECURITY.md +0 -0
  18. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/setup.cfg +0 -0
  19. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/__main__.py +0 -0
  20. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/adapters/__init__.py +0 -0
  21. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/adapters/config.py +0 -0
  22. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/adapters/unifi.py +0 -0
  23. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/__init__.py +0 -0
  24. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/__init__.py +0 -0
  25. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/access-point.svg +0 -0
  26. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/ISOPACKS_LICENSE +0 -0
  27. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/block.svg +0 -0
  28. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/cache.svg +0 -0
  29. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/cardterminal.svg +0 -0
  30. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/cloud.svg +0 -0
  31. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/cronjob.svg +0 -0
  32. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/cube.svg +0 -0
  33. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/desktop.svg +0 -0
  34. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/diamond.svg +0 -0
  35. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/dns.svg +0 -0
  36. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/document.svg +0 -0
  37. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/firewall.svg +0 -0
  38. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/function-module.svg +0 -0
  39. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/image.svg +0 -0
  40. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/laptop.svg +0 -0
  41. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/loadbalancer.svg +0 -0
  42. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/lock.svg +0 -0
  43. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/mail.svg +0 -0
  44. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/mailmultiple.svg +0 -0
  45. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/mobiledevice.svg +0 -0
  46. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/office.svg +0 -0
  47. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/package-module.svg +0 -0
  48. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/paymentcard.svg +0 -0
  49. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/plane.svg +0 -0
  50. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/printer.svg +0 -0
  51. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/pyramid.svg +0 -0
  52. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/queue.svg +0 -0
  53. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/router.svg +0 -0
  54. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/server.svg +0 -0
  55. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/speech.svg +0 -0
  56. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/sphere.svg +0 -0
  57. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/storage.svg +0 -0
  58. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/switch-module.svg +0 -0
  59. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/tower.svg +0 -0
  60. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/truck-2.svg +0 -0
  61. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/truck.svg +0 -0
  62. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/user.svg +0 -0
  63. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/isometric/vm.svg +0 -0
  64. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/laptop.svg +0 -0
  65. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/router-network.svg +0 -0
  66. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/server-network.svg +0 -0
  67. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/icons/server.svg +0 -0
  68. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/themes/dark.yaml +0 -0
  69. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/assets/themes/default.yaml +0 -0
  70. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/cli/__init__.py +0 -0
  71. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/cli/__main__.py +0 -0
  72. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/cli/args.py +0 -0
  73. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/cli/render.py +0 -0
  74. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/cli/runtime.py +0 -0
  75. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/io/__init__.py +0 -0
  76. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/io/debug.py +0 -0
  77. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/io/export.py +0 -0
  78. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/io/mkdocs_assets.py +0 -0
  79. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/io/mock_data.py +0 -0
  80. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/io/mock_generate.py +0 -0
  81. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/model/__init__.py +0 -0
  82. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/model/labels.py +0 -0
  83. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/model/lldp.py +0 -0
  84. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/model/mock.py +0 -0
  85. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/model/ports.py +0 -0
  86. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/model/topology.py +0 -0
  87. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/__init__.py +0 -0
  88. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/device_ports_md.py +0 -0
  89. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/legend.py +0 -0
  90. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/lldp_md.py +0 -0
  91. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/markdown_tables.py +0 -0
  92. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/mermaid.py +0 -0
  93. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/mermaid_theme.py +0 -0
  94. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/mkdocs.py +0 -0
  95. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/svg_theme.py +0 -0
  96. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/templates/device_port_block.md.j2 +0 -0
  97. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/templates/legend_compact.html.j2 +0 -0
  98. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/templates/lldp_device_section.md.j2 +0 -0
  99. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/templates/markdown_section.md.j2 +0 -0
  100. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/templates/mermaid_legend.mmd.j2 +0 -0
  101. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/templates/mkdocs_document.md.j2 +0 -0
  102. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/templates/mkdocs_dual_theme_style.html.j2 +0 -0
  103. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/templates/mkdocs_html_block.html.j2 +0 -0
  104. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/templates/mkdocs_legend.css.j2 +0 -0
  105. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/templates/mkdocs_legend.js.j2 +0 -0
  106. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/templates/mkdocs_mermaid_block.md.j2 +0 -0
  107. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/templating.py +0 -0
  108. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps/render/theme.py +0 -0
  109. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps.egg-info/SOURCES.txt +0 -0
  110. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps.egg-info/dependency_links.txt +0 -0
  111. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps.egg-info/entry_points.txt +0 -0
  112. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps.egg-info/requires.txt +0 -0
  113. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/src/unifi_network_maps.egg-info/top_level.txt +0 -0
  114. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_cli.py +0 -0
  115. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_clients.py +0 -0
  116. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_config.py +0 -0
  117. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_contract_unifi.py +0 -0
  118. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_contract_unifi_live.py +0 -0
  119. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_debug.py +0 -0
  120. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_device_ports_md.py +0 -0
  121. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_export.py +0 -0
  122. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_groups.py +0 -0
  123. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_labels.py +0 -0
  124. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_lldp.py +0 -0
  125. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_lldp_md.py +0 -0
  126. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_mermaid.py +0 -0
  127. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_mock_generate.py +0 -0
  128. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_svg_iso.py +0 -0
  129. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_theme.py +0 -0
  130. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_topology.py +0 -0
  131. {unifi_network_maps-1.4.5 → unifi_network_maps-1.4.6}/tests/test_unifi.py +0 -0
@@ -0,0 +1,165 @@
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.6] - 2026-01-15
9
+ ### Added
10
+ - Home Assistant docs pointing to the standalone integration repo.
11
+
12
+ ### Changed
13
+ - Home Assistant integration work moved to `unifi-network-maps-ha`; core repo focuses on renderer + CLI.
14
+
15
+ ### Removed
16
+ - HA POC export module, CLI flag, BDD scenarios, and smoketest outputs (now in the HA repo).
17
+
18
+ ### Fixed
19
+ - BDD theme-file scenario
20
+ - SVG links render correctly for vertically stacked nodes.
21
+ - Publish workflow now checks out the tagged source before building.
22
+
23
+ ## [1.4.5] - 2026-01-11
24
+ ### Added
25
+ - Jinja2 templating for MkDocs output, Mermaid legend blocks, and Markdown sections.
26
+ - MkDocs sidebar assets and legend HTML blocks moved into reusable templates.
27
+ - BDD scenarios for module/console entrypoints plus additional CLI validation errors.
28
+
29
+ ### Changed
30
+ - Refactored CLI orchestration into focused CLI/render/runtime modules.
31
+ - Extracted MkDocs rendering and sidebar asset output into dedicated modules.
32
+ - Moved mock generation into the model layer with a thin IO facade.
33
+ - Centralized legend rendering helpers and shared markdown table utilities.
34
+ - Publish workflow now runs only after successful tagged CI.
35
+ - Added explicit workflow permissions to CI/CodeQL workflows.
36
+
37
+ ### Fixed
38
+ - CLI error handling for invalid theme file paths.
39
+
40
+ ### Security
41
+ - Enabled Jinja2 autoescaping for HTML templates and marked trusted HTML blocks safe.
42
+
43
+ ## [1.4.4] - 2026-01-11
44
+ ### Added
45
+ - Added smoke tests for dual-theme MkDocs sidebar legend output.
46
+
47
+ ### Changed
48
+ - Improved dark theme Mermaid readability (labels + link borders).
49
+
50
+ ### Fixed
51
+ - MkDocs sidebar legend duplication with dual-theme output.
52
+
53
+ ## [1.4.2] - 2026-01-10
54
+ ### Added
55
+ - Static code analysis and stricter type-checking.
56
+ - Contract tests for UniFi API wrapper with fixture-based validation.
57
+ - Optional live UniFi contract tests (gated by `UNIFI_CONTRACT_LIVE=1`).
58
+ - Split CI into dedicated jobs and added a contract-test job.
59
+ - Behave-based BDD tests covering CLI outputs, mkdocs assets, and error handling.
60
+ - Mkdocs timestamp (timezone configurable via `--mkdocs-timestamp-zone`).
61
+ - Optional dual Mermaid blocks for MkDocs Material theme switching (`--mkdocs-dual-theme`).
62
+ - `--no-cache` to bypass UniFi API cache reads/writes.
63
+ - File locking around cache read/write operations to avoid concurrent corruption.
64
+ - Optional UniFi API request timeouts via `UNIFI_REQUEST_TIMEOUT_SECONDS`.
65
+ - Made `--output` writes atomic to avoid partial files on interruption.
66
+
67
+ ### Changed
68
+ - Switched UniFi API cache payloads to JSON for safer local storage.
69
+ - Skips cache usage when the cache directory is group/world-writable.
70
+
71
+ ### Fixed
72
+ - Hardened Mermaid label escaping for newlines and backslashes.
73
+ - Device cache serialization to preserve LLDP data when caching.
74
+
75
+ ## [1.4.1] - 2026-01-06
76
+ ### Fixed
77
+ - Fixed pip install failure.
78
+
79
+ ## [1.4.0] - 2026-01-06
80
+ ### Added
81
+ - MkDocs output with gateway/switch details and per-port tables.
82
+ - Port tables show speed, PoE status, power, and wired clients per port.
83
+ - Compact legend with sidebar injection (`--mkdocs-sidebar-legend`).
84
+ - LLDP markdown includes the same device details and port tables when enabled.
85
+ - `--mock-data` for safe, offline rendering from fixtures.
86
+ - Faker-powered `--generate-mock` for deterministic mock fixtures (dev-only).
87
+ - Mock fixtures + SVG/Mermaid examples, with mock smoketest/CI steps.
88
+
89
+ ### Changed
90
+ - Improved uplink labeling (gateway shows Internet for WAN/unknown).
91
+ - Aggregated ports are combined into single LAG rows.
92
+ - Bumped minimum Python to 3.13 and aligned CI to 3.13.
93
+ - Pinned runtime/dev/build dependencies and added `requirements*.txt` + `constraints.txt`.
94
+
95
+ ## [1.3.1] - 2026-01-04
96
+ ### Added
97
+ - `lldp-md` output with per-device details tables and optional client sections.
98
+ - `--client-scope wired|wireless|all` and dashed wireless client links in Mermaid/SVG.
99
+ - Expanded smoketest outputs for wireless/all client scopes and LLDP markdown.
100
+
101
+ ### Fixed
102
+ - Fixed SVG icon loading paths after package reorg.
103
+
104
+ ### Changed
105
+ - Isometric port label placement on front tiles.
106
+
107
+ ## [1.3.0] - 2026-01-04
108
+ ### Added
109
+ - YAML-based theming with default + dark themes and `--theme-file`.
110
+
111
+ ### Changed
112
+ - Reorganized package into submodules (`adapters/`, `model/`, `render/`, `io/`, `cli/`).
113
+ - CLI help now grouped by category; CLI logic split into focused helpers.
114
+ - Isometric SVG layout constants centralized; extra viewBox padding to avoid clipping.
115
+ - LLDP port index fallback matches `port_table` `ifname`/`name`.
116
+ - Added PoE/edge/device count logging and improved label ordering helpers.
117
+ - Coverage excludes asset packages; docs updated (options/groups + AI disclosure).
118
+
119
+ ## [1.2.4] - 2026-01-03
120
+ ### Added
121
+ - Typed `UplinkInfo`/`PortInfo` and uplink fallback for LLDP gaps.
122
+ - CI publish workflow (trusted publishing) and release docs.
123
+ - Project metadata and packaging updated for OSS readiness.
124
+
125
+ ### Changed
126
+ - Deterministic edge ordering for repeatable output.
127
+
128
+ ## [1.1.0] - 2026-01-03
129
+ ### Added
130
+ - Isometric SVG output with grid-aligned links and isometric icon set.
131
+ - Smoketest target with multiple outputs (ports/clients/legend).
132
+ - UniFi API response caching with TTL.
133
+
134
+ ### Changed
135
+ - Improved port label placement and client labeling in SVG outputs.
136
+ - Refined visuals: link gradients, tile gradients, icon placement tweaks.
137
+
138
+ ### Fixed
139
+ - Mermaid legend/grouped output parsing errors.
140
+
141
+ ## [1.0.0] - 2025-12-30
142
+ ### Added
143
+ - Mermaid legend can render as a separate graph.
144
+ - Straight Mermaid links with node type coloring.
145
+ - Wired client leaf nodes and uplink port labels.
146
+ - CLI loads `.env` automatically.
147
+
148
+ ## [0.2.0] - 2026-01-02
149
+ ### Added
150
+ - Versioning workflow and bump tooling.
151
+ - Introduced SVG renderer and tree layout fixes.
152
+ - Increased test coverage and added coverage tooling.
153
+
154
+ [Unreleased]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.4.5...HEAD
155
+ [1.4.5]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.4.4...v1.4.5
156
+ [1.4.4]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.4.2...v1.4.4
157
+ [1.4.2]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.4.1...v1.4.2
158
+ [1.4.1]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.4.0...v1.4.1
159
+ [1.4.0]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.3.1...v1.4.0
160
+ [1.3.1]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.3.0...v1.3.1
161
+ [1.3.0]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.2.4...v1.3.0
162
+ [1.2.4]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.1.0...v1.2.4
163
+ [1.1.0]: https://github.com/merlijntishauser/unifi-network-maps/compare/v1.0.0...v1.1.0
164
+ [1.0.0]: https://github.com/merlijntishauser/unifi-network-maps/compare/v0.2.0...v1.0.0
165
+ [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.6
4
4
  Summary: Dynamic UniFi -> network maps in mermaid or svg
5
5
  Author: Merlijn
6
6
  License-Expression: MIT
@@ -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.6"
8
8
  description = "Dynamic UniFi -> network maps in mermaid or svg"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13"
@@ -0,0 +1 @@
1
+ __version__ = "1.4.6"
@@ -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,15 @@ 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
+ extra_attrs = _svg_node_attrs(node_data, name)
613
+ if extra_attrs:
614
+ lines.append(f"<g{extra_attrs}>")
597
615
  lines.append(
598
616
  f'<rect x="{x}" y="{y}" width="{options.node_width}" height="{options.node_height}" '
599
617
  f'rx="6" ry="6" fill="{fill}" stroke="{stroke}" stroke-width="1"/>'
@@ -631,6 +649,18 @@ def _render_svg_nodes(
631
649
  lines.append(
632
650
  f'<text x="{text_x}" y="{text_y}" fill="#1f1f1f" text-anchor="start">{safe_name}</text>'
633
651
  )
652
+ if extra_attrs:
653
+ lines.append("</g>")
654
+
655
+
656
+ def _svg_node_attrs(node_data: dict[str, dict[str, str]] | None, name: str) -> str:
657
+ if not node_data:
658
+ return ""
659
+ attrs = node_data.get(name)
660
+ if not attrs:
661
+ return ""
662
+ rendered = [f' {key}="{_escape_attr(value, quote=True)}"' for key, value in attrs.items()]
663
+ return "".join(rendered)
634
664
 
635
665
 
636
666
  def _iso_project(layout: IsoLayout, gx: float, gy: float) -> tuple[float, float]:
@@ -759,6 +789,8 @@ def _render_iso_edges(
759
789
  node_port_prefix: dict[str, str],
760
790
  ) -> None:
761
791
  for edge in edges:
792
+ _record_iso_edge_label(edge, node_types, node_port_labels, node_port_prefix)
793
+ for edge in sorted(edges, key=lambda item: item.poe):
762
794
  if edge.left not in positions or edge.right not in positions:
763
795
  continue
764
796
  src_grid = grid_positions.get(edge.left)
@@ -800,7 +832,6 @@ def _render_iso_edges(
800
832
  f'<text x="{icon_x}" y="{icon_y}" text-anchor="middle" fill="#1e88e5" '
801
833
  f'font-size="{max(options.font_size, 10)}">⚡</text>'
802
834
  )
803
- _record_iso_edge_label(edge, node_types, node_port_labels, node_port_prefix)
804
835
 
805
836
 
806
837
  def _iso_edge_path(
@@ -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.6
4
4
  Summary: Dynamic UniFi -> network maps in mermaid or svg
5
5
  Author: Merlijn
6
6
  License-Expression: MIT
@@ -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).
@@ -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"