feat: initial scaffold and profiles for Schneider iEM2135, LUG heat meter v4
Seed the repo described in pda-fieldbus ADR-0009: a sibling repo that
ships device profiles independently of the fieldbus binary.
Layout:
- profiles/schneider-iem2135.json — distilled from the inline extract
rules in examples/poll-d27-g110.yaml
- profiles/lug-heat-meter-v4.json — heat-meter profile with derived
delta_temperature
Both validate against pda-fieldbus's profile.LoadDirs.
Packaging:
- nfpm.yaml builds pda-fieldbus-profiles.deb installing profiles/ to
/usr/share/pda-fieldbus/profiles/, where the loader's DirPackaged dir
picks them up. Recommends pda-fieldbus.
- .gitea/workflows/auto-tag.yml: same conventional-commit auto-tagging
as pda-fieldbus, on tag push installs nfpm, builds .deb, uploads to
repo.pda.cz/PDAT/main using the existing PDA_REPO_TOKEN secret.
- .gitea/workflows/ci.yml: JSON syntax check + schema validation by
importing pda-fieldbus's loader and calling LoadDirs against profiles/.
This commit is contained in:
@@ -0,0 +1,96 @@
|
|||||||
|
name: Auto Tag
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tag:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
new_tag: ${{ steps.bump.outputs.new_tag }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Determine version bump and create tag
|
||||||
|
id: bump
|
||||||
|
run: |
|
||||||
|
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
|
||||||
|
echo "Latest tag: $LATEST_TAG"
|
||||||
|
|
||||||
|
COMMITS=$(git log "${LATEST_TAG}..HEAD" --pretty=format:"%s" 2>/dev/null)
|
||||||
|
if [ -z "$COMMITS" ]; then
|
||||||
|
echo "No new commits since ${LATEST_TAG}, skipping."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
BUMP=""
|
||||||
|
if echo "$COMMITS" | grep -qiE "^[a-z]+(\(.+\))?!:|BREAKING CHANGE"; then
|
||||||
|
BUMP="major"
|
||||||
|
elif echo "$COMMITS" | grep -qiE "^feat(\(.+\))?:"; then
|
||||||
|
BUMP="minor"
|
||||||
|
elif echo "$COMMITS" | grep -qiE "^fix(\(.+\))?:"; then
|
||||||
|
BUMP="patch"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$BUMP" ]; then
|
||||||
|
echo "No conventional commit triggers, skipping."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERSION="${LATEST_TAG#v}"
|
||||||
|
MAJOR=$(echo "$VERSION" | cut -d. -f1)
|
||||||
|
MINOR=$(echo "$VERSION" | cut -d. -f2)
|
||||||
|
PATCH=$(echo "$VERSION" | cut -d. -f3)
|
||||||
|
|
||||||
|
case "$BUMP" in
|
||||||
|
major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
|
||||||
|
minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
|
||||||
|
patch) PATCH=$((PATCH + 1)) ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
NEW_TAG="v${MAJOR}.${MINOR}.${PATCH}"
|
||||||
|
echo "Creating tag: $NEW_TAG"
|
||||||
|
|
||||||
|
git tag "$NEW_TAG"
|
||||||
|
git push origin "$NEW_TAG"
|
||||||
|
echo "new_tag=$NEW_TAG" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
release:
|
||||||
|
needs: tag
|
||||||
|
if: needs.tag.outputs.new_tag != ''
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ needs.tag.outputs.new_tag }}
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: "1.25"
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
- name: Install nfpm
|
||||||
|
run: go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.41.3
|
||||||
|
|
||||||
|
- name: Build .deb
|
||||||
|
run: |
|
||||||
|
VER="${{ needs.tag.outputs.new_tag }}"
|
||||||
|
VER="${VER#v}"
|
||||||
|
mkdir -p dist
|
||||||
|
VERSION="$VER" nfpm pkg --packager deb --target dist/ -f nfpm.yaml
|
||||||
|
ls -la dist/
|
||||||
|
|
||||||
|
- name: Publish .deb to repo.pda.cz
|
||||||
|
run: |
|
||||||
|
for deb in dist/*.deb; do
|
||||||
|
echo "Uploading $deb"
|
||||||
|
curl --fail -X POST https://repo.pda.cz/api/v1/PDAT/main/upload \
|
||||||
|
-H "Authorization: Bearer ${{ secrets.PDA_REPO_TOKEN }}" \
|
||||||
|
-F "file=@${deb}"
|
||||||
|
done
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: "1.25"
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
- name: JSON syntax check
|
||||||
|
run: |
|
||||||
|
for f in profiles/*.json; do
|
||||||
|
echo "checking $f"
|
||||||
|
python3 -m json.tool "$f" > /dev/null
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Schema validation via pda-fieldbus loader
|
||||||
|
working-directory: ${{ runner.temp }}
|
||||||
|
run: |
|
||||||
|
mkdir -p validator && cd validator
|
||||||
|
go mod init validator
|
||||||
|
go get github.com/pdat-cz/pda-fieldbus@latest
|
||||||
|
cat > main.go <<'EOF'
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pdat-cz/pda-fieldbus/pkg/proto/profile"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) != 2 {
|
||||||
|
fmt.Fprintln(os.Stderr, "usage: validator <profiles-dir>")
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
m, err := profile.LoadDirs(os.Args[1])
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "FAIL:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
for n, p := range m {
|
||||||
|
fmt.Printf("OK %s device=%s/%s points=%d derived=%d\n",
|
||||||
|
n, p.Device.Manufacturer, p.Device.Model,
|
||||||
|
len(p.Points), len(p.Derived))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
go run . "$GITHUB_WORKSPACE/profiles"
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/dist/
|
||||||
|
/.cache/
|
||||||
|
*.deb
|
||||||
|
*.tar.gz
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 PDA Servers
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
VERSION ?= 0.0.0-dev
|
||||||
|
|
||||||
|
.PHONY: package clean
|
||||||
|
|
||||||
|
# Build a .deb at $(VERSION). Requires nfpm in PATH.
|
||||||
|
# Example: make package VERSION=0.1.0
|
||||||
|
package:
|
||||||
|
mkdir -p dist
|
||||||
|
VERSION=$(VERSION) nfpm pkg --packager deb --target dist/ -f nfpm.yaml
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf dist/
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# pda-fieldbus-profiles
|
||||||
|
|
||||||
|
Reusable device profiles for [pda-fieldbus](https://git.pda.cz/PDAT/pda-fieldbus).
|
||||||
|
|
||||||
|
A device profile is a JSON description of one device model: which
|
||||||
|
DIF/VIF records to read on M-Bus (or which registers on Modbus), what
|
||||||
|
units they have, how to scale them, and any derived values to compute
|
||||||
|
from them. Profiles let `pda-fieldbus poll` configurations stay short
|
||||||
|
(`profile: schneider-iem2135`) instead of repeating extract rules per
|
||||||
|
deployment.
|
||||||
|
|
||||||
|
See `pda-fieldbus` ADR-0009 and spec PDA-0010 for the full schema and
|
||||||
|
loader semantics.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
profiles/
|
||||||
|
schneider-iem2135.json
|
||||||
|
lug-heat-meter-v4.json
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Each file is one profile. The filename stem is the profile name
|
||||||
|
referenced from a poll config (`profile: schneider-iem2135`).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
The `pda-fieldbus-profiles` `.deb` installs files to:
|
||||||
|
|
||||||
|
```
|
||||||
|
/usr/share/pda-fieldbus/profiles/
|
||||||
|
```
|
||||||
|
|
||||||
|
The `pda-fieldbus` loader scans that directory plus
|
||||||
|
`/etc/pda-fieldbus/profiles.d/` (operator overrides — wins on filename
|
||||||
|
collision). To override a packaged profile on one box, copy it to the
|
||||||
|
operator dir and edit there; the package will not overwrite it.
|
||||||
|
|
||||||
|
## Adding a profile
|
||||||
|
|
||||||
|
1. Create `profiles/<name>.json` following an existing profile.
|
||||||
|
2. Validate it loads against the current `pda-fieldbus` loader:
|
||||||
|
```
|
||||||
|
pda-fieldbus poll --config <some.yaml> --profile-dir ./profiles --dry-run
|
||||||
|
```
|
||||||
|
3. Open a PR. Use a `feat:` commit (`feat: add <vendor>-<model> profile`).
|
||||||
|
The `feat:` prefix triggers a minor version bump and a release of the
|
||||||
|
`.deb` to repo.pda.cz.
|
||||||
|
|
||||||
|
## Conventional commits
|
||||||
|
|
||||||
|
Same convention as `pda-fieldbus`:
|
||||||
|
|
||||||
|
- `feat:` — new profile (minor bump)
|
||||||
|
- `fix:` — correction to an existing profile (patch bump)
|
||||||
|
- `docs:`, `chore:`, `ci:` — no version bump
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT. See `LICENSE`.
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# nfpm config — package profiles/ as /usr/share/pda-fieldbus/profiles/
|
||||||
|
# Version is injected at build time:
|
||||||
|
# nfpm pkg --packager deb --target dist/ -f nfpm.yaml -v "$VERSION"
|
||||||
|
|
||||||
|
name: pda-fieldbus-profiles
|
||||||
|
arch: all
|
||||||
|
platform: linux
|
||||||
|
version: ${VERSION}
|
||||||
|
section: contrib/utils
|
||||||
|
priority: optional
|
||||||
|
maintainer: p.d.a. <info@pda.cz>
|
||||||
|
vendor: p.d.a.
|
||||||
|
homepage: https://git.pda.cz/PDAT/pda-fieldbus-profiles
|
||||||
|
license: MIT
|
||||||
|
description: |
|
||||||
|
Device profiles for pda-fieldbus.
|
||||||
|
Reusable JSON descriptions of M-Bus and Modbus device models — points,
|
||||||
|
units, scaling, and derived values — consumed by pda-fieldbus poll
|
||||||
|
via `profile: <name>` references.
|
||||||
|
|
||||||
|
contents:
|
||||||
|
- src: profiles/
|
||||||
|
dst: /usr/share/pda-fieldbus/profiles/
|
||||||
|
type: tree
|
||||||
|
|
||||||
|
deb:
|
||||||
|
fields:
|
||||||
|
Recommends: pda-fieldbus
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"schema": "pda-fieldbus.profile/v1",
|
||||||
|
"device": {
|
||||||
|
"manufacturer": "LUG",
|
||||||
|
"model": "heat-meter-v4",
|
||||||
|
"protocol": "mbus"
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"version": "1"
|
||||||
|
},
|
||||||
|
"points": {
|
||||||
|
"energy": {
|
||||||
|
"addr": "mbus/dif:0C/vif:0F",
|
||||||
|
"unit": "gigajoule",
|
||||||
|
"scale": 1e-9,
|
||||||
|
"dimensions": "energy",
|
||||||
|
"description": "Total heat energy"
|
||||||
|
},
|
||||||
|
"power": {
|
||||||
|
"addr": "mbus/dif:0B/vif:2D",
|
||||||
|
"unit": "kilowatt",
|
||||||
|
"scale": 0.001,
|
||||||
|
"dimensions": "power",
|
||||||
|
"description": "Instantaneous thermal power"
|
||||||
|
},
|
||||||
|
"flow_temperature": {
|
||||||
|
"addr": "mbus/dif:0A/vif:5B",
|
||||||
|
"unit": "celsius",
|
||||||
|
"dimensions": "temperature",
|
||||||
|
"description": "Supply (flow) temperature"
|
||||||
|
},
|
||||||
|
"return_temperature": {
|
||||||
|
"addr": "mbus/dif:0A/vif:5F",
|
||||||
|
"unit": "celsius",
|
||||||
|
"dimensions": "temperature",
|
||||||
|
"description": "Return temperature"
|
||||||
|
},
|
||||||
|
"volume_flow": {
|
||||||
|
"addr": "mbus/dif:0B/vif:3B",
|
||||||
|
"unit": "cubic_metre_per_hour",
|
||||||
|
"dimensions": "flow",
|
||||||
|
"description": "Volumetric flow rate"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"derived": {
|
||||||
|
"delta_temperature": {
|
||||||
|
"function": "sub",
|
||||||
|
"inputs": ["flow_temperature", "return_temperature"],
|
||||||
|
"unit": "celsius",
|
||||||
|
"dimensions": "temperature",
|
||||||
|
"description": "Flow minus return temperature"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"schema": "pda-fieldbus.profile/v1",
|
||||||
|
"device": {
|
||||||
|
"manufacturer": "Schneider Electric",
|
||||||
|
"model": "iEM2135",
|
||||||
|
"protocol": "mbus"
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"version": "1",
|
||||||
|
"references": [
|
||||||
|
"Schneider iEM2000 series user manual"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"points": {
|
||||||
|
"current": {
|
||||||
|
"addr": "mbus/dif:05/vif:FD/vife:DC,FF,00",
|
||||||
|
"unit": "ampere",
|
||||||
|
"dimensions": "current",
|
||||||
|
"description": "Total current"
|
||||||
|
},
|
||||||
|
"power": {
|
||||||
|
"addr": "mbus/dif:05/vif:2E",
|
||||||
|
"unit": "watt",
|
||||||
|
"dimensions": "power",
|
||||||
|
"description": "Active power"
|
||||||
|
},
|
||||||
|
"energy": {
|
||||||
|
"addr": "mbus/dif:07/vif:03",
|
||||||
|
"unit": "kilowatt_hour",
|
||||||
|
"scale": 0.001,
|
||||||
|
"dimensions": "energy",
|
||||||
|
"description": "Total active energy import"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user