givenergy-cli 1.0.0__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.
@@ -0,0 +1 @@
1
+ skips: []
@@ -0,0 +1,19 @@
1
+ {
2
+ "hooks": {
3
+ "PostToolUse": [
4
+ {
5
+ "matcher": {
6
+ "tool_name": "Edit|Write",
7
+ "file_pattern": "**/*.py"
8
+ },
9
+ "hooks": [
10
+ {
11
+ "type": "command",
12
+ "command": "uv run ruff check --fix \"$CLAUDE_TOOL_INPUT_FILE_PATH\" && uv run ruff format \"$CLAUDE_TOOL_INPUT_FILE_PATH\"",
13
+ "statusMessage": "Linting and formatting..."
14
+ }
15
+ ]
16
+ }
17
+ ]
18
+ }
19
+ }
@@ -0,0 +1,20 @@
1
+ version: 2
2
+ updates:
3
+ # Python deps from pyproject.toml + uv.lock.
4
+ # Restrict to direct deps so transitive churn doesn't generate update PRs.
5
+ - package-ecosystem: "pip"
6
+ directory: "/"
7
+ schedule:
8
+ interval: "weekly"
9
+ open-pull-requests-limit: 5
10
+ groups:
11
+ pip-direct:
12
+ patterns: ["*"]
13
+ allow:
14
+ - dependency-type: "direct"
15
+
16
+ # Pin GitHub Actions versions; quiet cadence since these rarely change.
17
+ - package-ecosystem: "github-actions"
18
+ directory: "/"
19
+ schedule:
20
+ interval: "monthly"
@@ -0,0 +1,114 @@
1
+ name: Bump givenergy-modbus
2
+
3
+ # Triggered by a repository_dispatch event from dewet22/givenergy-modbus's
4
+ # release workflow. Also exposed as workflow_dispatch for manual runs.
5
+ on:
6
+ repository_dispatch:
7
+ types: [givenergy-modbus-release]
8
+ workflow_dispatch:
9
+ inputs:
10
+ version:
11
+ description: "Version to bump givenergy-modbus to (e.g. 1.2.0)"
12
+ required: true
13
+
14
+ jobs:
15
+ bump:
16
+ runs-on: ubuntu-latest
17
+ permissions:
18
+ contents: write
19
+ pull-requests: write
20
+ steps:
21
+ - name: Determine target version and base branch
22
+ id: ver
23
+ env:
24
+ DISPATCH_VERSION: ${{ github.event.client_payload.version }}
25
+ MANUAL_VERSION: ${{ inputs.version }}
26
+ run: |
27
+ NEW="${DISPATCH_VERSION:-$MANUAL_VERSION}"
28
+ if [ -z "$NEW" ]; then
29
+ echo "::error::no version supplied via client_payload.version or workflow inputs"
30
+ exit 1
31
+ fi
32
+ # Route the PR to the branch whose dependency range that major
33
+ # version belongs to. Update the case statement when a new release
34
+ # branch (and/or new modbus major) is introduced.
35
+ major="${NEW%%.*}"
36
+ case "$major" in
37
+ 1) base=main ;;
38
+ 2) base=v1.0 ;;
39
+ *)
40
+ echo "::error::no target branch configured for givenergy-modbus major $major (version $NEW)"
41
+ exit 1
42
+ ;;
43
+ esac
44
+ echo "new=$NEW" >> "$GITHUB_OUTPUT"
45
+ echo "base=$base" >> "$GITHUB_OUTPUT"
46
+
47
+ - uses: actions/checkout@v6
48
+ with:
49
+ ref: ${{ steps.ver.outputs.base }}
50
+
51
+ - uses: astral-sh/setup-uv@v7
52
+ with:
53
+ enable-cache: true
54
+
55
+ - name: Bump pin in pyproject.toml
56
+ env:
57
+ NEW: ${{ steps.ver.outputs.new }}
58
+ run: |
59
+ uv run --with packaging python - <<'PY'
60
+ import os, re, pathlib
61
+ from packaging.version import Version
62
+ new = os.environ["NEW"]
63
+ p = pathlib.Path("pyproject.toml")
64
+ text = p.read_text()
65
+ # Pin form: "givenergy-modbus>=X,<Y" — capture both bounds so the new
66
+ # floor can be validated against the existing cap before writing.
67
+ pin_re = re.compile(r'"givenergy-modbus>=([^",]+),<([^",]+)"')
68
+ m = pin_re.search(text)
69
+ if not m:
70
+ raise SystemExit("no givenergy-modbus pin with >=X,<Y form found in pyproject.toml")
71
+ upper = m.group(2)
72
+ # Compare against the release portion of NEW so e.g. "2.0.0rc1" is
73
+ # rejected against "<2.0.0" — otherwise uv would silently resolve the
74
+ # resulting spec to an empty version set.
75
+ if Version(Version(new).base_version) >= Version(upper):
76
+ print(
77
+ f"::error::new version {new} violates existing upper cap <{upper}; "
78
+ f"bump the cap manually in pyproject.toml first"
79
+ )
80
+ raise SystemExit(1)
81
+ p.write_text(pin_re.sub(f'"givenergy-modbus>={new},<{upper}"', text))
82
+ PY
83
+
84
+ - name: Refresh uv.lock
85
+ run: uv lock --upgrade-package givenergy-modbus
86
+
87
+ - name: Commit, push, open PR
88
+ env:
89
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
90
+ NEW: ${{ steps.ver.outputs.new }}
91
+ BASE: ${{ steps.ver.outputs.base }}
92
+ run: |
93
+ if git diff --quiet pyproject.toml uv.lock; then
94
+ echo "nothing to bump (pin already at ${NEW}?)"
95
+ exit 0
96
+ fi
97
+ git config user.name "github-actions[bot]"
98
+ git config user.email "github-actions[bot]@users.noreply.github.com"
99
+ branch="bump/givenergy-modbus-${NEW}"
100
+ git checkout -B "$branch"
101
+ git add pyproject.toml uv.lock
102
+ git commit -m "fix(deps): bump givenergy-modbus to ${NEW}"
103
+ git push -f origin "$branch"
104
+
105
+ existing=$(gh pr list --head "$branch" --json number --jq '.[0].number // empty')
106
+ if [ -n "$existing" ]; then
107
+ echo "PR #${existing} already open for ${branch}; updated branch in place"
108
+ else
109
+ gh pr create \
110
+ --title "fix(deps): bump givenergy-modbus to ${NEW}" \
111
+ --body "Automated bump triggered by [givenergy-modbus@${NEW}](https://github.com/dewet22/givenergy-modbus/releases/tag/v${NEW})." \
112
+ --base "$BASE" \
113
+ --head "$branch"
114
+ fi
@@ -0,0 +1,145 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ bump:
7
+ description: Version bump type (ignored when republish_tag is set)
8
+ required: true
9
+ type: choice
10
+ options:
11
+ - patch
12
+ - minor
13
+ - major
14
+ - prerelease
15
+ - finalize
16
+ prerelease_stage:
17
+ description: With major/minor/patch — start a prerelease line (e.g. major + alpha → 2.0.0a1). With prerelease — advance the stage forward-only (e.g. prerelease + rc on 2.0.0a6 → 2.0.0rc1).
18
+ required: false
19
+ default: none
20
+ type: choice
21
+ options:
22
+ - none
23
+ - alpha
24
+ - beta
25
+ - rc
26
+ republish_tag:
27
+ description: Existing tag to re-publish (e.g. v1.0.0a1). Skips bump/commit/tag and the GitHub Release creation; only builds and publishes to PyPI.
28
+ required: false
29
+ type: string
30
+
31
+ jobs:
32
+ release:
33
+ # Pure-Python package: the wheel is platform-independent, so a single
34
+ # Ubuntu runner is sufficient.
35
+ runs-on: ubuntu-latest
36
+ permissions:
37
+ contents: write
38
+ # Required for PyPI trusted publishing via OIDC (no API token).
39
+ id-token: write
40
+
41
+ steps:
42
+ - name: Checkout
43
+ uses: actions/checkout@v6
44
+ with:
45
+ # For republish: check out the tagged commit so the build picks up
46
+ # the exact pyproject.toml/uv.lock state that was originally released.
47
+ ref: ${{ inputs.republish_tag || github.ref }}
48
+
49
+ - name: Set up uv
50
+ uses: astral-sh/setup-uv@v7
51
+ with:
52
+ enable-cache: true
53
+
54
+ - name: Determine version
55
+ id: version
56
+ env:
57
+ BUMP: ${{ inputs.bump }}
58
+ PRERELEASE_STAGE: ${{ inputs.prerelease_stage }}
59
+ REPUBLISH_TAG: ${{ inputs.republish_tag }}
60
+ run: |
61
+ if [ -n "$REPUBLISH_TAG" ]; then
62
+ # Republish path: derive version from the tag, skip the bump.
63
+ NEW="${REPUBLISH_TAG#v}"
64
+ else
65
+ CURRENT=$(uv version --short)
66
+ # `prerelease_stage=none` (or unset) means no prerelease flag passed.
67
+ if [ -n "$PRERELEASE_STAGE" ] && [ "$PRERELEASE_STAGE" != "none" ]; then
68
+ NEW=$(python3 scripts/release.py bump "$CURRENT" "$BUMP" --prerelease "$PRERELEASE_STAGE")
69
+ else
70
+ NEW=$(python3 scripts/release.py bump "$CURRENT" "$BUMP")
71
+ fi
72
+ uv version "$NEW"
73
+ uv lock
74
+ fi
75
+ echo "version=$NEW" >> $GITHUB_OUTPUT
76
+ # Expose whether this is a prerelease so downstream steps can mark it.
77
+ if [[ "$NEW" =~ (a|b|rc)[0-9]+$ ]]; then
78
+ echo "prerelease=true" >> $GITHUB_OUTPUT
79
+ else
80
+ echo "prerelease=false" >> $GITHUB_OUTPUT
81
+ fi
82
+
83
+ - name: Generate CHANGELOG section
84
+ if: inputs.republish_tag == ''
85
+ id: changelog
86
+ env:
87
+ VERSION: ${{ steps.version.outputs.version }}
88
+ run: |
89
+ BODY=$(python3 scripts/release.py generate "$VERSION")
90
+ {
91
+ echo 'body<<EOF_BODY'
92
+ echo "$BODY"
93
+ echo 'EOF_BODY'
94
+ } >> $GITHUB_OUTPUT
95
+
96
+ - name: Commit and tag
97
+ if: inputs.republish_tag == ''
98
+ run: |
99
+ git config user.name "github-actions[bot]"
100
+ git config user.email "github-actions[bot]@users.noreply.github.com"
101
+ git add pyproject.toml CHANGELOG.md uv.lock
102
+ git commit -m "chore: release ${{ steps.version.outputs.version }}"
103
+ git tag "v${{ steps.version.outputs.version }}"
104
+ git push
105
+ git push origin "v${{ steps.version.outputs.version }}"
106
+
107
+ - name: Build
108
+ run: uv build
109
+
110
+ - name: Create GitHub Release
111
+ if: inputs.republish_tag == ''
112
+ uses: softprops/action-gh-release@v3
113
+ with:
114
+ tag_name: v${{ steps.version.outputs.version }}
115
+ name: v${{ steps.version.outputs.version }}
116
+ body: ${{ steps.changelog.outputs.body }}
117
+ files: dist/*.whl
118
+ draft: false
119
+ prerelease: ${{ steps.version.outputs.prerelease == 'true' }}
120
+
121
+ - name: Publish to PyPI
122
+ uses: pypa/gh-action-pypi-publish@release/v1
123
+ with:
124
+ skip-existing: true
125
+
126
+ - name: Wait for PyPI to index the new version
127
+ env:
128
+ VERSION: ${{ steps.version.outputs.version }}
129
+ run: |
130
+ # pypa/gh-action-pypi-publish returns once PyPI accepts the upload,
131
+ # but there is a brief eventual-consistency window before the new
132
+ # version is resolvable by `pip install` / `uv lock`. Hold the run
133
+ # open until the JSON API confirms the version is indexed so anyone
134
+ # tailing the workflow knows when the release is genuinely live.
135
+ URL="https://pypi.org/pypi/givenergy-cli/$VERSION/json"
136
+ for attempt in $(seq 1 60); do
137
+ if curl -fsSL "$URL" > /dev/null 2>&1; then
138
+ echo "PyPI has indexed $VERSION (attempt $attempt)"
139
+ exit 0
140
+ fi
141
+ echo "Attempt $attempt/60: $VERSION not yet resolvable, sleeping 10s..."
142
+ sleep 10
143
+ done
144
+ echo "::error::PyPI did not index $VERSION within 10 minutes"
145
+ exit 1
@@ -0,0 +1,141 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ target/
76
+
77
+ # Jupyter Notebook
78
+ .ipynb_checkpoints
79
+
80
+ # IPython
81
+ profile_default/
82
+ ipython_config.py
83
+
84
+ # pyenv
85
+ .python-version
86
+
87
+ # pipenv
88
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
90
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
91
+ # install all needed dependencies.
92
+ #Pipfile.lock
93
+
94
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95
+ __pypackages__/
96
+
97
+ # Celery stuff
98
+ celerybeat-schedule
99
+ celerybeat.pid
100
+
101
+ # SageMath parsed files
102
+ *.sage.py
103
+
104
+ # Environments
105
+ .env
106
+ .venv
107
+ env/
108
+ venv/
109
+ ENV/
110
+ env.bak/
111
+ venv.bak/
112
+ .envrc
113
+
114
+ # Spyder project settings
115
+ .spyderproject
116
+ .spyproject
117
+
118
+ # Rope project settings
119
+ .ropeproject
120
+
121
+ # mkdocs documentation
122
+ /site
123
+
124
+ # mypy
125
+ .mypy_cache/
126
+ .dmypy.json
127
+ dmypy.json
128
+
129
+ # Pyre type checker
130
+ .pyre/
131
+
132
+ # PyCharm
133
+ .idea/
134
+
135
+ # Claude Code — local-only, not shared
136
+ .claude/settings.local.json
137
+ .claude/worktrees/
138
+
139
+ # Scratch exports from manual `givenergy-cli export` runs — these contain real
140
+ # serial numbers and aren't fixtures
141
+ test*.json
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/) and the
6
+ project adheres to [Semantic Versioning](https://semver.org/). Sections are
7
+ generated at release time by `scripts/release.py generate`, which walks
8
+ `git log <last-v-tag>..HEAD` and buckets entries by conventional-commit
9
+ prefix. A per-commit `Changelog: <section>` or `Changelog: skip` git
10
+ trailer overrides the automatic bucketing.
11
+
12
+ ## [1.0.0] - 2026-05-22
13
+
14
+ ### 🔧 Maintenance
15
+
16
+ - align README Python requirement with pyproject (3.14+) (#11) ([238b87c](https://github.com/dewet22/givenergy-cli/commit/238b87c2da52ee24acc178088a4467be47934f76))
17
+
@@ -0,0 +1,31 @@
1
+ # Known issues
2
+
3
+ Things the dashboard doesn't handle cleanly today, with enough context to
4
+ revisit when they actually bite.
5
+
6
+ ## Third-party generation sources on the same AC bus
7
+
8
+ The inverter's `p_load_demand` register is computed by the inverter from
9
+ what it's delivering at its AC port plus/minus what flows through to the
10
+ grid meter. The inverter assumes it's the only generator on the AC bus.
11
+
12
+ If another inverter, a generator, or some other AC source is back-feeding
13
+ the same circuit:
14
+
15
+ - `p_load_demand` will under-report by however much the third party is
16
+ contributing, and can go **negative** when the other source overproduces
17
+ past the house's consumption.
18
+ - The Loads box would render a negative kW value (the value formatter is
19
+ sign-aware but the sparkline and value column aren't designed to draw
20
+ attention to it).
21
+ - The trunk animation still flows inverter → loads, even when the actual
22
+ net flow has reversed.
23
+ - The EnergyBalance ledger's "Total Output" understates real output by the
24
+ third-party contribution, which would show up as a persistent positive
25
+ imbalance — easy to mistake for inverter conversion loss.
26
+
27
+ I haven't fixed this because every GivEnergy install I've seen is
28
+ single-inverter. If it ever becomes relevant we'd want either (a) a
29
+ separate "External" source node in the diagram, (b) a third-party-detected
30
+ annotation on the Grid or Loads box, or (c) a sign-aware Load renderer
31
+ that flips its visual when load is net negative.