hadsync 0.2.2__tar.gz → 0.2.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.
- {hadsync-0.2.2 → hadsync-0.2.3}/.gitignore +1 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/CHANGELOG.md +10 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/PKG-INFO +45 -12
- {hadsync-0.2.2 → hadsync-0.2.3}/README.md +44 -11
- hadsync-0.2.3/docs/cli-push.jpg +0 -0
- hadsync-0.2.3/docs/cli-status.jpg +0 -0
- hadsync-0.2.3/docs/cli-validate-all-pass.jpg +0 -0
- hadsync-0.2.3/docs/cli-validate-pass.jpg +0 -0
- hadsync-0.2.3/docs/cli-validate-warnings.jpg +0 -0
- hadsync-0.2.3/hadsync/__init__.py +1 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/hadsync/cli.py +9 -2
- {hadsync-0.2.2 → hadsync-0.2.3}/pyproject.toml +1 -1
- {hadsync-0.2.2 → hadsync-0.2.3}/vscode-hadsync/src/statusBar.ts +6 -1
- hadsync-0.2.2/hadsync/__init__.py +0 -1
- {hadsync-0.2.2 → hadsync-0.2.3}/.claude/settings.local.json +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/.hadsync.yaml +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/CONTRIBUTING.md +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/LICENSE +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/docs/diff-conflict-summary.jpg +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/docs/diff-show-flag.jpg +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/docs/vscode-autocomplete.jpg +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/docs/vscode-command-palette.jpg +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/docs/vscode-entities-screenshot.jpg +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/docs/vscode-problems-panel.jpg +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/docs/vscode-screenshot.jpg +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/hadsync/config.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/hadsync/converter.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/hadsync/entities.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/hadsync/ha_rest.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/hadsync/ha_ws.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/hadsync/output.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/hadsync/schema.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/hadsync/state.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/hadsync/validator.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/hadsync/watcher.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/hadsync_design.docx +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/tests/__init__.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/tests/unit/__init__.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/tests/unit/test_config.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/tests/unit/test_converter.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/tests/unit/test_entities.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/tests/unit/test_schema.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/tests/unit/test_state.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/tests/unit/test_validator.py +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/vscode-hadsync/.vscodeignore +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/vscode-hadsync/LICENSE +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/vscode-hadsync/README.md +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/vscode-hadsync/package-lock.json +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/vscode-hadsync/package.json +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/vscode-hadsync/src/commands.ts +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/vscode-hadsync/src/completion.ts +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/vscode-hadsync/src/diagnostics.ts +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/vscode-hadsync/src/extension.ts +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/vscode-hadsync/src/runner.ts +0 -0
- {hadsync-0.2.2 → hadsync-0.2.3}/vscode-hadsync/tsconfig.json +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v0.2.3] — 2026-05-11
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **False-positive "modified" in `hadsync status` after a clean pull** — macOS APFS can commit a file's mtime a few microseconds after Python records `last_pull`, making `mtime > last_pull` by a sub-second amount even though the pull itself wrote the file. Fixed by comparing timestamps at whole-second granularity: a difference of less than one second means the pull wrote the file, not a user edit. Applied in `hadsync status`, `hadsync diff` conflict detection, and the VS Code status bar.
|
|
8
|
+
|
|
9
|
+
[v0.2.3]: https://github.com/gevgev/hadsync/releases/tag/v0.2.3
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
3
13
|
## [v0.2.2] — 2026-05-10
|
|
4
14
|
|
|
5
15
|
### Fixed — VS Code Extension
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hadsync
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Home Assistant Dashboard Sync — pull, edit, and push Lovelace dashboards as code
|
|
5
5
|
Project-URL: Homepage, https://github.com/gevgev/hadsync
|
|
6
6
|
Project-URL: Repository, https://github.com/gevgev/hadsync
|
|
@@ -93,29 +93,32 @@ HA stores Lovelace dashboard configs in its internal storage layer. There is no
|
|
|
93
93
|
## Installation
|
|
94
94
|
|
|
95
95
|
```bash
|
|
96
|
-
pip install hadsync
|
|
96
|
+
pip install hadsync
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
Requires Python 3.11+.
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
**With uv — installs `hadsync` globally so it works from any directory:**
|
|
101
|
+
**With uv (recommended — installs globally so `hadsync` works from any directory):**
|
|
104
102
|
|
|
105
103
|
```bash
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
uv tool install hadsync
|
|
105
|
+
```
|
|
108
106
|
|
|
109
|
-
|
|
110
|
-
uv tool install --editable /path/to/hadsync
|
|
107
|
+
After installation, `hadsync` is available system-wide. Upgrade to a newer release with:
|
|
111
108
|
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
```bash
|
|
110
|
+
pip install --upgrade hadsync
|
|
111
|
+
# or
|
|
112
|
+
uv tool upgrade hadsync
|
|
114
113
|
```
|
|
115
114
|
|
|
116
|
-
|
|
115
|
+
### Install from source
|
|
117
116
|
|
|
118
117
|
```bash
|
|
118
|
+
# editable install — code changes take effect immediately
|
|
119
|
+
uv tool install --editable /path/to/hadsync
|
|
120
|
+
|
|
121
|
+
# or with pip
|
|
119
122
|
pip install -e ".[dev]"
|
|
120
123
|
```
|
|
121
124
|
|
|
@@ -258,6 +261,36 @@ battery-status
|
|
|
258
261
|
|
|
259
262
|
## In Action
|
|
260
263
|
|
|
264
|
+
### `hadsync validate` — issues found
|
|
265
|
+
|
|
266
|
+

|
|
267
|
+
|
|
268
|
+
*Validation catches two problems before anything reaches HA: an entity ID that no longer exists in the registry, and a `sensor` card missing its required `entity` field. Both are reported with exact line numbers.*
|
|
269
|
+
|
|
270
|
+
### `hadsync validate` — all clear
|
|
271
|
+
|
|
272
|
+

|
|
273
|
+
|
|
274
|
+
*After fixing the two issues the dashboard passes cleanly. The same command validates all dashboards at once when run without an ID argument.*
|
|
275
|
+
|
|
276
|
+
### `hadsync validate` — full suite
|
|
277
|
+
|
|
278
|
+

|
|
279
|
+
|
|
280
|
+
*All 13 storage-mode dashboards pass in a single run — safe to use in CI pipelines since the command exits non-zero on any error.*
|
|
281
|
+
|
|
282
|
+
### `hadsync status` — sync overview
|
|
283
|
+
|
|
284
|
+

|
|
285
|
+
|
|
286
|
+
*A quick at-a-glance table showing when each dashboard was last pulled, when it was last pushed, and whether the local file has been modified since the pull. `climate-overview` shows as `modified` — a local edit is pending.*
|
|
287
|
+
|
|
288
|
+
### `hadsync push` — safe confirmation
|
|
289
|
+
|
|
290
|
+

|
|
291
|
+
|
|
292
|
+
*Before pushing, hadsync shows the current HA state alongside what would be sent — 6 views, 32 cards on both sides here — and requires an explicit `y` to proceed. A `--dry-run` flag shows this summary without connecting to HA at all.*
|
|
293
|
+
|
|
261
294
|
### `hadsync diff` — conflict summary
|
|
262
295
|
|
|
263
296
|

|
|
@@ -36,29 +36,32 @@ HA stores Lovelace dashboard configs in its internal storage layer. There is no
|
|
|
36
36
|
## Installation
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
pip install hadsync
|
|
39
|
+
pip install hadsync
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
Requires Python 3.11+.
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
**With uv — installs `hadsync` globally so it works from any directory:**
|
|
44
|
+
**With uv (recommended — installs globally so `hadsync` works from any directory):**
|
|
47
45
|
|
|
48
46
|
```bash
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
uv tool install hadsync
|
|
48
|
+
```
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
uv tool install --editable /path/to/hadsync
|
|
50
|
+
After installation, `hadsync` is available system-wide. Upgrade to a newer release with:
|
|
54
51
|
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
```bash
|
|
53
|
+
pip install --upgrade hadsync
|
|
54
|
+
# or
|
|
55
|
+
uv tool upgrade hadsync
|
|
57
56
|
```
|
|
58
57
|
|
|
59
|
-
|
|
58
|
+
### Install from source
|
|
60
59
|
|
|
61
60
|
```bash
|
|
61
|
+
# editable install — code changes take effect immediately
|
|
62
|
+
uv tool install --editable /path/to/hadsync
|
|
63
|
+
|
|
64
|
+
# or with pip
|
|
62
65
|
pip install -e ".[dev]"
|
|
63
66
|
```
|
|
64
67
|
|
|
@@ -201,6 +204,36 @@ battery-status
|
|
|
201
204
|
|
|
202
205
|
## In Action
|
|
203
206
|
|
|
207
|
+
### `hadsync validate` — issues found
|
|
208
|
+
|
|
209
|
+

|
|
210
|
+
|
|
211
|
+
*Validation catches two problems before anything reaches HA: an entity ID that no longer exists in the registry, and a `sensor` card missing its required `entity` field. Both are reported with exact line numbers.*
|
|
212
|
+
|
|
213
|
+
### `hadsync validate` — all clear
|
|
214
|
+
|
|
215
|
+

|
|
216
|
+
|
|
217
|
+
*After fixing the two issues the dashboard passes cleanly. The same command validates all dashboards at once when run without an ID argument.*
|
|
218
|
+
|
|
219
|
+
### `hadsync validate` — full suite
|
|
220
|
+
|
|
221
|
+

|
|
222
|
+
|
|
223
|
+
*All 13 storage-mode dashboards pass in a single run — safe to use in CI pipelines since the command exits non-zero on any error.*
|
|
224
|
+
|
|
225
|
+
### `hadsync status` — sync overview
|
|
226
|
+
|
|
227
|
+

|
|
228
|
+
|
|
229
|
+
*A quick at-a-glance table showing when each dashboard was last pulled, when it was last pushed, and whether the local file has been modified since the pull. `climate-overview` shows as `modified` — a local edit is pending.*
|
|
230
|
+
|
|
231
|
+
### `hadsync push` — safe confirmation
|
|
232
|
+
|
|
233
|
+

|
|
234
|
+
|
|
235
|
+
*Before pushing, hadsync shows the current HA state alongside what would be sent — 6 views, 32 cards on both sides here — and requires an explicit `y` to proceed. A `--dry-run` flag shows this summary without connecting to HA at all.*
|
|
236
|
+
|
|
204
237
|
### `hadsync diff` — conflict summary
|
|
205
238
|
|
|
206
239
|

|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.3"
|
|
@@ -578,7 +578,9 @@ async def _diff_async(dashboard_id: Optional[str], show: bool) -> None:
|
|
|
578
578
|
mtime = datetime.fromtimestamp(
|
|
579
579
|
yaml_path.stat().st_mtime, tz=timezone.utc
|
|
580
580
|
)
|
|
581
|
-
local_modified =
|
|
581
|
+
local_modified = (
|
|
582
|
+
mtime.replace(microsecond=0) > pull_dt.replace(microsecond=0)
|
|
583
|
+
)
|
|
582
584
|
except Exception:
|
|
583
585
|
pass
|
|
584
586
|
|
|
@@ -843,7 +845,12 @@ def status() -> None:
|
|
|
843
845
|
if pull_dt.tzinfo is None:
|
|
844
846
|
pull_dt = pull_dt.replace(tzinfo=timezone.utc)
|
|
845
847
|
mtime = datetime.fromtimestamp(yaml_path.stat().st_mtime, tz=timezone.utc)
|
|
846
|
-
|
|
848
|
+
# Compare at whole-second granularity. macOS APFS can commit the
|
|
849
|
+
# file mtime a few microseconds after Python records last_pull,
|
|
850
|
+
# causing a false-positive "modified" on freshly-pulled files.
|
|
851
|
+
# A sub-second difference means the pull itself wrote the file.
|
|
852
|
+
modified = mtime.replace(microsecond=0) > pull_dt.replace(microsecond=0)
|
|
853
|
+
return "[yellow]modified[/yellow]" if modified else "[green]clean[/green]"
|
|
847
854
|
except Exception:
|
|
848
855
|
return "[dim]unknown[/dim]"
|
|
849
856
|
|
|
@@ -40,7 +40,12 @@ function isLocallyModified(cwd: string, urlPath: string, lastPull: string): bool
|
|
|
40
40
|
const yamlPath = path.join(cwd, urlPath, 'lovelace.yaml');
|
|
41
41
|
const stat = fs.statSync(yamlPath);
|
|
42
42
|
const pullTime = new Date(lastPull).getTime();
|
|
43
|
-
|
|
43
|
+
// Compare at whole-second granularity to avoid a false-positive on freshly
|
|
44
|
+
// pulled files: macOS APFS can commit the mtime a few microseconds after
|
|
45
|
+
// the pull records last_pull, making mtime > pullTime by a tiny amount.
|
|
46
|
+
const mtimeSec = Math.floor(stat.mtimeMs / 1000);
|
|
47
|
+
const pullSec = Math.floor(pullTime / 1000);
|
|
48
|
+
return mtimeSec > pullSec;
|
|
44
49
|
} catch {
|
|
45
50
|
return false;
|
|
46
51
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|