Skip to content

Commit 217961f

Browse files
committed
feat: add initial EntityMap custom integration scaffold
Signed-off-by: Damian Skrzyński <polprog.tech@gmail.com>
1 parent bc27e5a commit 217961f

66 files changed

Lines changed: 8733 additions & 1 deletion

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
lint:
15+
name: Lint & Format
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
- uses: actions/setup-python@v5
20+
with:
21+
python-version: "3.12"
22+
- name: Install dependencies
23+
run: |
24+
pip install ruff
25+
- name: Ruff check
26+
run: ruff check .
27+
- name: Ruff format check
28+
run: ruff format --check .
29+
30+
typecheck:
31+
name: Type Check
32+
runs-on: ubuntu-latest
33+
steps:
34+
- uses: actions/checkout@v4
35+
- uses: actions/setup-python@v5
36+
with:
37+
python-version: "3.12"
38+
- name: Install dependencies
39+
run: |
40+
pip install mypy homeassistant
41+
- name: Mypy
42+
run: mypy custom_components/entitymap --ignore-missing-imports
43+
44+
test:
45+
name: Tests
46+
runs-on: ubuntu-latest
47+
strategy:
48+
matrix:
49+
python-version: ["3.12", "3.13"]
50+
steps:
51+
- uses: actions/checkout@v4
52+
- uses: actions/setup-python@v5
53+
with:
54+
python-version: ${{ matrix.python-version }}
55+
- name: Install dependencies
56+
run: |
57+
pip install pytest pytest-asyncio pytest-cov homeassistant voluptuous aiohttp
58+
- name: Run tests
59+
run: |
60+
pytest tests/ -v --cov=custom_components/entitymap --cov-report=term-missing
61+
- name: Check coverage
62+
run: |
63+
pytest tests/ --cov=custom_components/entitymap --cov-fail-under=70
64+
65+
hacs:
66+
name: HACS Validation
67+
runs-on: ubuntu-latest
68+
steps:
69+
- uses: actions/checkout@v4
70+
- name: HACS Validation
71+
uses: hacs/action@main
72+
with:
73+
category: integration

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Changelog
2+
3+
All notable changes to EntityMap 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.0.0] - 2025-03-13
9+
10+
### Added
11+
12+
- Initial release of EntityMap
13+
- Full dependency graph generation from HA registries, automations, scripts, scenes, groups, helpers, and template entities
14+
- Interactive force-directed graph visualization panel with D3.js
15+
- Node type filtering, search, zoom/pan, and neighborhood highlighting
16+
- Impact analysis engine with risk scoring and severity classification
17+
- Fragility detection for missing references, device_id usage, disabled/unavailable entities, tight coupling, and hidden dependencies
18+
- Migration guidance with step-by-step recommendations for device/entity replacement
19+
- Summary sensors: total nodes, total dependencies, fragility issues, last scan timestamp
20+
- Rescan button entity
21+
- Services: `entitymap.scan`, `entitymap.analyze_impact`, `entitymap.get_dependencies`
22+
- WebSocket API for frontend: `entitymap/graph`, `entitymap/impact`, `entitymap/neighborhood`, `entitymap/scan`, `entitymap/findings`, `entitymap/migration`
23+
- Repair issues for fragile device_id usage and missing entity references
24+
- Diagnostics support with redacted output
25+
- Config flow with options for scan-on-startup, auto-refresh, scan interval, template/group inclusion
26+
- Options flow for runtime configuration changes
27+
- Full test suite with pytest
28+
- CI workflow with linting (ruff), type checking (mypy), testing, and HACS validation
29+
- Comprehensive documentation: README, architecture, dependency model, contributing guide

CONTRIBUTING.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# Contributing to EntityMap
2+
3+
Thank you for your interest in contributing! EntityMap is a community-driven project and welcomes contributions of all kinds.
4+
5+
## Development Setup
6+
7+
### Prerequisites
8+
9+
- Python 3.12+
10+
- Home Assistant development environment (or a running HA instance for manual testing)
11+
- Git
12+
13+
### Local Setup
14+
15+
```bash
16+
# Clone the repository
17+
git clone https://github.com/polprog-tech/EntityMap.git
18+
cd EntityMap
19+
20+
# Create a virtual environment
21+
python -m venv venv
22+
source venv/bin/activate # or `venv\Scripts\activate` on Windows
23+
24+
# Install development dependencies
25+
pip install homeassistant voluptuous aiohttp pytest pytest-asyncio pytest-cov ruff mypy
26+
```
27+
28+
### Running Tests
29+
30+
```bash
31+
# Run all tests
32+
pytest tests/ -v
33+
34+
# Run with coverage
35+
pytest tests/ -v --cov=custom_components/entitymap --cov-report=term-missing
36+
37+
# Run a specific test file
38+
pytest tests/components/entitymap/test_models.py -v
39+
```
40+
41+
### Linting & Formatting
42+
43+
```bash
44+
# Check for lint errors
45+
ruff check .
46+
47+
# Auto-fix lint errors
48+
ruff check --fix .
49+
50+
# Format code
51+
ruff format .
52+
53+
# Type checking
54+
mypy custom_components/entitymap --ignore-missing-imports
55+
```
56+
57+
## Project Structure
58+
59+
```
60+
custom_components/entitymap/
61+
├── __init__.py # Integration setup, event wiring, WebSocket API
62+
├── config_flow.py # Config and options flow
63+
├── const.py # Constants, enums (NodeType, DependencyKind, etc.)
64+
├── models.py # Domain model (GraphNode, GraphEdge, DependencyGraph, etc.)
65+
├── graph.py # Graph builder orchestration
66+
├── analysis.py # Impact analysis engine
67+
├── fragility.py # Fragility detection engine
68+
├── migration.py # Migration suggestion engine
69+
├── sensor.py # Summary sensor entities
70+
├── button.py # Rescan button entity
71+
├── services.py # Service handlers
72+
├── diagnostics.py # Diagnostics support
73+
├── repairs.py # Repair flows
74+
├── panel.py # HTTP handler for frontend JS
75+
├── adapters/
76+
│ ├── base.py # SourceAdapter abstract base class
77+
│ ├── registry.py # Entity/device/area registry adapter
78+
│ ├── automation.py # Automation config parser
79+
│ ├── script.py # Script config parser
80+
│ ├── scene.py # Scene config parser
81+
│ ├── group.py # Group state parser
82+
│ └── template.py # Template entity reference extractor
83+
├── frontend/
84+
│ └── entitymap-panel.js # LitElement + D3.js frontend panel
85+
├── manifest.json
86+
├── services.yaml
87+
├── strings.json
88+
└── translations/
89+
└── en.json
90+
```
91+
92+
## Architecture Guidelines
93+
94+
### Adding a New Graph Source (Adapter)
95+
96+
1. Create a new file in `adapters/` (e.g., `blueprint.py`)
97+
2. Extend `SourceAdapter` from `adapters/base.py`
98+
3. Implement `async_populate(self, graph: DependencyGraph) -> None`
99+
4. Add the adapter to the `GraphBuilder._build()` method in `graph.py`
100+
5. Add configuration option if the adapter is optional
101+
6. Write tests in `tests/components/entitymap/`
102+
103+
```python
104+
from .base import SourceAdapter
105+
106+
class BlueprintAdapter(SourceAdapter):
107+
async def async_populate(self, graph: DependencyGraph) -> None:
108+
# Your logic here
109+
pass
110+
```
111+
112+
### Adding a New Finding Type
113+
114+
1. Add the type to `FragilityType` enum in `const.py`
115+
2. Add a detection function in `fragility.py`
116+
3. Call it from `detect_fragility()`
117+
4. Add a translation key in `strings.json` if it generates repair issues
118+
5. Write tests
119+
120+
### Key Principles
121+
122+
- **Prefer supported/public Home Assistant APIs** over internal implementation details
123+
- **Isolate fragile access** behind adapters with clear compatibility notes
124+
- **Separate "cannot determine" from "no dependency found"** — always track confidence levels
125+
- **Keep entities thin** — they project backend state, don't contain business logic
126+
- **Async-first** — never block the event loop
127+
- **Test everything** — aim for 95%+ coverage on core logic
128+
129+
## Branching & PR Guidance
130+
131+
- **main** — stable release branch
132+
- Feature branches: `feature/description`
133+
- Bug fixes: `fix/description`
134+
- PRs should include tests and pass all CI checks
135+
- Keep commits atomic and well-described
136+
137+
## Commit Messages
138+
139+
Use conventional commits:
140+
141+
```
142+
feat: add blueprint dependency scanning
143+
fix: handle missing automation store gracefully
144+
test: add tests for device_id fragility detection
145+
docs: update architecture diagram
146+
```
147+
148+
## Testing Philosophy
149+
150+
EntityMap uses **scenario-oriented Given/When/Then (GWT) style tests** for readability and maintainability.
151+
152+
### Structure
153+
154+
Every test follows a clear three-part structure:
155+
156+
1. **Given** — Set up preconditions (create a graph, add nodes/edges, configure mocks)
157+
2. **When** — Perform the action under test (run a function, call a service, submit a flow)
158+
3. **Then** — Assert the expected outcome (check return values, verify state changes)
159+
160+
### In Practice
161+
162+
- Test classes are organized by **feature/scenario**, not by module. For example, `TestImpactOnEntityWithDependents` rather than `TestAnalysis`.
163+
- Test method names describe the scenario: `test_reports_high_severity_when_many_dependents`.
164+
- Each test uses inline `# Given`, `# When`, `# Then` comments to mark sections.
165+
- We include **happy-path**, **edge-case**, and **failure-path** scenarios for every feature.
166+
167+
### Example
168+
169+
```python
170+
class TestImpactOnIsolatedNode:
171+
"""Given a node with no dependents."""
172+
173+
def test_reports_zero_risk(self):
174+
# Given — a graph with one isolated node
175+
graph = DependencyGraph()
176+
graph.add_node(GraphNode(node_id="light.solo", node_type=NodeType.ENTITY, title="Solo"))
177+
178+
# When — impact is analyzed
179+
report = analyze_impact(graph, "light.solo")
180+
181+
# Then — risk is zero, severity is info
182+
assert report.risk_score == 0.0
183+
assert report.severity == "info"
184+
```
185+
186+
### Guidelines
187+
188+
- Prefer **many small test classes** (one scenario each) over large test classes with mixed scenarios.
189+
- Use descriptive class docstrings that read as "Given ..." to set context.
190+
- Aim for **95%+ coverage** on core logic (models, analysis, fragility, migration).
191+
- Test both the happy path and meaningful edge cases (empty inputs, cycles, missing nodes).
192+
193+
---
194+
195+
## Avoiding Reliance on Unstable HA Internals
196+
197+
Home Assistant's internal APIs can change without notice. When accessing non-public APIs:
198+
199+
1. **Isolate** the access in a dedicated adapter method
200+
2. **Wrap** in try/except with graceful fallback
201+
3. **Document** which HA version the API was tested against
202+
4. **Log** a clear warning if the API shape changes
203+
5. **Test** the fallback path

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 EntityMap Contributors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

0 commit comments

Comments
 (0)