kopia lustrzana https://github.com/weetmuts/wmbusmeters
Porównaj commity
223 Commity
Autor | SHA1 | Data |
---|---|---|
Fredrik Öhrström | 73dd60d67b | |
Fredrik Öhrström | b3771a9b8e | |
Fredrik Öhrström | 257a9c1ad2 | |
Fredrik Öhrström | f954860d80 | |
Fredrik Öhrström | 251b575f68 | |
Fredrik Öhrström | 7c117cc26c | |
jetmcquack | 40229d81ff | |
Fredrik Öhrström | 8748a2d52f | |
Fredrik Öhrström | 1396fff5a0 | |
Fredrik Öhrström | 759160b415 | |
Fredrik Öhrström | 9684345cb6 | |
Fredrik Öhrström | 1a4169ed23 | |
PovilasID | aa4f1956e8 | |
Fredrik Öhrström | d90dbb196e | |
Fredrik Öhrström | 14d021426a | |
Michał Morański | f32179a939 | |
Fredrik Öhrström | a32fcfdf9a | |
Fredrik Öhrström | efcc41d107 | |
Fredrik Öhrström | a69e547a17 | |
Fredrik Öhrström | 23f2279a64 | |
Fredrik Öhrström | 05edab0882 | |
Fredrik Öhrström | ff72e1debc | |
Fredrik Öhrström | c1509f6139 | |
Fredrik Öhrström | 9facddf019 | |
Fredrik Öhrström | 9a34a55abb | |
Fredrik Öhrström | 465a450a8b | |
Jean-Samuel REYNAUD | 9576550f85 | |
Fredrik Öhrström | 1f2cd10160 | |
Fredrik Öhrström | 8c09f7b2d8 | |
Fredrik Öhrström | 3247a4a576 | |
Fredrik Öhrström | 23779cb9f7 | |
Fredrik Öhrström | 5962e727ff | |
Arthur van Dorp | 03d95e780e | |
Fredrik Öhrström | 11c83c1f37 | |
Fredrik Öhrström | 7634b95438 | |
Fredrik Öhrström | 78e7c47503 | |
Fredrik Öhrström | 0c98b474bb | |
Fredrik Öhrström | 9d27ab3fb3 | |
Fredrik Öhrström | c21efd1d69 | |
testuser7 | 67230b4213 | |
Fredrik Öhrström | 569efa3af2 | |
Fredrik Öhrström | 58dd9d32ff | |
testuser7 | 7f7f7e4df4 | |
Fredrik Öhrström | af83f15ca9 | |
Fredrik Öhrström | 0587c6d1dd | |
Jean-Samuel Reynaud | dd45c14970 | |
Fredrik Öhrström | 097f91fac0 | |
Fredrik Öhrström | dbd99698b8 | |
Fredrik Öhrström | 40605ebb40 | |
Fredrik Öhrström | 74e6c89af1 | |
testuser7 | 4634431c59 | |
Fredrik Öhrström | c3d1b6f3c5 | |
Fredrik Öhrström | 4b55407aa9 | |
testuser7 | 97f2082dbc | |
testuser7 | a5f4f43375 | |
testuser7 | 000e4f89ad | |
Fredrik Öhrström | 14ca481c63 | |
Fredrik Öhrström | 0d0338c9a4 | |
Fredrik Öhrström | 4d3e306f30 | |
Fredrik Öhrström | 378c367475 | |
Fredrik Öhrström | 004d8b751f | |
Fredrik Öhrström | 72ecb86b91 | |
Fredrik Öhrström | 24b47fbd48 | |
Fredrik Öhrström | 8efad28289 | |
Fredrik Öhrström | 46fc3995bd | |
Fredrik Öhrström | 9b6c84d2f2 | |
Fredrik Öhrström | 54a8930918 | |
Fredrik Öhrström | f446efa11d | |
Fredrik Öhrström | 2610af43b8 | |
Fredrik Öhrström | fefbf507fe | |
Fredrik Öhrström | 04ed4d7628 | |
Fredrik Öhrström | 440ea263b1 | |
Fredrik Öhrström | 8aabcdaca0 | |
Fredrik Öhrström | a9862be62c | |
dependabot[bot] | 4d89d119da | |
dependabot[bot] | eea8307f22 | |
Fredrik Öhrström | 03e03905ec | |
Fredrik Öhrström | fe04152246 | |
BIBO | f33a676dee | |
Fredrik Öhrström | 12cb6c9d17 | |
Petr Švarc | 6641def49f | |
Fredrik Öhrström | 3d1319bd78 | |
Fredrik Öhrström | 1ab3368b8c | |
Fredrik Öhrström | b6b6fe85d0 | |
Fredrik Öhrström | b49309bce3 | |
Fredrik Öhrström | 42511190e7 | |
Fredrik Öhrström | 4f21c04804 | |
Fredrik Öhrström | 9a0a818fee | |
Fredrik Öhrström | abad0c8d74 | |
Fredrik Öhrström | 704e715582 | |
Fredrik Öhrström | 866df740ad | |
Andreas Horrer | cbc422ac94 | |
Fredrik Öhrström | 295053ebd6 | |
Fredrik Öhrström | b6878b503c | |
Fredrik Öhrström | 7af4db244d | |
Fredrik Öhrström | fd0a4b6d63 | |
Fredrik Öhrström | cddcc9ae26 | |
Fredrik Öhrström | c08bf05521 | |
Andreas Horrer | 757b56d2c8 | |
Andreas Horrer | f69f9d6aa7 | |
Andreas Horrer | dd2df53280 | |
BIBO | dc783e1e87 | |
dependabot[bot] | d22701f978 | |
Fredrik Öhrström | 331c5a4018 | |
Fredrik Öhrström | 73bb7ef096 | |
Fredrik Öhrström | 10fbac056b | |
Fredrik Öhrström | aff37db2c1 | |
Fredrik Öhrström | b011505d7b | |
Fredrik Öhrström | b0cb4fa44b | |
Fredrik Öhrström | 044a522399 | |
Fredrik Öhrström | 47f3256a50 | |
Fredrik Öhrström | 0c3fa02d4f | |
Fredrik Öhrström | 8077791846 | |
Fredrik Öhrström | 2605ff9cb3 | |
pimlie | 6a9cfcee21 | |
pimlie | efea401f6a | |
pimlie | 4c194ba802 | |
pimlie | db37acbcdc | |
Jacman777 | 92fb04e246 | |
Fredrik Öhrström | b32236f600 | |
Sergio Catalan | 64602a079f | |
Fredrik Öhrström | 162658a29a | |
Fredrik Öhrström | 9fbb49b0fc | |
Fredrik Öhrström | 82234acd21 | |
Fredrik Öhrström | c4b9f0d104 | |
Fredrik Öhrström | 9a99daf0ed | |
Fredrik Öhrström | 3696118db1 | |
Fredrik Öhrström | 92090073cb | |
Fredrik Öhrström | c5dc2ada51 | |
Andreas Horrer | f4299e2d6f | |
Fredrik Öhrström | a385737984 | |
Fredrik Öhrström | 22d6880385 | |
Fredrik Öhrström | b9306be914 | |
Fredrik Öhrström | 3a097388f5 | |
Fredrik Öhrström | b2dd2801db | |
Fredrik Öhrström | 6f0773ae7a | |
Fredrik Öhrström | 128aa77b78 | |
Fredrik Öhrström | 872bc53ace | |
Fredrik Öhrström | a1f0911a57 | |
Fredrik Öhrström | f017694d78 | |
Fredrik Öhrström | 6e3bac97d4 | |
simonr-de | 7c5949d6b1 | |
geraldhuber | eb6311fec3 | |
Fredrik Öhrström | ce6e2822e5 | |
Fredrik Öhrström | eb9c90bd2d | |
Fredrik Öhrström | a8040e5eae | |
Fredrik Öhrström | 099fc1472c | |
Fredrik Öhrström | c6d0b79984 | |
dependabot[bot] | 3c7b37a14b | |
dependabot[bot] | 143f4f2ff0 | |
dependabot[bot] | bc90cf36d3 | |
dependabot[bot] | 108712be3b | |
dependabot[bot] | c4a4d05e46 | |
Fredrik Öhrström | 239a39de0e | |
dependabot[bot] | be3c413664 | |
Fredrik Öhrström | 0b852fb420 | |
Fredrik Öhrström | 9084a0323f | |
Fredrik Öhrström | e577c0b30c | |
Fredrik Öhrström | 71fe4b4392 | |
Fredrik Öhrström | 94d4ddce16 | |
Fredrik Öhrström | 86ba358c7d | |
Fredrik Öhrström | 906c288298 | |
Fredrik Öhrström | a920e25d45 | |
Fredrik Öhrström | 414e55a17a | |
Fredrik Öhrström | 1faabb7526 | |
Fredrik Öhrström | a41bce1145 | |
Fredrik Öhrström | d0f9ffc820 | |
Fredrik Öhrström | b8ac245aeb | |
Fredrik Öhrström | b368647c73 | |
Fredrik Öhrström | 81505354fc | |
Fredrik Öhrström | bd0747eefe | |
PovilasID | c43b8da2bf | |
Fredrik Öhrström | 765f38cdc4 | |
Chris Bednarczyk | 1c960475ca | |
Chris Bednarczyk | 95a4e15baa | |
Chris Bednarczyk | f7b70e4c64 | |
Fredrik Öhrström | 217f0a25fe | |
Fredrik Öhrström | ab9d612b63 | |
Fredrik Öhrström | 2bc88ba1bf | |
Fredrik Öhrström | b9e6f3f028 | |
Fredrik Öhrström | a13104125a | |
Fredrik Öhrström | b64c3e7ccd | |
Fredrik Öhrström | 1611f12753 | |
Fredrik Öhrström | 876b5ce0af | |
Fredrik Öhrström | c1be4b6c34 | |
Mikołaj Milej | 77ad8410eb | |
Christian Güdel | c63cb1ef01 | |
Vyacheslav Karpukhin | 13d18ef711 | |
Fredrik Öhrström | 53c589ed11 | |
Fredrik Öhrström | 17791e5fb2 | |
Fredrik Öhrström | f377ade8c7 | |
Fredrik Öhrström | c85fd3691c | |
Fredrik Öhrström | ccac483b1e | |
Fredrik Öhrström | c14b7dd8f9 | |
Fredrik Öhrström | 14aded94ca | |
Fredrik Öhrström | f174e98423 | |
Fredrik Öhrström | 572d8f3a27 | |
Peter Vágner | f11e72cfdf | |
Peter Vágner | 86c39e5570 | |
Fredrik Öhrström | f26b56caff | |
Fredrik Öhrström | 247829d9a6 | |
demetz | d9476f114e | |
Vyacheslav Karpukhin | 0a7f8a8e2b | |
Fredrik Öhrström | 6a14bb8c77 | |
Fredrik Öhrström | f5e4606778 | |
Fredrik Öhrström | 205b5593a8 | |
Fredrik Öhrström | 006c88239f | |
Fredrik Öhrström | 6fcdaca8ad | |
Fredrik Öhrström | 2983957e32 | |
Fredrik Öhrström | f1858c028f | |
Fredrik Öhrström | 3dd7c0c2c1 | |
Fredrik Öhrström | 217ab96f98 | |
Fredrik Öhrström | 39ed3d2a52 | |
Fredrik Öhrström | 54012a0f30 | |
Fredrik Öhrström | 15aba2318f | |
BIBO | 4ce0834ac6 | |
Fredrik Öhrström | acaf159f33 | |
Fredrik Öhrström | 88a811d434 | |
Fredrik Öhrström | b0bca78c78 | |
Fredrik Öhrström | 3fdd0fb4fe | |
BIBOLV | fd1d8daff9 | |
Fredrik Öhrström | 262c731aca | |
Fredrik Öhrström | 14bf9868cc |
|
@ -7,12 +7,12 @@ jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- id: INSTALL_ADDITIONAL_BUILD_DEPENDENCIES
|
- id: INSTALL_ADDITIONAL_BUILD_DEPENDENCIES
|
||||||
run: |
|
run: |
|
||||||
sudo apt install -y eatmydata
|
sudo apt install -y eatmydata
|
||||||
sudo eatmydata apt install -y devscripts debhelper
|
sudo eatmydata apt install -y devscripts debhelper
|
||||||
sudo eatmydata apt build-dep -y . || sudo eatmydata apt install -y librtlsdr-dev adduser
|
sudo eatmydata apt build-dep -y . || sudo eatmydata apt install -y librtlsdr-dev libxml2-dev libxslt1-dev adduser
|
||||||
- id: PREPARE_SOURCE
|
- id: PREPARE_SOURCE
|
||||||
run: |
|
run: |
|
||||||
ln -s deb debian
|
ln -s deb debian
|
||||||
|
|
|
@ -8,56 +8,111 @@ on:
|
||||||
- '[0-9]+\.[0-9]+\.[0-9]+'
|
- '[0-9]+\.[0-9]+\.[0-9]+'
|
||||||
- '[0-9]+\.[0-9]+\.[0-9]+-RC[0-9]+'
|
- '[0-9]+\.[0-9]+\.[0-9]+-RC[0-9]+'
|
||||||
|
|
||||||
|
env:
|
||||||
|
DOCKERHUB_IMAGE: ${{ github.repository }}
|
||||||
|
IMAGE_TAG: |
|
||||||
|
${{ github.ref_type == 'tag' && format('{0}-{1}', (contains(github.ref_name, '-RC') &&
|
||||||
|
'candidate' || 'release'), github.ref_name) || 'latest' }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
platform:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm/v7
|
||||||
|
- linux/arm64
|
||||||
steps:
|
steps:
|
||||||
|
-
|
||||||
|
name: Prepare
|
||||||
|
run: |
|
||||||
|
platform=${{ matrix.platform }}
|
||||||
|
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Docker meta
|
name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: wmbusmeters/wmbusmeters
|
tags: |
|
||||||
tags: type=ref,event=tag
|
${{ env.IMAGE_TAG }}
|
||||||
-
|
images: |
|
||||||
name: Set up QEMU
|
${{ env.DOCKERHUB_IMAGE }}
|
||||||
uses: docker/setup-qemu-action@v2
|
|
||||||
-
|
-
|
||||||
name: Set up Docker Buildx
|
name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
-
|
-
|
||||||
name: Login to DockerHub
|
name: Login to DockerHub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_PAT }}
|
password: ${{ secrets.DOCKERHUB_PAT }}
|
||||||
-
|
-
|
||||||
name: Build and push not tagged release
|
name: Build and push
|
||||||
if: ${{ !steps.meta.outputs.tags }}
|
uses: docker/build-push-action@v5
|
||||||
uses: docker/build-push-action@v4
|
id: docker_build
|
||||||
with:
|
with:
|
||||||
context: docker/
|
context: docker/
|
||||||
platforms: linux/amd64,linux/arm64,linux/armhf
|
platforms: ${{ matrix.platform }}
|
||||||
push: true
|
provenance: false
|
||||||
tags: wmbusmeters/wmbusmeters:latest
|
outputs: |
|
||||||
|
type=image,name=${{ env.DOCKERHUB_IMAGE }},push-by-digest=true,name-canonical=true,push=true
|
||||||
-
|
-
|
||||||
name: Build and push candidate
|
name: Export digest
|
||||||
if: ${{ steps.meta.outputs.tags && contains(steps.meta.outputs.tags, '-RC') }}
|
run: |
|
||||||
uses: docker/build-push-action@v4
|
mkdir -p /tmp/digests
|
||||||
with:
|
digest="${{ steps.docker_build.outputs.digest }}"
|
||||||
context: docker/
|
touch "/tmp/digests/${digest#sha256:}"
|
||||||
platforms: linux/amd64,linux/arm64,linux/armhf
|
|
||||||
push: true
|
|
||||||
tags: wmbusmeters/wmbusmeters:candidate-${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
|
|
||||||
-
|
-
|
||||||
name: Build and push tagged release
|
name: Upload digest
|
||||||
if: ${{ steps.meta.outputs.tags && !contains(steps.meta.outputs.tags, '-RC') }}
|
uses: actions/upload-artifact@v4
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
with:
|
with:
|
||||||
context: docker/
|
name: digests-${{ env.PLATFORM_PAIR }}
|
||||||
platforms: linux/amd64,linux/arm64,linux/armhf
|
path: /tmp/digests/*
|
||||||
push: true
|
if-no-files-found: error
|
||||||
tags: wmbusmeters/wmbusmeters:release-${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
|
retention-days: 1
|
||||||
|
|
||||||
|
merge:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- build
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Download digests
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: /tmp/digests
|
||||||
|
pattern: digests-*
|
||||||
|
merge-multiple: true
|
||||||
|
-
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
-
|
||||||
|
name: Docker meta
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
tags: |
|
||||||
|
${{ env.IMAGE_TAG }}
|
||||||
|
images: |
|
||||||
|
${{ env.DOCKERHUB_IMAGE }}
|
||||||
|
-
|
||||||
|
name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_PAT }}
|
||||||
|
-
|
||||||
|
name: Create manifest list and push
|
||||||
|
working-directory: /tmp/digests
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||||
|
$(printf '${{ env.DOCKERHUB_IMAGE }}@sha256:%s ' *)
|
||||||
|
-
|
||||||
|
name: Inspect image
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools inspect ${{ env.DOCKERHUB_IMAGE }}:${{ steps.meta.outputs.version }}
|
|
@ -3,11 +3,12 @@ on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- id: INSTALL_ADDITIONAL_BUILD_DEPENDENCIES
|
- id: INSTALL_ADDITIONAL_BUILD_DEPENDENCIES
|
||||||
run: brew install librtlsdr libusb
|
run: brew install librtlsdr libusb
|
||||||
- id: CONFIGURE
|
- id: CONFIGURE
|
||||||
|
|
|
@ -20,14 +20,14 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Set up QEMU
|
name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v3
|
||||||
-
|
-
|
||||||
name: Docker meta
|
name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: wmbusmeters/wmbusmeters
|
images: wmbusmeters/wmbusmeters
|
||||||
tags: type=ref,event=tag
|
tags: type=ref,event=tag
|
||||||
|
|
|
@ -7,11 +7,11 @@ jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- id: INSTALL_ADDITIONAL_BUILD_DEPENDENCIES
|
- id: INSTALL_ADDITIONAL_BUILD_DEPENDENCIES
|
||||||
run: |
|
run: |
|
||||||
sudo apt install -y eatmydata
|
sudo apt install -y eatmydata
|
||||||
sudo eatmydata apt-get install librtlsdr-dev libusb-dev
|
sudo eatmydata apt-get install librtlsdr-dev libusb-dev libxml2-dev libxslt1-dev
|
||||||
- id: CONFIGURE
|
- id: CONFIGURE
|
||||||
run: eatmydata ./configure
|
run: eatmydata ./configure
|
||||||
- id: MAKE
|
- id: MAKE
|
||||||
|
|
|
@ -10,13 +10,13 @@ jobs:
|
||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v8
|
- uses: actions/stale@v9
|
||||||
with:
|
with:
|
||||||
stale-issue-message: 'This issue is stale because it has been open for 2 month with no activity. Remove stale label or comment or this will be closed in 1 month.'
|
stale-issue-message: 'This issue is stale because it has been open for 2 month with no activity. Remove stale label or comment or this will be closed in 1 month.'
|
||||||
close-issue-message: 'This issue was closed because it has been stalled for 1 month with no activity.'
|
close-issue-message: 'This issue was closed because it has been stalled for 1 month with no activity.'
|
||||||
days-before-stale: 60
|
days-before-stale: 60
|
||||||
days-before-close: 30
|
days-before-close: 30
|
||||||
operations-per-run: 1000
|
operations-per-run: 1000
|
||||||
exempt-issue-labels: 'enhancement, Work in progress, Planned'
|
exempt-issue-labels: 'enhancement, work in progress, planned, keep open'
|
||||||
days-before-pr-stale: -1
|
days-before-pr-stale: -1
|
||||||
days-before-pr-close: -1
|
days-before-pr-close: -1
|
||||||
|
|
|
@ -10,9 +10,9 @@ jobs:
|
||||||
dockerHubDescription:
|
dockerHubDescription:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Docker Hub Description
|
- name: Docker Hub Description
|
||||||
uses: peter-evans/dockerhub-description@v3
|
uses: peter-evans/dockerhub-description@v4
|
||||||
env:
|
env:
|
||||||
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PAT }}
|
DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PAT }}
|
||||||
|
|
|
@ -12,7 +12,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repository
|
- name: Check out repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- run: git fetch --prune --unshallow
|
- run: git fetch --prune --unshallow
|
||||||
|
|
||||||
- name: Get wmbusmeters version
|
- name: Get wmbusmeters version
|
||||||
|
@ -28,7 +28,7 @@ jobs:
|
||||||
|
|
||||||
- name: Trigger build for edge release
|
- name: Trigger build for edge release
|
||||||
if: ${{ github.ref_name == 'master' }}
|
if: ${{ github.ref_name == 'master' }}
|
||||||
uses: peter-evans/repository-dispatch@v2
|
uses: peter-evans/repository-dispatch@v3
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.HA_PAT }}
|
token: ${{ secrets.HA_PAT }}
|
||||||
repository: wmbusmeters/wmbusmeters-ha-addon
|
repository: wmbusmeters/wmbusmeters-ha-addon
|
||||||
|
@ -37,7 +37,7 @@ jobs:
|
||||||
|
|
||||||
- name: Trigger build for stable release
|
- name: Trigger build for stable release
|
||||||
if: ${{ github.ref_name != 'master' }}
|
if: ${{ github.ref_name != 'master' }}
|
||||||
uses: peter-evans/repository-dispatch@v2
|
uses: peter-evans/repository-dispatch@v3
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.HA_PAT }}
|
token: ${{ secrets.HA_PAT }}
|
||||||
repository: wmbusmeters/wmbusmeters-ha-addon
|
repository: wmbusmeters/wmbusmeters-ha-addon
|
||||||
|
|
|
@ -6,6 +6,7 @@ packaging/
|
||||||
testaes/
|
testaes/
|
||||||
testoutput/
|
testoutput/
|
||||||
tests_tmp/
|
tests_tmp/
|
||||||
|
3rdparty/
|
||||||
*~
|
*~
|
||||||
config.log
|
config.log
|
||||||
autom4te.cache/
|
autom4te.cache/
|
||||||
|
|
118
CHANGES
118
CHANGES
|
@ -1,3 +1,121 @@
|
||||||
|
|
||||||
|
Fixed long standing confusion wether the DIF binary values are by
|
||||||
|
default signed or unsigned. It turns out that they are signed!
|
||||||
|
Thank you Mathias (Zeppelin500) and KaVauA for sorting this out!
|
||||||
|
|
||||||
|
For unknown VIFS and non-compliant meters the signedness
|
||||||
|
can be overriden to unsigned.
|
||||||
|
|
||||||
|
New improved address specification. E.g. use 12345678.M=KAM.V=1b.T=16
|
||||||
|
to listen to exactly the telegrams with id 12345678 manufacturer KAM,
|
||||||
|
version 0x1b and type 0x16. You if you do not specify any M,V or T, they
|
||||||
|
become wildcards which will be the old default behaviour.
|
||||||
|
|
||||||
|
If you receive multiple telegram versions from the same id, and you want to
|
||||||
|
filter out some versions, do: 12345678,!12345678.V=77
|
||||||
|
|
||||||
|
You can now specify p0 to p250, to read from an mbus using the primary address.
|
||||||
|
E.g. wmbusmeters --pollinterval=5s /dev/ttyUSB1:mbus:2400 TEMP piigth:mbus p0 NOKEY
|
||||||
|
|
||||||
|
Added option --identitymode=(id|id-mfct|full|none) to specify how
|
||||||
|
wmbusmeters groups meter state when receiving telegrams.
|
||||||
|
|
||||||
|
The default (which is the same as before) is to map state based only on id.
|
||||||
|
This usually works ok, however if you have two meters with the same id, but
|
||||||
|
from different manufacturers, you must separate their state with --identitymode=id-mfct
|
||||||
|
Full takes into account version and type as well. None means do not separate state
|
||||||
|
at all, used with wildcards and meters that do not need to keep state, ie all info
|
||||||
|
is in every telegram.
|
||||||
|
|
||||||
|
Version 1.16.1 2024-02-22
|
||||||
|
|
||||||
|
Fix docker file generation.
|
||||||
|
|
||||||
|
Version 1.16.0 2024-02-22
|
||||||
|
|
||||||
|
New build to trigger proper docker versioning.
|
||||||
|
|
||||||
|
Version 1.15.0 2024-02-14
|
||||||
|
Version 1.15.0-RC2 2024-02-14
|
||||||
|
|
||||||
|
Update wmbusmeters-ha-addon with new dockerfile.
|
||||||
|
|
||||||
|
Version 1.15.0-RC1 2024-02-13
|
||||||
|
|
||||||
|
For the daemon you can now drop a driver file (such as iperl.xmq) in /etc/wmbusmeters.driver.d
|
||||||
|
and it will automatically be used (overriding any builtin iperl driver).
|
||||||
|
|
||||||
|
From the command line you can also load a driver file with --driver=file.xmq
|
||||||
|
or load a whole directory with --driverdir=/drivers or in a tuple just use
|
||||||
|
a file name ending with xmq. E.g. "Water driver.xmq 12345678 NOKEY"
|
||||||
|
|
||||||
|
The two first builtin text drivers are elster and iperl.
|
||||||
|
|
||||||
|
ATTENTION! Wmbusmeters now use new -f option when starting rtl_wmbus. There is a
|
||||||
|
warning if rtl_wmbus does not support the -f option and an upgrade is recommended.
|
||||||
|
This option will cause rtl_wmbus to exit with an error if the rtl_sdr dongle stops sending data.
|
||||||
|
This in turn will cause wmbusmeters to restart the pipeline.
|
||||||
|
|
||||||
|
Up till now, the stderr from rtl_sdr has been sent to /dev/null. This is a problem
|
||||||
|
since we cannot see any errors from rtl_sdr that could have caused it to stall.
|
||||||
|
|
||||||
|
However the reason for /dev/null was this bug in rtl_sdr.
|
||||||
|
https://github.com/osmocom/rtl-sdr/commit/142325a93c6ad70f851f43434acfdf75e12dfe03
|
||||||
|
which prevented us from sending the rtl_sdr stderr to wmbusmeters.
|
||||||
|
If we did, rtl_sdr went into a 100% cpu hang when we restarted a wmbusmeters daemon.
|
||||||
|
|
||||||
|
A temporary workaround has been found that both sends the stderr output to wmbusmeters
|
||||||
|
and permits the restart of the daemon. Stderr from rtl_sdr is now sent to
|
||||||
|
/tmp/tmp.XXXXXXX_wmbusmeters_rtlsdr and then tailed into wmbusmeters.
|
||||||
|
This is a temporary solution until the real rtl_sdr bugfix has propagated into enough distributions.
|
||||||
|
|
||||||
|
Add second extension energy MWh VIF 7b00-7b01.
|
||||||
|
|
||||||
|
Sunflowerenergias improved the iwmtx5 driver! Thanks Sunflowerenergias!
|
||||||
|
|
||||||
|
Jacman777 improved the kamheat driver! Thanks Jacman777!
|
||||||
|
|
||||||
|
Pim added a --metershell setting which will invoke a shell command line
|
||||||
|
when a meter is seen for the first time. This can be used to trigger extra
|
||||||
|
commands in HA and other systems, to add the new meter. Thanks Pim!
|
||||||
|
|
||||||
|
Pim added support for the Lansen repeater which sends its own status messages!
|
||||||
|
Pim also fixed a small typo in the human readable date timestamp format!
|
||||||
|
Thanks Pim!
|
||||||
|
|
||||||
|
ATTENTION! The hydrus driver could report the wrong value for total_at_date_m3
|
||||||
|
if an at_date had not been reached yet. This is fixed.
|
||||||
|
|
||||||
|
Added initial support for drivers that can be loaded from config files.
|
||||||
|
Properly receive telegrams from amb8465 which is in command mode.
|
||||||
|
Chris Bednarczyk improved the build process for Darwin/MacOS platform. Thanks Chris!
|
||||||
|
PovilasID added another Hydrodigit version. Thanks PovilasID!
|
||||||
|
Added new units for phase angle: deg rad.
|
||||||
|
Added more fields to the abbb23.
|
||||||
|
|
||||||
|
Version 1.14.0 2023-07-02
|
||||||
|
Version 1.14.0-RC1 2023-07-02
|
||||||
|
|
||||||
|
Added more fields to em24 driver.
|
||||||
|
Added another mfct/type/version combo to em42 driver and the power_kw field.
|
||||||
|
|
||||||
|
Mikołaj Milej imporved the installation script! Thanks Mikołaj!
|
||||||
|
Christian Güdel improved the topaseskr driver to consider negative flows. Thanks Christian!
|
||||||
|
Peter Vágner improved the lansenth driver to consider negative temperatures. Thanks Peter!
|
||||||
|
Dennis Metz improved the ultraheat driver to consider negative flow and temperatures. Thanks Dennis!
|
||||||
|
Bibo added support for the hcae2 driver. Thanks Bibo!
|
||||||
|
|
||||||
|
Improve error messages when telegrams with bad length byte are read.
|
||||||
|
Improve the apator162 driver.
|
||||||
|
|
||||||
|
Added the iwmtx5 driver.
|
||||||
|
|
||||||
|
Vyacheslav Karpukhin improved the qheat driver to decode proprietary format telegrams. Thanks Vyacheslav!
|
||||||
|
|
||||||
|
pvagner improved the lansenth driver to handle negative values. Thanks pvagner!
|
||||||
|
|
||||||
|
demetz imporproved ultraheat driver to handle negative values. Thanks demetz!
|
||||||
|
|
||||||
Version 1.13.1 2023-05-07
|
Version 1.13.1 2023-05-07
|
||||||
Version 1.13.1-RC1 2023-05-07
|
Version 1.13.1-RC1 2023-05-07
|
||||||
|
|
||||||
|
|
61
Makefile
61
Makefile
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (C) 2017-2022 Fredrik Öhrström (gpl-3.0-or-later)
|
# Copyright (C) 2017-2024 Fredrik Öhrström (gpl-3.0-or-later)
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU General Public License as published by
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
@ -138,16 +138,27 @@ ifeq ($(shell uname -s),FreeBSD)
|
||||||
USBLIB = -lusb
|
USBLIB = -lusb
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(shell uname -s),Darwin)
|
||||||
|
CXXFLAGS += -I$(shell brew --prefix)/include
|
||||||
|
LDFLAGS += -L$(shell brew --prefix)/lib
|
||||||
|
endif
|
||||||
|
|
||||||
$(BUILD)/%.o: src/%.cc $(wildcard src/%.h)
|
$(BUILD)/%.o: src/%.cc $(wildcard src/%.h)
|
||||||
$(CXX) $(CXXFLAGS) $< -c -E > $@.src
|
$(CXX) $(CXXFLAGS) $< -c -E > $@.src
|
||||||
$(CXX) $(CXXFLAGS) $< -MMD -c -o $@
|
$(CXX) $(CXXFLAGS) $< -MMD -c -o $@
|
||||||
|
|
||||||
|
$(BUILD)/%.o: src/%.c $(wildcard src/%.h)
|
||||||
|
$(CXX) -I/usr/include/libxml2 $(CXXFLAGS) $< -c -E > $@.src
|
||||||
|
$(CXX) -I/usr/include/libxml2 -fpermissive $(CXXFLAGS) $< -MMD -c -o $@
|
||||||
|
|
||||||
PROG_OBJS:=\
|
PROG_OBJS:=\
|
||||||
|
$(BUILD)/address.o \
|
||||||
$(BUILD)/aes.o \
|
$(BUILD)/aes.o \
|
||||||
$(BUILD)/aescmac.o \
|
$(BUILD)/aescmac.o \
|
||||||
$(BUILD)/bus.o \
|
$(BUILD)/bus.o \
|
||||||
$(BUILD)/cmdline.o \
|
$(BUILD)/cmdline.o \
|
||||||
$(BUILD)/config.o \
|
$(BUILD)/config.o \
|
||||||
|
$(BUILD)/drivers.o \
|
||||||
$(BUILD)/dvparser.o \
|
$(BUILD)/dvparser.o \
|
||||||
$(BUILD)/formula.o \
|
$(BUILD)/formula.o \
|
||||||
$(BUILD)/mbus_rawtty.o \
|
$(BUILD)/mbus_rawtty.o \
|
||||||
|
@ -173,6 +184,7 @@ PROG_OBJS:=\
|
||||||
$(BUILD)/wmbus_rawtty.o \
|
$(BUILD)/wmbus_rawtty.o \
|
||||||
$(BUILD)/wmbus_rc1180.o \
|
$(BUILD)/wmbus_rc1180.o \
|
||||||
$(BUILD)/wmbus_utils.o \
|
$(BUILD)/wmbus_utils.o \
|
||||||
|
$(BUILD)/xmq.o \
|
||||||
$(BUILD)/lora_iu880b.o \
|
$(BUILD)/lora_iu880b.o \
|
||||||
|
|
||||||
# If you run: "make DRIVER=minomess" then only driver_minomess.cc will be compiled into wmbusmeters.
|
# If you run: "make DRIVER=minomess" then only driver_minomess.cc will be compiled into wmbusmeters.
|
||||||
|
@ -182,33 +194,13 @@ ifeq ($(DRIVER),)
|
||||||
DRIVER_OBJS:=$(wildcard src/meter_*.cc) $(wildcard src/driver_*.cc)
|
DRIVER_OBJS:=$(wildcard src/meter_*.cc) $(wildcard src/driver_*.cc)
|
||||||
else
|
else
|
||||||
$(info Building a single driver $(DRIVER))
|
$(info Building a single driver $(DRIVER))
|
||||||
DRIVER_OBJS:=src/driver_auto.cc src/driver_unknown.cc $(wildcard src/meter_*.cc) src/driver_$(DRIVER).cc
|
DRIVER_OBJS:=src/driver_auto.cc src/driver_unknown.cc src/driver_dynamic.cc $(wildcard src/meter_*.cc) src/driver_$(DRIVER).cc
|
||||||
endif
|
endif
|
||||||
DRIVER_OBJS:=$(patsubst src/%.cc,$(BUILD)/%.o,$(DRIVER_OBJS))
|
DRIVER_OBJS:=$(patsubst src/%.cc,$(BUILD)/%.o,$(DRIVER_OBJS))
|
||||||
|
|
||||||
all: $(BUILD)/wmbusmeters $(BUILD)/wmbusmetersd $(BUILD)/wmbusmeters.g $(BUILD)/testinternals
|
all: $(BUILD)/wmbusmeters $(BUILD)/wmbusmetersd $(BUILD)/wmbusmeters.g $(BUILD)/testinternals
|
||||||
|
|
||||||
deb_release:
|
# Create a local binary only package.
|
||||||
@if [ "$(RELEASE)" = "" ] ; then echo "Usage: make deb RELEASE=1.2.3" ; exit 1 ; fi
|
|
||||||
@if [ "$$(cat deb/changelog | grep wmbusmeters\ \( | grep -o $(RELEASE))" != "$(RELEASE)" ]; then \
|
|
||||||
echo "Changelog not updated with this release! It says:" ; \
|
|
||||||
head -n 1 deb/changelog ; \
|
|
||||||
exit 1 ; \
|
|
||||||
fi
|
|
||||||
@rm -rf packaging
|
|
||||||
@mkdir -p packaging
|
|
||||||
@echo "Checking out tag $(RELEASE)..."
|
|
||||||
@(cd packaging ; git clone $(PWD) wmbusmeters-$(RELEASE) ; cd wmbusmeters-$(RELEASE) ; git -c advice.detachedHead=false checkout tags/$(RELEASE) )
|
|
||||||
@(cd packaging/wmbusmeters-$(RELEASE) ; git show -s --format=%ct > ../release_date )
|
|
||||||
@echo "Removing git history..."
|
|
||||||
@(cd packaging ; rm -rf wmbusmeters-$(RELEASE)/.git )
|
|
||||||
@echo "Setting file timestamps to commit date..."
|
|
||||||
@(cd packaging ; export UT=$$(cat ./release_date) ; find . -exec touch -d "@$$UT" \{\} \; )
|
|
||||||
@echo "Creating orig archive..."
|
|
||||||
@(cd packaging ; tar czf ./wmbusmeters_$(RELEASE).orig.tar.gz wmbusmeters-$(RELEASE) )
|
|
||||||
@echo "Running debbuild..."
|
|
||||||
@(cd packaging/wmbusmeters-$(RELEASE) ; cp -a deb debian; debuild )
|
|
||||||
|
|
||||||
deb_local:
|
deb_local:
|
||||||
@rm -rf packaging
|
@rm -rf packaging
|
||||||
@mkdir -p packaging
|
@mkdir -p packaging
|
||||||
|
@ -270,7 +262,7 @@ $(BUILD)/authors.h:
|
||||||
|
|
||||||
# Build binary with debug information. ~15M size binary.
|
# Build binary with debug information. ~15M size binary.
|
||||||
$(BUILD)/wmbusmeters.g: $(PROG_OBJS) $(DRIVER_OBJS) $(BUILD)/main.o $(BUILD)/short_manual.h
|
$(BUILD)/wmbusmeters.g: $(PROG_OBJS) $(DRIVER_OBJS) $(BUILD)/main.o $(BUILD)/short_manual.h
|
||||||
$(CXX) -o $(BUILD)/wmbusmeters.g $(PROG_OBJS) $(DRIVER_OBJS) $(BUILD)/main.o $(LDFLAGS) -lrtlsdr $(USBLIB) -lpthread
|
$(CXX) -o $(BUILD)/wmbusmeters.g $(PROG_OBJS) $(DRIVER_OBJS) $(BUILD)/main.o $(LDFLAGS) -lrtlsdr -lxml2 $(USBLIB) -lpthread
|
||||||
|
|
||||||
# Production build will have debug information stripped. ~1.5M size binary.
|
# Production build will have debug information stripped. ~1.5M size binary.
|
||||||
# DEBUG=true builds, which has address sanitizer code, will always keep the debug information.
|
# DEBUG=true builds, which has address sanitizer code, will always keep the debug information.
|
||||||
|
@ -293,10 +285,10 @@ testinternals: $(BUILD)/testinternals
|
||||||
$(BUILD)/testinternals.o: $(PROG_OBJS) $(DRIVER_OBJS) $(wildcard src/*.h)
|
$(BUILD)/testinternals.o: $(PROG_OBJS) $(DRIVER_OBJS) $(wildcard src/*.h)
|
||||||
|
|
||||||
$(BUILD)/testinternals: $(BUILD)/testinternals.o
|
$(BUILD)/testinternals: $(BUILD)/testinternals.o
|
||||||
$(CXX) -o $(BUILD)/testinternals $(PROG_OBJS) $(DRIVER_OBJS) $(BUILD)/testinternals.o $(LDFLAGS) -lrtlsdr $(USBLIB) -lpthread
|
$(CXX) -o $(BUILD)/testinternals $(PROG_OBJS) $(DRIVER_OBJS) $(BUILD)/testinternals.o $(LDFLAGS) -lrtlsdr -lxml2 $(USBLIB) -lpthread
|
||||||
|
|
||||||
$(BUILD)/fuzz: $(PROG_OBJS) $(DRIVER_OBJS) $(BUILD)/fuzz.o
|
$(BUILD)/fuzz: $(PROG_OBJS) $(DRIVER_OBJS) $(BUILD)/fuzz.o
|
||||||
$(CXX) -o $(BUILD)/fuzz $(PROG_OBJS) $(DRIVER_OBJS) $(BUILD)/fuzz.o $(LDFLAGS) -lrtlsdr -lpthread
|
$(CXX) -o $(BUILD)/fuzz $(PROG_OBJS) $(DRIVER_OBJS) $(BUILD)/fuzz.o $(LDFLAGS) -lrtlsdr -lxml2 -lpthread
|
||||||
|
|
||||||
clean_executables:
|
clean_executables:
|
||||||
rm -rf build/wmbusmeters* build_arm/wmbusmeters* build_debug/wmbusmeters* build_arm_debug/wmbusmeters* *~
|
rm -rf build/wmbusmeters* build_arm/wmbusmeters* build_debug/wmbusmeters* build_arm_debug/wmbusmeters* *~
|
||||||
|
@ -333,16 +325,16 @@ lcov:
|
||||||
(cd build_debug; genhtml lcov.info)
|
(cd build_debug; genhtml lcov.info)
|
||||||
xdg-open build_debug/src/index.html
|
xdg-open build_debug/src/index.html
|
||||||
|
|
||||||
test:
|
test: build/xmq
|
||||||
@./test.sh build/wmbusmeters
|
@./test.sh build/wmbusmeters
|
||||||
|
|
||||||
testd:
|
testd: build/xmq
|
||||||
@./test.sh build_debug/wmbusmeters
|
@./test.sh build_debug/wmbusmeters
|
||||||
|
|
||||||
testdriver:
|
testdriver: build/xmq
|
||||||
@./tests/test_drivers.sh build/wmbusmeters driver_${DRIVER}.cc
|
@./tests/test_drivers.sh build/wmbusmeters driver_${DRIVER}.cc
|
||||||
|
|
||||||
testdriverd:
|
testdriverd: build/xmq
|
||||||
@./tests/test_drivers.sh build_debug/wmbusmeters driver_${DRIVER}.cc
|
@./tests/test_drivers.sh build_debug/wmbusmeters driver_${DRIVER}.cc
|
||||||
|
|
||||||
update_manufacturers:
|
update_manufacturers:
|
||||||
|
@ -461,6 +453,15 @@ deploy:
|
||||||
collect_copyrights:
|
collect_copyrights:
|
||||||
./scripts/collect_copyrights.sh deb/copyright
|
./scripts/collect_copyrights.sh deb/copyright
|
||||||
|
|
||||||
|
3rdparty/xmq/build/default/release/xmq: $(wildcard 3rdparty/xmq/src/main/c/* 3rdparty/xmq/src/main/c/parts/*)
|
||||||
|
@mkdir -p 3rdparty
|
||||||
|
@(cd 3rdparty; git clone --depth 1 https://github.com/libxmq/xmq.git; cd xmq; ./configure)
|
||||||
|
@cat 3rdparty/xmq/build/default/spec.mk
|
||||||
|
@if [ "$$(cat 3rdparty/xmq/build/default/spec.mk | grep CC)" = "CC:=gcc" ]; then (cd 3rdparty/xmq; make VERBOSE=) ; else rm -f $@ ; mkdir -p $$(dirname $@); touch $@ ; echo "Could not build xmq." ; fi
|
||||||
|
|
||||||
|
build/xmq: 3rdparty/xmq/build/default/release/xmq
|
||||||
|
@cp $< $@
|
||||||
|
|
||||||
# Include dependency information generated by gcc in a previous compile.
|
# Include dependency information generated by gcc in a previous compile.
|
||||||
include $(wildcard $(patsubst %.o,%.d,$(PROG_OBJS) $(DRIVER_OBJS)))
|
include $(wildcard $(patsubst %.o,%.d,$(PROG_OBJS) $(DRIVER_OBJS)))
|
||||||
|
|
||||||
|
|
108
README.md
108
README.md
|
@ -6,6 +6,33 @@ wireless wm-bus meters. The readings can then be published using
|
||||||
MQTT, curled to a REST api, inserted into a database or stored in a
|
MQTT, curled to a REST api, inserted into a database or stored in a
|
||||||
log file.
|
log file.
|
||||||
|
|
||||||
|
# What does it do?
|
||||||
|
|
||||||
|
Wmbusmeters converts incoming telegrams from (w)mbus/OMS compatible meters like:
|
||||||
|
`1844AE4C4455223368077A55000000_041389E20100023B0000`
|
||||||
|
|
||||||
|
into human readable:
|
||||||
|
`MyTapWater 33225544 123.529 m³ 0 m³/h 2024-03-03 19:36:22`
|
||||||
|
|
||||||
|
or into csv:
|
||||||
|
`MyTapWater;33225544;123.529;0;2024-03-03 19:36:45`
|
||||||
|
|
||||||
|
or into json:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"media":"water",
|
||||||
|
"meter":"iperl",
|
||||||
|
"name":"MyTapWater",
|
||||||
|
"id":"33225544",
|
||||||
|
"max_flow_m3h":0,
|
||||||
|
"total_m3":123.529,
|
||||||
|
"timestamp":"2024-03-03T18:37:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Wmbusmeters can collect telegrams from radio using hardware dongles or rtl-sdr software radio dongles,
|
||||||
|
or from m-bus meters using serial ports, or from files/pipes.
|
||||||
|
|
||||||
[FAQ/WIKI/MANUAL pages](https://wmbusmeters.github.io/wmbusmeters-wiki/)
|
[FAQ/WIKI/MANUAL pages](https://wmbusmeters.github.io/wmbusmeters-wiki/)
|
||||||
|
|
||||||
The program runs on GNU/Linux, MacOSX, FreeBSD, and Raspberry Pi.
|
The program runs on GNU/Linux, MacOSX, FreeBSD, and Raspberry Pi.
|
||||||
|
@ -64,6 +91,11 @@ wmbus dongles when wmbusmeters startup.
|
||||||
If the serial device (ttyUSB0) might change you can also use `device=im871a:c1,t1`
|
If the serial device (ttyUSB0) might change you can also use `device=im871a:c1,t1`
|
||||||
which will probe all serial devices but only scans for im871a which also speeds it up.
|
which will probe all serial devices but only scans for im871a which also speeds it up.
|
||||||
|
|
||||||
|
Note that the rtl-sdr devices are not found under the tty devices (e.g. `/dev/tty...`).
|
||||||
|
Instead the rtl-sdr devices are accessed through character device special files named `/dev/swradio0` to `/dev/swradio255`[^kernel_docs_sdr]. Wmbusmeters uses librtsldr to probe these devices.
|
||||||
|
|
||||||
|
[^kernel_docs_sdr]: https://docs.kernel.org/userspace-api/media/v4l/dev-sdr.html?highlight=sdr#software-defined-radio-interface-sdr
|
||||||
|
|
||||||
If you have to scan serial devices, then remember that some Raspberry PIs are upset when
|
If you have to scan serial devices, then remember that some Raspberry PIs are upset when
|
||||||
random data is sent to `/dev/ttyAMA0` when it is configured in bluetooth mode.
|
random data is sent to `/dev/ttyAMA0` when it is configured in bluetooth mode.
|
||||||
To solve this, add `donotprobe=/dev/ttyAMA0`
|
To solve this, add `donotprobe=/dev/ttyAMA0`
|
||||||
|
@ -125,9 +157,11 @@ bus the mbus poll request should be sent to.
|
||||||
wmbusmeters --pollinterval=60s MAIN=/dev/ttyUSB0:mbus:2400 MyTempMeter piigth:MAIN:mbus 12001932 NOKEY
|
wmbusmeters --pollinterval=60s MAIN=/dev/ttyUSB0:mbus:2400 MyTempMeter piigth:MAIN:mbus 12001932 NOKEY
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to poll an mbus meter using the primary address, just use
|
If you want to poll an mbus meter using the primary address, use p0 to p250 (deciman numbers)
|
||||||
a number between 0 and 250 instead of the full 8 digit secondary
|
instead of the full 8 digit secondary address.
|
||||||
address.
|
```
|
||||||
|
wmbusmeters --pollinterval=60s MAIN=/dev/ttyUSB0:mbus:2400 MyTempMeter piigth:MAIN:mbus p0 NOKEY
|
||||||
|
```
|
||||||
|
|
||||||
# Example wmbusmeter.conf file
|
# Example wmbusmeter.conf file
|
||||||
|
|
||||||
|
@ -168,7 +202,7 @@ And an mbus meter file in /etc/wmbusmeters.d/MyTempHygro
|
||||||
```ini
|
```ini
|
||||||
name=MyTempHygro
|
name=MyTempHygro
|
||||||
id=11223344
|
id=11223344
|
||||||
driver=piigth:mbus
|
driver=piigth:MAIN:mbus
|
||||||
pollinterval=60s
|
pollinterval=60s
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -212,9 +246,15 @@ The latest reading of the meter can also be found here: `/var/lib/wmbusmeters/me
|
||||||
You can use several ids using `id=1111111,2222222,3333333` or you can listen to all
|
You can use several ids using `id=1111111,2222222,3333333` or you can listen to all
|
||||||
meters of a certain type `id=*` or you can suffix with star `id=8765*` to match
|
meters of a certain type `id=*` or you can suffix with star `id=8765*` to match
|
||||||
all meters with a given prefix. If you supply at least one positive match rule, then you
|
all meters with a given prefix. If you supply at least one positive match rule, then you
|
||||||
can add negative match rules as well. For example `id=*,!2222*`
|
can add filter out rules as well. For example `id=*,!2222*`
|
||||||
which will match all meter ids, except those that begin with 2222.
|
which will match all meter ids, except those that begin with 2222.
|
||||||
|
|
||||||
|
You can also specify the exact manufacturer, version and type: `id=11111111.M=KAM.V=1b.T=16`
|
||||||
|
or a subset: `id=11111111.T=16` or all telegrams from 22222222 except those with version 77:
|
||||||
|
`id=22222222,!22222222.V=77` You can also use the fully specified secondary address that is
|
||||||
|
printed by libmbus after doing a bus scan, ie `100002842941011B` which is equivalent to
|
||||||
|
`10000284.M=PII.V=01.T=1B`
|
||||||
|
|
||||||
When matching all meters from the command line you can use `ANYID` instead of `*` to avoid shell quotes.
|
When matching all meters from the command line you can use `ANYID` instead of `*` to avoid shell quotes.
|
||||||
|
|
||||||
# Add static and calculated fields to the output
|
# Add static and calculated fields to the output
|
||||||
|
@ -390,7 +430,7 @@ depending on if you are running as a daemon or not.
|
||||||
# Running without config files, good for experimentation and test.
|
# Running without config files, good for experimentation and test.
|
||||||
|
|
||||||
```
|
```
|
||||||
wmbusmeters version: 1.13.1
|
wmbusmeters version: 1.15.0
|
||||||
Usage: wmbusmeters {options} [device] { [meter_name] [meter_driver] [meter_id] [meter_key] }*
|
Usage: wmbusmeters {options} [device] { [meter_name] [meter_driver] [meter_id] [meter_key] }*
|
||||||
wmbusmeters {options} [hex] { [meter_name] [meter_driver] [meter_id] [meter_key] }*
|
wmbusmeters {options} [hex] { [meter_name] [meter_driver] [meter_id] [meter_key] }*
|
||||||
wmbusmetersd {options} [pid_file]
|
wmbusmetersd {options} [pid_file]
|
||||||
|
@ -409,9 +449,12 @@ As {options} you can use:
|
||||||
--calculate_flow_f=flow_temperature_c
|
--calculate_flow_f=flow_temperature_c
|
||||||
--debug for a lot of information
|
--debug for a lot of information
|
||||||
--donotprobe=<tty> do not auto-probe this tty. Use multiple times for several ttys or specify "all" for all ttys.
|
--donotprobe=<tty> do not auto-probe this tty. Use multiple times for several ttys or specify "all" for all ttys.
|
||||||
|
--driver=<file> load a driver
|
||||||
|
--driversdir=<dir> load all drivers in dir
|
||||||
--exitafter=<time> exit program after time, eg 20h, 10m 5s
|
--exitafter=<time> exit program after time, eg 20h, 10m 5s
|
||||||
--format=<hr/json/fields> for human readable, json or semicolon separated fields
|
--format=<hr/json/fields> for human readable, json or semicolon separated fields
|
||||||
--help list all options
|
--help list all options
|
||||||
|
--identitymode=(id|id-mfct|full|none) group meter state based on the identity mode. Default is id.
|
||||||
--ignoreduplicates=<bool> ignore duplicate telegrams, remember the last 10 telegrams
|
--ignoreduplicates=<bool> ignore duplicate telegrams, remember the last 10 telegrams
|
||||||
--field_xxx=yyy always add "xxx"="yyy" to the json output and add shell env METER_xxx=yyy (--json_xxx=yyy also works)
|
--field_xxx=yyy always add "xxx"="yyy" to the json output and add shell env METER_xxx=yyy (--json_xxx=yyy also works)
|
||||||
--license print GPLv3+ license
|
--license print GPLv3+ license
|
||||||
|
@ -430,6 +473,7 @@ As {options} you can use:
|
||||||
--meterfilesnaming=(name|id|name-id) the meter file is the meter's: name, id or name-id
|
--meterfilesnaming=(name|id|name-id) the meter file is the meter's: name, id or name-id
|
||||||
--meterfilestimestamp=(never|day|hour|minute|micros) the meter file is suffixed with a
|
--meterfilestimestamp=(never|day|hour|minute|micros) the meter file is suffixed with a
|
||||||
timestamp (localtime) with the given resolution.
|
timestamp (localtime) with the given resolution.
|
||||||
|
--metershell=<cmdline> invokes cmdline with env variables the first time a meter is seen since startup
|
||||||
--nodeviceexit if no wmbus devices are found, then exit immediately
|
--nodeviceexit if no wmbus devices are found, then exit immediately
|
||||||
--normal for normal logging
|
--normal for normal logging
|
||||||
--oneshot wait for an update from each meter, then quit
|
--oneshot wait for an update from each meter, then quit
|
||||||
|
@ -473,10 +517,24 @@ These telegrams are expected to have the data link layer crc bytes removed alrea
|
||||||
|
|
||||||
`MAIN=/dev/ttyUSB0:mbus:2400`, assume ttyUSB0 is an serial to mbus-master converter. The speed is set to 2400 bps.
|
`MAIN=/dev/ttyUSB0:mbus:2400`, assume ttyUSB0 is an serial to mbus-master converter. The speed is set to 2400 bps.
|
||||||
|
|
||||||
`rtlwmbus`, to spawn the background process: `rtl_sdr -f 868.625M -s 1600000 - 2>/dev/null | rtl_wmbus -s`
|
`rtlwmbus`, to spawn the background process: `rtl_sdr -f 868.625M -s 1600000 - 2>/dev/null | rtl_wmbus -f -s`
|
||||||
for each attached rtlsdr dongle. This will listen to S1,T1 and C1 meters in parallel.
|
for each attached rtlsdr dongle. This will listen to S1,T1 and C1 meters in parallel.
|
||||||
|
|
||||||
Note that this uses a noticeable amount of CPU time by rtl_wmbus.
|
For the moment, it is necessary to send the stderr to a file (/dev/null) because of a bug:
|
||||||
|
https://github.com/osmocom/rtl-sdr/commit/142325a93c6ad70f851f43434acfdf75e12dfe03
|
||||||
|
|
||||||
|
Until this bug fix has propagated into Debian/Fedora etc, wmbusmeters uses a tmp file
|
||||||
|
to see the stderr output from rtl_sdr. This tmp file is created in /tmp and will
|
||||||
|
generate 420 bytes of data once ever 23 hours.
|
||||||
|
|
||||||
|
The current command line used by wmbusmeters to start the rtl_wmbus pipeline is therefore a bit longer:
|
||||||
|
```
|
||||||
|
ERRFILE=$(mktemp --suffix=_wmbusmeters_rtlsdr) ;
|
||||||
|
echo ERRFILE=$ERRFILE ; date -Iseconds > $ERRFILE ;
|
||||||
|
tail -f $ERRFILE & /usr/bin/rtl_sdr -d 0 -f 868.625M -s 1.6e6 - 2>>$ERRFILE | /usr/bin/rtl_wmbus -s -f
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the standard -s option uses a noticeable amount of CPU time by rtl_wmbus.
|
||||||
You can therefore use a tailored rtl_wmbus command that is more suitable for your needs.
|
You can therefore use a tailored rtl_wmbus command that is more suitable for your needs.
|
||||||
|
|
||||||
`rtlwmbus:CMD(<command line>)`, to specify the entire background
|
`rtlwmbus:CMD(<command line>)`, to specify the entire background
|
||||||
|
@ -486,9 +544,10 @@ The command line cannot contain parentheses.
|
||||||
Likewise for rtl433.
|
Likewise for rtl433.
|
||||||
|
|
||||||
Here is an example command line that reduces the rtl_wmbus CPU usage if you only need T1/C1 telegrams.
|
Here is an example command line that reduces the rtl_wmbus CPU usage if you only need T1/C1 telegrams.
|
||||||
It disable S1 decoding (`-p s`) and trades lower cpu usage for reception performance (`-a`):
|
It disable S1 decoding (`-p s`) and trades lower cpu usage for reception performance (`-a`).
|
||||||
|
You should always add the `-f` option to enable detection if rtl_sdr has stalled:
|
||||||
|
|
||||||
`rtlwmbus:CMD(rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus -p s -a)`
|
`rtlwmbus:CMD(rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus -p s -a -f)`
|
||||||
|
|
||||||
`rtlwmbus(ppm=17)`, to tune your rtlsdr dongle accordingly.
|
`rtlwmbus(ppm=17)`, to tune your rtlsdr dongle accordingly.
|
||||||
Use this to tune your dongle and at the same time listen to S1,T1 and C1.
|
Use this to tune your dongle and at the same time listen to S1,T1 and C1.
|
||||||
|
@ -552,6 +611,7 @@ Apator Ultrimis (ultrimis)
|
||||||
Aquametro/Integra Topas Es Kr (topaseskr)
|
Aquametro/Integra Topas Es Kr (topaseskr)
|
||||||
Axioma W1 (q400)
|
Axioma W1 (q400)
|
||||||
Bmeters Hydrodigit (hydrodigit) (partly non-standard protocol)
|
Bmeters Hydrodigit (hydrodigit) (partly non-standard protocol)
|
||||||
|
Bmeters GSD8-I with IWM-TX5 module (iwmtx5)
|
||||||
Diehl/Sappel IZAR RC 868 I R4 PL and R3 (izar) (non-standard protocol)
|
Diehl/Sappel IZAR RC 868 I R4 PL and R3 (izar) (non-standard protocol)
|
||||||
Diehl HYDRUS (hydrus)
|
Diehl HYDRUS (hydrus)
|
||||||
Diehl IZAR RC I G4 (dme_07)
|
Diehl IZAR RC I G4 (dme_07)
|
||||||
|
@ -574,6 +634,7 @@ Zenner Minomess (minomess)
|
||||||
|
|
||||||
Supported heat cost allocators:
|
Supported heat cost allocators:
|
||||||
Apator E-ITN 30.51 (apatoreitn)
|
Apator E-ITN 30.51 (apatoreitn)
|
||||||
|
Engelmann HCA e2 (hcae2)
|
||||||
Innotas EurisII (eurisii)
|
Innotas EurisII (eurisii)
|
||||||
Qundis Q caloric (qcaloric)
|
Qundis Q caloric (qcaloric)
|
||||||
Sontex 868 (sontex868)
|
Sontex 868 (sontex868)
|
||||||
|
@ -789,12 +850,12 @@ wmbusmeters --format=json --meterfiles /dev/ttyUSB0:im871a:c1 MyTapWater multica
|
||||||
# Using wmbusmeters in a pipe
|
# Using wmbusmeters in a pipe
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
rtl_sdr -f 868.625M -s 1600000 - 2>/dev/null | rtl_wmbus -s | wmbusmeters --format=json stdin:rtlwmbus MyMeter auto 12345678 NOKEY | ...more processing...
|
rtl_sdr -f 868.625M -s 1600000 - 2>/dev/null | rtl_wmbus -f -s | wmbusmeters --format=json stdin:rtlwmbus MyMeter auto 12345678 NOKEY | ...more processing...
|
||||||
```
|
```
|
||||||
|
|
||||||
Or you can send rtl_wmbus formatted telegrams using nc over UDP to wmbusmeters.
|
Or you can send rtl_wmbus formatted telegrams using nc over UDP to wmbusmeters.
|
||||||
```shell
|
```shell
|
||||||
rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus -p s -a | nc -u localhost 4444
|
rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus -f -p s -a | nc -u localhost 4444
|
||||||
```
|
```
|
||||||
|
|
||||||
And receive the telegrams with nc spawned by wmbusmeters.
|
And receive the telegrams with nc spawned by wmbusmeters.
|
||||||
|
@ -807,6 +868,12 @@ Or start nc explicitly in a pipe.
|
||||||
nc -lku 4444 | wmbusmeters stdin:rtlwmbus
|
nc -lku 4444 | wmbusmeters stdin:rtlwmbus
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Telegrams can also be pulled in by listening on MQTT topics if they were captured by other tools like [rtl_433](https://github.com/merbanan/rtl_433)
|
||||||
|
```shell
|
||||||
|
wmbusmeters 'hex:CMD(/usr/bin/mosquitto_sub -h 192.168.x.x -t rtl_433/device/devices/6/Wireless-MBus/+/data | tr -d "\n" )'
|
||||||
|
```
|
||||||
|
`+` is a wild card that listens to all the captured telegrams but can be replaced with a specific meter's ID
|
||||||
|
|
||||||
# Decoding hex string telegrams
|
# Decoding hex string telegrams
|
||||||
|
|
||||||
If you have a single telegram as hex, which you want decoded, you do not need to create a simulation file,
|
If you have a single telegram as hex, which you want decoded, you do not need to create a simulation file,
|
||||||
|
@ -963,10 +1030,21 @@ If you like to send the bytes manually, the correct bytes are:
|
||||||
|
|
||||||
# How to add a new driver
|
# How to add a new driver
|
||||||
|
|
||||||
Drivers are self contained source code files named `src/driver_xyz.cc`
|
Drivers for OMS-compliant meters are text files `drivers/src/*.xmq`
|
||||||
They register themselves at startup. The source file also contains the necessary tests for that driver.
|
First collect an unecrypted telegram as a hex string <hex> using --logtelegrams and any other driver.
|
||||||
|
Then run `wmbusmeters --analyze <hex>` to see the best match.
|
||||||
|
|
||||||
Read more here: [doc/CreateDriver.md](doc/CreateDriver.md)
|
Copy that meters aaa,xmq file to a new filename bbb.xmq and change the name field from aaa to bbb in the driver source.
|
||||||
|
|
||||||
|
Now run the new driver with `wmbusmeters --analyze=drivers/src/bbb.xmq <hex>`
|
||||||
|
and start modifying the driver until it produces the desired json output.
|
||||||
|
|
||||||
|
You can now run `make; make install` from within the drivers directory
|
||||||
|
and then rebuild from the wmbusmeters directory `make`. The new driver is now
|
||||||
|
compiled into the binary.
|
||||||
|
|
||||||
|
You can also put the new driver file bbb.xmq into /etc/wmbusmeters.drivers.d and it will immediately
|
||||||
|
be available to the wmbusmeters program without recompiling.
|
||||||
|
|
||||||
# Caveat
|
# Caveat
|
||||||
|
|
||||||
|
|
Plik diff jest za duży
Load Diff
Plik diff jest za duży
Load Diff
|
@ -2283,6 +2283,12 @@ freebsd*)
|
||||||
CFLAGS="$CFLAGS -I/usr/local/include"
|
CFLAGS="$CFLAGS -I/usr/local/include"
|
||||||
CXXFLAGS="$CXXFLAGS -I/usr/local/include"
|
CXXFLAGS="$CXXFLAGS -I/usr/local/include"
|
||||||
LDFLAGS="$LDFLAGS -L/usr/local/lib"
|
LDFLAGS="$LDFLAGS -L/usr/local/lib"
|
||||||
|
;;
|
||||||
|
darwin*)
|
||||||
|
CFLAGS="$CFLAGS -I/usr/local/include -I/opt/homebrew/include"
|
||||||
|
CXXFLAGS="$CXXFLAGS -I/usr/local/include -I/opt/homebrew/include"
|
||||||
|
LDFLAGS="$LDFLAGS -L/usr/local/lib -L/opt/homebrew/lib"
|
||||||
|
find /opt -name "*rtlsdr*"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
@ -2969,6 +2975,53 @@ else $as_nop
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for xmlFreeDoc in -lxml2" >&5
|
||||||
|
printf %s "checking for xmlFreeDoc in -lxml2... " >&6; }
|
||||||
|
if test ${ac_cv_lib_xml2_xmlFreeDoc+y}
|
||||||
|
then :
|
||||||
|
printf %s "(cached) " >&6
|
||||||
|
else $as_nop
|
||||||
|
ac_check_lib_save_LIBS=$LIBS
|
||||||
|
LIBS="-lxml2 $LIBS"
|
||||||
|
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||||
|
/* end confdefs.h. */
|
||||||
|
|
||||||
|
namespace conftest {
|
||||||
|
extern "C" int xmlFreeDoc ();
|
||||||
|
}
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
return conftest::xmlFreeDoc ();
|
||||||
|
;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_ACEOF
|
||||||
|
if ac_fn_cxx_try_link "$LINENO"
|
||||||
|
then :
|
||||||
|
ac_cv_lib_xml2_xmlFreeDoc=yes
|
||||||
|
else $as_nop
|
||||||
|
ac_cv_lib_xml2_xmlFreeDoc=no
|
||||||
|
fi
|
||||||
|
rm -f core conftest.err conftest.$ac_objext conftest.beam \
|
||||||
|
conftest$ac_exeext conftest.$ac_ext
|
||||||
|
LIBS=$ac_check_lib_save_LIBS
|
||||||
|
fi
|
||||||
|
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xml2_xmlFreeDoc" >&5
|
||||||
|
printf "%s\n" "$ac_cv_lib_xml2_xmlFreeDoc" >&6; }
|
||||||
|
if test "x$ac_cv_lib_xml2_xmlFreeDoc" = xyes
|
||||||
|
then :
|
||||||
|
printf "%s\n" "#define HAVE_LIBXML2 1" >>confdefs.h
|
||||||
|
|
||||||
|
LIBS="-lxml2 $LIBS"
|
||||||
|
|
||||||
|
else $as_nop
|
||||||
|
|
||||||
|
as_fn_error $? "Could not find libxml2 library. Try: sudo apt install libxml2-dev" "$LINENO" 5
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
ac_config_files="$ac_config_files $OUTPUT_ROOT/spec.gmk:$SRC_ROOT/autoconf/spec.gmk.in"
|
ac_config_files="$ac_config_files $OUTPUT_ROOT/spec.gmk:$SRC_ROOT/autoconf/spec.gmk.in"
|
||||||
|
|
||||||
ac_config_files="$ac_config_files $OUTPUT_ROOT/Makefile:$SRC_ROOT/autoconf/Makefile.in"
|
ac_config_files="$ac_config_files $OUTPUT_ROOT/Makefile:$SRC_ROOT/autoconf/Makefile.in"
|
||||||
|
|
10
configure.ac
10
configure.ac
|
@ -40,6 +40,11 @@ freebsd*)
|
||||||
CFLAGS="$CFLAGS -I/usr/local/include"
|
CFLAGS="$CFLAGS -I/usr/local/include"
|
||||||
CXXFLAGS="$CXXFLAGS -I/usr/local/include"
|
CXXFLAGS="$CXXFLAGS -I/usr/local/include"
|
||||||
LDFLAGS="$LDFLAGS -L/usr/local/lib"
|
LDFLAGS="$LDFLAGS -L/usr/local/lib"
|
||||||
|
;;
|
||||||
|
darwin*)
|
||||||
|
CFLAGS="$CFLAGS -I/usr/local/include -I/opt/homebrew/include"
|
||||||
|
CXXFLAGS="$CXXFLAGS -I/usr/local/include -I/opt/homebrew/include"
|
||||||
|
LDFLAGS="$LDFLAGS -L/usr/local/lib -L/opt/homebrew/lib"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
@ -53,6 +58,11 @@ AC_CHECK_LIB(rtlsdr, rtlsdr_get_device_count, [],
|
||||||
AC_MSG_ERROR([Could not find rtlsdr library. Try: sudo apt install librtlsdr-dev])
|
AC_MSG_ERROR([Could not find rtlsdr library. Try: sudo apt install librtlsdr-dev])
|
||||||
])
|
])
|
||||||
|
|
||||||
|
AC_CHECK_LIB(xml2, xmlFreeDoc, [],
|
||||||
|
[
|
||||||
|
AC_MSG_ERROR([Could not find libxml2 library. Try: sudo apt install libxml2-dev])
|
||||||
|
])
|
||||||
|
|
||||||
AC_CONFIG_FILES([$OUTPUT_ROOT/spec.gmk:$SRC_ROOT/autoconf/spec.gmk.in])
|
AC_CONFIG_FILES([$OUTPUT_ROOT/spec.gmk:$SRC_ROOT/autoconf/spec.gmk.in])
|
||||||
AC_CONFIG_FILES([$OUTPUT_ROOT/Makefile:$SRC_ROOT/autoconf/Makefile.in])
|
AC_CONFIG_FILES([$OUTPUT_ROOT/Makefile:$SRC_ROOT/autoconf/Makefile.in])
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
wmbusmeters (1.10.2-1) unstable; urgency=medium
|
wmbusmeters (0.0.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
* First package upload (Closes: #998721).
|
* Test build
|
||||||
* Upload sponsored by Petter Reinholdtsen.
|
|
||||||
|
|
||||||
-- Fredrik Öhrström <oehrstroem@gmail.com> Sat, 26 Jun 2021 12:39:00 +0100
|
-- No User <nouser@nowhere.zzz> Sat, 26 Jun 2021 12:39:00 +0100
|
||||||
|
|
|
@ -15,6 +15,11 @@ Files: src/aes.h
|
||||||
Copyright: 2019 kokke
|
Copyright: 2019 kokke
|
||||||
License: CC0
|
License: CC0
|
||||||
|
|
||||||
|
Files: src/driver_apatoreitn.cc
|
||||||
|
Copyright: 2023 Fredrik Öhrström
|
||||||
|
2022 Kajetan Krykwiński
|
||||||
|
License: GPL-3+
|
||||||
|
|
||||||
Files: src/driver_emerlin868.cc
|
Files: src/driver_emerlin868.cc
|
||||||
Copyright: 2022 Fredrik Öhrström
|
Copyright: 2022 Fredrik Öhrström
|
||||||
2018 David Mallon
|
2018 David Mallon
|
||||||
|
@ -25,25 +30,26 @@ Copyright: 2022 Fredrik Öhrström
|
||||||
2018 David Mallon
|
2018 David Mallon
|
||||||
License: GPL-3+
|
License: GPL-3+
|
||||||
|
|
||||||
Files: src/driver_iperl.cc
|
Files: src/driver_hydrus.cc
|
||||||
|
Copyright: 2023 Fredrik Öhrström
|
||||||
|
2021 Vincent Privat
|
||||||
|
License: GPL-3+
|
||||||
|
|
||||||
|
Files: src/driver_izar.cc
|
||||||
|
Copyright: 2019 Jacek Tomasiak
|
||||||
|
2023 Fredrik Öhrström
|
||||||
|
2021 Vincent Privat
|
||||||
|
License: GPL-3+
|
||||||
|
|
||||||
|
Files: src/driver_kamheat.cc
|
||||||
Copyright: 2022 Fredrik Öhrström
|
Copyright: 2022 Fredrik Öhrström
|
||||||
2018 David Mallon
|
2020 Eric Bus
|
||||||
|
2022 thecem
|
||||||
License: GPL-3+
|
License: GPL-3+
|
||||||
|
|
||||||
Files: src/driver_minomess.cc
|
Files: src/driver_minomess.cc
|
||||||
Copyright: 2021 Olli Salonen
|
Copyright: 2021 Olli Salonen
|
||||||
2022 Fredrik Öhrström
|
2023 Fredrik Öhrström
|
||||||
License: GPL-3+
|
|
||||||
|
|
||||||
Files: src/driver_multical303.cc
|
|
||||||
Copyright: 2022 thecem
|
|
||||||
2022 Fredrik Öhrström
|
|
||||||
License: GPL-3+
|
|
||||||
|
|
||||||
Files: src/driver_multical603.cc
|
|
||||||
Copyright: 2022 Fredrik Öhrström
|
|
||||||
2020 Eric Bus
|
|
||||||
2022 thecem
|
|
||||||
License: GPL-3+
|
License: GPL-3+
|
||||||
|
|
||||||
Files: src/driver_sensostar.cc
|
Files: src/driver_sensostar.cc
|
||||||
|
@ -61,6 +67,11 @@ Copyright: 2021 Vincent Privat
|
||||||
2022 Fredrik Öhrström
|
2022 Fredrik Öhrström
|
||||||
License: GPL-3+
|
License: GPL-3+
|
||||||
|
|
||||||
|
Files: src/driver_topaseskr.cc
|
||||||
|
Copyright: 2022 Fredrik Öhrström
|
||||||
|
2020 Avandorp
|
||||||
|
License: GPL-3+
|
||||||
|
|
||||||
Files: src/manufacturer_specificities.cc
|
Files: src/manufacturer_specificities.cc
|
||||||
Copyright: 2019 Jacek Tomasiak
|
Copyright: 2019 Jacek Tomasiak
|
||||||
2021 Vincent Privat
|
2021 Vincent Privat
|
||||||
|
@ -71,38 +82,6 @@ Copyright: 2019 Jacek Tomasiak
|
||||||
2021 Vincent Privat
|
2021 Vincent Privat
|
||||||
License: GPL-3+
|
License: GPL-3+
|
||||||
|
|
||||||
Files: src/meter_hydrus.cc
|
|
||||||
Copyright: 2020 Fredrik Öhrström
|
|
||||||
2021 Vincent Privat
|
|
||||||
License: GPL-3+
|
|
||||||
|
|
||||||
Files: src/meter_izar.cc
|
|
||||||
Copyright: 2019 Jacek Tomasiak
|
|
||||||
2020 Fredrik Öhrström
|
|
||||||
2021 Vincent Privat
|
|
||||||
License: GPL-3+
|
|
||||||
|
|
||||||
Files: src/meter_multical403.cc
|
|
||||||
Copyright: 2020 Fredrik Öhrström
|
|
||||||
2020 Eric Bus
|
|
||||||
License: GPL-3+
|
|
||||||
|
|
||||||
Files: src/meter_multical602.cc
|
|
||||||
Copyright: 2021 Fredrik Öhrström
|
|
||||||
2020 Eric Bus
|
|
||||||
License: GPL-3+
|
|
||||||
|
|
||||||
Files: src/meter_multical803.cc
|
|
||||||
Copyright: 2020 Fredrik Öhrström
|
|
||||||
2020 Eric Bus
|
|
||||||
2020 Nikodem Jędrzejczak
|
|
||||||
License: GPL-3+
|
|
||||||
|
|
||||||
Files: src/meter_topaseskr.cc
|
|
||||||
Copyright: 2020 Fredrik Öhrström
|
|
||||||
2020 Avandorp
|
|
||||||
License: GPL-3+
|
|
||||||
|
|
||||||
Files: src/sha256.cc
|
Files: src/sha256.cc
|
||||||
Copyright: 2013 Tom St Denis <tomstdenis@gmail.com>
|
Copyright: 2013 Tom St Denis <tomstdenis@gmail.com>
|
||||||
2013 WaterJuice <waterjuice.org>
|
2013 WaterJuice <waterjuice.org>
|
||||||
|
@ -122,6 +101,25 @@ License: GPL-3+
|
||||||
On Debian systems, the complete text of the GNU General Public License
|
On Debian systems, the complete text of the GNU General Public License
|
||||||
version 3 can be found in file "/usr/share/common-licenses/GPL-3".
|
version 3 can be found in file "/usr/share/common-licenses/GPL-3".
|
||||||
|
|
||||||
|
License: MIT
|
||||||
|
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.
|
||||||
|
|
||||||
License: CC0
|
License: CC0
|
||||||
The authors, and therefore would be copyright holders, have as much
|
The authors, and therefore would be copyright holders, have as much
|
||||||
as possible relinguished their copyright to the public domain.
|
as possible relinguished their copyright to the public domain.
|
||||||
|
|
|
@ -15,3 +15,10 @@ export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
|
||||||
|
|
||||||
%:
|
%:
|
||||||
dh $@
|
dh $@
|
||||||
|
|
||||||
|
LPAR=(
|
||||||
|
RPAR=)
|
||||||
|
CHANGELOG_RELEASE=$(shell cat debian/changelog | head -n 1 | grep -o '$(LPAR).*$(RPAR)' | tr -d '$(LPAR)$(RPAR)' )
|
||||||
|
|
||||||
|
override_dh_auto_build:
|
||||||
|
$(MAKE) COMMIT_HASH= TAG=$(CHANGELOG_RELEASE) BRANCH= CHANGES=
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
3.0 (quilt)
|
1.0
|
||||||
|
|
|
@ -1,362 +0,0 @@
|
||||||
# Creating a new driver
|
|
||||||
|
|
||||||
To create a new driver, it is very convenient, to start with an existing driver,
|
|
||||||
that mostly matches the one you want to integrate. I will use the EurisII as a base for
|
|
||||||
Integration of Aventies HCA.
|
|
||||||
|
|
||||||
In fact, most of the manufacturers already exist in the source code
|
|
||||||
with their respective shortcodes, e.g. Aventies with "AAA".
|
|
||||||
|
|
||||||
Create the `/etc/wmbusmeters.conf` with content adapted to your installation, e.g.:
|
|
||||||
```
|
|
||||||
loglevel=debug
|
|
||||||
device=/dev/ttyUSB0:t1
|
|
||||||
logtelegrams=true
|
|
||||||
format=json
|
|
||||||
meterfiles=/var/lib/wmbusmeters/meter_readings
|
|
||||||
meterfilesaction=overwrite
|
|
||||||
meterfilesnaming=name-id
|
|
||||||
logfile=/var/log/wmbusmeters/wmbusmeters.log
|
|
||||||
```
|
|
||||||
|
|
||||||
Create a meter config in `/etc/wmbusmeters.d/` with content like this, using the existing type, you selected as a base for your class (adjust also name, id and key to your needs):
|
|
||||||
|
|
||||||
```
|
|
||||||
name=ABOGGLKZ
|
|
||||||
driver=eurisii
|
|
||||||
id=60900126
|
|
||||||
key=xxxxxxxxx
|
|
||||||
```
|
|
||||||
|
|
||||||
If you now start wmbusmeters with `wmbusmeters --debug --verbose --useconfig=/etc` and your meter transmits a message,
|
|
||||||
you will see a log like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
[2021-07-05_21:07:30] (meter) ABOGGLKZ: meter detection did not match the selected driver eurisii! correct driver is: unknown!
|
|
||||||
(meter) Not printing this warning again for id: 60900126 mfct: (AAA) Aventies, Germany (0x421) type: Heat Cost Allocator (0x08) ver: 0x55
|
|
||||||
[2021-07-05_21:07:30] (meter) please consider opening an issue at https://github.com/wmbusmeters/wmbusmeters/
|
|
||||||
[2021-07-05_21:07:30] (meter) to add support for this unknown mfct,media,version combination
|
|
||||||
(meter) ABOGGLKZ: yes for me
|
|
||||||
(meter) ABOGGLKZ eurisii handling telegram from 60900126
|
|
||||||
(meter) ABOGGLKZ 60900126 "7644210426019060550872260190602104550806006005CA4269D455F02AE4A475AD546F7FF1EDF3C959E5480AA0A2341B6B6EA28884FA1E0EC355A23E66D055E3C790298553C3870727149DF88612ABF2EA184AEF0821B16BC11DA5BAABEFB34E4E68C6F2728D935011EAB98FCAA29274CC685B8079F7"
|
|
||||||
(wmbus) parseDLL @0 119
|
|
||||||
(telegram) DLL L=76 C=44 (from meter SND_NR) M=0421 (AAA) A=60900126 VER=55 TYPE=08 (Heat Cost Allocator) (driver unknown!) DEV=im871a[00101387] RSSI=-77
|
|
||||||
(wmbus) parseELL @10 109
|
|
||||||
(wmbus) parseAFL @10 109
|
|
||||||
(wmbus) parseTPL @10 109
|
|
||||||
(TPL) decrypting "CA4269D455F02AE4A475AD546F7FF1EDF3C959E5480AA0A2341B6B6EA28884FA1E0EC355A23E66D055E3C790298553C3870727149DF88612ABF2EA184AEF0821B16BC11DA5BAABEFB34E4E68C6F2728D935011EAB98FCAA29274CC685B8079F7"
|
|
||||||
(TPL) num encrypted blocks 6 (96 bytes and remaining unencrypted 0 bytes)
|
|
||||||
(TPL) IV 21042601906055080606060606060606
|
|
||||||
(TPL) decrypted "2F2F0B6E660100426EA60082016EA600C2016E9E0082026E7E00C2026E5B0082036E4200C2036E770182046E5B01C2046E4C0182056E4701C2056E3E0182066E3B01C2066E3B0182076E3B01C2076E3B0182086E1301C2086E9C0002FD170000"
|
|
||||||
(telegram) TPL CI=72 ACC=06 STS=00 CFG=0560 (AES_CBC_IV nb=6 cntn=0 ra=0 hc=0) ID=26019060 MFT=2104 VER=55 TYPE=08 (Heat Cost Allocator)
|
|
||||||
telegram=|76442104260190605508722601906021045508060060052F2F|0B6E660100426EA60082016EA600C2016E9E0082026E7E00C2026E5B0082036E4200C2036E770182046E5B01C2046E4C0182056E4701C2056E3E0182066E3B01C2066E3B0182076E3B01C2076E3B0182086E1301C2086E9C0002FD170000|+6647
|
|
||||||
(eurisii) 00: 76 length (118 bytes)
|
|
||||||
(eurisii) 01: 44 dll-c (from meter SND_NR)
|
|
||||||
(eurisii) 02: 2104 dll-mfct (AAA)
|
|
||||||
(eurisii) 04: 26019060 dll-id (60900126)
|
|
||||||
(eurisii) 08: 55 dll-version
|
|
||||||
(eurisii) 09: 08 dll-type (Heat Cost Allocator)
|
|
||||||
(eurisii) 0a: 72 tpl-ci-field (EN 13757-3 Application Layer (long tplh))
|
|
||||||
(eurisii) 0b: 26019060 tpl-id (60900126)
|
|
||||||
(eurisii) 0f: 2104 tpl-mfct (AAA)
|
|
||||||
(eurisii) 11: 55 tpl-version
|
|
||||||
(eurisii) 12: 08 tpl-type (Heat Cost Allocator)
|
|
||||||
(eurisii) 13: 06 tpl-acc-field
|
|
||||||
(eurisii) 14: 00 tpl-sts-field (OK)
|
|
||||||
(eurisii) 15: 6005 tpl-cfg 0560 (AES_CBC_IV nb=6 cntn=0 ra=0 hc=0 )
|
|
||||||
(eurisii) 17: 2f2f decrypt check bytes
|
|
||||||
(eurisii) 19: 0B dif (6 digit BCD Instantaneous value)
|
|
||||||
(eurisii) 1a: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 1b: * 660100 current consumption (166.000000 hca)
|
|
||||||
(eurisii) 1e: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(eurisii) 1f: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 20: * A600 consumption at set date 1 (166.000000 hca)
|
|
||||||
(eurisii) 22: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(eurisii) 23: 01 dife (subunit=0 tariff=0 storagenr=2)
|
|
||||||
(eurisii) 24: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 25: * A600 consumption at set date 2 (166.000000 hca)
|
|
||||||
(eurisii) 27: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(eurisii) 28: 01 dife (subunit=0 tariff=0 storagenr=3)
|
|
||||||
(eurisii) 29: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 2a: * 9E00 consumption at set date 3 (158.000000 hca)
|
|
||||||
(eurisii) 2c: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(eurisii) 2d: 02 dife (subunit=0 tariff=0 storagenr=4)
|
|
||||||
(eurisii) 2e: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 2f: * 7E00 consumption at set date 4 (126.000000 hca)
|
|
||||||
(eurisii) 31: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(eurisii) 32: 02 dife (subunit=0 tariff=0 storagenr=5)
|
|
||||||
(eurisii) 33: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 34: * 5B00 consumption at set date 5 (91.000000 hca)
|
|
||||||
(eurisii) 36: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(eurisii) 37: 03 dife (subunit=0 tariff=0 storagenr=6)
|
|
||||||
(eurisii) 38: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 39: * 4200 consumption at set date 6 (66.000000 hca)
|
|
||||||
(eurisii) 3b: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(eurisii) 3c: 03 dife (subunit=0 tariff=0 storagenr=7)
|
|
||||||
(eurisii) 3d: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 3e: * 7701 consumption at set date 7 (375.000000 hca)
|
|
||||||
(eurisii) 40: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(eurisii) 41: 04 dife (subunit=0 tariff=0 storagenr=8)
|
|
||||||
(eurisii) 42: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 43: * 5B01 consumption at set date 8 (347.000000 hca)
|
|
||||||
(eurisii) 45: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(eurisii) 46: 04 dife (subunit=0 tariff=0 storagenr=9)
|
|
||||||
(eurisii) 47: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 48: * 4C01 consumption at set date 9 (332.000000 hca)
|
|
||||||
(eurisii) 4a: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(eurisii) 4b: 05 dife (subunit=0 tariff=0 storagenr=10)
|
|
||||||
(eurisii) 4c: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 4d: * 4701 consumption at set date 10 (327.000000 hca)
|
|
||||||
(eurisii) 4f: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(eurisii) 50: 05 dife (subunit=0 tariff=0 storagenr=11)
|
|
||||||
(eurisii) 51: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 52: * 3E01 consumption at set date 11 (318.000000 hca)
|
|
||||||
(eurisii) 54: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(eurisii) 55: 06 dife (subunit=0 tariff=0 storagenr=12)
|
|
||||||
(eurisii) 56: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 57: * 3B01 consumption at set date 12 (315.000000 hca)
|
|
||||||
(eurisii) 59: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(eurisii) 5a: 06 dife (subunit=0 tariff=0 storagenr=13)
|
|
||||||
(eurisii) 5b: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 5c: * 3B01 consumption at set date 13 (315.000000 hca)
|
|
||||||
(eurisii) 5e: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(eurisii) 5f: 07 dife (subunit=0 tariff=0 storagenr=14)
|
|
||||||
(eurisii) 60: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 61: * 3B01 consumption at set date 14 (315.000000 hca)
|
|
||||||
(eurisii) 63: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(eurisii) 64: 07 dife (subunit=0 tariff=0 storagenr=15)
|
|
||||||
(eurisii) 65: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 66: * 3B01 consumption at set date 15 (315.000000 hca)
|
|
||||||
(eurisii) 68: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(eurisii) 69: 08 dife (subunit=0 tariff=0 storagenr=16)
|
|
||||||
(eurisii) 6a: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 6b: * 1301 consumption at set date 16 (275.000000 hca)
|
|
||||||
(eurisii) 6d: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(eurisii) 6e: 08 dife (subunit=0 tariff=0 storagenr=17)
|
|
||||||
(eurisii) 6f: 6E vif (Units for H.C.A.)
|
|
||||||
(eurisii) 70: * 9C00 consumption at set date 17 (156.000000 hca)
|
|
||||||
(eurisii) 72: 02 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(eurisii) 73: FD vif (Second extension FD of VIF-codes)
|
|
||||||
(eurisii) 74: 17 vife (Error flags (binary))
|
|
||||||
(eurisii) 75: * 0000 error flags (0000)
|
|
||||||
```
|
|
||||||
|
|
||||||
Now keep especially the following lines in mind for integation into the meter definitions:
|
|
||||||
```
|
|
||||||
[2021-07-05_21:07:30] (meter) ABOGGLKZ: meter detection did not match the selected driver eurisii! correct driver is: unknown!
|
|
||||||
(meter) Not printing this warning again for id: 60900126 mfct: (AAA) Aventies, Germany (0x421) type: Heat Cost Allocator (0x08) ver: 0x55
|
|
||||||
```
|
|
||||||
|
|
||||||
and the telegram line with the decrypted content.
|
|
||||||
```
|
|
||||||
telegram=|76442104260190605508722601906021045508060060052F2F#0B6E660100426EA60082016EA600C2016E9E0082026E7E00C2026E5B0082036E4200C2036E770182046E5B01C2046E4C0182056E4701C2056E3E0182066E3B01C2066E3B0182076E3B01C2076E3B0182086E1301C2086E9C0002FD170000|+6647
|
|
||||||
```
|
|
||||||
|
|
||||||
You can now analyze the telegram with this command:
|
|
||||||
```
|
|
||||||
wmbusmeters --analyze 76442104260190605508722601906021045508060060052F2F0B6E660100426EA60082016EA600C2016E9E0082026E7E00C2026E5B0082036E4200C2036E770182046E5B01C2046E4C0182056E4701C2056E3E0182066E3B01C2066E3B0182076E3B01C2076E3B0182086E1301C2086E9C0002FD170000
|
|
||||||
```
|
|
||||||
|
|
||||||
This will test all existing drivers to see which drivers seems to be matching the best. (This method can be improved so do some sanity checks as well.) If the driver already decodes 100% of the content then perhaps you should only add
|
|
||||||
```
|
|
||||||
di.addDetection(MANUFACTURER_AAA, 0x08, 0x55);
|
|
||||||
```
|
|
||||||
to the existing driver_eursii.cc file. But in this case we chose to create a new driver.
|
|
||||||
(Remember to add a new test case at the end of the cc file.)
|
|
||||||
|
|
||||||
## Creating your new driver
|
|
||||||
|
|
||||||
As we used the eurisii meter as a template, you need to copy the driver_eurisii.cc to driver_xyz.cc replacing xyz
|
|
||||||
with a name created by you. In my case it is driver_aventieshca.cc
|
|
||||||
|
|
||||||
As long as you do not need any new (not-yet-implemented) wmbus protocol features, then the
|
|
||||||
entire driver is self contained within a single cc file, including tests.
|
|
||||||
|
|
||||||
Now replace all occurences of class name MeterEurisII by the class name you created (e.g. MeterAventiesHCA in my case).
|
|
||||||
|
|
||||||
Adjust the test cases at the end of the cc file! These test cases are picked up automatically
|
|
||||||
by the testing system. Such regression tests are very important for the future stability of wmbusmeters.
|
|
||||||
|
|
||||||
Media/type 0x08 is the code for a Heat Cost allocator. You can find other codes here [here](https://m-bus.com/assets/downloads/MBDOC48.PDF) (on page 76). At least you should check for plausibility...
|
|
||||||
|
|
||||||
### Further adjustments
|
|
||||||
|
|
||||||
If you need to do processing that is not support by the current addFieldWithExtractor code, then
|
|
||||||
you will have to implement the processContent function.
|
|
||||||
|
|
||||||
To collect the releavant data correctly, you need to adjust the code
|
|
||||||
for processing and printing the data from the meter. For this you
|
|
||||||
should edit the `MeterXXX::processContent` method to fit your
|
|
||||||
needs. Adhere to the debug log output of the processing function and
|
|
||||||
have a look at other meter's classes for examples how to process
|
|
||||||
multiple values with different sorage numbers or types.
|
|
||||||
|
|
||||||
If you get errors, it is quite probable that you missed the correct value information (e.g. ValueInformation::Volume):
|
|
||||||
```
|
|
||||||
if(findKey(MeasurementType::Unknown, ValueInformation::Volume, 0, 0, &key, &t->values)) {
|
|
||||||
extractDVdouble(&t->values, key, &offset, &total_water_consumption_m3_);
|
|
||||||
t->addMoreExplanation(offset, " total consumption (%f m3)", total_water_consumption_m3_);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Possible ValueInformation types are:
|
|
||||||
|
|
||||||
- Volume (0x10,0x17)
|
|
||||||
- OperatingTime (0x24,0x27)
|
|
||||||
- VolumeFlow (0x38,0x3F)
|
|
||||||
- FlowTemperature (0x58,0x5B)
|
|
||||||
- ReturnTemperature (0x5C,0x5F)
|
|
||||||
- TemperatureDifference (0x60,0x63)
|
|
||||||
- ExternalTemperature (0x64,0x67)
|
|
||||||
- HeatCostAllocation (0x6E,0x6E)
|
|
||||||
- Date (0x6C,0x6C)
|
|
||||||
- DateTime (0x6D,0x6D)
|
|
||||||
- EnergyMJ (0x0E,0x0F)
|
|
||||||
- EnergyWh (0x00,0x07)
|
|
||||||
- PowerW (0x28,0x2f)
|
|
||||||
- ActualityDuration (0x74,0x77)
|
|
||||||
|
|
||||||
You can add also special properties and getters for special data of your meter, like it is handled with the error codes for EurisII.
|
|
||||||
|
|
||||||
## Compiling and testing
|
|
||||||
|
|
||||||
If everything was right, you can try compiling the project:
|
|
||||||
|
|
||||||
```
|
|
||||||
./configure
|
|
||||||
make -j4
|
|
||||||
```
|
|
||||||
|
|
||||||
Test your new version with the analyze command: `./build/wmbusmeters --analyze ...`
|
|
||||||
|
|
||||||
Now test your code:
|
|
||||||
./build/wmbusmeters --format=json <hex> MyHCA aventieshca 12345678 NOKEY
|
|
||||||
|
|
||||||
It should print:
|
|
||||||
```
|
|
||||||
{"media":"heat cost allocation","meter":"aventieshca","name":"MyHCA","id":"60900126","current_consumption_hca":166,"consumption_at_set_date_hca":166,"consumption_at_set_date_2_hca":166,"consumption_at_set_date_3_hca":158,"consumption_at_set_date_4_hca":126,"consumption_at_set_date_5_hca":91,"consumption_at_set_date_6_hca":66,"consumption_at_set_date_7_hca":375,"consumption_at_set_date_8_hca":347,"consumption_at_set_date_9_hca":332,"consumption_at_set_date_10_hca":327,"consumption_at_set_date_11_hca":318,"consumption_at_set_date_12_hca":315,"consumption_at_set_date_13_hca":315,"consumption_at_set_date_14_hca":315,"consumption_at_set_date_15_hca":315,"consumption_at_set_date_16_hca":275,"consumption_at_set_date_17_hca":156,"error_flags":"","timestamp":"2022-01-18T10:04:08Z"}
|
|
||||||
```
|
|
||||||
|
|
||||||
And now you can run it for real:
|
|
||||||
./build_debug/wmbusmeters --format=json auto:t1 Water MyMeter 12345678 <key>
|
|
||||||
|
|
||||||
or using the config that you setup before:
|
|
||||||
`./build/wmbusmeters --debug --verbose useconfig=/`
|
|
||||||
|
|
||||||
When it looks good, now run the tests:
|
|
||||||
```
|
|
||||||
make test
|
|
||||||
```
|
|
||||||
|
|
||||||
If you have the time please try to build with debug information as well: `make DEBUG=true` and `make testd`
|
|
||||||
This will test the code and check that there are no memory leaks or buffer overflows etc.
|
|
||||||
|
|
||||||
Finally try the daemon: make; sudo make install
|
|
||||||
(Do the daemon reload command if such is requested.)
|
|
||||||
sudo systemctl start wmbusmeters.service
|
|
||||||
|
|
||||||
Update the regression tests in the end of driver_aventieshca.cc
|
|
||||||
|
|
||||||
If again everything was ok, `sudo make install` the new wmbusmeters and start it.
|
|
||||||
|
|
||||||
## Provide some Information (for decumentation and debugging)
|
|
||||||
|
|
||||||
Now edit your meter config in `/etc/wmbusmeters.d/` to use the new meter type (mine is aventieshca).
|
|
||||||
|
|
||||||
The log should show the correct recognition (aventieshca)-tags at the beginning of each decoding line for your meter.
|
|
||||||
```
|
|
||||||
(aventieshca) 17: 2f2f decrypt check bytes
|
|
||||||
(aventieshca) 19: 0B dif (6 digit BCD Instantaneous value)
|
|
||||||
(aventieshca) 1a: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 1b: * 660100 current consumption (166.000000 hca)
|
|
||||||
(aventieshca) 1e: 42 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(aventieshca) 1f: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 20: * A600 consumption at set date 1 (166.000000 hca)
|
|
||||||
(aventieshca) 22: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(aventieshca) 23: 01 dife (subunit=0 tariff=0 storagenr=2)
|
|
||||||
(aventieshca) 24: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 25: * A600 consumption at set date 2 (166.000000 hca)
|
|
||||||
(aventieshca) 27: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(aventieshca) 28: 01 dife (subunit=0 tariff=0 storagenr=3)
|
|
||||||
(aventieshca) 29: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 2a: * 9E00 consumption at set date 3 (158.000000 hca)
|
|
||||||
(aventieshca) 2c: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(aventieshca) 2d: 02 dife (subunit=0 tariff=0 storagenr=4)
|
|
||||||
(aventieshca) 2e: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 2f: * 7E00 consumption at set date 4 (126.000000 hca)
|
|
||||||
(aventieshca) 31: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(aventieshca) 32: 02 dife (subunit=0 tariff=0 storagenr=5)
|
|
||||||
(aventieshca) 33: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 34: * 5B00 consumption at set date 5 (91.000000 hca)
|
|
||||||
(aventieshca) 36: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(aventieshca) 37: 03 dife (subunit=0 tariff=0 storagenr=6)
|
|
||||||
(aventieshca) 38: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 39: * 4200 consumption at set date 6 (66.000000 hca)
|
|
||||||
(aventieshca) 3b: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(aventieshca) 3c: 03 dife (subunit=0 tariff=0 storagenr=7)
|
|
||||||
(aventieshca) 3d: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 3e: * 7701 consumption at set date 7 (375.000000 hca)
|
|
||||||
(aventieshca) 40: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(aventieshca) 41: 04 dife (subunit=0 tariff=0 storagenr=8)
|
|
||||||
(aventieshca) 42: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 43: * 5B01 consumption at set date 8 (347.000000 hca)
|
|
||||||
(aventieshca) 45: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(aventieshca) 46: 04 dife (subunit=0 tariff=0 storagenr=9)
|
|
||||||
(aventieshca) 47: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 48: * 4C01 consumption at set date 9 (332.000000 hca)
|
|
||||||
(aventieshca) 4a: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(aventieshca) 4b: 05 dife (subunit=0 tariff=0 storagenr=10)
|
|
||||||
(aventieshca) 4c: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 4d: * 4701 consumption at set date 10 (327.000000 hca)
|
|
||||||
(aventieshca) 4f: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(aventieshca) 50: 05 dife (subunit=0 tariff=0 storagenr=11)
|
|
||||||
(aventieshca) 51: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 52: * 3E01 consumption at set date 11 (318.000000 hca)
|
|
||||||
(aventieshca) 54: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(aventieshca) 55: 06 dife (subunit=0 tariff=0 storagenr=12)
|
|
||||||
(aventieshca) 56: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 57: * 3B01 consumption at set date 12 (315.000000 hca)
|
|
||||||
(aventieshca) 59: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(aventieshca) 5a: 06 dife (subunit=0 tariff=0 storagenr=13)
|
|
||||||
(aventieshca) 5b: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 5c: * 3B01 consumption at set date 13 (315.000000 hca)
|
|
||||||
(aventieshca) 5e: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(aventieshca) 5f: 07 dife (subunit=0 tariff=0 storagenr=14)
|
|
||||||
(aventieshca) 60: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 61: * 3B01 consumption at set date 14 (315.000000 hca)
|
|
||||||
(aventieshca) 63: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(aventieshca) 64: 07 dife (subunit=0 tariff=0 storagenr=15)
|
|
||||||
(aventieshca) 65: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 66: * 3B01 consumption at set date 15 (315.000000 hca)
|
|
||||||
(aventieshca) 68: 82 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(aventieshca) 69: 08 dife (subunit=0 tariff=0 storagenr=16)
|
|
||||||
(aventieshca) 6a: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 6b: * 1301 consumption at set date 16 (275.000000 hca)
|
|
||||||
(aventieshca) 6d: C2 dif (16 Bit Integer/Binary Instantaneous value storagenr=1)
|
|
||||||
(aventieshca) 6e: 08 dife (subunit=0 tariff=0 storagenr=17)
|
|
||||||
(aventieshca) 6f: 6E vif (Units for H.C.A.)
|
|
||||||
(aventieshca) 70: * 9C00 consumption at set date 17 (156.000000 hca)
|
|
||||||
(aventieshca) 72: 02 dif (16 Bit Integer/Binary Instantaneous value)
|
|
||||||
(aventieshca) 73: FD vif (Second extension FD of VIF-codes)
|
|
||||||
(aventieshca) 74: 17 vife (Error flags (binary))
|
|
||||||
(aventieshca) 75: * 0000 error flags (0000)
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also provide an encrypted telegram, the AES key for the telegram and the decoded data in your pull request for Fredrik to add it to the regression tests. The information is part of the log and looks like this:
|
|
||||||
```
|
|
||||||
(serial) received binary "A5C203764421042601906055087226019060210455080A00600537A7B807E3BA027FE98D75848595628733C29F2D3262F23BA4D2C01D37084E784691E115674D6D8CB874698D4D2C9DB3832A38A39021457A46F151FCBC86947EE7E35CF7AFC049381E74FB2B19E0F835B867B40D22E61129D395263441F4DED061B541"
|
|
||||||
(im871a) checkIM871AFrame "A5C203764421042601906055087226019060210455080A00600537A7B807E3BA027FE98D75848595628733C29F2D3262F23BA4D2C01D37084E784691E115674D6D8CB874698D4D2C9DB3832A38A39021457A46F151FCBC86947EE7E35CF7AFC049381E74FB2B19E0F835B867B40D22E61129D395263441F4DED061B541"
|
|
||||||
(im871a) has_timestamp=0 has_rssi=1 has_crc16=1
|
|
||||||
[...]
|
|
||||||
(TPL) decrypting "37A7B807E3BA027FE98D75848595628733C29F2D3262F23BA4D2C01D37084E784691E115674D6D8CB874698D4D2C9DB3832A38A39021457A46F151FCBC86947EE7E35CF7AFC049381E74FB2B19E0F835B867B40D22E61129D395263441F4DED0"
|
|
||||||
(TPL) num encrypted blocks 6 (96 bytes and remaining unencrypted 0 bytes)
|
|
||||||
(TPL) IV 21042601906055080A0A0A0A0A0A0A0A
|
|
||||||
(TPL) decrypted "2F2F0B6E660100426EA60082016EA600C2016E9E0082026E7E00C2026E5B0082036E4200C2036E770182046E5B01C2046E4C0182056E4701C2056E3E0182066E3B01C2066E3B0182076E3B01C2076E3B0182086E1301C2086E9C0002FD170000"
|
|
||||||
(telegram) TPL CI=72 ACC=0a STS=00 CFG=0560 (AES_CBC_IV nb=6 cntn=0 ra=0 hc=0) ID=26019060 MFT=2104 VER=55 TYPE=08 (Heat Cost Allocator)
|
|
||||||
telegram=|764421042601906055087226019060210455080A0060052F2F|0B6E660100426EA60082016EA600C2016E9E0082026E7E00C2026E5B0082036E4200C2036E770182046E5B01C2046E4C0182056E4701C2056E3E0182066E3B01C2066E3B0182076E3B01C2076E3B0182086E1301C2086E9C0002FD170000|+7599
|
|
||||||
```
|
|
||||||
|
|
||||||
It would also be handy to provide a datasheet of the sensor within the pull request.
|
|
||||||
|
|
||||||
That's all :-)
|
|
||||||
Have fun!
|
|
|
@ -1,22 +1,41 @@
|
||||||
FROM multiarch/alpine:${TARGETARCH}${TARGETVARIANT}-latest-stable AS build
|
FROM alpine AS build
|
||||||
RUN apk add --no-cache alpine-sdk gcc linux-headers librtlsdr-dev cmake libusb-dev bash
|
RUN apk add --no-cache alpine-sdk gcc linux-headers libxml2-dev cmake libusb-dev bash samurai
|
||||||
RUN git clone https://github.com/wmbusmeters/wmbusmeters.git && \
|
|
||||||
|
ADD https://api.github.com/repos/wmbusmeters/wmbusmeters/git/refs/heads/master version.json
|
||||||
|
RUN git clone https://github.com/steve-m/librtlsdr.git && \
|
||||||
|
git clone https://github.com/wmbusmeters/wmbusmeters.git && \
|
||||||
git clone https://github.com/weetmuts/rtl-wmbus.git && \
|
git clone https://github.com/weetmuts/rtl-wmbus.git && \
|
||||||
git clone https://github.com/merbanan/rtl_433.git
|
git clone https://github.com/merbanan/rtl_433.git && \
|
||||||
|
git clone https://github.com/ED6E0F17/rtl_reset.git
|
||||||
|
WORKDIR /librtlsdr
|
||||||
|
RUN cmake -B build -G Ninja \
|
||||||
|
-DCMAKE_BUILD_TYPE=MinSizeRel \
|
||||||
|
-DCMAKE_INSTALL_PREFIX:PATH=/usr \
|
||||||
|
-DDETACH_KERNEL_DRIVER=ON \
|
||||||
|
-Wno-dev && \
|
||||||
|
cmake --build build && \
|
||||||
|
cmake --install build
|
||||||
WORKDIR /wmbusmeters
|
WORKDIR /wmbusmeters
|
||||||
RUN make
|
RUN make
|
||||||
WORKDIR /rtl-wmbus
|
WORKDIR /rtl-wmbus
|
||||||
RUN make release && chmod 755 build/rtl_wmbus
|
RUN make release && chmod 755 build/rtl_wmbus
|
||||||
WORKDIR /rtl_433
|
WORKDIR /rtl_433
|
||||||
RUN mkdir build && cd build && cmake ../ && make
|
RUN cmake -B build -G Ninja \
|
||||||
|
-DCMAKE_BUILD_TYPE=MinSizeRel && \
|
||||||
|
cmake --build build
|
||||||
|
WORKDIR /rtl_reset
|
||||||
|
RUN make
|
||||||
|
|
||||||
FROM multiarch/alpine:${TARGETARCH}${TARGETVARIANT}-latest-stable as scratch
|
FROM alpine as scratch
|
||||||
ENV QEMU_EXECVE=1
|
RUN apk add --no-cache mosquitto-clients libstdc++ curl libusb libxml2 netcat-openbsd
|
||||||
RUN apk add --no-cache mosquitto-clients libstdc++ curl libusb rtl-sdr netcat-openbsd
|
|
||||||
WORKDIR /wmbusmeters
|
WORKDIR /wmbusmeters
|
||||||
|
COPY --from=build /librtlsdr/build/src/librtlsdr.so.* /usr/lib/
|
||||||
|
COPY --from=build /librtlsdr/rtl-sdr.rules /usr/lib/udev/rules.d/rtl-sdr.rules
|
||||||
|
COPY --from=build /librtlsdr/build/src/rtl_* /usr/bin/
|
||||||
COPY --from=build /wmbusmeters/build/wmbusmeters /wmbusmeters/wmbusmeters
|
COPY --from=build /wmbusmeters/build/wmbusmeters /wmbusmeters/wmbusmeters
|
||||||
COPY --from=build /rtl-wmbus/build/rtl_wmbus /usr/bin/rtl_wmbus
|
COPY --from=build /rtl-wmbus/build/rtl_wmbus /usr/bin/rtl_wmbus
|
||||||
COPY --from=build /rtl_433/build/src/rtl_433 /usr/bin/rtl_433
|
COPY --from=build /rtl_433/build/src/rtl_433 /usr/bin/rtl_433
|
||||||
|
COPY --from=build /rtl_reset/rtl_reset /usr/bin/rtl_reset
|
||||||
COPY --from=build /wmbusmeters/docker/docker-entrypoint.sh /wmbusmeters/docker-entrypoint.sh
|
COPY --from=build /wmbusmeters/docker/docker-entrypoint.sh /wmbusmeters/docker-entrypoint.sh
|
||||||
VOLUME /wmbusmeters_data/
|
VOLUME /wmbusmeters_data/
|
||||||
CMD ["sh", "/wmbusmeters/docker-entrypoint.sh"]
|
CMD ["sh", "/wmbusmeters/docker-entrypoint.sh"]
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
$(shell mkdir -p build/test)
|
||||||
|
|
||||||
|
PROG?=wmbusmeters
|
||||||
|
|
||||||
|
database:
|
||||||
|
@./generate.sh $(PROG)
|
||||||
|
|
||||||
|
web:
|
||||||
|
@mkdir -p build/web
|
||||||
|
@for i in $(wildcard src/*) ; do xmq $$i render-html --darkbg > build/web/$$(basename $${i}).html; done
|
||||||
|
@./generate_index.sh > build/web/index.html
|
||||||
|
|
||||||
|
tests:
|
||||||
|
@echo -n "// Generated " > build/generated_tests.xmq
|
||||||
|
@date +%Y-%m-%d_%H:%M >> build/generated_tests.xmq
|
||||||
|
@for i in $(wildcard src/*) ; do xmq $$i select /driver/test to-xmq >> build/generated_tests.xmq; done
|
||||||
|
|
||||||
|
install: database tests
|
||||||
|
@grep -v "// Generated " < build/generated_database.cc > build/a
|
||||||
|
@grep -v "// Generated " < ../src/generated_database.cc > build/b
|
||||||
|
@if ! diff build/a build/b ; then \
|
||||||
|
cp build/generated_database.cc ../src ; echo "Installed db"; else echo "No changes db." ; fi
|
||||||
|
@grep -v "// Generated " < build/generated_tests.xmq > build/a
|
||||||
|
@grep -v "// Generated " < ../tests/generated_tests.xmq > build/b
|
||||||
|
@if ! diff build/a build/b ; then \
|
||||||
|
cp build/generated_tests.xmq ../tests/generated_tests.xmq ; echo "Installed tests." ; else echo "No changes test." ; fi
|
||||||
|
|
||||||
|
test:
|
||||||
|
@for i in $(wildcard src/*) ; do ./test.sh $(PROG) $$i ; done
|
||||||
|
|
||||||
|
.PHONY: database web tests install test
|
|
@ -0,0 +1,58 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export PROG="$1"
|
||||||
|
export OUT="build/generated_database.cc"
|
||||||
|
|
||||||
|
cat > $OUT <<EOF
|
||||||
|
/*
|
||||||
|
Copyright (C) 2024 Fredrik Öhrström (gpl-3.0-or-later)
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Generated $(date +%Y-%m-%d_%H:%M)
|
||||||
|
|
||||||
|
BuiltinDriver builtins_[] =
|
||||||
|
{
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# { "elster", "driver{name=elster meter_type=GasMeter default_fields=name,id,total_m3,timestamp detect{mvt=ELS,81,03}field{name=total quantity=Volume match{measurement_type=Instantaneous vif_range=Volume}}}", false },
|
||||||
|
|
||||||
|
for i in src/*.xmq
|
||||||
|
do
|
||||||
|
NAME=$(basename $i)
|
||||||
|
NAME="${NAME%.*}"
|
||||||
|
CONTENT="$(xmq $i delete /driver/test to-xmq --compact | sed 's/"/\\"/g')"
|
||||||
|
cat >>$OUT <<EOF
|
||||||
|
{ "$NAME", "$CONTENT", false },
|
||||||
|
EOF
|
||||||
|
done
|
||||||
|
|
||||||
|
cat >> $OUT <<EOF
|
||||||
|
};
|
||||||
|
|
||||||
|
MapToDriver builtins_mvts_[] =
|
||||||
|
{
|
||||||
|
EOF
|
||||||
|
|
||||||
|
for i in src/*.xmq
|
||||||
|
do
|
||||||
|
NAME=$(basename $i)
|
||||||
|
export NAME="${NAME%.*}"
|
||||||
|
(xmq $i for-each /driver/detect/mvt --shell='./print_mvt.sh "${.}" "$NAME"') >> $OUT
|
||||||
|
done
|
||||||
|
|
||||||
|
cat >> $OUT <<EOF
|
||||||
|
};
|
||||||
|
EOF
|
|
@ -0,0 +1 @@
|
||||||
|
#!/bin/bash
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
MVT="$1"
|
||||||
|
DRIVER="$2"
|
||||||
|
|
||||||
|
M=$(echo "$1" | cut -f 1 -d ',')
|
||||||
|
V=$(echo "$1" | cut -f 2 -d ',')
|
||||||
|
T=$(echo "$1" | cut -f 3 -d ',')
|
||||||
|
|
||||||
|
echo " { { MANUFACTURER_${M},0x${V},0x${T} }, \"$DRIVER\" },"
|
|
@ -0,0 +1,29 @@
|
||||||
|
driver {
|
||||||
|
name = elster
|
||||||
|
meter_type = GasMeter
|
||||||
|
default_fields = name,id,total_m3,timestamp
|
||||||
|
detect {
|
||||||
|
mvt = ELS,81,03
|
||||||
|
}
|
||||||
|
use = actuality_duration_s
|
||||||
|
field {
|
||||||
|
name = total
|
||||||
|
quantity = Volume
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_range = Volume
|
||||||
|
}
|
||||||
|
about {
|
||||||
|
de = 'Der Gesamtwasserverbrauch.'
|
||||||
|
en = 'The total water consumption.'
|
||||||
|
fr = '''La consommation totale d'eau.'''
|
||||||
|
sv = 'Den totala vattenförbrukningen.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test {
|
||||||
|
args = 'Gas elster 05105025 NOKEY'
|
||||||
|
telegram = 3644A511640010253837722550100593158103E70020052F2F_0374E602000C137034220302FD74EE0F2F2F2F2F2F2F2F2F2F2F2F2F2F2F
|
||||||
|
json = '{"media":"gas","meter":"elster","name":"Gas","id":"05105025","actuality_duration_s":742,"total_m3":3223.47,"timestamp":"1111-11-11T11:11:11Z"}'
|
||||||
|
fields = 'Gas;05105025;3223.47;1111-11-11 11:11.11'
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,275 @@
|
||||||
|
driver {
|
||||||
|
name = eltako
|
||||||
|
default_fields = name,id,total_kwh,timestamp
|
||||||
|
meter_type = ElectricityMeter
|
||||||
|
detect {
|
||||||
|
mvt = ELT,01,02
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = status
|
||||||
|
quantity = Text
|
||||||
|
info = status_and_error_flags
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_range = ErrorFlags
|
||||||
|
}
|
||||||
|
lookup {
|
||||||
|
name = ERROR_FLAGS
|
||||||
|
map_type = BitToString
|
||||||
|
mask_bits = 0xff
|
||||||
|
default_message = OK
|
||||||
|
map {
|
||||||
|
name = BUSY
|
||||||
|
info = 'System is busy'
|
||||||
|
value = 0x01
|
||||||
|
test = Set
|
||||||
|
}
|
||||||
|
map {
|
||||||
|
name = GENERIC_APP_ERROR
|
||||||
|
info = 'Generic application error'
|
||||||
|
value = 0x02
|
||||||
|
test = Set
|
||||||
|
}
|
||||||
|
map {
|
||||||
|
name = CURRENT_LOW
|
||||||
|
info = 'Current too low'
|
||||||
|
value = 0x04
|
||||||
|
test = Set
|
||||||
|
}
|
||||||
|
map {
|
||||||
|
name = PERMANENT_ERROR
|
||||||
|
info = 'permanent error'
|
||||||
|
value = 0x08
|
||||||
|
test = Set
|
||||||
|
}
|
||||||
|
map {
|
||||||
|
name = TEMPORARY_ERROR
|
||||||
|
info = 'temporary error'
|
||||||
|
value = 0x10
|
||||||
|
test = Set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = 'total_tariff_1'
|
||||||
|
quantity = Energy
|
||||||
|
info = 'Total cumulative active energy per tariff.'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = AnyEnergyVIF
|
||||||
|
tariff_nr = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = 'total_tariff_2'
|
||||||
|
quantity = Energy
|
||||||
|
info = 'Total cumulative active energy per tariff.'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = AnyEnergyVIF
|
||||||
|
tariff_nr = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = 'total_reactive_tariff_{tariff_counter}'
|
||||||
|
quantity = Energy
|
||||||
|
info = 'Total cumulative reactive energy per tariff.'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = AnyEnergyVIF
|
||||||
|
storage_nr = 2
|
||||||
|
tariff_nr = 1,10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = voltage_l1_n
|
||||||
|
quantity = Voltage
|
||||||
|
info = 'Instantaneous voltage between L1 and neutral.'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = Voltage
|
||||||
|
add_combinable = Mfct01
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = current_l1
|
||||||
|
quantity = Amperage
|
||||||
|
info = 'Instantaneous current in the L1 phase.'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = Amperage
|
||||||
|
add_combinable = Mfct01
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = power_l1
|
||||||
|
quantity = Power
|
||||||
|
info = 'active Power in L1 phase'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = AnyPowerVIF
|
||||||
|
add_combinable = Mfct01
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = reactive_power_l1
|
||||||
|
quantity = Power
|
||||||
|
info = 'reactive Power in L1 phase'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = AnyPowerVIF
|
||||||
|
subunit_nr = 1
|
||||||
|
add_combinable = Mfct01
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = voltage_l2_n
|
||||||
|
quantity = Voltage
|
||||||
|
info = 'Instantaneous voltage between L2 and neutral.'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = Voltage
|
||||||
|
add_combinable = Mfct02
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = current_l2
|
||||||
|
quantity = Amperage
|
||||||
|
info = 'Instantaneous current in the L2 phase.'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = Amperage
|
||||||
|
add_combinable = Mfct02
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = power_l2
|
||||||
|
quantity = Power
|
||||||
|
info = 'active Power in L2 phase'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = AnyPowerVIF
|
||||||
|
add_combinable = Mfct02
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = reactive_power_l2
|
||||||
|
quantity = Power
|
||||||
|
info = 'reactive Power in L2 phase'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = AnyPowerVIF
|
||||||
|
subunit_nr = 1
|
||||||
|
add_combinable = Mfct02
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = voltage_l3_n
|
||||||
|
quantity = Voltage
|
||||||
|
info = 'Instantaneous voltage between L3 and neutral.'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = Voltage
|
||||||
|
add_combinable = Mfct03
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = current_l3
|
||||||
|
quantity = Amperage
|
||||||
|
info = 'Instantaneous current in the L3 phase.'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = Amperage
|
||||||
|
add_combinable = Mfct03
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = power_l3
|
||||||
|
quantity = Power
|
||||||
|
info = 'active Power in L3 phase'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = AnyPowerVIF
|
||||||
|
add_combinable = Mfct03
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = reactive_power_l3
|
||||||
|
quantity = Power
|
||||||
|
info = 'reactive Power in L3 phase'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = AnyPowerVIF
|
||||||
|
subunit_nr = 1
|
||||||
|
add_combinable = Mfct03
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = ct_numerator
|
||||||
|
info = 'Current transformer ratio (numerator)'
|
||||||
|
quantity = Dimensionless
|
||||||
|
vif_scaling = None
|
||||||
|
match {
|
||||||
|
difvifkey = 02FF68
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = active_power_overall
|
||||||
|
quantity = Power
|
||||||
|
info = 'active Power overall'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = AnyPowerVIF
|
||||||
|
add_combinable = Mfct00
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = reactive_power_overall
|
||||||
|
quantity = Power
|
||||||
|
info = 'reactive Power overall'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_scaling = Auto
|
||||||
|
vif_range = AnyPowerVIF
|
||||||
|
subunit_nr = 1
|
||||||
|
add_combinable = Mfct00
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = active_tariff
|
||||||
|
info = 'active tariff'
|
||||||
|
quantity = Dimensionless
|
||||||
|
vif_scaling = None
|
||||||
|
match {
|
||||||
|
difvifkey = 01FF13
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = 'total'
|
||||||
|
quantity = Energy
|
||||||
|
info = 'Total active energy over all tariffs.'
|
||||||
|
calculate = total_tariff_1_kwh+total_tariff_2_kwh
|
||||||
|
}
|
||||||
|
test {
|
||||||
|
args = 'Eltako eltako 23451236 NOKEY'
|
||||||
|
telegram = 689292680800723612452394150102cd0000008c1004688902008c1104688902008c2004000000008c21040000000002fdc9ff01ee0002fddBff01100002acff0120008240acff010a0002fdc9ff02ef0002fddBff02110002acff0224008240acff02070002fdc9ff03ee0002fddBff030e0002acff031c008240acff03060002ff68010002acff0062008240acff00190001ff1300f416
|
||||||
|
json = '{"active_power_overall_kw": 0.98,"active_tariff_counter": 0,"ct_numerator_counter": 1,"current_l1_a": 1.6,"current_l2_a": 1.7,"current_l3_a": 1.4,"id": "23451236","media": "electricity","meter": "eltako","name": "Eltako","power_l1_kw": 0.32,"power_l2_kw": 0.36,"power_l3_kw": 0.28,"reactive_power_l1_kw": 0.1,"reactive_power_l2_kw": 0.07,"reactive_power_l3_kw": 0.06,"reactive_power_overall_kw": 0.25,"timestamp": "1111-11-11T11:11:11Z","total_kwh": 289.68,"total_reactive_tariff_1_kwh": 289.68,"total_reactive_tariff_2_kwh": 0,"total_tariff_1_kwh": 289.68,"total_tariff_2_kwh": 0,"voltage_l1_n_v": 238,"voltage_l2_n_v": 239,"voltage_l3_n_v": 238}'
|
||||||
|
fields = 'Eltako;23451236;289.68;1111-11-11 11:11.11'
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
driver {
|
||||||
|
name = iperl
|
||||||
|
meter_type = WaterMeter
|
||||||
|
default_fields = name,id,total_m3,max_flow_m3h,timestamp
|
||||||
|
detect {
|
||||||
|
mvt = SEN,68,06
|
||||||
|
mvt = SEN,68,07
|
||||||
|
mvt = SEN,7c,07
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = total
|
||||||
|
quantity = Volume
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_range = Volume
|
||||||
|
}
|
||||||
|
about {
|
||||||
|
de = 'Der Gesamtwasserverbrauch.'
|
||||||
|
en = 'The total water consumption.'
|
||||||
|
fr = '''La consommation totale d'eau.'''
|
||||||
|
sv = 'Den totala vattenförbrukningen.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = max_flow
|
||||||
|
quantity = Flow
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_range = VolumeFlow
|
||||||
|
}
|
||||||
|
about {
|
||||||
|
en = 'The maximum flow recorded during previous period.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test {
|
||||||
|
args = 'MoreWater iperl 12345699 NOKEY'
|
||||||
|
coment = 'Test iPerl T1 telegram, that after decryption, has 2f2f markers.'
|
||||||
|
telegram = 1E44AE4C9956341268077A36001000_2F2F0413181E0000023B00002F2F2F2F
|
||||||
|
json = '{"media":"water","meter":"iperl","name":"MoreWater","id":"12345699","total_m3":7.704,"max_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}'
|
||||||
|
fields = 'MoreWater;12345699;7.704;0;1111-11-11 11:11.11'
|
||||||
|
}
|
||||||
|
test {
|
||||||
|
args = 'WaterWater iperl 33225544 NOKEY'
|
||||||
|
comment = 'Test iPerl T1 telegram not encrypted, which has no 2f2f markers.'
|
||||||
|
telegram = 1844AE4C4455223368077A55000000_041389E20100023B0000
|
||||||
|
json = '{"media":"water","meter":"iperl","name":"WaterWater","id":"33225544","total_m3":123.529,"max_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}'
|
||||||
|
fields = 'WaterWater;33225544;123.529;0;1111-11-11 11:11.11'
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
driver {
|
||||||
|
name = kampress
|
||||||
|
default_fields = name,id,status,pressure_bar,max_pressure_bar,min_pressure_bar,timestamp
|
||||||
|
meter_type = PressureSensor
|
||||||
|
detect {
|
||||||
|
mvt = KAM,01,18
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = status
|
||||||
|
quantity = Text
|
||||||
|
info = status_and_error_flags
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_range = ErrorFlags
|
||||||
|
}
|
||||||
|
lookup {
|
||||||
|
name = ERROR_FLAGS
|
||||||
|
map_type = BitToString
|
||||||
|
mask_bits = 0xffff
|
||||||
|
default_message = OK
|
||||||
|
map {
|
||||||
|
name = DROP
|
||||||
|
info = 'Unexpected drop in pressure in relation to average pressure.'
|
||||||
|
value = 0x01
|
||||||
|
test = Set
|
||||||
|
}
|
||||||
|
map {
|
||||||
|
name = SURGE
|
||||||
|
info = 'Unexpected increase in pressure in relation to average pressure.'
|
||||||
|
value = 0x02
|
||||||
|
test = Set
|
||||||
|
}
|
||||||
|
map {
|
||||||
|
name = HIGH
|
||||||
|
info = 'Average pressure has reached configurable limit. Default 15 bar.'
|
||||||
|
value = 0x04
|
||||||
|
test = Set
|
||||||
|
}
|
||||||
|
map {
|
||||||
|
name = LOW
|
||||||
|
info = 'Average pressure has reached configurable limit. Default 1.5 bar.'
|
||||||
|
value = 0x08
|
||||||
|
test = Set
|
||||||
|
}
|
||||||
|
map {
|
||||||
|
name = TRANSIENT
|
||||||
|
info = 'Pressure changes quickly over short timeperiods. Average is fluctuating.'
|
||||||
|
value = 0x10
|
||||||
|
test = Set
|
||||||
|
}
|
||||||
|
map {
|
||||||
|
name = COMM_ERROR
|
||||||
|
info = 'Cannot measure properly or bad internal communication.'
|
||||||
|
value = 0x20
|
||||||
|
test = Set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = pressure
|
||||||
|
quantity = Pressure
|
||||||
|
info = 'The measured pressure.'
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_range = Pressure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = max_pressure
|
||||||
|
quantity = Pressure
|
||||||
|
info = 'The maximum pressure measured during ?'
|
||||||
|
match {
|
||||||
|
measurement_type = Maximum
|
||||||
|
vif_range = Pressure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = min_pressure
|
||||||
|
quantity = Pressure
|
||||||
|
info = 'The minimum pressure measured during ?'
|
||||||
|
match {
|
||||||
|
measurement_type = Minimum
|
||||||
|
vif_range = Pressure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field {
|
||||||
|
name = alfa
|
||||||
|
info = 'We do not know what this is.'
|
||||||
|
quantity = Dimensionless
|
||||||
|
vif_scaling = None
|
||||||
|
match {
|
||||||
|
difvifkey = 05FF09
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
field {
|
||||||
|
name = beta
|
||||||
|
info = 'We do not know what this is.'
|
||||||
|
quantity = Dimensionless
|
||||||
|
vif_scaling = None
|
||||||
|
match {
|
||||||
|
difvifkey = 05FF0A
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
args = 'Pressing kampress 77000317 NOKEY'
|
||||||
|
telegram = 32442D2C1703007701188D280080E39322DB8F78_22696600126967000269660005FF091954A33A05FF0A99BD823A02FD170800
|
||||||
|
json = '{"media":"pressure","meter":"kampress","name":"Pressing","id":"77000317","status":"LOW","pressure_bar":1.02,"max_pressure_bar":1.03,"min_pressure_bar":1.02,"alfa_counter":0.001246,"beta_counter":0.000997,"timestamp":"1111-11-11T11:11:11Z"}'
|
||||||
|
fields = 'Pressing;77000317;LOW;1.02;1.03;1.02;1111-11-11 11:11.11'
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
args = 'Pressing kampress 77000317 NOKEY'
|
||||||
|
telegram = '32442D2C1703007701188D280080E39322DB8F78_22696600126967000269660005FF091954A33A05FF0A99BD823A02FD170800
|
||||||
|
27442D2C1703007701188D280194E393226EC679DE735657_660067006600962B913A21B9423A0800'
|
||||||
|
json = '{"media":"pressure","meter":"kampress","name":"Pressing","id":"77000317","status":"LOW","pressure_bar":1.02,"max_pressure_bar":1.03,"min_pressure_bar":1.02,"alfa_counter":0.001108,"beta_counter":0.000743,"timestamp":"1111-11-11T11:11:11Z"}'
|
||||||
|
fields = 'Pressing;77000317;LOW;1.02;1.03;1.02;1111-11-11 11:11.11'
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
args = 'Pressing kampress 77000317 NOKEY'
|
||||||
|
telegram = '32442D2C1703007701188D280080E39322DB8F78_22696600126967000269660005FF091954A33A05FF0A99BD823A02FD170800
|
||||||
|
27442D2C1703007701188D289554F295224ED579DE73188A_650066006600E80EA43A6B97A3BA0800'
|
||||||
|
json = '{"media":"pressure","meter":"kampress","name":"Pressing","id":"77000317","status":"LOW","pressure_bar":1.02,"max_pressure_bar":1.02,"min_pressure_bar":1.01,"alfa_counter":0.001252,"beta_counter":-0.001248,"timestamp":"1111-11-11T11:11:11Z"}'
|
||||||
|
fields = 'Pressing;77000317;LOW;1.02;1.02;1.01;1111-11-11 11:11.11'
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
driver {
|
||||||
|
name = werhlemodwm
|
||||||
|
meter_type = WaterMeter
|
||||||
|
default_fields = name,id,total_m3,timestamp
|
||||||
|
detect {
|
||||||
|
mvt = WZG,03,16
|
||||||
|
}
|
||||||
|
use = meter_datetime
|
||||||
|
use = target_date
|
||||||
|
use = target_m3
|
||||||
|
use = total_m3
|
||||||
|
use = fabrication_no
|
||||||
|
field {
|
||||||
|
name = next_target
|
||||||
|
quantity = PointInTime
|
||||||
|
display_unit = date
|
||||||
|
match {
|
||||||
|
measurement_type = Instantaneous
|
||||||
|
vif_range = Date
|
||||||
|
add_combinable = FutureValue
|
||||||
|
storage_nr = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
export PROG="$1"
|
||||||
|
export DRIVER="$2"
|
||||||
|
echo Testing $DRIVER
|
||||||
|
xmq $DRIVER for-each /driver/test --shell='./test_case.sh "$PROG" "$DRIVER" "${args}" "${telegram}" "${json}" "${fields}" "$1"'
|
|
@ -0,0 +1,47 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
TEST=build/test
|
||||||
|
|
||||||
|
PROG="$1"
|
||||||
|
DRIVER="$2"
|
||||||
|
ARGS="$3"
|
||||||
|
HEX="$4"
|
||||||
|
JSON="$5"
|
||||||
|
FIELDS="$6"
|
||||||
|
|
||||||
|
rm -f $TEST/test_output.txt $TEST/test_expected.txt $TEST/simulation_tmp.txt
|
||||||
|
|
||||||
|
echo "$HEX" | sed 's/^/telegram=/g' > $TEST/simulation_tmp.txt
|
||||||
|
|
||||||
|
$PROG --driver=$DRIVER --format=json $TEST/simulation_tmp.txt $ARGS \
|
||||||
|
| tail -n 1 \
|
||||||
|
| jq . --sort-keys \
|
||||||
|
| sed 's/"timestamp": "....-..-..T..:..:..Z"/"timestamp": "1111-11-11T11:11:11Z"/' \
|
||||||
|
> $TEST/test_output.txt
|
||||||
|
|
||||||
|
echo "$JSON" | jq . --sort-keys > $TEST/test_expected.txt
|
||||||
|
|
||||||
|
if ! diff $TEST/test_expected.txt $TEST/test_output.txt
|
||||||
|
then
|
||||||
|
if [ "$USE_MELD" = "true" ]
|
||||||
|
then
|
||||||
|
meld $TEST/test_expected.txt $TEST/test_output.txt
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f $TEST/test_output.txt $TEST/test_expected.txt
|
||||||
|
|
||||||
|
$PROG --driver=$DRIVER --format=fields $TEST/simulation_tmp.txt $ARGS \
|
||||||
|
| sed 's/....-..-.. ..:..:../1111-11-11 11:11.11/' \
|
||||||
|
| tail -n 1 \
|
||||||
|
> $TEST/test_output.txt
|
||||||
|
|
||||||
|
echo "$FIELDS" > $TEST/test_expected.txt
|
||||||
|
|
||||||
|
if ! diff $TEST/test_expected.txt $TEST/test_output.txt
|
||||||
|
then
|
||||||
|
if [ "$USE_MELD" = "true" ]
|
||||||
|
then
|
||||||
|
meld $TEST/test_expected.txt $TEST/test_output.txt
|
||||||
|
fi
|
||||||
|
fi
|
17
install.sh
17
install.sh
|
@ -44,19 +44,22 @@ do
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
SRC=$SRC ROOT=$ROOT /bin/sh ./scripts/install_binaries.sh
|
SRC=$SRC ROOT=$ROOT /bin/sh ./scripts/install_binaries.sh || exit $?
|
||||||
|
|
||||||
ROOT=$ROOT /bin/sh ./scripts/install_manpage.sh
|
ROOT=$ROOT /bin/sh ./scripts/install_manpage.sh || exit $?
|
||||||
|
|
||||||
if [ "$ADDUSER" = "true" ]
|
if [ "$ADDUSER" = "true" ]
|
||||||
then
|
then
|
||||||
ROOT=$ROOT /bin/sh ./scripts/add_wmbusmeters_user.sh
|
ROOT=$ROOT /bin/sh ./scripts/add_wmbusmeters_user.sh || exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ROOT=$ROOT /bin/sh ./scripts/prepare_logfiles.sh
|
ROOT=$ROOT /bin/sh ./scripts/prepare_logfiles.sh || exit $?
|
||||||
|
|
||||||
ROOT=$ROOT /bin/sh ./scripts/install_default_configuration.sh
|
ROOT=$ROOT /bin/sh ./scripts/install_default_configuration.sh || exit $?
|
||||||
|
|
||||||
ROOT=$ROOT /bin/sh ./scripts/install_systemd_service.sh
|
ROOT=$ROOT /bin/sh ./scripts/install_systemd_service.sh || exit $?
|
||||||
|
|
||||||
ROOT=$ROOT /bin/sh ./scripts/add_myself_to_dialout.sh
|
ROOT=$ROOT /bin/sh ./scripts/add_myself_to_dialout.sh || exit $?
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "wmbusmeters sucessfully installed."
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"name": "Wmbusmeters",
|
|
||||||
"url": "https://github.com/weetmuts/wmbusmeters/",
|
|
||||||
"maintainer": "weetmuts"
|
|
||||||
}
|
|
|
@ -44,6 +44,9 @@ do
|
||||||
elif grep -q -i "CC0" $f
|
elif grep -q -i "CC0" $f
|
||||||
then
|
then
|
||||||
license="CC0"
|
license="CC0"
|
||||||
|
elif grep -q -i "MIT" $f
|
||||||
|
then
|
||||||
|
license="MIT"
|
||||||
else
|
else
|
||||||
echo "Unknown license in file: "+$f
|
echo "Unknown license in file: "+$f
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -70,6 +73,25 @@ License: GPL-3+
|
||||||
On Debian systems, the complete text of the GNU General Public License
|
On Debian systems, the complete text of the GNU General Public License
|
||||||
version 3 can be found in file "/usr/share/common-licenses/GPL-3".
|
version 3 can be found in file "/usr/share/common-licenses/GPL-3".
|
||||||
|
|
||||||
|
License: MIT
|
||||||
|
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.
|
||||||
|
|
||||||
License: CC0
|
License: CC0
|
||||||
The authors, and therefore would be copyright holders, have as much
|
The authors, and therefore would be copyright holders, have as much
|
||||||
as possible relinguished their copyright to the public domain.
|
as possible relinguished their copyright to the public domain.
|
||||||
|
|
|
@ -13,7 +13,7 @@ then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
(cd src; grep -Eo "Copyright \(C\) (....-)?.... [^\(]+ \(.+\)" * | cut -f 2 -d ':' | tr -s ' ' | sed 's/(C) \([0-9][0-9][0-9][0-9]\) /(C) \1-\1 /' > $TMP)
|
(cd src; grep -Eo ".*Copyright \(C\) (....-)?.... [^\(]+ \(.+\)" * | cut -f 2 -d ':' | tr -s ' ' | sed 's/(C) \([0-9][0-9][0-9][0-9]\) /(C) \1-\1 /' > $TMP)
|
||||||
|
|
||||||
echo 'R"AUTHORS(' > $OUT
|
echo 'R"AUTHORS(' > $OUT
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,26 @@
|
||||||
# Copyright (C) 2021-2023 Fredrik Öhrström (gpl-3.0-or-later)
|
# Copyright (C) 2021-2023 Fredrik Öhrström (gpl-3.0-or-later)
|
||||||
|
|
||||||
rm -f "$ROOT"/usr/bin/wmbusmeters "$ROOT"/usr/sbin/wmbusmetersd
|
# Command binary /usr/bin/wmbusmeters
|
||||||
mkdir -p "$ROOT"/usr/bin
|
wmbusmeters_dir="${ROOT}/usr/bin"
|
||||||
mkdir -p "$ROOT"/usr/sbin
|
wmbusmeters_path="${wmbusmeters_dir}/wmbusmeters"
|
||||||
cp "$SRC" "$ROOT"/usr/bin/wmbusmeters
|
|
||||||
|
|
||||||
(cd "$ROOT"/usr/sbin; ln -s ../bin/wmbusmeters wmbusmetersd)
|
# Daemon /usr/sbin/wmbusmetersd which is a symlink to the command binary.
|
||||||
|
wmbusmetersd_dir="${ROOT}/usr/sbin"
|
||||||
|
wmbusmetersd_path="${wmbusmetersd_dir}/wmbusmetersd"
|
||||||
|
|
||||||
echo "binaries: installed $ROOT/usr/bin/wmbusmeters $ROOT/usr/sbin/wmbusmetersd"
|
# Remove any existing installed components.
|
||||||
|
rm -f "$wmbusmeters_path" "$wmbusmetersd_path" || exit $?
|
||||||
|
|
||||||
|
# Install the command binary and create the bin directory if necessary.
|
||||||
|
install -D -m 755 "$SRC" "$wmbusmeters_path" || exit $?
|
||||||
|
|
||||||
|
# Create the sbin directory if necessary.
|
||||||
|
mkdir -p "$wmbusmetersd_dir" || exit $?
|
||||||
|
|
||||||
|
# Calculate the relative symlink from sbin to bin.
|
||||||
|
wmbusmetersd_target="$(realpath -s --relative-to="${wmbusmetersd_dir}" "${wmbusmeters_path}")"
|
||||||
|
|
||||||
|
# Create the actual link.
|
||||||
|
ln -s "$wmbusmetersd_target" "$wmbusmetersd_path" || exit $?
|
||||||
|
|
||||||
|
echo "binaries: installed ${wmbusmeters_path} ${wmbusmetersd_path}"
|
||||||
|
|
|
@ -50,3 +50,18 @@ then
|
||||||
else
|
else
|
||||||
echo "conf dir: $ROOT/etc/wmbusmeters.d unchanged"
|
echo "conf dir: $ROOT/etc/wmbusmeters.d unchanged"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
####################################################################
|
||||||
|
##
|
||||||
|
## Create /etc/wmbusmeters.drivers.d
|
||||||
|
##
|
||||||
|
|
||||||
|
if [ ! -d "$ROOT"/etc/wmbusmeters.drivers.d ]
|
||||||
|
then
|
||||||
|
# Create the drivers directory
|
||||||
|
mkdir -p "$ROOT"/etc/wmbusmeters.drivers.d
|
||||||
|
chmod -R 755 "$ROOT"/etc/wmbusmeters.drivers.d
|
||||||
|
echo "conf dir: created $ROOT/etc/wmbusmeters.drivers.d"
|
||||||
|
else
|
||||||
|
echo "conf dir: $ROOT/etc/wmbusmeters.drivers.d unchanged"
|
||||||
|
fi
|
||||||
|
|
|
@ -44,6 +44,7 @@ then
|
||||||
postrotate
|
postrotate
|
||||||
/bin/kill -HUP \`cat /run/wmbusmeters/wmbusmeters.pid 2> /dev/null\` 2> /dev/null || true
|
/bin/kill -HUP \`cat /run/wmbusmeters/wmbusmeters.pid 2> /dev/null\` 2> /dev/null || true
|
||||||
endscript
|
endscript
|
||||||
|
}
|
||||||
EOF
|
EOF
|
||||||
echo "logrotate: created $ROOT/etc/logrotate.d/wmbusmeters"
|
echo "logrotate: created $ROOT/etc/logrotate.d/wmbusmeters"
|
||||||
else
|
else
|
||||||
|
|
|
@ -60,3 +60,13 @@ telegram=|6E4401062020202705077A3D0060852F2F|0F|151794|94|0A0200|43|0403|81|D87F
|
||||||
telegram=|3E4401061405410305077A190030852F2F|0F|86B4B8|95|290200|40|C6C1|B4|F0F3F3|41|5559|42|FA701000|F0|01010000|10|BC780000|FFFFFFFFFFFFFFFFFFFFFF2483|
|
telegram=|3E4401061405410305077A190030852F2F|0F|86B4B8|95|290200|40|C6C1|B4|F0F3F3|41|5559|42|FA701000|F0|01010000|10|BC780000|FFFFFFFFFFFFFFFFFFFFFF2483|
|
||||||
{"media":"water","meter":"apator162","name":"MyTapWaterf","id":"03410514","total_m3":30.908,"timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"water","meter":"apator162","name":"MyTapWaterf","id":"03410514","total_m3":30.908,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|MyTapWaterf;03410514;30.908;1111-11-11 11:11.11
|
|MyTapWaterf;03410514;30.908;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
# Yet another
|
||||||
|
telegram=|3C4401061111441105077A280030852F2F|0F|064CB597180200|43|A0068300055A2D69610156BB0C101B1208007101A60AC5AA6DE6A5F0880E9ADD08393C|
|
||||||
|
{"id": "11441111", "media": "water", "meter": "apator162", "name": "MyTapWaterg", "timestamp": "1111-11-11T11:11:11Z", "total_m3": 528.923}
|
||||||
|
|MyTapWaterg;11441111;528.923;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
# Yet another
|
||||||
|
telegram=|3E4401060403820305077A090030852F2F0F9B5B229700000044C2DED310A25500007201C64A0000853C000094310000A0464B1904FFFFFFFFFFFFFFFF2ED6|
|
||||||
|
{"id": "03820304", "media": "water", "meter": "apator162", "name": "MyTapWaterh", "timestamp": "1111-11-11T11:11:11Z", "total_m3": 21.922 }
|
||||||
|
|MyTapWaterh;03820304;21.922;1111-11-11 11:11.11
|
||||||
|
|
|
@ -55,4 +55,4 @@ telegram=|494468509494949495377286868686A85CFE07A90030052F2F_0413100000000F52FCF
|
||||||
|
|
||||||
# Test Zenner Minomess C1 water meter
|
# Test Zenner Minomess C1 water meter
|
||||||
telegram=|6644496A1064035514377251345015496A0007EE0050052F2F_0C1359000000026CBE2B82046CA12B8C0413FFFFFFFF8D0493132CFBFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02FD1700002F2F|
|
telegram=|6644496A1064035514377251345015496A0007EE0050052F2F_0C1359000000026CBE2B82046CA12B8C0413FFFFFFFF8D0493132CFBFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF02FD1700002F2F|
|
||||||
{"media":"water","meter":"minomess","name":"Mino","id":"15503451","meter_date":"2021-11-30","total_m3":0.059,"target_m3":244444.442,"target_date":"2021-11-01","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"water","meter":"minomess","name":"Mino","id":"15503451","meter_date":"2021-11-30","total_m3":0.059,"target_date":"2021-11-01","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
telegram=|7B4479169977997730378C208B900F002C25E4EF0A002EA98E7D58B3ADC57299779977991611028B005087102F2F#0DFD090F34302e3030562030303030303030300D790E31323334353637383839595345310DFD100AAAAAAAAAAAAAAAAAAAAA0D780E31323334353637383930594553312F2F2F2F2F2F2F2F2F2F2F|
|
||||||
|
telegram=|7B4479169977997730378C20F0900F002C2549EE0A0077C19D3D1A08ABCD729977997779161102F0005007102F2F#0702F5C3FA000000000007823C5407000000000000841004E081020084200415000000042938AB000004A9FF01FA0A000004A9FF02050A000004A9FF03389600002F2F2F2F2F2F2F2F2F2F2F2F2F|
|
|
@ -0,0 +1,19 @@
|
||||||
|
telegram=|A244EE4D785634123C067A8F000000_0C1348550000426CE1F14C130000000082046C21298C0413330000008D04931E3A3CFE3300000033000000330000003300000033000000330000003300000033000000330000003300000033000000330000004300000034180000046D0D0B5C2B03FD6C5E150082206C5C290BFD0F0200018C4079678885238310FD3100000082106C01018110FD610002FD66020002FD170000|
|
||||||
|
{"media":"warm water","meter":"supercom587","name":"MyWarmWater","id":"12345678","total_m3":5.548,"software_version":"010002","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|MyWarmWater;12345678;5.548;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
telegram=|A244EE4D111111113C077AAC000000_0C1389490000426CE1F14C130000000082046C21298C0413010000008D04931E3A3CFE0100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000001600000031130000046D0A0C5C2B03FD6C60150082206C5C290BFD0F0200018C4079629885238310FD3100000082106C01018110FD610002FD66020002FD170000|
|
||||||
|
{"media":"water","meter":"supercom587","name":"MyColdWater","id":"11111111","total_m3":4.989,"software_version":"010002","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|MyColdWater;11111111;4.989;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
telegram=|1E44AE4C9956341268077A36001000_2F2F0413181E0000023B00002F2F2F2F|
|
||||||
|
{"media":"water","meter":"iperl","name":"MoreWater","id":"12345699","total_m3":7.704,"max_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|MoreWater;12345699;7.704;0;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
telegram=|1844AE4C4455223368077A55000000_041389E20100023B0000|
|
||||||
|
{"media":"water","meter":"iperl","name":"WaterWater","id":"33225544","total_m3":123.529,"max_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|WaterWater;33225544;123.529;0;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
telegram=|31446850226677116980A0119F27020480048300C408F709143C003D341A2B0B2A0707000000000000062D114457563D71A1850000|
|
||||||
|
{"media":"heat cost allocator","meter":"fhkvdataiii","name":"Room","id":"11776622","current_hca":131,"current_date":"2020-02-08T02:00:00Z","previous_hca":1026,"previous_date":"2019-12-31T02:00:00Z","temp_room_c":22.44,"temp_radiator_c":25.51,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|Room;11776622;131;2020-02-08T02:00:00Z;1026;2019-12-31T02:00:00Z;22.44;25.51;1111-11-11 11:11.11
|
|
@ -8,7 +8,8 @@ telegram=|6893936808007275404810C514000431270000#04786BF99F00046D040F632B0415E02
|
||||||
# Test ultraheat heat meter sent over mbus.
|
# Test ultraheat heat meter sent over mbus.
|
||||||
|
|
||||||
telegram=|68F8F86808007200464470A7320404270000000974040970040C0E082303000C14079519000B2D0500000B3B0808000A5B52000A5F51000A6206004C14061818004C0E490603000C7800464470891071609B102D020100DB102D0201009B103B6009009A105B78009A105F74000C22726701003C22000000007C2200000000426C01018C2006000000008C3006000000008C80100600000000CC200600000000CC300600000000CC801006000000009A115B64009A115F63009B113B5208009B112D020100BC0122000000008C010E490603008C2106000000008C3106000000008C811006000000008C011406181800046D310ACA210F21040010A0C116|
|
telegram=|68F8F86808007200464470A7320404270000000974040970040C0E082303000C14079519000B2D0500000B3B0808000A5B52000A5F51000A6206004C14061818004C0E490603000C7800464470891071609B102D020100DB102D0201009B103B6009009A105B78009A105F74000C22726701003C22000000007C2200000000426C01018C2006000000008C3006000000008C80100600000000CC200600000000CC300600000000CC801006000000009A115B64009A115F63009B113B5208009B112D020100BC0122000000008C010E490603008C2106000000008C3106000000008C811006000000008C011406181800046D310ACA210F21040010A0C116|
|
||||||
{"media":"heat","meter":"ultraheat","name":"MyUltra","id":"70444600","heat_kwh":8974.444444,"volume_m3":1995.07,"power_kw":0.5,"flow_m3h":0.808,"flow_c":52,"return_c":51,"timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"heat","meter":"ultraheat","name":"MyUltra","id":"70444600","heat_kwh":8974.444444,"volume_m3":1995.07,"power_kw":0.5,"flow_m3h":0.808,"flow_c":52,"return_c":51,"fabrication_no":"70444600","meter_datetime":"2022-01-10 10:49","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
||||||
|
|
||||||
# Test multical 403 heat meter sent over mbus.
|
# Test multical 403 heat meter sent over mbus.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
telegram=|A244EE4D785634123C067A8F000000|0C1348550000426CE1F14C130000000082046C21298C0413330000008D04931E3A3CFE3300000033000000330000003300000033000000330000003300000033000000330000003300000033000000330000004300000034180000046D0D0B5C2B03FD6C5E150082206C5C290BFD0F0200018C4079678885238310FD3100000082106C01018110FD610002FD66020002FD170000|
|
||||||
|
{"media":"warm water","meter":"supercom587","name":"MyWarmWater","id":"12345678","total_m3":5.548,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
telegram=|A244EE4D785634123C067A8F000000|0C1348560000426CE1F14C130000000082046C21298C0413330000008D04931E3A3CFE3300000033000000330000003300000033000000330000003300000033000000330000003300000033000000330000004300000034180000046D0D0B5C2B03FD6C5E150082206C5C290BFD0F0200018C4079678885238310FD3100000082106C01018110FD610002FD66020002FD170000|
|
||||||
|
{"media":"warm water","meter":"supercom587","name":"MyWarmWater","id":"12345678","total_m3":6.548,"timestamp":"1111-11-11T11:11:11Z"}
|
|
@ -0,0 +1,4 @@
|
||||||
|
telegram=|414493447514916746377275149167934446044D000020_0C06490000004C0600000000426CFF2CCC080611000000C2086C1F3102FD170000326CFFFF046D330F1432|
|
||||||
|
telegram=|5b44934475149167463778077975149167934446040dff5f3500823d0000810007c006ffff49000000ff2c000000001f3111000000008000800080008000800080008000800080000000000B002f02fd170000046d390d1432488408|
|
||||||
|
telegram=|414493447514916746377275149167934446044D000020_0C06490000004C0600000000426CFF2CCC080611000000C2086C1F3102FD170000326CFFFF046D330F1432|
|
||||||
|
telegram=|5b44934475149167463778077975149167934446040dff5f3500823d0000810007c006ffff49000000ff2c000000001f3111000000008000800080008000800080008000800080000000000B002f02fd170000046d390d1432488408|
|
|
@ -43,8 +43,8 @@ telegram=|5E44B6105843250000027A2A005005_2F2F0C7835221400066D404708AC2A400E03202
|
||||||
# There is a problem in the decoding here, the data stored inside the telegram does not seem to properly encode/decode the year....
|
# There is a problem in the decoding here, the data stored inside the telegram does not seem to properly encode/decode the year....
|
||||||
# We should not report a current_date with a full year, if the year is actually not part of the telegram.
|
# We should not report a current_date with a full year, if the year is actually not part of the telegram.
|
||||||
telegram=|2F446850313233347462A2_069F255900B029310000000306060906030609070606050509050505050407040605070500|
|
telegram=|2F446850313233347462A2_069F255900B029310000000306060906030609070606050509050505050407040605070500|
|
||||||
{"media":"warm water","meter":"mkradio3","name":"Duschen","id":"34333231","total_m3":13.8,"target_m3":8.9,"current_date":"2023-04-27T02:00:00Z","prev_date":"2018-12-31T02:00:00Z","timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"warm water","meter":"mkradio3","name":"Duschen","id":"34333231","total_m3":13.8,"target_m3":8.9,"current_date":"2024-04-27T02:00:00Z","prev_date":"2018-12-31T02:00:00Z","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|Duschen;34333231;13.8;8.9;2023-04-27T02:00:00Z;2018-12-31T02:00:00Z;1111-11-11 11:11.11
|
|Duschen;34333231;13.8;8.9;2024-04-27T02:00:00Z;2018-12-31T02:00:00Z;1111-11-11 11:11.11
|
||||||
|
|
||||||
# Test MKRadio4 T1 telegrams
|
# Test MKRadio4 T1 telegrams
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ telegram=|374468506549235827C3A2_129F25383300A8622600008200800A2AF86211517555287
|
||||||
# Test FHKV data II/III
|
# Test FHKV data II/III
|
||||||
# There is a problem in the decoding here, the data stored inside the telegram does not seem to properly encode/decode the year....
|
# There is a problem in the decoding here, the data stored inside the telegram does not seem to properly encode/decode the year....
|
||||||
# We should not report a current_date with a full year, if the year is actually not part of the telegram.
|
# We should not report a current_date with a full year, if the year is actually not part of the telegram.
|
||||||
telegram=|31446850226677116980A0119F27020480048300C408F709143C003D341A2B0B2A0707000000000000062D114457563D71A1850000|
|
telegram=|34446850226677116980A0119F27020480048300C408F709143C003D341A2B0B2A0707000000000000062D114457563D71A1850000|
|
||||||
{"media":"heat cost allocator","meter":"fhkvdataiii","name":"Room","id":"11776622","current_hca":131,"current_date":"2020-02-08T02:00:00Z","previous_hca":1026,"previous_date":"2019-12-31T02:00:00Z","temp_room_c":22.44,"temp_radiator_c":25.51,"timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"heat cost allocator","meter":"fhkvdataiii","name":"Room","id":"11776622","current_hca":131,"current_date":"2020-02-08T02:00:00Z","previous_hca":1026,"previous_date":"2019-12-31T02:00:00Z","temp_room_c":22.44,"temp_radiator_c":25.51,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|Room;11776622;131;2020-02-08T02:00:00Z;1026;2019-12-31T02:00:00Z;22.44;25.51;1111-11-11 11:11.11
|
|Room;11776622;131;2020-02-08T02:00:00Z;1026;2019-12-31T02:00:00Z;22.44;25.51;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
@ -122,13 +122,13 @@ telegram=|5744b40988227711101b7ab20800000265a00842658f088201659f08226589081265a0
|
||||||
|
|
||||||
# Test Hydrus water meter telegram
|
# Test Hydrus water meter telegram
|
||||||
telegram=|4E44A5116464646470077AED004005_2F2F01FD08300C13741100007C1300000000FC101300000000FC201300000000726C00000B3B00000002FD748713025A6800C4016D3B177F2ACC011300020000|
|
telegram=|4E44A5116464646470077AED004005_2F2F01FD08300C13741100007C1300000000FC101300000000FC201300000000726C00000B3B00000002FD748713025A6800C4016D3B177F2ACC011300020000|
|
||||||
{"at_datetime": "2019-10-31 23:59","flow_m3h": 0,"flow_temperature_c": 10.4,"id": "64646464","media": "water","meter": "hydrus","name": "HydrusWater","remaining_battery_life_y": 13.686797,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3": 0.2,"total_m3": 1.174}
|
{"target_datetime": "2019-10-31 23:59","flow_m3h": 0,"flow_temperature_c": 10.4,"id": "64646464","media": "water","meter": "hydrus","name": "HydrusWater","remaining_battery_life_y": 13.686797,"status": "OK","timestamp": "1111-11-11T11:11:11Z","target_m3": 0.2,"total_m3": 1.174}
|
||||||
|HydrusWater;64646464;1.174;0.2;OK;1111-11-11 11:11.11
|
|HydrusWater;64646464;1.174;null;OK;1111-11-11 11:11.11
|
||||||
|
|
||||||
# Test Hydrus new version water meter telegram
|
# Test Hydrus new version water meter telegram
|
||||||
telegram=|3E44A5116565656570067AFB0030052F2F_0C13503400000DFD110A383731303134423032410B3B00000002FD74DC15C4016D3B178D29CC0113313400002F2F|
|
telegram=|3E44A5116565656570067AFB0030052F2F_0C13503400000DFD110A383731303134423032410B3B00000002FD74DC15C4016D3B178D29CC0113313400002F2F|
|
||||||
{"at_datetime": "2020-09-13 23:59","customer": "A20B410178","flow_m3h": 0,"id": "65656565","media": "warm water","meter": "hydrus","name": "HydrusVater","remaining_battery_life_y": 15.321328,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3": 3.431,"total_m3": 3.45}
|
{"target_datetime": "2020-09-13 23:59","customer": "A20B410178","flow_m3h": 0,"id": "65656565","media": "warm water","meter": "hydrus","name": "HydrusVater","remaining_battery_life_y": 15.321328,"status": "OK","timestamp": "1111-11-11T11:11:11Z","target_m3": 3.431,"total_m3": 3.45}
|
||||||
|HydrusVater;65656565;3.45;3.431;OK;1111-11-11 11:11.11
|
|HydrusVater;65656565;3.45;null;OK;1111-11-11 11:11.11
|
||||||
|
|
||||||
# Test Hydrus with default AES encryption
|
# Test Hydrus with default AES encryption
|
||||||
telegram=||6644242328001081640E7266567464A51170071F0050052C411A08674048DD6BA82A0DF79FFD401309179A893A1BE3CE8EDC50C2A45CD7AFEC3B4CE765820BE8056C124A17416C3722985FFFF7FCEB7094901AB3A16294B511B9A740C9F9911352B42A72FB3B0C|
|
telegram=||6644242328001081640E7266567464A51170071F0050052C411A08674048DD6BA82A0DF79FFD401309179A893A1BE3CE8EDC50C2A45CD7AFEC3B4CE765820BE8056C124A17416C3722985FFFF7FCEB7094901AB3A16294B511B9A740C9F9911352B42A72FB3B0C|
|
||||||
|
@ -152,7 +152,7 @@ telegram=|2E4409077272727210077AD71020052F2F_046D040D742C041377000000446D0000612
|
||||||
|
|
||||||
# Test Axioma W1 telegram with additional fields compared to the older q400 meter.
|
# Test Axioma W1 telegram with additional fields compared to the older q400 meter.
|
||||||
telegram=|5E4409077372727210077A710050052F2F_046D0110A92704130000000004933B0000000004933C00000000023B000002592A0A446D0000A12744130000000044933B0000000044933C0000000001FD74622F2F2F2F2F2F2F2F2F2F2F2F2F2F|
|
telegram=|5E4409077372727210077A710050052F2F_046D0110A92704130000000004933B0000000004933C00000000023B000002592A0A446D0000A12744130000000044933B0000000044933C0000000001FD74622F2F2F2F2F2F2F2F2F2F2F2F2F2F|
|
||||||
{"media":"water","meter":"q400","name":"AxiomaWater","id":"72727273","meter_datetime":"2021-07-09 16:01","total_m3":0,"total_forward_m3":0,"total_backward_m3":0,"flow_temperature_c":26.02,"volume_flow_m3h":0,"status":"OK","set_datetime":"2021-07-01 00:00","consumption_at_set_date_m3":0,"forward_at_set_date_m3":0,"backward_at_set_date_m3":0,"timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"water","meter":"q400","name":"AxiomaWater","id":"72727273","meter_datetime":"2021-07-09 16:01","total_m3":0,"total_forward_m3":0,"total_backward_m3":0,"flow_temperature_c":26.02,"volume_flow_m3h":0,"status":"OK","set_datetime":"2021-07-01 00:00","consumption_at_set_date_m3":0,"forward_at_set_date_m3":0,"backward_at_set_date_m3":0,"battery_pct":98,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|AxiomaWater;72727273;0;1111-11-11 11:11.11
|
|AxiomaWater;72727273;0;1111-11-11 11:11.11
|
||||||
|
|
||||||
# Test electricity meter with eBZ wMB E01.
|
# Test electricity meter with eBZ wMB E01.
|
||||||
|
@ -199,7 +199,7 @@ telegram=|3944FA122162092002067A3600202567C94D48D00DC47B11213E23383DB51968A705AA
|
||||||
# Test topaseskr water meter
|
# Test topaseskr water meter
|
||||||
|
|
||||||
telegram=|4E44B40512345678F1077A310040052F2F_01FD08040C13991848004C1359423500CC101300000000CC201359423500426C7F2C0B3B00000002FD74DA10025AD300C4016D3B179F27CC011387124600|
|
telegram=|4E44B40512345678F1077A310040052F2F_01FD08040C13991848004C1359423500CC101300000000CC201359423500426C7F2C0B3B00000002FD74DA10025AD300C4016D3B179F27CC011387124600|
|
||||||
{"media":"water","meter":"topaseskr","name":"Witer","id":"78563412","total_m3":481.899,"access_counter":4,"temperature_c":21.1,"current_flow_m3h":0,"volume_year_period_m3":354.259,"reverse_volume_year_period_m3":0,"meter_year_period_start_date":"2019-12-31","volume_month_period_m3":461.287,"meter_month_period_start_datetime":"2020-07-31 23:59","battery_y":11.811331,"timestamp":"1111-11-11T11:11:11Z"}
|
{"media":"water","meter":"topaseskr","name":"Witer","id":"78563412","total_m3":481.899,"access_counter":4,"temperature_c":21.1,"current_flow_m3h":0,"volume_year_period_m3":354.259,"reverse_volume_year_period_m3":0,"meter_year_period_end_date":"2019-12-31","volume_month_period_m3":461.287,"meter_month_period_end_datetime":"2020-07-31 23:59","battery_y":11.811331,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|Witer;78563412;481.899;21.1;0;354.259;0;2019-12-31;461.287;2020-07-31 23:59;1111-11-11 11:11.11
|
|Witer;78563412;481.899;21.1;0;354.259;0;2019-12-31;461.287;2020-07-31 23:59;1111-11-11 11:11.11
|
||||||
|
|
||||||
# Test Ultrimis water meter
|
# Test Ultrimis water meter
|
||||||
|
|
|
@ -49,6 +49,8 @@ parts:
|
||||||
- g++
|
- g++
|
||||||
- make
|
- make
|
||||||
- librtlsdr-dev
|
- librtlsdr-dev
|
||||||
|
- libxml2-dev
|
||||||
|
- libxslt1-dev
|
||||||
stage-packages:
|
stage-packages:
|
||||||
- mosquitto-clients
|
- mosquitto-clients
|
||||||
- curl
|
- curl
|
||||||
|
@ -56,6 +58,7 @@ parts:
|
||||||
- sysvinit-utils
|
- sysvinit-utils
|
||||||
- libusb-1.0-0
|
- libusb-1.0-0
|
||||||
- rtl-sdr
|
- rtl-sdr
|
||||||
|
- libxml2
|
||||||
prime:
|
prime:
|
||||||
- sbin/
|
- sbin/
|
||||||
- usr/bin/
|
- usr/bin/
|
||||||
|
|
|
@ -0,0 +1,656 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2017-2024 Fredrik Öhrström (gpl-3.0-or-later)
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include"address.h"
|
||||||
|
#include"manufacturers.h"
|
||||||
|
|
||||||
|
#include<assert.h>
|
||||||
|
#include<algorithm>
|
||||||
|
#include<string.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
vector<string> splitSequenceOfAddressExpressionsAtCommas(const string& mes);
|
||||||
|
bool isValidMatchExpression(const std::string& s, bool *has_wildcard);
|
||||||
|
bool doesIdMatchExpression(const std::string& id, std::string match_rule);
|
||||||
|
bool doesAddressMatchExpressions(Address &address,
|
||||||
|
std::vector<AddressExpression>& address_expressions,
|
||||||
|
bool *used_wildcard,
|
||||||
|
bool *filtered_out,
|
||||||
|
bool *required_found,
|
||||||
|
bool *required_failed);
|
||||||
|
|
||||||
|
bool isValidMatchExpression(const string& s, bool *has_wildcard)
|
||||||
|
{
|
||||||
|
string me = s;
|
||||||
|
|
||||||
|
// Examples of valid match expressions:
|
||||||
|
// 12345678
|
||||||
|
// *
|
||||||
|
// 123*
|
||||||
|
// !12345677
|
||||||
|
// 2222222*
|
||||||
|
// !22222222
|
||||||
|
// We also accept an secondary libmbus address:
|
||||||
|
// 100002842941011B
|
||||||
|
|
||||||
|
// A match expression cannot be empty.
|
||||||
|
if (me.length() == 0) return false;
|
||||||
|
|
||||||
|
// An me can be filtered out with an exclamation mark first.
|
||||||
|
if (me.front() == '!') me.erase(0, 1);
|
||||||
|
|
||||||
|
// More than one negation is not allowed.
|
||||||
|
if (me.front() == '!') return false;
|
||||||
|
|
||||||
|
// A match expression cannot be only a negation mark.
|
||||||
|
if (me.length() == 0) return false;
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
// Some non-compliant meters have full hex in the id,
|
||||||
|
// but according to the standard there should only be bcd here...
|
||||||
|
// We accept hex anyway.
|
||||||
|
while (me.length() > 0 &&
|
||||||
|
((me.front() >= '0' && me.front() <= '9') ||
|
||||||
|
(me.front() >= 'A' && me.front() <= 'F') ||
|
||||||
|
(me.front() >= 'a' && me.front() <= 'f')))
|
||||||
|
{
|
||||||
|
me.erase(0,1);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (me.length() == 0 && count == 16)
|
||||||
|
{
|
||||||
|
// A secondary libmbus address: 100002842941011B
|
||||||
|
// Strictly speaking the leading 8 digits should be bcd,
|
||||||
|
// but we accept hex as well.
|
||||||
|
*has_wildcard = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wildcard_used = false;
|
||||||
|
// An expression can end with a *
|
||||||
|
if (me.length() > 0 && me.front() == '*')
|
||||||
|
{
|
||||||
|
me.erase(0,1);
|
||||||
|
wildcard_used = true;
|
||||||
|
if (has_wildcard) *has_wildcard = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we should have eaten the whole expression.
|
||||||
|
if (me.length() > 0) return false;
|
||||||
|
|
||||||
|
// Check the length of the matching bcd/hex
|
||||||
|
// If no wildcard is used, then the match expression must be exactly 8 digits.
|
||||||
|
if (!wildcard_used) return count == 8;
|
||||||
|
|
||||||
|
// If wildcard is used, then the match expressions must be 7 or less digits,
|
||||||
|
// even zero is allowed which means a single *, which matches any bcd/hex id.
|
||||||
|
return count <= 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string> splitSequenceOfAddressExpressionsAtCommas(const string& mes)
|
||||||
|
{
|
||||||
|
vector<string> r;
|
||||||
|
bool eof, err;
|
||||||
|
vector<uchar> v (mes.begin(), mes.end());
|
||||||
|
auto i = v.begin();
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
auto id = eatTo(v, i, ',', 64, &eof, &err);
|
||||||
|
if (err) break;
|
||||||
|
trimWhitespace(&id);
|
||||||
|
if (id == "ANYID") id = "*";
|
||||||
|
r.push_back(id);
|
||||||
|
if (eof) break;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isValidSequenceOfAddressExpressions(const string& mes)
|
||||||
|
{
|
||||||
|
vector<string> v = splitSequenceOfAddressExpressionsAtCommas(mes);
|
||||||
|
|
||||||
|
for (string me : v)
|
||||||
|
{
|
||||||
|
AddressExpression ae;
|
||||||
|
if (!ae.parse(me)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<AddressExpression> splitAddressExpressions(const string &aes)
|
||||||
|
{
|
||||||
|
vector<string> v = splitSequenceOfAddressExpressionsAtCommas(aes);
|
||||||
|
|
||||||
|
vector<AddressExpression> r;
|
||||||
|
|
||||||
|
for (string me : v)
|
||||||
|
{
|
||||||
|
AddressExpression ae;
|
||||||
|
if (ae.parse(me))
|
||||||
|
{
|
||||||
|
r.push_back(ae);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool doesIdMatchExpression(const string& s, string match)
|
||||||
|
{
|
||||||
|
string id = s;
|
||||||
|
if (id.length() == 0) return false;
|
||||||
|
|
||||||
|
// Here we assume that the match expression has been
|
||||||
|
// verified to be valid.
|
||||||
|
bool can_match = true;
|
||||||
|
|
||||||
|
// Now match bcd/hex until end of id, or '*' in match.
|
||||||
|
while (id.length() > 0 && match.length() > 0 && match.front() != '*')
|
||||||
|
{
|
||||||
|
if (id.front() != match.front())
|
||||||
|
{
|
||||||
|
// We hit a difference, it cannot match.
|
||||||
|
can_match = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
id.erase(0,1);
|
||||||
|
match.erase(0,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wildcard_used = false;
|
||||||
|
if (match.length() && match.front() == '*')
|
||||||
|
{
|
||||||
|
wildcard_used = true;
|
||||||
|
match.erase(0,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (can_match)
|
||||||
|
{
|
||||||
|
// Ok, now the match expression should be empty.
|
||||||
|
// If wildcard is true, then the id can still have digits,
|
||||||
|
// otherwise it must also be empty.
|
||||||
|
if (wildcard_used)
|
||||||
|
{
|
||||||
|
can_match = match.length() == 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
can_match = match.length() == 0 && id.length() == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return can_match;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasWildCard(const string& mes)
|
||||||
|
{
|
||||||
|
return mes.find('*') != string::npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AddressExpression::match(const std::string &i, uint16_t m, uchar v, uchar t)
|
||||||
|
{
|
||||||
|
if (!(mfct == 0xffff || mfct == m)) return false;
|
||||||
|
if (!(version == 0xff || version == v)) return false;
|
||||||
|
if (!(type == 0xff || type == t)) return false;
|
||||||
|
if (!doesIdMatchExpression(i, id)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddressExpression::trimToIdentity(IdentityMode im, Address &a)
|
||||||
|
{
|
||||||
|
switch (im)
|
||||||
|
{
|
||||||
|
case IdentityMode::FULL:
|
||||||
|
id = a.id;
|
||||||
|
mfct = a.mfct;
|
||||||
|
version = a.version;
|
||||||
|
type = a.type;
|
||||||
|
required = true;
|
||||||
|
break;
|
||||||
|
case IdentityMode::ID_MFCT:
|
||||||
|
id = a.id;
|
||||||
|
mfct = a.mfct;
|
||||||
|
version = 0xff;
|
||||||
|
type = 0xff;
|
||||||
|
required = true;
|
||||||
|
break;
|
||||||
|
case IdentityMode::ID:
|
||||||
|
id = a.id;
|
||||||
|
mfct = 0xffff;
|
||||||
|
version = 0xff;
|
||||||
|
type = 0xff;
|
||||||
|
required = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AddressExpression::parse(const string &in)
|
||||||
|
{
|
||||||
|
string s = in;
|
||||||
|
// Example: 12345678
|
||||||
|
// or 12345678.M=PII.T=1B.V=01
|
||||||
|
// or 1234*
|
||||||
|
// or 1234*.M=PII
|
||||||
|
// or 1234*.V=01
|
||||||
|
// or 12 // mbus primary
|
||||||
|
// or 0 // mbus primary
|
||||||
|
// or 250.MPII.V01.T1B // mbus primary
|
||||||
|
// or !12345678
|
||||||
|
// or !*.M=ABC
|
||||||
|
// or libmbus secondary style:
|
||||||
|
// 123456782941011B
|
||||||
|
id = "";
|
||||||
|
mbus_primary = false;
|
||||||
|
mfct = 0xffff;
|
||||||
|
type = 0xff;
|
||||||
|
version = 0xff;
|
||||||
|
filter_out = false;
|
||||||
|
|
||||||
|
if (s.size() == 0) return false;
|
||||||
|
|
||||||
|
if (s.size() > 1 && s[0] == '!')
|
||||||
|
{
|
||||||
|
filter_out = true;
|
||||||
|
s = s.substr(1);
|
||||||
|
// Double ! not allowed.
|
||||||
|
if (s.size() > 1 && s[0] == '!') return false;
|
||||||
|
}
|
||||||
|
vector<string> parts = splitString(s, '.');
|
||||||
|
|
||||||
|
assert(parts.size() > 0);
|
||||||
|
|
||||||
|
id = parts[0];
|
||||||
|
if (!isValidMatchExpression(id, &has_wildcard))
|
||||||
|
{
|
||||||
|
// Not a long id, so lets check if it is p0 to p250 for primary mbus ids.
|
||||||
|
if (id.size() < 2) return false;
|
||||||
|
if (id[0] != 'p') return false;
|
||||||
|
for (size_t i=1; i < id.length(); ++i)
|
||||||
|
{
|
||||||
|
if (!isdigit(id[i])) return false;
|
||||||
|
}
|
||||||
|
// All digits good.
|
||||||
|
int v = atoi(id.c_str()+1);
|
||||||
|
if (v < 0 || v > 250) return false;
|
||||||
|
// It is 0-250 which means it is an mbus primary address.
|
||||||
|
mbus_primary = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts.size() == 1 && id.length() == 16)
|
||||||
|
{
|
||||||
|
// This is a secondary libmbus address.
|
||||||
|
string mfct_hex = id.substr(8,4);
|
||||||
|
string version_hex = id.substr(12,2);
|
||||||
|
string type_hex = id.substr(14,2);
|
||||||
|
id = id.substr(0,8);
|
||||||
|
|
||||||
|
vector<uchar> data;
|
||||||
|
bool ok = hex2bin(mfct_hex.c_str(), &data);
|
||||||
|
if (!ok) return false;
|
||||||
|
if (data.size() != 2) return false;
|
||||||
|
mfct = data[1] << 8 | data[0];
|
||||||
|
|
||||||
|
data.clear();
|
||||||
|
ok = hex2bin(version_hex.c_str(), &data);
|
||||||
|
if (!ok) return false;
|
||||||
|
if (data.size() != 1) return false;
|
||||||
|
version = data[0];
|
||||||
|
|
||||||
|
data.clear();
|
||||||
|
ok = hex2bin(type_hex.c_str(), &data);
|
||||||
|
if (!ok) return false;
|
||||||
|
if (data.size() != 1) return false;
|
||||||
|
type = data[0];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i=1; i<parts.size(); ++i)
|
||||||
|
{
|
||||||
|
if (parts[i].size() == 4) // V=xy or T=xy
|
||||||
|
{
|
||||||
|
if (parts[i][1] != '=') return false;
|
||||||
|
|
||||||
|
vector<uchar> data;
|
||||||
|
bool ok = hex2bin(&parts[i][2], &data);
|
||||||
|
if (!ok) return false;
|
||||||
|
if (data.size() != 1) return false;
|
||||||
|
|
||||||
|
if (parts[i][0] == 'V')
|
||||||
|
{
|
||||||
|
version = data[0];
|
||||||
|
}
|
||||||
|
else if (parts[i][0] == 'T')
|
||||||
|
{
|
||||||
|
type = data[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (parts[i].size() == 5) // M=xyz
|
||||||
|
{
|
||||||
|
if (parts[i][1] != '=') return false;
|
||||||
|
if (parts[i][0] != 'M') return false;
|
||||||
|
|
||||||
|
bool ok = flagToManufacturer(&parts[i][2], &mfct);
|
||||||
|
if (!ok) return false;
|
||||||
|
}
|
||||||
|
else if (parts[i].size() == 6) // M=abcd explicit hex version
|
||||||
|
{
|
||||||
|
if (parts[i][1] != '=') return false;
|
||||||
|
if (parts[i][0] != 'M') return false;
|
||||||
|
|
||||||
|
vector<uchar> data;
|
||||||
|
bool ok = hex2bin(&parts[i][2], &data);
|
||||||
|
if (!ok) return false;
|
||||||
|
if (data.size() != 2) return false;
|
||||||
|
|
||||||
|
mfct = data[1] << 8 | data[0];
|
||||||
|
if (!ok) return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool flagToManufacturer(const char *s, uint16_t *out_mfct)
|
||||||
|
{
|
||||||
|
if (s[0] == 0 || s[1] == 0 || s[2] == 0 || s[3] != 0) return false;
|
||||||
|
if (s[0] < '@' || s[0] > 'Z' ||
|
||||||
|
s[1] < '@' || s[1] > 'Z' ||
|
||||||
|
s[2] < '@' || s[2] > 'Z') return false;
|
||||||
|
|
||||||
|
*out_mfct = MANFCODE(s[0],s[1],s[2]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string AddressExpression::str()
|
||||||
|
{
|
||||||
|
string s;
|
||||||
|
|
||||||
|
if (filter_out) s = "!";
|
||||||
|
if (required) s = "R";
|
||||||
|
|
||||||
|
s.append(id);
|
||||||
|
if (mfct != 0xffff)
|
||||||
|
{
|
||||||
|
s += ".M="+manufacturerFlag(mfct);
|
||||||
|
}
|
||||||
|
if (version != 0xff)
|
||||||
|
{
|
||||||
|
s += ".V="+tostrprintf("%02x", version);
|
||||||
|
}
|
||||||
|
if (type != 0xff)
|
||||||
|
{
|
||||||
|
s += ".T="+tostrprintf("%02x", type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
string Address::str()
|
||||||
|
{
|
||||||
|
string s;
|
||||||
|
|
||||||
|
s.append(id);
|
||||||
|
if (mfct != 0xffff)
|
||||||
|
{
|
||||||
|
s += ".M="+manufacturerFlag(mfct);
|
||||||
|
}
|
||||||
|
if (version != 0xff)
|
||||||
|
{
|
||||||
|
s += ".V="+tostrprintf("%02x", version);
|
||||||
|
}
|
||||||
|
if (type != 0xff)
|
||||||
|
{
|
||||||
|
s += ".T="+tostrprintf("%02x", type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
string Address::concat(std::vector<Address> &addresses)
|
||||||
|
{
|
||||||
|
string s;
|
||||||
|
for (Address& a: addresses)
|
||||||
|
{
|
||||||
|
if (s.size() > 0) s.append(",");
|
||||||
|
s.append(a.str());
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
string AddressExpression::concat(std::vector<AddressExpression> &address_expressions)
|
||||||
|
{
|
||||||
|
string s;
|
||||||
|
for (AddressExpression& a: address_expressions)
|
||||||
|
{
|
||||||
|
if (s.size() > 0) s.append(",");
|
||||||
|
s.append(a.str());
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
string manufacturerFlag(int m_field) {
|
||||||
|
char a = (m_field/1024)%32+64;
|
||||||
|
char b = (m_field/32)%32+64;
|
||||||
|
char c = (m_field)%32+64;
|
||||||
|
|
||||||
|
string flag;
|
||||||
|
flag += a;
|
||||||
|
flag += b;
|
||||||
|
flag += c;
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Address::decodeMfctFirst(const vector<uchar>::iterator &pos)
|
||||||
|
{
|
||||||
|
mfct = *(pos+1) << 8 | *(pos+0);
|
||||||
|
id = tostrprintf("%02x%02x%02x%02x", *(pos+5), *(pos+4), *(pos+3), *(pos+2));
|
||||||
|
version = *(pos+6);
|
||||||
|
type = *(pos+7);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Address::decodeIdFirst(const vector<uchar>::iterator &pos)
|
||||||
|
{
|
||||||
|
id = tostrprintf("%02x%02x%02x%02x", *(pos+3), *(pos+2), *(pos+1), *(pos+0));
|
||||||
|
mfct = *(pos+5) << 8 | *(pos+4);
|
||||||
|
version = *(pos+6);
|
||||||
|
type = *(pos+7);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool doesTelegramMatchExpressions(std::vector<Address> &addresses,
|
||||||
|
std::vector<AddressExpression>& address_expressions,
|
||||||
|
bool *used_wildcard)
|
||||||
|
{
|
||||||
|
bool match = false;
|
||||||
|
bool filtered_out = false;
|
||||||
|
bool required_found = false; // An R12345678 field was found.
|
||||||
|
bool required_failed = true; // Init to fail, set to true if R is satistifed anywhere.
|
||||||
|
|
||||||
|
for (Address &a : addresses)
|
||||||
|
{
|
||||||
|
if (doesAddressMatchExpressions(a,
|
||||||
|
address_expressions,
|
||||||
|
used_wildcard,
|
||||||
|
&filtered_out,
|
||||||
|
&required_found,
|
||||||
|
&required_failed))
|
||||||
|
{
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
// Go through all ids even though there is an early match.
|
||||||
|
// This way we can see if theres an exact match later.
|
||||||
|
}
|
||||||
|
// If any expression triggered a filter out, then the whole telegram does not match.
|
||||||
|
if (filtered_out) match = false;
|
||||||
|
// If a required field was found and it failed....
|
||||||
|
if (required_found && required_failed) match = false;
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool doesAddressMatchExpressions(Address &address,
|
||||||
|
vector<AddressExpression>& address_expressions,
|
||||||
|
bool *used_wildcard,
|
||||||
|
bool *filtered_out,
|
||||||
|
bool *required_found,
|
||||||
|
bool *required_failed)
|
||||||
|
{
|
||||||
|
bool found_match = false;
|
||||||
|
bool found_negative_match = false;
|
||||||
|
bool exact_match = false;
|
||||||
|
|
||||||
|
// Goes through all possible match expressions.
|
||||||
|
// If no expression matches, neither positive nor negative,
|
||||||
|
// then the result is false. (ie no match)
|
||||||
|
|
||||||
|
// If more than one positive match is found, and no negative,
|
||||||
|
// then the result is true.
|
||||||
|
|
||||||
|
// If more than one negative match is found, irrespective
|
||||||
|
// if there is any positive matches or not, then the result is false.
|
||||||
|
|
||||||
|
// If a positive match is found, using a wildcard not any exact match,
|
||||||
|
// then *used_wildcard is set to true.
|
||||||
|
|
||||||
|
// If an expression is required and it fails, then the match fails.
|
||||||
|
for (AddressExpression &ae : address_expressions)
|
||||||
|
{
|
||||||
|
bool has_wildcard = ae.has_wildcard;
|
||||||
|
bool is_negative_rule = ae.filter_out;
|
||||||
|
// We currently assume that only a single expression is required, the last one!
|
||||||
|
bool is_required = ae.required;
|
||||||
|
|
||||||
|
if (is_required) *required_found = true;
|
||||||
|
|
||||||
|
bool m = ae.match(address.id, address.mfct, address.version, address.type);
|
||||||
|
|
||||||
|
if (is_negative_rule)
|
||||||
|
{
|
||||||
|
if (m) found_negative_match = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (m)
|
||||||
|
{
|
||||||
|
// A match, but the required does not count.
|
||||||
|
if (!is_required)
|
||||||
|
{
|
||||||
|
found_match = true;
|
||||||
|
if (!has_wildcard)
|
||||||
|
{
|
||||||
|
exact_match = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*required_failed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found_negative_match)
|
||||||
|
{
|
||||||
|
*filtered_out = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (found_match)
|
||||||
|
{
|
||||||
|
if (exact_match)
|
||||||
|
{
|
||||||
|
*used_wildcard = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*used_wildcard = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *toString(IdentityMode im)
|
||||||
|
{
|
||||||
|
switch (im)
|
||||||
|
{
|
||||||
|
case IdentityMode::ID: return "id";
|
||||||
|
case IdentityMode::ID_MFCT: return "id-mfct";
|
||||||
|
case IdentityMode::FULL: return "full";
|
||||||
|
case IdentityMode::NONE: return "none";
|
||||||
|
case IdentityMode::INVALID: return "invalid";
|
||||||
|
}
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityMode toIdentityMode(const char *s)
|
||||||
|
{
|
||||||
|
if (!strcmp(s,"id")) return IdentityMode::ID;
|
||||||
|
if (!strcmp(s,"id-mfct")) return IdentityMode::ID_MFCT;
|
||||||
|
if (!strcmp(s, "full")) return IdentityMode::FULL;
|
||||||
|
if (!strcmp(s, "none")) return IdentityMode::NONE;
|
||||||
|
return IdentityMode::INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddressExpression::clear()
|
||||||
|
{
|
||||||
|
id = "";
|
||||||
|
has_wildcard = false;
|
||||||
|
mbus_primary = false;
|
||||||
|
mfct = 0xffff;
|
||||||
|
version = 0xff;
|
||||||
|
type = 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddressExpression::appendIdentity(IdentityMode im,
|
||||||
|
AddressExpression *identity_expression,
|
||||||
|
std::vector<Address> &as,
|
||||||
|
std::vector<AddressExpression> &es)
|
||||||
|
{
|
||||||
|
identity_expression->clear();
|
||||||
|
if (im == IdentityMode::NONE) return;
|
||||||
|
|
||||||
|
// Copy id, id-mfct, id-mfct-v-t to identity_expression from the last address.
|
||||||
|
identity_expression->trimToIdentity(im, as.back());
|
||||||
|
|
||||||
|
// Is this identity expression already in the list of address expressions?
|
||||||
|
if (std::find(es.begin(), es.end(), *identity_expression) == es.end())
|
||||||
|
{
|
||||||
|
// No, then add it at the end.
|
||||||
|
es.push_back(*identity_expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AddressExpression::operator==(const AddressExpression&ae) const
|
||||||
|
{
|
||||||
|
return id == ae.id &&
|
||||||
|
has_wildcard == ae.has_wildcard&&
|
||||||
|
mbus_primary == ae.mbus_primary &&
|
||||||
|
mfct == ae.mfct &&
|
||||||
|
version == ae.version &&
|
||||||
|
type == ae.type &&
|
||||||
|
filter_out == ae.filter_out;
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2017-2022 Fredrik Öhrström (gpl-3.0-or-later)
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ADDRESS_H_
|
||||||
|
#define ADDRESS_H_
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/**
|
||||||
|
IdentityMode:
|
||||||
|
|
||||||
|
@ID: The default, only the id groups the meter content.
|
||||||
|
@ID_MFCT: Used when you have two meters with the same id but different manufacturers.
|
||||||
|
@FULL: Used when you want to fully separate meter content on id.mft.v.t
|
||||||
|
@NONE: Do not separate any meters! This might lead to telegrams overwriting each others state.
|
||||||
|
Use this when no state is to be kept in the wmbusmeters object.
|
||||||
|
@INVALID: Cannot parse cmdline.
|
||||||
|
*/
|
||||||
|
enum class IdentityMode
|
||||||
|
{
|
||||||
|
ID,
|
||||||
|
ID_MFCT,
|
||||||
|
FULL,
|
||||||
|
NONE,
|
||||||
|
INVALID
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *toString(IdentityMode im);
|
||||||
|
IdentityMode toIdentityMode(const char *s);
|
||||||
|
|
||||||
|
struct Address
|
||||||
|
{
|
||||||
|
std::string id; // p1 or 12345678 or non-compliant hex: 1234abcd
|
||||||
|
uint16_t mfct {};
|
||||||
|
uchar type {};
|
||||||
|
uchar version {};
|
||||||
|
|
||||||
|
void decodeMfctFirst(const std::vector<uchar>::iterator &pos);
|
||||||
|
void decodeIdFirst(const std::vector<uchar>::iterator &pos);
|
||||||
|
|
||||||
|
std::string str();
|
||||||
|
static std::string concat(std::vector<Address> &addresses);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AddressExpression
|
||||||
|
{
|
||||||
|
// An address expression is used to select which telegrams to decode for a driver.
|
||||||
|
// An address expression is also used to select a specific meter to poll for data.
|
||||||
|
// Example address: 12345678
|
||||||
|
// Or fully qualified: 12345678.M=PII.T=1b.V=01
|
||||||
|
// which means manufacturer triplet PII, type/media=0x1b, version=0x01
|
||||||
|
// Or wildcards in id: 12*.T=16
|
||||||
|
// which matches all cold water meters whose ids start with 12.
|
||||||
|
// Or negated tests: 12345678.V!=66
|
||||||
|
// which will decode all telegrams from 12345678 except those where the version is 0x66.
|
||||||
|
// Or every telegram which is does not start with 12 and is not from ABB:
|
||||||
|
// !12*.M!=ABB
|
||||||
|
|
||||||
|
std::string id; // p1 or 12345678 or non-compliant hex: 1234abcd
|
||||||
|
bool has_wildcard {}; // The id contains a *
|
||||||
|
bool mbus_primary {}; // Signals that the id is 0-250
|
||||||
|
|
||||||
|
uint16_t mfct { 0xffff }; // If 0xffff then any mfct matches this address.
|
||||||
|
uchar version { 0xff }; // If 0xff then any version matches this address.
|
||||||
|
uchar type { 0xff }; // If 0xff then any type matches this address.
|
||||||
|
|
||||||
|
bool filter_out {}; // Telegrams matching this rule should be filtered out!
|
||||||
|
bool required {}; // If true, then this address expression must be matched!
|
||||||
|
|
||||||
|
AddressExpression() {}
|
||||||
|
AddressExpression(Address &a) : id(a.id), mfct(a.mfct), version(a.version), type(a.type) { }
|
||||||
|
bool operator==(const AddressExpression&) const;
|
||||||
|
void clear();
|
||||||
|
void trimToIdentity(IdentityMode im, Address &a);
|
||||||
|
bool parse(const std::string &s);
|
||||||
|
bool match(const std::string &id, uint16_t mfct, uchar version, uchar type);
|
||||||
|
std::string str();
|
||||||
|
static std::string concat(std::vector<AddressExpression> &address_expressions);
|
||||||
|
static void appendIdentity(IdentityMode im,
|
||||||
|
AddressExpression *identity_expression,
|
||||||
|
std::vector<Address> &as,
|
||||||
|
std::vector<AddressExpression> &es);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
isValidSequenceOfAddressExpressions:
|
||||||
|
|
||||||
|
Valid sequenes look like this:
|
||||||
|
12345678
|
||||||
|
12345678,22334455,34*
|
||||||
|
12*.T=16,!*.M=XYZ
|
||||||
|
!*.V=33
|
||||||
|
*/
|
||||||
|
bool isValidSequenceOfAddressExpressions(const std::string& s);
|
||||||
|
std::vector<AddressExpression> splitAddressExpressions(const std::string &aes);
|
||||||
|
bool flagToManufacturer(const char *s, uint16_t *out_mfct);
|
||||||
|
std::string manufacturerFlag(int m_field);
|
||||||
|
bool doesTelegramMatchExpressions(std::vector<Address> &addresses,
|
||||||
|
std::vector<AddressExpression>& address_expressions,
|
||||||
|
bool *used_wildcard);
|
||||||
|
|
||||||
|
#endif
|
|
@ -25,7 +25,6 @@
|
||||||
#include"shell.h"
|
#include"shell.h"
|
||||||
#include"threads.h"
|
#include"threads.h"
|
||||||
#include"util.h"
|
#include"util.h"
|
||||||
#include"version.h"
|
|
||||||
#include"wmbus.h"
|
#include"wmbus.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
|
@ -75,7 +75,6 @@ private:
|
||||||
// Then check if the rtl_sdr and/or rtl_wmbus and/or rtl_433 is available.
|
// Then check if the rtl_sdr and/or rtl_wmbus and/or rtl_433 is available.
|
||||||
bool rtlsdr_found_ = false;
|
bool rtlsdr_found_ = false;
|
||||||
bool rtlwmbus_found_ = false;
|
bool rtlwmbus_found_ = false;
|
||||||
bool rtl433_found_ = false;
|
|
||||||
|
|
||||||
// Remember devices that were not detected as wmbus devices.
|
// Remember devices that were not detected as wmbus devices.
|
||||||
// To avoid probing them again and again.
|
// To avoid probing them again and again.
|
||||||
|
|
194
src/cmdline.cc
194
src/cmdline.cc
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include"cmdline.h"
|
#include"cmdline.h"
|
||||||
|
#include"drivers.h"
|
||||||
#include"meters.h"
|
#include"meters.h"
|
||||||
#include"util.h"
|
#include"util.h"
|
||||||
|
|
||||||
|
@ -59,27 +60,58 @@ shared_ptr<Configuration> parseCommandLine(int argc, char **argv)
|
||||||
return parseNormalCommandLine(c, argc, argv);
|
return parseNormalCommandLine(c, argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void enableEarlyLoggingFromCommandLine(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int i = 1;
|
||||||
|
// First find all logging flags, --silent --verbose --normal --debug
|
||||||
|
while (argv[i] && argv[i][0] == '-')
|
||||||
|
{
|
||||||
|
if (!strcmp(argv[i], "--silent")) {
|
||||||
|
i++;
|
||||||
|
silentLogging(true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(argv[i], "--verbose")) {
|
||||||
|
verboseEnabled(true);
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(argv[i], "--normal")) {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(argv[i], "--debug")) {
|
||||||
|
verboseEnabled(true);
|
||||||
|
debugEnabled(true);
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(argv[i], "--trace")) {
|
||||||
|
verboseEnabled(true);
|
||||||
|
debugEnabled(true);
|
||||||
|
traceEnabled(true);
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static shared_ptr<Configuration> parseNormalCommandLine(Configuration *c, int argc, char **argv)
|
static shared_ptr<Configuration> parseNormalCommandLine(Configuration *c, int argc, char **argv)
|
||||||
{
|
{
|
||||||
int i = 1;
|
int i = 1;
|
||||||
|
// First find all logging flags, --silent --verbose --normal --debug
|
||||||
while (argv[i] && argv[i][0] == '-')
|
while (argv[i] && argv[i][0] == '-')
|
||||||
{
|
{
|
||||||
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help") || !strcmp(argv[i], "--help")) {
|
|
||||||
c->need_help = true;
|
|
||||||
return shared_ptr<Configuration>(c);
|
|
||||||
}
|
|
||||||
if (!strncmp(argv[i], "--device=", 9) || // Deprecated
|
|
||||||
!strncmp(argv[i], "--overridedevice=", 17))
|
|
||||||
{
|
|
||||||
error("You can only use --overridedevice=xyz with --useconfig=xyz\n");
|
|
||||||
}
|
|
||||||
if (!strcmp(argv[i], "--silent")) {
|
if (!strcmp(argv[i], "--silent")) {
|
||||||
c->silent = true;
|
c->silent = true;
|
||||||
i++;
|
i++;
|
||||||
|
silentLogging(true);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strcmp(argv[i], "--verbose")) {
|
if (!strcmp(argv[i], "--verbose")) {
|
||||||
c->verbose = true;
|
c->verbose = true;
|
||||||
|
verboseEnabled(true);
|
||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -91,6 +123,48 @@ static shared_ptr<Configuration> parseNormalCommandLine(Configuration *c, int ar
|
||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strcmp(argv[i], "--debug")) {
|
||||||
|
c->debug = true;
|
||||||
|
verboseEnabled(true);
|
||||||
|
debugEnabled(true);
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strcmp(argv[i], "--trace")) {
|
||||||
|
c->debug = true;
|
||||||
|
c->trace = true;
|
||||||
|
verboseEnabled(true);
|
||||||
|
debugEnabled(true);
|
||||||
|
traceEnabled(true);
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now do the rest of the arguments.
|
||||||
|
i = 1;
|
||||||
|
while (argv[i] && argv[i][0] == '-')
|
||||||
|
{
|
||||||
|
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help") || !strcmp(argv[i], "--help")) {
|
||||||
|
c->need_help = true;
|
||||||
|
return shared_ptr<Configuration>(c);
|
||||||
|
}
|
||||||
|
if (!strcmp(argv[i], "--silent") ||
|
||||||
|
!strcmp(argv[i], "--verbose") ||
|
||||||
|
!strcmp(argv[i], "--normal") ||
|
||||||
|
!strcmp(argv[i], "--debug") ||
|
||||||
|
!strcmp(argv[i], "--trace"))
|
||||||
|
{
|
||||||
|
// Handled already.
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strncmp(argv[i], "--device=", 9) || // Deprecated
|
||||||
|
!strncmp(argv[i], "--overridedevice=", 17))
|
||||||
|
{
|
||||||
|
error("You can only use --overridedevice=xyz with --useconfig=xyz\n");
|
||||||
|
}
|
||||||
if (!strcmp(argv[i], "--version")) {
|
if (!strcmp(argv[i], "--version")) {
|
||||||
c->version = true;
|
c->version = true;
|
||||||
return shared_ptr<Configuration>(c);
|
return shared_ptr<Configuration>(c);
|
||||||
|
@ -162,18 +236,6 @@ static shared_ptr<Configuration> parseNormalCommandLine(Configuration *c, int ar
|
||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(argv[i], "--debug")) {
|
|
||||||
c->debug = true;
|
|
||||||
i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcmp(argv[i], "--trace")) {
|
|
||||||
c->debug = true;
|
|
||||||
c->trace = true;
|
|
||||||
i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strncmp(argv[i], "--logtimestamps=", 16))
|
if (!strncmp(argv[i], "--logtimestamps=", 16))
|
||||||
{
|
{
|
||||||
string ts = string(argv[i]+16);
|
string ts = string(argv[i]+16);
|
||||||
|
@ -451,6 +513,15 @@ static shared_ptr<Configuration> parseNormalCommandLine(Configuration *c, int ar
|
||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strncmp(argv[i], "--metershell=", 13)) {
|
||||||
|
string cmd = string(argv[i]+13);
|
||||||
|
if (cmd == "") {
|
||||||
|
error("The meter shell command cannot be empty.\n");
|
||||||
|
}
|
||||||
|
c->meter_shells.push_back(cmd);
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!strncmp(argv[i], "--alarmshell=", 13)) {
|
if (!strncmp(argv[i], "--alarmshell=", 13)) {
|
||||||
string cmd = string(argv[i]+13);
|
string cmd = string(argv[i]+13);
|
||||||
if (cmd == "") {
|
if (cmd == "") {
|
||||||
|
@ -546,6 +617,15 @@ static shared_ptr<Configuration> parseNormalCommandLine(Configuration *c, int ar
|
||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strncmp(argv[i], "--identitymode=", 15) && strlen(argv[i]) > 15) {
|
||||||
|
c->identity_mode = toIdentityMode(argv[i]+15);
|
||||||
|
if (c->identity_mode == IdentityMode::INVALID)
|
||||||
|
{
|
||||||
|
error("Not a valid identity mode. \"%s\"\n", argv[i]+15);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!strncmp(argv[i], "--resetafter=", 13) && strlen(argv[i]) > 13) {
|
if (!strncmp(argv[i], "--resetafter=", 13) && strlen(argv[i]) > 13) {
|
||||||
c->resetafter = parseTime(argv[i]+13);
|
c->resetafter = parseTime(argv[i]+13);
|
||||||
if (c->resetafter <= 0) {
|
if (c->resetafter <= 0) {
|
||||||
|
@ -572,6 +652,31 @@ static shared_ptr<Configuration> parseNormalCommandLine(Configuration *c, int ar
|
||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strncmp(argv[i], "--driversdir=", 13))
|
||||||
|
{
|
||||||
|
size_t len = strlen(argv[i]) - 13;
|
||||||
|
c->drivers_dir = string(argv[i]+13, len);
|
||||||
|
if (!checkIfDirExists(c->drivers_dir.c_str()))
|
||||||
|
{
|
||||||
|
error("You must supply a valid directory to --driversdir=<dir>\n");
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
loadDriversFromDir(c->drivers_dir);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!strncmp(argv[i], "--driver=", 9))
|
||||||
|
{
|
||||||
|
size_t len = strlen(argv[i]) - 9;
|
||||||
|
string file_name = string(argv[i]+9, len);
|
||||||
|
if (!checkFileExists(file_name.c_str()))
|
||||||
|
{
|
||||||
|
error("You must supply a valid file to --driver=<file>\n");
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
loadDriver(file_name, NULL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
error("Unknown option \"%s\"\n", argv[i]);
|
error("Unknown option \"%s\"\n", argv[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,50 +737,31 @@ static shared_ptr<Configuration> parseNormalCommandLine(Configuration *c, int ar
|
||||||
string bus;
|
string bus;
|
||||||
string name = argv[m*4+i+0];
|
string name = argv[m*4+i+0];
|
||||||
string driver = argv[m*4+i+1];
|
string driver = argv[m*4+i+1];
|
||||||
string id = argv[m*4+i+2];
|
string address_expressions = argv[m*4+i+2];
|
||||||
string key = argv[m*4+i+3];
|
string key = argv[m*4+i+3];
|
||||||
|
|
||||||
MeterInfo mi;
|
MeterInfo mi;
|
||||||
mi.parse(name, driver, id, key);
|
|
||||||
|
if (!isValidSequenceOfAddressExpressions(address_expressions))
|
||||||
|
{
|
||||||
|
error("Not a valid meter id nor a valid sequence of match expression \"%s\"\n", address_expressions.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
mi.parse(name, driver, address_expressions, key);
|
||||||
mi.poll_interval = c->pollinterval;
|
mi.poll_interval = c->pollinterval;
|
||||||
|
mi.identity_mode = c->identity_mode;
|
||||||
|
|
||||||
|
if (!isValidKey(key, mi))
|
||||||
|
{
|
||||||
|
error("Not a valid meter key \"%s\"\n", key.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
if (mi.driver_name.str() == "")
|
if (mi.driver_name.str() == "")
|
||||||
{
|
{
|
||||||
error("Not a valid meter driver \"%s\"\n", driver.c_str());
|
error("Not a valid meter driver \"%s\"\n", driver.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
//LinkModeSet default_modes = toMeterLinkModeSet(mi.driver);
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (default_modes.has(LinkMode::MBUS))
|
|
||||||
{
|
|
||||||
// MBus primary address 0-250
|
|
||||||
// secondary hex address iiiiiiiimmmmvvmm
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// WMBus ids are 8 hex digits iiiiiiii
|
|
||||||
if (!isValidMatchExpressions(id, true)) error("Not a valid id nor a valid meter match expression \"%s\"\n", id.c_str());
|
|
||||||
}
|
|
||||||
if (!isValidKey(key, mi)) error("Not a valid meter key \"%s\"\n", key.c_str());
|
|
||||||
*/
|
|
||||||
|
|
||||||
c->meters.push_back(mi);
|
c->meters.push_back(mi);
|
||||||
|
|
||||||
// Check if the devices can listen to the meter link mode(s).
|
|
||||||
/*
|
|
||||||
Ignore this check for now until all meters have been refactored.
|
|
||||||
if (!default_modes.hasAll(mi.link_modes))
|
|
||||||
{
|
|
||||||
string want = mi.link_modes.hr();
|
|
||||||
string has = default_modes.hr();
|
|
||||||
error("(cmdline) cannot set link modes to: %s because meter %s only transmits on: %s\n",
|
|
||||||
want.c_str(), mi.driverName().str().c_str(), has.c_str());
|
|
||||||
}
|
|
||||||
string modeshr = mi.link_modes.hr();
|
|
||||||
debug("(cmdline) setting link modes to %s for meter %s\n",
|
|
||||||
mi.link_modes.hr().c_str(), name.c_str());
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return shared_ptr<Configuration>(c);
|
return shared_ptr<Configuration>(c);
|
||||||
|
|
|
@ -27,5 +27,6 @@
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
shared_ptr<Configuration> parseCommandLine(int argc, char **argv);
|
shared_ptr<Configuration> parseCommandLine(int argc, char **argv);
|
||||||
|
void enableEarlyLoggingFromCommandLine(int argc, char **argv);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include"config.h"
|
#include"config.h"
|
||||||
|
#include"drivers.h"
|
||||||
#include"meters.h"
|
#include"meters.h"
|
||||||
#include"units.h"
|
#include"units.h"
|
||||||
|
|
||||||
|
@ -52,11 +53,13 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
|
||||||
string bus;
|
string bus;
|
||||||
string name;
|
string name;
|
||||||
string driver = "auto";
|
string driver = "auto";
|
||||||
string id;
|
string address_expressions;
|
||||||
string key = "";
|
string key = "";
|
||||||
string linkmodes;
|
string linkmodes;
|
||||||
int poll_interval = 0;
|
int poll_interval = 0;
|
||||||
|
IdentityMode identity_mode {};
|
||||||
vector<string> telegram_shells;
|
vector<string> telegram_shells;
|
||||||
|
vector<string> meter_shells;
|
||||||
vector<string> alarm_shells;
|
vector<string> alarm_shells;
|
||||||
vector<string> extra_constant_fields;
|
vector<string> extra_constant_fields;
|
||||||
vector<string> extra_calculated_fields;
|
vector<string> extra_calculated_fields;
|
||||||
|
@ -106,7 +109,7 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
|
||||||
else
|
else
|
||||||
if (p.first == "driver") driver = p.second;
|
if (p.first == "driver") driver = p.second;
|
||||||
else
|
else
|
||||||
if (p.first == "id") id = p.second;
|
if (p.first == "id") address_expressions = p.second;
|
||||||
else
|
else
|
||||||
if (p.first == "key")
|
if (p.first == "key")
|
||||||
{
|
{
|
||||||
|
@ -127,10 +130,23 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
if (p.first == "identitymode") {
|
||||||
|
identity_mode = toIdentityMode(p.second.c_str());
|
||||||
|
|
||||||
|
if (identity_mode == IdentityMode::INVALID)
|
||||||
|
{
|
||||||
|
error("Invalid identity mode: \"%s\"!\n", p.second.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
if (p.first == "shell") {
|
if (p.first == "shell") {
|
||||||
telegram_shells.push_back(p.second);
|
telegram_shells.push_back(p.second);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
if (p.first == "metershell") {
|
||||||
|
meter_shells.push_back(p.second);
|
||||||
|
}
|
||||||
|
else
|
||||||
if (p.first == "alarmshell") {
|
if (p.first == "alarmshell") {
|
||||||
alarm_shells.push_back(p.second);
|
alarm_shells.push_back(p.second);
|
||||||
}
|
}
|
||||||
|
@ -170,36 +186,28 @@ void parseMeterConfig(Configuration *c, vector<char> &buf, string file)
|
||||||
|
|
||||||
MeterInfo mi;
|
MeterInfo mi;
|
||||||
|
|
||||||
mi.parse(name, driver, id, key); // sets driver, extras, name, bus, bps, link_modes, ids, name, key
|
if (!isValidSequenceOfAddressExpressions(address_expressions))
|
||||||
mi.poll_interval = poll_interval;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Ignore link mode checking until all drivers have been refactored.
|
|
||||||
LinkModeSet default_modes = toMeterLinkModeSet(mi.driver);
|
|
||||||
if (!default_modes.hasAll(mi.link_modes))
|
|
||||||
{
|
{
|
||||||
string want = mi.link_modes.hr();
|
warning("In config, not a valid meter id nor a valid sequence of match expression \"%s\"\n", address_expressions.c_str());
|
||||||
string has = default_modes.hr();
|
|
||||||
error("(cmdline) cannot set link modes to: %s because meter %s only transmits on: %s\n",
|
|
||||||
want.c_str(), mi.driverName().str().c_str(), has.c_str());
|
|
||||||
}
|
|
||||||
string modeshr = mi.link_modes.hr();
|
|
||||||
debug("(cmdline) setting link modes to %s for meter %s\n",
|
|
||||||
mi.link_modes.hr().c_str(), name.c_str());
|
|
||||||
*/
|
|
||||||
if (!isValidMatchExpressions(id, true)) {
|
|
||||||
warning("Not a valid meter id nor a valid meter match expression \"%s\"\n", id.c_str());
|
|
||||||
use = false;
|
use = false;
|
||||||
}
|
}
|
||||||
if (!isValidKey(key, mi)) {
|
|
||||||
warning("Not a valid meter key \"%s\"\n", key.c_str());
|
mi.parse(name, driver, address_expressions, key); // sets driver, extras, name, bus, bps, link_modes, ids, name, key
|
||||||
|
mi.poll_interval = poll_interval;
|
||||||
|
mi.identity_mode = identity_mode;
|
||||||
|
|
||||||
|
if (!isValidKey(key, mi))
|
||||||
|
{
|
||||||
|
warning("In config, not a valid meter key in config \"%s\"\n", key.c_str());
|
||||||
use = false;
|
use = false;
|
||||||
}
|
}
|
||||||
if (use) {
|
|
||||||
|
if (use)
|
||||||
|
{
|
||||||
mi.extra_constant_fields = extra_constant_fields;
|
mi.extra_constant_fields = extra_constant_fields;
|
||||||
mi.extra_calculated_fields = extra_calculated_fields;
|
mi.extra_calculated_fields = extra_calculated_fields;
|
||||||
mi.shells = telegram_shells;
|
mi.shells = telegram_shells;
|
||||||
mi.idsc = toIdsCommaSeparated(mi.ids);
|
mi.meter_shells = meter_shells;
|
||||||
mi.selected_fields = selected_fields;
|
mi.selected_fields = selected_fields;
|
||||||
c->meters.push_back(mi);
|
c->meters.push_back(mi);
|
||||||
}
|
}
|
||||||
|
@ -636,6 +644,11 @@ void handleShell(Configuration *c, string cmdline)
|
||||||
c->telegram_shells.push_back(cmdline);
|
c->telegram_shells.push_back(cmdline);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleMeterShell(Configuration *c, string cmdline)
|
||||||
|
{
|
||||||
|
c->meter_shells.push_back(cmdline);
|
||||||
|
}
|
||||||
|
|
||||||
void handleAlarmShell(Configuration *c, string cmdline)
|
void handleAlarmShell(Configuration *c, string cmdline)
|
||||||
{
|
{
|
||||||
c->alarm_shells.push_back(cmdline);
|
c->alarm_shells.push_back(cmdline);
|
||||||
|
@ -668,12 +681,14 @@ shared_ptr<Configuration> loadConfiguration(string root, ConfigOverrides overrid
|
||||||
string conf_dir = root;
|
string conf_dir = root;
|
||||||
string conf_file = root+"/etc/wmbusmeters.conf";
|
string conf_file = root+"/etc/wmbusmeters.conf";
|
||||||
string conf_meter_dir = root+"/etc/wmbusmeters.d";
|
string conf_meter_dir = root+"/etc/wmbusmeters.d";
|
||||||
|
string conf_drivers_dir = root+"/etc/wmbusmeters.drivers.d";
|
||||||
|
|
||||||
if (!checkFileExists(conf_file.c_str()))
|
if (!checkFileExists(conf_file.c_str()))
|
||||||
{
|
{
|
||||||
conf_dir = root+"/etc";
|
conf_dir = root+"/etc";
|
||||||
conf_file = root+"/wmbusmeters.conf";
|
conf_file = root+"/wmbusmeters.conf";
|
||||||
conf_meter_dir = root+"/wmbusmeters.d";
|
conf_meter_dir = root+"/wmbusmeters.d";
|
||||||
|
conf_drivers_dir = root+"/wmbusmeters.drivers.d";
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("(config) loading %s\n", conf_file.c_str());
|
debug("(config) loading %s\n", conf_file.c_str());
|
||||||
|
@ -714,6 +729,7 @@ shared_ptr<Configuration> loadConfiguration(string root, ConfigOverrides overrid
|
||||||
else if (p.first == "selectfields") handleSelectedFields(c, p.second);
|
else if (p.first == "selectfields") handleSelectedFields(c, p.second);
|
||||||
else if (p.first == "shell") handleShell(c, p.second);
|
else if (p.first == "shell") handleShell(c, p.second);
|
||||||
else if (p.first == "resetafter") handleResetAfter(c, p.second);
|
else if (p.first == "resetafter") handleResetAfter(c, p.second);
|
||||||
|
else if (p.first == "metershell") handleMeterShell(c, p.second);
|
||||||
else if (p.first == "alarmshell") handleAlarmShell(c, p.second);
|
else if (p.first == "alarmshell") handleAlarmShell(c, p.second);
|
||||||
else if (startsWith(p.first, "json_") ||
|
else if (startsWith(p.first, "json_") ||
|
||||||
startsWith(p.first, "field_"))
|
startsWith(p.first, "field_"))
|
||||||
|
@ -793,6 +809,8 @@ shared_ptr<Configuration> loadConfiguration(string root, ConfigOverrides overrid
|
||||||
handleLogfile(c, overrides.logfile_override);
|
handleLogfile(c, overrides.logfile_override);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadDriversFromDir(conf_drivers_dir);
|
||||||
|
|
||||||
return shared_ptr<Configuration>(c);
|
return shared_ptr<Configuration>(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,7 @@ struct Configuration
|
||||||
ConfigOverrides overrides;
|
ConfigOverrides overrides;
|
||||||
bool useconfig {};
|
bool useconfig {};
|
||||||
std::string config_root;
|
std::string config_root;
|
||||||
|
std::string drivers_dir;
|
||||||
bool need_help {};
|
bool need_help {};
|
||||||
bool silent {};
|
bool silent {};
|
||||||
bool verbose {};
|
bool verbose {};
|
||||||
|
@ -98,9 +99,11 @@ struct Configuration
|
||||||
bool json {};
|
bool json {};
|
||||||
bool pretty_print_json {};
|
bool pretty_print_json {};
|
||||||
int pollinterval {}; // Time between polling of mbus meters.
|
int pollinterval {}; // Time between polling of mbus meters.
|
||||||
|
IdentityMode identity_mode {}; // How to group meters identities into state objects.
|
||||||
bool fields {};
|
bool fields {};
|
||||||
char separator { ';' };
|
char separator { ';' };
|
||||||
std::vector<std::string> telegram_shells;
|
std::vector<std::string> telegram_shells;
|
||||||
|
std::vector<std::string> meter_shells;
|
||||||
std::vector<std::string> alarm_shells;
|
std::vector<std::string> alarm_shells;
|
||||||
int alarm_timeout {}; // Maximum number of seconds between dongle receiving two telegrams.
|
int alarm_timeout {}; // Maximum number of seconds between dongle receiving two telegrams.
|
||||||
std::string alarm_expected_activity; // Only warn when within these time periods.
|
std::string alarm_expected_activity; // Only warn when within these time periods.
|
||||||
|
|
1342
src/driver_abbb23.cc
1342
src/driver_abbb23.cc
Plik diff jest za duży
Load Diff
|
@ -41,7 +41,7 @@ namespace
|
||||||
"The total gas consumption recorded by this meter.",
|
"The total gas consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -53,7 +53,7 @@ namespace
|
||||||
"The current gas flow.",
|
"The current gas flow.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Flow,
|
Quantity::Flow,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::VolumeFlow)
|
.set(VIFRange::VolumeFlow)
|
||||||
|
@ -64,7 +64,7 @@ namespace
|
||||||
"The current temperature.",
|
"The current temperature.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::FlowTemperature)
|
.set(VIFRange::FlowTemperature)
|
||||||
|
@ -75,7 +75,7 @@ namespace
|
||||||
"Date time when previous billing period ended.",
|
"Date time when previous billing period ended.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::PointInTime,
|
Quantity::PointInTime,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::DateTime)
|
.set(VIFRange::DateTime)
|
||||||
|
@ -87,7 +87,7 @@ namespace
|
||||||
"The total gas consumption recorded when the previous billing period ended.",
|
"The total gas consumption recorded when the previous billing period ended.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
|
|
@ -43,7 +43,7 @@ namespace
|
||||||
"The total energy consumption recorded by this meter.",
|
"The total energy consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -54,7 +54,7 @@ namespace
|
||||||
"Current power consumption.",
|
"Current power consumption.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::PowerW)
|
.set(VIFRange::PowerW)
|
||||||
|
@ -65,7 +65,7 @@ namespace
|
||||||
"The total energy production recorded by this meter.",
|
"The total energy production recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("0E833C"))
|
.set(DifVifKey("0E833C"))
|
||||||
);
|
);
|
||||||
|
@ -75,7 +75,7 @@ namespace
|
||||||
"Current power production.",
|
"Current power production.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("0BAB3C"))
|
.set(DifVifKey("0BAB3C"))
|
||||||
);
|
);
|
||||||
|
@ -85,7 +85,7 @@ namespace
|
||||||
"Voltage at phase L1.",
|
"Voltage at phase L1.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Voltage,
|
Quantity::Voltage,
|
||||||
VifScaling::None,
|
VifScaling::None, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("0AFDC9FC01"))
|
.set(DifVifKey("0AFDC9FC01"))
|
||||||
);
|
);
|
||||||
|
@ -95,7 +95,7 @@ namespace
|
||||||
"Voltage at phase L2.",
|
"Voltage at phase L2.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Voltage,
|
Quantity::Voltage,
|
||||||
VifScaling::None,
|
VifScaling::None, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("0AFDC9FC02"))
|
.set(DifVifKey("0AFDC9FC02"))
|
||||||
);
|
);
|
||||||
|
@ -105,7 +105,7 @@ namespace
|
||||||
"Voltage at phase L3.",
|
"Voltage at phase L3.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Voltage,
|
Quantity::Voltage,
|
||||||
VifScaling::None,
|
VifScaling::None, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("0AFDC9FC03"))
|
.set(DifVifKey("0AFDC9FC03"))
|
||||||
);
|
);
|
||||||
|
@ -124,7 +124,7 @@ namespace
|
||||||
"The total energy consumption recorded by this meter on tariff 1.",
|
"The total energy consumption recorded by this meter on tariff 1.",
|
||||||
DEFAULT_PRINT_PROPERTIES, // ,
|
DEFAULT_PRINT_PROPERTIES, // ,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -136,7 +136,7 @@ namespace
|
||||||
"The total energy consumption recorded by this meter on tariff 2.",
|
"The total energy consumption recorded by this meter on tariff 2.",
|
||||||
DEFAULT_PRINT_PROPERTIES, // ,
|
DEFAULT_PRINT_PROPERTIES, // ,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -148,7 +148,7 @@ namespace
|
||||||
"The total energy consumption recorded by this meter on tariff 3.",
|
"The total energy consumption recorded by this meter on tariff 3.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -160,7 +160,7 @@ namespace
|
||||||
"The total energy production recorded by this meter on tariff 1.",
|
"The total energy production recorded by this meter on tariff 1.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("8E10833C"))
|
.set(DifVifKey("8E10833C"))
|
||||||
);
|
);
|
||||||
|
@ -170,7 +170,7 @@ namespace
|
||||||
"The total energy production recorded by this meter on tariff 2.",
|
"The total energy production recorded by this meter on tariff 2.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("8E20833C"))
|
.set(DifVifKey("8E20833C"))
|
||||||
);
|
);
|
||||||
|
@ -180,7 +180,7 @@ namespace
|
||||||
"The total energy production recorded by this meter on tariff 3.",
|
"The total energy production recorded by this meter on tariff 3.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("8E30833C"))
|
.set(DifVifKey("8E30833C"))
|
||||||
);
|
);
|
||||||
|
@ -190,7 +190,7 @@ namespace
|
||||||
"The maximum demand indicator (maximum 15-min average power consumption recorded this month).",
|
"The maximum demand indicator (maximum 15-min average power consumption recorded this month).",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Maximum)
|
.set(MeasurementType::Maximum)
|
||||||
.set(VIFRange::AnyPowerVIF)
|
.set(VIFRange::AnyPowerVIF)
|
||||||
|
|
|
@ -56,12 +56,14 @@ namespace
|
||||||
vector<uchar> content;
|
vector<uchar> content;
|
||||||
t->extractPayload(&content);
|
t->extractPayload(&content);
|
||||||
|
|
||||||
|
if (content.size() < 4) return;
|
||||||
|
|
||||||
map<string,pair<int,DVEntry>> vendor_values;
|
map<string,pair<int,DVEntry>> vendor_values;
|
||||||
|
|
||||||
string total;
|
string total;
|
||||||
strprintf(&total, "%02x%02x%02x%02x", content[0], content[1], content[2], content[3]);
|
strprintf(&total, "%02x%02x%02x%02x", content[0], content[1], content[2], content[3]);
|
||||||
|
|
||||||
vendor_values["0413"] = { 25, DVEntry(25, DifVifKey("0413"), MeasurementType::Instantaneous, 0x13, {}, 0, 0, 0, total) };
|
vendor_values["0413"] = { 25, DVEntry(25, DifVifKey("0413"), MeasurementType::Instantaneous, 0x13, {}, {}, 0, 0, 0, total) };
|
||||||
int offset;
|
int offset;
|
||||||
string key;
|
string key;
|
||||||
if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &vendor_values))
|
if(findKey(MeasurementType::Instantaneous, VIFRange::Volume, 0, 0, &key, &vendor_values))
|
||||||
|
|
|
@ -80,7 +80,7 @@ namespace
|
||||||
// 0F - Spcial function / packet
|
// 0F - Spcial function / packet
|
||||||
// next 4B : Date - In default frame
|
// next 4B : Date - In default frame
|
||||||
// next 3B : Faults - In default frame example: please see description of 0x01 register
|
// next 3B : Faults - In default frame example: please see description of 0x01 register
|
||||||
|
|
||||||
size_t i=8;
|
size_t i=8;
|
||||||
while (i < content.size())
|
while (i < content.size())
|
||||||
{
|
{
|
||||||
|
@ -89,7 +89,7 @@ namespace
|
||||||
if (c == 0xff) break; // An FF signals end of telegram padded to encryption boundary,
|
if (c == 0xff) break; // An FF signals end of telegram padded to encryption boundary,
|
||||||
// FFFFFFF623A where 4 last are perhaps crc or counter?
|
// FFFFFFF623A where 4 last are perhaps crc or counter?
|
||||||
i++;
|
i++;
|
||||||
if (size == -1 || i+size >= content.size())
|
if (size == -1 || i+size > content.size())
|
||||||
{
|
{
|
||||||
vector<uchar> frame;
|
vector<uchar> frame;
|
||||||
t->extractFrame(&frame);
|
t->extractFrame(&frame);
|
||||||
|
@ -97,9 +97,19 @@ namespace
|
||||||
|
|
||||||
if (t->beingAnalyzed() == false)
|
if (t->beingAnalyzed() == false)
|
||||||
{
|
{
|
||||||
warning("(apator162) telegram contains a register (%02x) with unknown size.\n"
|
if (size == -1)
|
||||||
"Please open an issue at https://github.com/wmbusmeters/wmbusmeters/\n"
|
{
|
||||||
"and report this telegram: %s\n", c, hex.c_str());
|
warning("(apator162) telegram contains a register (%02x) with unknown size.\n"
|
||||||
|
"Please open an issue at https://github.com/wmbusmeters/wmbusmeters/\n"
|
||||||
|
"and report this telegram: %s\n", c, hex.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
warning("(apator162) telegram decoding fails since last register (%02x size %d) does not\n"
|
||||||
|
"align with telegram size %zu > %zu.\n"
|
||||||
|
"Please open an issue at https://github.com/wmbusmeters/wmbusmeters/\n"
|
||||||
|
"and report this telegram: %s\n", c, size, i+size, content.size(), hex.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -109,7 +119,7 @@ namespace
|
||||||
string total;
|
string total;
|
||||||
strprintf(&total, "%02x%02x%02x%02x", content[i+0], content[i+1], content[i+2], content[i+3]);
|
strprintf(&total, "%02x%02x%02x%02x", content[i+0], content[i+1], content[i+2], content[i+3]);
|
||||||
int offset = i-1+t->header_size;
|
int offset = i-1+t->header_size;
|
||||||
vendor_values["0413"] = {offset, DVEntry(offset, DifVifKey("0413"), MeasurementType::Instantaneous, 0x13, {}, 0, 0, 0, total) };
|
vendor_values["0413"] = {offset, DVEntry(offset, DifVifKey("0413"), MeasurementType::Instantaneous, 0x13, {}, {}, 0, 0, 0, total) };
|
||||||
double total_water_consumption_m3 {};
|
double total_water_consumption_m3 {};
|
||||||
extractDVdouble(&vendor_values, "0413", &offset, &total_water_consumption_m3);
|
extractDVdouble(&vendor_values, "0413", &offset, &total_water_consumption_m3);
|
||||||
total = "*** 10-"+total+" total consumption (%f m3)";
|
total = "*** 10-"+total+" total consumption (%f m3)";
|
||||||
|
@ -135,14 +145,14 @@ namespace
|
||||||
// which also means dif = manufacturer data follows.
|
// which also means dif = manufacturer data follows.
|
||||||
// After 0x0F there is always:
|
// After 0x0F there is always:
|
||||||
// next 4B : Date - In default frame
|
// next 4B : Date - In default frame
|
||||||
// next 3B : Faults - In default frame
|
// next 3B : Faults - In default frame
|
||||||
|
|
||||||
case 0x00: return 4; // Date
|
case 0x00: return 4; // Date
|
||||||
case 0x01: return 3; // Faults - In default frame f.ex. 0F 09 4D A1 97 18 02 00 -> 18 02 00 -> 00 02 18 -> 0x0218
|
case 0x01: return 3; // Faults - In default frame f.ex. 0F 09 4D A1 97 18 02 00 -> 18 02 00 -> 00 02 18 -> 0x0218
|
||||||
|
|
||||||
case 0xA1:
|
case 0xA1:
|
||||||
case 0x10: return 4; // Total volume - In default frame
|
case 0x10: return 4; // Total volume - In default frame
|
||||||
|
|
||||||
case 0x11: return 2; // Flow
|
case 0x11: return 2; // Flow
|
||||||
|
|
||||||
case 0x40: return 6; // Detectors
|
case 0x40: return 6; // Detectors
|
||||||
|
@ -150,49 +160,52 @@ namespace
|
||||||
case 0x42: return 4; // Energy
|
case 0x42: return 4; // Energy
|
||||||
case 0x43: return 2; // Life days - In default frame f.ex. 43 6E 0A -> 2670 days from first run
|
case 0x43: return 2; // Life days - In default frame f.ex. 43 6E 0A -> 2670 days from first run
|
||||||
|
|
||||||
case 0x71: return 9;
|
case 0x44: return 3;
|
||||||
|
|
||||||
|
case 0x71: return 1+2*4; // ?
|
||||||
|
case 0x72: return 1+3*4; // ?
|
||||||
case 0x73: return 1+4*4; // Historical data
|
case 0x73: return 1+4*4; // Historical data
|
||||||
case 0x75: return 1+6*4; // Historical data
|
case 0x75: return 1+6*4; // Historical data
|
||||||
case 0x7B: return 1+12*4; // Historical data
|
case 0x7B: return 1+12*4; // Historical data
|
||||||
|
|
||||||
case 0x80:
|
case 0x80:
|
||||||
case 0x81:
|
case 0x81:
|
||||||
case 0x82:
|
case 0x82:
|
||||||
case 0x83:
|
case 0x83:
|
||||||
case 0x84:
|
case 0x84:
|
||||||
case 0x86:
|
case 0x86:
|
||||||
case 0x87: return 10; // Events
|
case 0x87: return 10; // Events
|
||||||
|
|
||||||
case 0x85:
|
case 0x85:
|
||||||
case 0x88:
|
case 0x88:
|
||||||
case 0x8F: return 11; // Events
|
case 0x8F: return 11; // Events
|
||||||
|
|
||||||
case 0x8A: return 9; // Events
|
case 0x8A: return 9; // Events
|
||||||
|
|
||||||
case 0x8B:
|
case 0x8B:
|
||||||
case 0x8C: return 6; // Events
|
case 0x8C: return 6; // Events
|
||||||
|
|
||||||
case 0x8E: return 7; // Events
|
case 0x8E: return 7; // Events
|
||||||
|
|
||||||
case 0xA0: return 4;
|
case 0xA0: return 4;
|
||||||
|
|
||||||
case 0xA2: return 1;
|
case 0xA2: return 1;
|
||||||
|
|
||||||
case 0xA3: return 7;
|
case 0xA3: return 7;
|
||||||
|
|
||||||
case 0xA4: return 4;
|
case 0xA4: return 4;
|
||||||
|
|
||||||
case 0xA5:
|
case 0xA5:
|
||||||
case 0xA9:
|
case 0xA9:
|
||||||
case 0xAF: return 1;
|
case 0xAF: return 1;
|
||||||
|
|
||||||
case 0xA6: return 3;
|
case 0xA6: return 3;
|
||||||
|
|
||||||
case 0xA7:
|
case 0xA7:
|
||||||
case 0xA8:
|
case 0xA8:
|
||||||
case 0xAA:
|
case 0xAA:
|
||||||
case 0xAB:
|
case 0xAB:
|
||||||
case 0xAC:
|
case 0xAC:
|
||||||
case 0xAD: return 2;
|
case 0xAD: return 2;
|
||||||
|
|
||||||
case 0xB0: return 5;
|
case 0xB0: return 5;
|
||||||
|
@ -201,7 +214,7 @@ namespace
|
||||||
case 0xB3: return 8;
|
case 0xB3: return 8;
|
||||||
case 0xB4: return 2;
|
case 0xB4: return 2;
|
||||||
case 0xB5: return 16;
|
case 0xB5: return 16;
|
||||||
|
|
||||||
// Unknown
|
// Unknown
|
||||||
case 0xB6: return 3;
|
case 0xB6: return 3;
|
||||||
case 0xB7: return 3;
|
case 0xB7: return 3;
|
||||||
|
|
|
@ -64,7 +64,7 @@ namespace
|
||||||
string total;
|
string total;
|
||||||
strprintf(&total, "%02x%02x%02x%02x", content[i+0], content[i+1], content[i+2], content[i+3]);
|
strprintf(&total, "%02x%02x%02x%02x", content[i+0], content[i+1], content[i+2], content[i+3]);
|
||||||
int offset = i-1+t->header_size;
|
int offset = i-1+t->header_size;
|
||||||
vendor_values["0413"] = {offset, DVEntry(offset, DifVifKey("0413"), MeasurementType::Instantaneous, 0x13, {}, 0, 0, 0, total) };
|
vendor_values["0413"] = {offset, DVEntry(offset, DifVifKey("0413"), MeasurementType::Instantaneous, 0x13, {}, {}, 0, 0, 0, total) };
|
||||||
double tmp = 0;
|
double tmp = 0;
|
||||||
extractDVdouble(&vendor_values, "0413", &offset, &tmp);
|
extractDVdouble(&vendor_values, "0413", &offset, &tmp);
|
||||||
// Single tick seems to be 1/3 of a m3. Divide by 3 and keep a single decimal.
|
// Single tick seems to be 1/3 of a m3. Divide by 3 and keep a single decimal.
|
||||||
|
|
|
@ -114,7 +114,7 @@ namespace
|
||||||
uchar season_start_date_lo = content[1];
|
uchar season_start_date_lo = content[1];
|
||||||
uchar season_start_date_hi = content[0];
|
uchar season_start_date_hi = content[0];
|
||||||
string season_start_date = dateToString(season_start_date_lo, season_start_date_hi);
|
string season_start_date = dateToString(season_start_date_lo, season_start_date_hi);
|
||||||
setStringValue("season_start_date", season_start_date);
|
setStringValue("season_start_date", season_start_date, NULL);
|
||||||
|
|
||||||
// Previous season total allocation
|
// Previous season total allocation
|
||||||
uchar prev_lo = content[4];
|
uchar prev_lo = content[4];
|
||||||
|
@ -126,7 +126,7 @@ namespace
|
||||||
uchar esb_date_lo = content[6];
|
uchar esb_date_lo = content[6];
|
||||||
uchar esb_date_hi = content[7];
|
uchar esb_date_hi = content[7];
|
||||||
string esb_date = dateToString(esb_date_lo, esb_date_hi);
|
string esb_date = dateToString(esb_date_lo, esb_date_hi);
|
||||||
setStringValue("esb_date", esb_date);
|
setStringValue("esb_date", esb_date, NULL);
|
||||||
|
|
||||||
// Current season allocation
|
// Current season allocation
|
||||||
uchar curr_lo = content[8];
|
uchar curr_lo = content[8];
|
||||||
|
@ -138,7 +138,7 @@ namespace
|
||||||
uchar date_curr_lo = content[10];
|
uchar date_curr_lo = content[10];
|
||||||
uchar date_curr_hi = content[11];
|
uchar date_curr_hi = content[11];
|
||||||
string current_date = dateToString(date_curr_lo, date_curr_hi);
|
string current_date = dateToString(date_curr_lo, date_curr_hi);
|
||||||
setStringValue("current_date", current_date);
|
setStringValue("current_date", current_date, NULL);
|
||||||
|
|
||||||
// Previous season average temperature
|
// Previous season average temperature
|
||||||
double temp_room_prev_avg_frac = content[12];
|
double temp_room_prev_avg_frac = content[12];
|
||||||
|
|
|
@ -48,7 +48,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xffff),
|
AlwaysTrigger, MaskBits(0xffff),
|
||||||
"OK",
|
"OK",
|
||||||
{
|
{
|
||||||
|
@ -75,7 +75,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xffff),
|
AlwaysTrigger, MaskBits(0xffff),
|
||||||
"",
|
"",
|
||||||
{
|
{
|
||||||
|
@ -95,7 +95,7 @@ namespace
|
||||||
"The current heat cost allocation.",
|
"The current heat cost allocation.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::HeatCostAllocation)
|
.set(VIFRange::HeatCostAllocation)
|
||||||
|
@ -106,7 +106,7 @@ namespace
|
||||||
"Heat cost allocation at the most recent billing period date.",
|
"Heat cost allocation at the most recent billing period date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::HeatCostAllocation)
|
.set(VIFRange::HeatCostAllocation)
|
||||||
|
@ -118,7 +118,7 @@ namespace
|
||||||
"The heat cost allocation at set date #.",
|
"The heat cost allocation at set date #.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::HeatCostAllocation)
|
.set(VIFRange::HeatCostAllocation)
|
||||||
|
|
|
@ -49,7 +49,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xffff),
|
AlwaysTrigger, MaskBits(0xffff),
|
||||||
"OK",
|
"OK",
|
||||||
{
|
{
|
||||||
|
@ -69,7 +69,7 @@ namespace
|
||||||
"The total water consumption recorded by this meter.",
|
"The total water consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -80,7 +80,7 @@ namespace
|
||||||
"Water consumption at the # billing period date.",
|
"Water consumption at the # billing period date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -100,7 +100,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xffff),
|
AlwaysTrigger, MaskBits(0xffff),
|
||||||
"",
|
"",
|
||||||
{
|
{
|
||||||
|
|
|
@ -125,7 +125,7 @@ namespace
|
||||||
t->addSpecialExplanation(4+t->header_size, 2, KindOfData::CONTENT, Understanding::FULL, msg.c_str());
|
t->addSpecialExplanation(4+t->header_size, 2, KindOfData::CONTENT, Understanding::FULL, msg.c_str());
|
||||||
|
|
||||||
string device_date = tostrprintf("20%02x-%02x-%02x", content[39], content[39-1], content[39-2]);
|
string device_date = tostrprintf("20%02x-%02x-%02x", content[39], content[39-1], content[39-2]);
|
||||||
setStringValue("device_date", device_date);
|
setStringValue("device_date", device_date, NULL);
|
||||||
|
|
||||||
msg = tostrprintf("*** %02X%02X%02X \"device_date\":\"%s\"", content[39-2], content[39-1], content[39],
|
msg = tostrprintf("*** %02X%02X%02X \"device_date\":\"%s\"", content[39-2], content[39-1], content[39],
|
||||||
device_date.c_str());
|
device_date.c_str());
|
||||||
|
|
|
@ -72,7 +72,7 @@ namespace
|
||||||
"The total heat energy consumption recorded by this meter.",
|
"The total heat energy consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -83,7 +83,7 @@ namespace
|
||||||
"The total heating media volume recorded by this meter.",
|
"The total heating media volume recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -101,7 +101,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::DecimalsToString,
|
Translate::MapType::DecimalsToString,
|
||||||
AlwaysTrigger, MaskBits(9999),
|
AlwaysTrigger, MaskBits(9999),
|
||||||
"OK",
|
"OK",
|
||||||
{
|
{
|
||||||
|
@ -152,7 +152,7 @@ namespace
|
||||||
"The total heat energy consumption recorded at end of previous month.",
|
"The total heat energy consumption recorded at end of previous month.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(StorageNr(32+i))
|
.set(StorageNr(32+i))
|
||||||
|
@ -169,7 +169,7 @@ namespace
|
||||||
tostrprintf("Previous month %d last date.", i+1),
|
tostrprintf("Previous month %d last date.", i+1),
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(StorageNr(32+i))
|
.set(StorageNr(32+i))
|
||||||
|
@ -184,7 +184,7 @@ namespace
|
||||||
"The total heat energy consumption at the due date.",
|
"The total heat energy consumption at the due date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(StorageNr(8))
|
.set(StorageNr(8))
|
||||||
|
@ -206,7 +206,7 @@ namespace
|
||||||
"The current heat media volume flow.",
|
"The current heat media volume flow.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Flow,
|
Quantity::Flow,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::VolumeFlow)
|
.set(VIFRange::VolumeFlow)
|
||||||
|
@ -217,7 +217,7 @@ namespace
|
||||||
"The current power consumption.",
|
"The current power consumption.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::PowerW)
|
.set(VIFRange::PowerW)
|
||||||
|
@ -228,7 +228,7 @@ namespace
|
||||||
"The total heat energy consumption recorded at end of last month.",
|
"The total heat energy consumption recorded at end of last month.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(StorageNr(32))
|
.set(StorageNr(32))
|
||||||
|
@ -249,7 +249,7 @@ namespace
|
||||||
"Maximum power consumption last month.",
|
"Maximum power consumption last month.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Maximum)
|
.set(MeasurementType::Maximum)
|
||||||
.set(StorageNr(32))
|
.set(StorageNr(32))
|
||||||
|
@ -262,7 +262,7 @@ namespace
|
||||||
"The current forward heat media temperature.",
|
"The current forward heat media temperature.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::FlowTemperature)
|
.set(VIFRange::FlowTemperature)
|
||||||
|
@ -273,7 +273,7 @@ namespace
|
||||||
"The current return heat media temperature.",
|
"The current return heat media temperature.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::ReturnTemperature)
|
.set(VIFRange::ReturnTemperature)
|
||||||
|
@ -284,21 +284,20 @@ namespace
|
||||||
// Test: Heat c5isf 55445555 NOKEY
|
// Test: Heat c5isf 55445555 NOKEY
|
||||||
|
|
||||||
// telegram=|E544496A55554455880D7A320200002F2F_04060000000004130000000002FD17240084800106000000008280016C2124C480010600000080C280016CFFFF84810106000000808281016CFFFFC481010600000080C281016CFFFF84820106000000808282016CFFFFC482010600000080C282016CFFFF84830106000000808283016CFFFFC483010600000080C283016CFFFF84840106000000808284016CFFFFC484010600000080C284016CFFFF84850106000000808285016CFFFFC485010600000080C285016CFFFF84860106000000808286016CFFFFC486010600000080C286016CFFFF|
|
// telegram=|E544496A55554455880D7A320200002F2F_04060000000004130000000002FD17240084800106000000008280016C2124C480010600000080C280016CFFFF84810106000000808281016CFFFFC481010600000080C281016CFFFF84820106000000808282016CFFFFC482010600000080C282016CFFFF84830106000000808283016CFFFFC483010600000080C283016CFFFF84840106000000808284016CFFFFC484010600000080C284016CFFFF84850106000000808285016CFFFFC485010600000080C285016CFFFF84860106000000808286016CFFFFC486010600000080C286016CFFFF|
|
||||||
// {"media":"heat/cooling load","meter":"c5isf","name":"Heat","id":"55445555","total_energy_consumption_kwh":0,"total_volume_m3":0,"status":"ERROR REVERSE_FLOW SUPPLY_SENSOR_INTERRUPTED","prev_1_month":"2017-04-01","prev_2_month":"2127-15-31","prev_3_month":"2127-15-31","prev_4_month":"2127-15-31","prev_5_month":"2127-15-31","prev_6_month":"2127-15-31","prev_7_month":"2127-15-31","prev_8_month":"2127-15-31","prev_9_month":"2127-15-31","prev_10_month":"2127-15-31","prev_11_month":"2127-15-31","prev_12_month":"2127-15-31","prev_13_month":"2127-15-31","prev_14_month":"2127-15-31","prev_1_month_kwh":0,"prev_2_month_kwh":2147483648,"prev_3_month_kwh":2147483648,"prev_4_month_kwh":2147483648,"prev_5_month_kwh":2147483648,"prev_6_month_kwh":2147483648,"prev_7_month_kwh":2147483648,"prev_8_month_kwh":2147483648,"prev_9_month_kwh":2147483648,"prev_10_month_kwh":2147483648,"prev_11_month_kwh":2147483648,"prev_12_month_kwh":2147483648,"prev_13_month_kwh":2147483648,"prev_14_month_kwh":2147483648,"total_energy_consumption_last_month_kwh":0,"timestamp":"1111-11-11T11:11:11Z"}
|
// {"media":"heat/cooling load","meter":"c5isf","name":"Heat","id":"55445555","total_energy_consumption_kwh":0,"total_volume_m3":0,"status":"ERROR REVERSE_FLOW SUPPLY_SENSOR_INTERRUPTED","prev_1_month":"2017-04-01","prev_2_month":"2127-15-31","prev_3_month":"2127-15-31","prev_4_month":"2127-15-31","prev_5_month":"2127-15-31","prev_6_month":"2127-15-31","prev_7_month":"2127-15-31","prev_8_month":"2127-15-31","prev_9_month":"2127-15-31","prev_10_month":"2127-15-31","prev_11_month":"2127-15-31","prev_12_month":"2127-15-31","prev_13_month":"2127-15-31","prev_14_month":"2127-15-31","prev_1_month_kwh":0,"prev_2_month_kwh":-2147483648,"prev_3_month_kwh":-2147483648,"prev_4_month_kwh":-2147483648,"prev_5_month_kwh":-2147483648,"prev_6_month_kwh":-2147483648,"prev_7_month_kwh":-2147483648,"prev_8_month_kwh":-2147483648,"prev_9_month_kwh":-2147483648,"prev_10_month_kwh":-2147483648,"prev_11_month_kwh":-2147483648,"prev_12_month_kwh":-2147483648,"prev_13_month_kwh":-2147483648,"prev_14_month_kwh":-2147483648,"total_energy_consumption_last_month_kwh":0,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
// |Heat;55445555;0;0;ERROR REVERSE_FLOW SUPPLY_SENSOR_INTERRUPTED;1111-11-11 11:11.11
|
// |Heat;55445555;0;0;ERROR REVERSE_FLOW SUPPLY_SENSOR_INTERRUPTED;1111-11-11 11:11.11
|
||||||
|
|
||||||
// Type T1A2 telegram:
|
// Type T1A2 telegram:
|
||||||
// telegram=|DA44496A5555445588077A320200002F2F_04140000000084800114000000008280016C2124C480011400000080C280016CFFFF84810114000000808281016CFFFFC481011400000080C281016CFFFF84820114000000808282016CFFFFC482011400000080C282016CFFFF84830114000000808283016CFFFFC483011400000080C283016CFFFF84840114000000808284016CFFFFC484011400000080C284016CFFFF84850114000000808285016CFFFFC485011400000080C285016CFFFF84860114000000808286016CFFFFC486011400000080C286016CFFFF|
|
// telegram=|DA44496A5555445588077A320200002F2F_04140000000084800114000000008280016C2124C480011400000080C280016CFFFF84810114000000808281016CFFFFC481011400000080C281016CFFFF84820114000000808282016CFFFFC482011400000080C282016CFFFF84830114000000808283016CFFFFC483011400000080C283016CFFFF84840114000000808284016CFFFFC484011400000080C284016CFFFF84850114000000808285016CFFFFC485011400000080C285016CFFFF84860114000000808286016CFFFFC486011400000080C286016CFFFF|
|
||||||
// {"media":"water","meter":"c5isf","name":"Heat","id":"55445555","total_energy_consumption_kwh":0,"total_volume_m3":0,"status":"ERROR","prev_1_month":"2017-04-01","prev_2_month":"2127-15-31","prev_3_month":"2127-15-31","prev_4_month":"2127-15-31","prev_5_month":"2127-15-31","prev_6_month":"2127-15-31","prev_7_month":"2127-15-31","prev_8_month":"2127-15-31","prev_9_month":"2127-15-31","prev_10_month":"2127-15-31","prev_11_month":"2127-15-31","prev_12_month":"2127-15-31","prev_13_month":"2127-15-31","prev_14_month":"2127-15-31","prev_1_month_m3":0,"prev_2_month_m3":21474836.48,"prev_3_month_m3":21474836.48,"prev_4_month_m3":21474836.48,"prev_5_month_m3":21474836.48,"prev_6_month_m3":21474836.48,"prev_7_month_m3":21474836.48,"prev_8_month_m3":21474836.48,"prev_9_month_m3":21474836.48,"prev_10_month_m3":21474836.48,"prev_11_month_m3":21474836.48,"prev_12_month_m3":21474836.48,"prev_13_month_m3":21474836.48,"prev_14_month_m3":21474836.48,"total_energy_consumption_last_month_kwh":0,"timestamp":"1111-11-11T11:11:11Z"}
|
// {"id": "55445555","media": "water","meter": "c5isf","name": "Heat","prev_10_month": "2127-15-31","prev_10_month_kwh":-2147483648,"prev_10_month_m3":-21474836.48,"prev_11_month": "2127-15-31","prev_11_month_kwh":-2147483648,"prev_11_month_m3":-21474836.48,"prev_12_month": "2127-15-31","prev_12_month_kwh":-2147483648,"prev_12_month_m3":-21474836.48,"prev_13_month": "2127-15-31","prev_13_month_kwh":-2147483648,"prev_13_month_m3":-21474836.48,"prev_14_month": "2127-15-31","prev_14_month_kwh":-2147483648,"prev_14_month_m3":-21474836.48,"prev_1_month": "2017-04-01","prev_1_month_kwh": 0,"prev_1_month_m3": 0,"prev_2_month": "2127-15-31","prev_2_month_kwh":-2147483648,"prev_2_month_m3":-21474836.48,"prev_3_month": "2127-15-31","prev_3_month_kwh":-2147483648,"prev_3_month_m3":-21474836.48,"prev_4_month": "2127-15-31","prev_4_month_kwh":-2147483648,"prev_4_month_m3":-21474836.48,"prev_5_month": "2127-15-31","prev_5_month_kwh":-2147483648,"prev_5_month_m3":-21474836.48,"prev_6_month": "2127-15-31","prev_6_month_kwh":-2147483648,"prev_6_month_m3":-21474836.48,"prev_7_month": "2127-15-31","prev_7_month_kwh":-2147483648,"prev_7_month_m3":-21474836.48,"prev_8_month": "2127-15-31","prev_8_month_kwh":-2147483648,"prev_8_month_m3":-21474836.48,"prev_9_month": "2127-15-31","prev_9_month_kwh":-2147483648,"prev_9_month_m3":-21474836.48,"status": "ERROR","timestamp": "1111-11-11T11:11:11Z","total_energy_consumption_kwh": 0,"total_energy_consumption_last_month_kwh": 0,"total_volume_m3": 0}
|
||||||
// |Heat;55445555;0;0;ERROR;1111-11-11 11:11.11
|
// |Heat;55445555;0;0;ERROR;1111-11-11 11:11.11
|
||||||
|
|
||||||
// Type T1B telegram:
|
// Type T1B telegram:
|
||||||
// telegram=|5E44496A5555445588047A0A0050052F2F_04061A0000000413C20800008404060000000082046CC121043BA4000000042D1900000002591216025DE21002FD17000084800106000000008280016CC121948001AE25000000002F2F2F2F2F2F|
|
// telegram=|5E44496A5555445588047A0A0050052F2F_04061A0000000413C20800008404060000000082046CC121043BA4000000042D1900000002591216025DE21002FD17000084800106000000008280016CC121948001AE25000000002F2F2F2F2F2F|
|
||||||
// {"media":"heat","meter":"c5isf","name":"Heat","id":"55445555","total_energy_consumption_kwh":26,"total_volume_m3":2.242,"status":"OK","prev_1_month":"2022-01-01","prev_2_month":"2127-15-31","prev_3_month":"2127-15-31","prev_4_month":"2127-15-31","prev_5_month":"2127-15-31","prev_6_month":"2127-15-31","prev_7_month":"2127-15-31","prev_8_month":"2127-15-31","prev_9_month":"2127-15-31","prev_10_month":"2127-15-31","prev_11_month":"2127-15-31","prev_12_month":"2127-15-31","prev_13_month":"2127-15-31","prev_14_month":"2127-15-31","prev_1_month_kwh":0,"prev_2_month_kwh":2147483648,"prev_3_month_kwh":2147483648,"prev_4_month_kwh":2147483648,"prev_5_month_kwh":2147483648,"prev_6_month_kwh":2147483648,"prev_7_month_kwh":2147483648,"prev_8_month_kwh":2147483648,"prev_9_month_kwh":2147483648,"prev_10_month_kwh":2147483648,"prev_11_month_kwh":2147483648,"prev_12_month_kwh":2147483648,"prev_13_month_kwh":2147483648,"prev_14_month_kwh":2147483648,"prev_2_month_m3":21474836.48,"prev_3_month_m3":21474836.48,"prev_4_month_m3":21474836.48,"prev_5_month_m3":21474836.48,"prev_6_month_m3":21474836.48,"prev_7_month_m3":21474836.48,"prev_8_month_m3":21474836.48,"prev_9_month_m3":21474836.48,"prev_10_month_m3":21474836.48,"prev_11_month_m3":21474836.48,"prev_12_month_m3":21474836.48,"prev_13_month_m3":21474836.48,"prev_14_month_m3":21474836.48,"due_energy_consumption_kwh":0,"due_date":"2022-01-01","volume_flow_m3h":0.164,"power_kw":2.5,"total_energy_consumption_last_month_kwh":0,"max_power_last_month_kw":0,"flow_temperature_c":56.5,"return_temperature_c":43.22,"timestamp":"1111-11-11T11:11:11Z"}
|
// {"due_date": "2022-01-01","due_energy_consumption_kwh": 0,"flow_temperature_c": 56.5,"id": "55445555","max_power_last_month_kw": 0,"media": "heat","meter": "c5isf","name": "Heat","power_kw": 2.5,"prev_10_month": "2127-15-31","prev_10_month_kwh":-2147483648,"prev_10_month_m3":-21474836.48,"prev_11_month": "2127-15-31","prev_11_month_kwh":-2147483648,"prev_11_month_m3":-21474836.48,"prev_12_month": "2127-15-31","prev_12_month_kwh":-2147483648,"prev_12_month_m3":-21474836.48,"prev_13_month": "2127-15-31","prev_13_month_kwh":-2147483648,"prev_13_month_m3":-21474836.48,"prev_14_month": "2127-15-31","prev_14_month_kwh":-2147483648,"prev_14_month_m3":-21474836.48,"prev_1_month": "2022-01-01","prev_1_month_kwh": 0,"prev_1_month_m3": 0,"prev_2_month": "2127-15-31","prev_2_month_kwh":-2147483648,"prev_2_month_m3":-21474836.48,"prev_3_month": "2127-15-31","prev_3_month_kwh":-2147483648,"prev_3_month_m3":-21474836.48,"prev_4_month": "2127-15-31","prev_4_month_kwh":-2147483648,"prev_4_month_m3":-21474836.48,"prev_5_month": "2127-15-31","prev_5_month_kwh":-2147483648,"prev_5_month_m3":-21474836.48,"prev_6_month": "2127-15-31","prev_6_month_kwh":-2147483648,"prev_6_month_m3":-21474836.48,"prev_7_month": "2127-15-31","prev_7_month_kwh":-2147483648,"prev_7_month_m3":-21474836.48,"prev_8_month": "2127-15-31","prev_8_month_kwh":-2147483648,"prev_8_month_m3":-21474836.48,"prev_9_month": "2127-15-31","prev_9_month_kwh":-2147483648,"prev_9_month_m3":-21474836.48,"return_temperature_c": 43.22,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_energy_consumption_kwh": 26,"total_energy_consumption_last_month_kwh": 0,"total_volume_m3": 2.242,"volume_flow_m3h": 0.164}
|
||||||
// |Heat;55445555;26;2.242;OK;1111-11-11 11:11.11
|
// |Heat;55445555;26;2.242;OK;1111-11-11 11:11.11
|
||||||
|
|
||||||
// Test: Heat c5isf 32002044 NOKEY
|
// Test: Heat c5isf 32002044 NOKEY
|
||||||
|
|
||||||
// Test telegram with max_power_last_month_kwh which is non-zero
|
// Test telegram with max_power_last_month_kwh which is non-zero
|
||||||
// telegram=|5E44496A4420003288047AFC0050052F2F_0406D00E00000413B28A05008404060000000082046CC121043B00000000042D000000000259E719025D051402FD17000084800106C00C00008280016CC125948001AE25090000002F2F2F2F2F2F|
|
// telegram=|5E44496A4420003288047AFC0050052F2F_0406D00E00000413B28A05008404060000000082046CC121043B00000000042D000000000259E719025D051402FD17000084800106C00C00008280016CC125948001AE25090000002F2F2F2F2F2F|
|
||||||
// {"media":"heat","meter":"c5isf","name":"Heat","id":"32002044","total_energy_consumption_kwh":3792,"total_volume_m3":363.186,"status":"OK","prev_1_month":"2022-05-01","prev_1_month_kwh":3264,"due_energy_consumption_kwh":0,"due_date":"2022-01-01","volume_flow_m3h":0,"power_kw":0,"total_energy_consumption_last_month_kwh":3264,"max_power_last_month_kw":9,"flow_temperature_c":66.31,"return_temperature_c":51.25,"timestamp":"1111-11-11T11:11:11Z"}
|
// {"media":"heat","meter":"c5isf","name":"Heat","id":"32002044","total_energy_consumption_kwh":3792,"total_volume_m3":363.186,"status":"OK","prev_1_month":"2022-05-01","prev_1_month_kwh":3264,"due_energy_consumption_kwh":0,"due_date":"2022-01-01","volume_flow_m3h":0,"power_kw":0,"total_energy_consumption_last_month_kwh":3264,"max_power_last_month_kw":9,"flow_temperature_c":66.31,"return_temperature_c":51.25,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace
|
||||||
|
|
||||||
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
||||||
{
|
{
|
||||||
addOptionalCommonFields("software_version");
|
addOptionalLibraryFields("software_version");
|
||||||
|
|
||||||
addStringField(
|
addStringField(
|
||||||
"status",
|
"status",
|
||||||
|
@ -51,7 +51,7 @@ namespace
|
||||||
"The current temperature.",
|
"The current temperature.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::ExternalTemperature)
|
.set(VIFRange::ExternalTemperature)
|
||||||
|
@ -62,7 +62,7 @@ namespace
|
||||||
"The average temperature over the last hour.",
|
"The average temperature over the last hour.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::ExternalTemperature)
|
.set(VIFRange::ExternalTemperature)
|
||||||
|
@ -77,7 +77,7 @@ namespace
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::DigitalInput),
|
.set(VIFRange::DigitalInput),
|
||||||
Translate::Lookup()
|
Translate::Lookup()
|
||||||
.add(Translate::Rule("BATTERY", Translate::Type::BitToString)
|
.add(Translate::Rule("BATTERY", Translate::MapType::BitToString)
|
||||||
.set(MaskBits(0xffff)))
|
.set(MaskBits(0xffff)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ namespace
|
||||||
string prevs;
|
string prevs;
|
||||||
strprintf(&prevs, "%02x%02x", prev_lo, prev_hi);
|
strprintf(&prevs, "%02x%02x", prev_lo, prev_hi);
|
||||||
int offset = t->parsed.size()+3;
|
int offset = t->parsed.size()+3;
|
||||||
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, prevs) };
|
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, {}, 0, 0, 0, prevs) };
|
||||||
Explanation pe(offset, 2, prevs, KindOfData::CONTENT, Understanding::FULL);
|
Explanation pe(offset, 2, prevs, KindOfData::CONTENT, Understanding::FULL);
|
||||||
t->explanations.push_back(pe);
|
t->explanations.push_back(pe);
|
||||||
t->addMoreExplanation(offset, " energy used in previous billing period (%f KWH)", prev);
|
t->addMoreExplanation(offset, " energy used in previous billing period (%f KWH)", prev);
|
||||||
|
@ -92,7 +92,7 @@ namespace
|
||||||
string currs;
|
string currs;
|
||||||
strprintf(&currs, "%02x%02x", curr_lo, curr_hi);
|
strprintf(&currs, "%02x%02x", curr_lo, curr_hi);
|
||||||
offset = t->parsed.size()+7;
|
offset = t->parsed.size()+7;
|
||||||
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, 0, 0, 0, currs) };
|
vendor_values["0215"] = { offset, DVEntry(offset, DifVifKey("0215"), MeasurementType::Instantaneous, 0x15, {}, {}, 0, 0, 0, currs) };
|
||||||
Explanation ce(offset, 2, currs, KindOfData::CONTENT, Understanding::FULL);
|
Explanation ce(offset, 2, currs, KindOfData::CONTENT, Understanding::FULL);
|
||||||
t->explanations.push_back(ce);
|
t->explanations.push_back(ce);
|
||||||
t->addMoreExplanation(offset, " energy used in current billing period (%f KWH)", curr);
|
t->addMoreExplanation(offset, " energy used in current billing period (%f KWH)", curr);
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xffff),
|
AlwaysTrigger, MaskBits(0xffff),
|
||||||
"OK",
|
"OK",
|
||||||
{
|
{
|
||||||
|
@ -61,7 +61,7 @@ namespace
|
||||||
"The total water consumption recorded by this meter.",
|
"The total water consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
|
Plik diff jest za duży
Load Diff
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2023-2024 Fredrik Öhrström (gpl-3.0-or-later)
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DRIVER_LOADER_H_
|
||||||
|
#define DRIVER_LOADER_H_
|
||||||
|
|
||||||
|
#include "meters_common_implementation.h"
|
||||||
|
|
||||||
|
struct DriverDynamic : public virtual MeterCommonImplementation
|
||||||
|
{
|
||||||
|
DriverDynamic(MeterInfo &mi, DriverInfo &di);
|
||||||
|
~DriverDynamic();
|
||||||
|
static bool load(DriverInfo *di, const string &name, const char *content);
|
||||||
|
static XMQProceed add_detect(XMQDoc *doc, XMQNode *detect, DriverInfo *di);
|
||||||
|
static XMQProceed add_use(XMQDoc *doc, XMQNode *field, DriverDynamic *dd);
|
||||||
|
static XMQProceed add_field(XMQDoc *doc, XMQNode *field, DriverDynamic *dd);
|
||||||
|
static XMQProceed add_match(XMQDoc *doc, XMQNode *match, DriverDynamic *dd);
|
||||||
|
static XMQProceed add_combinable(XMQDoc *doc, XMQNode *match, DriverDynamic *dd);
|
||||||
|
|
||||||
|
static XMQProceed add_lookup(XMQDoc *doc, XMQNode *lookup, DriverDynamic *dd);
|
||||||
|
static XMQProceed add_map(XMQDoc *doc, XMQNode *map, DriverDynamic *dd);
|
||||||
|
|
||||||
|
const string &fileName() { return file_name_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
string file_name_;
|
||||||
|
FieldMatcher *tmp_matcher_;
|
||||||
|
Translate::Lookup *tmp_lookup_;
|
||||||
|
Translate::Rule *tmp_rule_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -41,7 +41,7 @@ namespace
|
||||||
"The total energy consumption recorded by this meter.",
|
"The total energy consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -52,7 +52,7 @@ namespace
|
||||||
"Current power consumption at phase 1.",
|
"Current power consumption at phase 1.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("04A9FF01"))
|
.set(DifVifKey("04A9FF01"))
|
||||||
);
|
);
|
||||||
|
@ -62,7 +62,7 @@ namespace
|
||||||
"Current power consumption at phase 2.",
|
"Current power consumption at phase 2.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("04A9FF02"))
|
.set(DifVifKey("04A9FF02"))
|
||||||
);
|
);
|
||||||
|
@ -72,7 +72,7 @@ namespace
|
||||||
"Current power consumption at phase 3.",
|
"Current power consumption at phase 3.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("04A9FF03"))
|
.set(DifVifKey("04A9FF03"))
|
||||||
);
|
);
|
||||||
|
@ -92,7 +92,7 @@ namespace
|
||||||
"Calculated sum of power consumption of all phases.",
|
"Calculated sum of power consumption of all phases.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyPowerVIF)
|
.set(VIFRange::AnyPowerVIF)
|
||||||
|
|
|
@ -40,14 +40,14 @@ namespace
|
||||||
"Meter status. Includes both meter error field and tpl status field.",
|
"Meter status. Includes both meter error field and tpl status field.",
|
||||||
PrintProperty::STATUS | PrintProperty::INCLUDE_TPL_STATUS);
|
PrintProperty::STATUS | PrintProperty::INCLUDE_TPL_STATUS);
|
||||||
|
|
||||||
addOptionalCommonFields("on_time_h");
|
addOptionalLibraryFields("on_time_h");
|
||||||
|
|
||||||
addNumericFieldWithExtractor(
|
addNumericFieldWithExtractor(
|
||||||
"total_energy_consumption",
|
"total_energy_consumption",
|
||||||
"The total energy consumption recorded by this meter.",
|
"The total energy consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -58,7 +58,7 @@ namespace
|
||||||
"Current power consumption.",
|
"Current power consumption.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyPowerVIF)
|
.set(VIFRange::AnyPowerVIF)
|
||||||
|
@ -69,7 +69,7 @@ namespace
|
||||||
"The total energy production recorded by this meter.",
|
"The total energy production recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace
|
||||||
{
|
{
|
||||||
setMfctTPLStatusBits(
|
setMfctTPLStatusBits(
|
||||||
Translate::Lookup()
|
Translate::Lookup()
|
||||||
.add(Translate::Rule("TPL_STS", Translate::Type::BitToString)
|
.add(Translate::Rule("TPL_STS", Translate::MapType::BitToString)
|
||||||
.set(MaskBits(0xe0))
|
.set(MaskBits(0xe0))
|
||||||
.set(DefaultMessage("OK"))
|
.set(DefaultMessage("OK"))
|
||||||
.add(Translate::Map(0x04 ,"RTC_INVALID", TestBit::Set))));
|
.add(Translate::Map(0x04 ,"RTC_INVALID", TestBit::Set))));
|
||||||
|
@ -54,7 +54,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xffff),
|
AlwaysTrigger, MaskBits(0xffff),
|
||||||
"OK",
|
"OK",
|
||||||
{
|
{
|
||||||
|
@ -90,7 +90,7 @@ namespace
|
||||||
"Number of times the smoke alarm has triggered.",
|
"Number of times the smoke alarm has triggered.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Dimensionless,
|
Quantity::Dimensionless,
|
||||||
VifScaling::None,
|
VifScaling::None, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(SubUnitNr(1))
|
.set(SubUnitNr(1))
|
||||||
|
@ -121,7 +121,7 @@ namespace
|
||||||
"Time the smoke alarm has been removed.",
|
"Time the smoke alarm has been removed.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Time,
|
Quantity::Time,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(SubUnitNr(1))
|
.set(SubUnitNr(1))
|
||||||
|
@ -145,7 +145,7 @@ namespace
|
||||||
"Number of times the smoke alarm has been removed.",
|
"Number of times the smoke alarm has been removed.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Dimensionless,
|
Quantity::Dimensionless,
|
||||||
VifScaling::None,
|
VifScaling::None, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(SubUnitNr(1))
|
.set(SubUnitNr(1))
|
||||||
|
@ -169,7 +169,7 @@ namespace
|
||||||
"Number of times the test button has been pressed.",
|
"Number of times the test button has been pressed.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Dimensionless,
|
Quantity::Dimensionless,
|
||||||
VifScaling::None,
|
VifScaling::None, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(SubUnitNr(1))
|
.set(SubUnitNr(1))
|
||||||
|
@ -208,7 +208,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"DUST",
|
"DUST",
|
||||||
Translate::Type::IndexToString,
|
Translate::MapType::IndexToString,
|
||||||
AlwaysTrigger, MaskBits(0x1f),
|
AlwaysTrigger, MaskBits(0x1f),
|
||||||
"",
|
"",
|
||||||
{
|
{
|
||||||
|
@ -228,7 +228,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"BATTERY_VOLTAGE",
|
"BATTERY_VOLTAGE",
|
||||||
Translate::Type::IndexToString,
|
Translate::MapType::IndexToString,
|
||||||
AlwaysTrigger, MaskBits(0x0f00),
|
AlwaysTrigger, MaskBits(0x0f00),
|
||||||
"",
|
"",
|
||||||
{
|
{
|
||||||
|
@ -267,7 +267,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"OBSTACLE_DISTANCE",
|
"OBSTACLE_DISTANCE",
|
||||||
Translate::Type::IndexToString,
|
Translate::MapType::IndexToString,
|
||||||
AlwaysTrigger, MaskBits(0x700000),
|
AlwaysTrigger, MaskBits(0x700000),
|
||||||
"",
|
"",
|
||||||
{
|
{
|
||||||
|
@ -297,7 +297,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"HEAD_STATUS",
|
"HEAD_STATUS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xff8ff0e0),
|
AlwaysTrigger, MaskBits(0xff8ff0e0),
|
||||||
"OK",
|
"OK",
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,7 +45,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xffffffff),
|
AlwaysTrigger, MaskBits(0xffffffff),
|
||||||
"OK",
|
"OK",
|
||||||
{
|
{
|
||||||
|
@ -69,7 +69,7 @@ namespace
|
||||||
"The total energy consumption recorded by this meter.",
|
"The total energy consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -80,7 +80,7 @@ namespace
|
||||||
"Current power consumption.",
|
"Current power consumption.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::PowerW)
|
.set(VIFRange::PowerW)
|
||||||
|
@ -91,7 +91,7 @@ namespace
|
||||||
"Total volume of heat media.",
|
"Total volume of heat media.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -102,7 +102,7 @@ namespace
|
||||||
"The total energy consumption recorded at the target date.",
|
"The total energy consumption recorded at the target date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -114,7 +114,7 @@ namespace
|
||||||
"The flow temperature.",
|
"The flow temperature.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::FlowTemperature)
|
.set(VIFRange::FlowTemperature)
|
||||||
|
@ -125,7 +125,7 @@ namespace
|
||||||
"The return temperature.",
|
"The return temperature.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::ReturnTemperature)
|
.set(VIFRange::ReturnTemperature)
|
||||||
|
@ -136,7 +136,7 @@ namespace
|
||||||
"The external temperature.",
|
"The external temperature.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::ExternalTemperature)
|
.set(VIFRange::ExternalTemperature)
|
||||||
|
@ -147,7 +147,7 @@ namespace
|
||||||
"How long the meter has been collecting data.",
|
"How long the meter has been collecting data.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Time,
|
Quantity::Time,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::OperatingTime)
|
.set(VIFRange::OperatingTime)
|
||||||
|
@ -167,7 +167,7 @@ namespace
|
||||||
"Battery voltage.",
|
"Battery voltage.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Voltage,
|
Quantity::Voltage,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Voltage)
|
.set(VIFRange::Voltage)
|
||||||
|
|
|
@ -36,6 +36,7 @@ namespace
|
||||||
di.setMeterType(MeterType::ElectricityMeter);
|
di.setMeterType(MeterType::ElectricityMeter);
|
||||||
di.addLinkMode(LinkMode::C1);
|
di.addLinkMode(LinkMode::C1);
|
||||||
di.addDetection(MANUFACTURER_KAM, 0x02, 0x33);
|
di.addDetection(MANUFACTURER_KAM, 0x02, 0x33);
|
||||||
|
di.addDetection(MANUFACTURER_GAV, 0x02, 0x00);
|
||||||
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
|
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xff),
|
AlwaysTrigger, MaskBits(0xff),
|
||||||
"OK",
|
"OK",
|
||||||
{
|
{
|
||||||
|
@ -79,7 +80,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xff),
|
AlwaysTrigger, MaskBits(0xff),
|
||||||
"",
|
"",
|
||||||
{
|
{
|
||||||
|
@ -100,18 +101,29 @@ namespace
|
||||||
"The total energy consumption recorded by this meter.",
|
"The total energy consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
addNumericFieldWithExtractor(
|
||||||
|
"power",
|
||||||
|
"Power measured by this meter at the moment.",
|
||||||
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
|
Quantity::Power,
|
||||||
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
|
FieldMatcher::build()
|
||||||
|
.set(MeasurementType::Instantaneous)
|
||||||
|
.set(VIFRange::AnyPowerVIF)
|
||||||
|
);
|
||||||
|
|
||||||
addNumericFieldWithExtractor(
|
addNumericFieldWithExtractor(
|
||||||
"total_energy_production",
|
"total_energy_production",
|
||||||
"The total energy backward (production) recorded by this meter.",
|
"The total energy backward (production) recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -123,7 +135,7 @@ namespace
|
||||||
"The reactive total energy consumption recorded by this meter.",
|
"The reactive total energy consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Reactive_Energy,
|
Quantity::Reactive_Energy,
|
||||||
VifScaling::None,
|
VifScaling::None, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("04FB8275"))
|
.set(DifVifKey("04FB8275"))
|
||||||
);
|
);
|
||||||
|
@ -133,7 +145,7 @@ namespace
|
||||||
"The total reactive energy backward (production) recorded by this meter.",
|
"The total reactive energy backward (production) recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Reactive_Energy,
|
Quantity::Reactive_Energy,
|
||||||
VifScaling::None,
|
VifScaling::None, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(DifVifKey("04FB82F53C"))
|
.set(DifVifKey("04FB82F53C"))
|
||||||
|
@ -160,10 +172,96 @@ namespace
|
||||||
(total_reactive_energy_production_kvarh * total_reactive_energy_production_kvarh) )
|
(total_reactive_energy_production_kvarh * total_reactive_energy_production_kvarh) )
|
||||||
)STR");
|
)STR");
|
||||||
|
|
||||||
|
addNumericFieldWithExtractor(
|
||||||
|
"amperage_at_phase_1",
|
||||||
|
"Amperage at phase L1.",
|
||||||
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
|
Quantity::Amperage,
|
||||||
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
|
FieldMatcher::build()
|
||||||
|
.set(DifVifKey("04FDD9FC01"))
|
||||||
|
);
|
||||||
|
|
||||||
|
addNumericFieldWithExtractor(
|
||||||
|
"amperage_at_phase_2",
|
||||||
|
"Amperage at phase L2.",
|
||||||
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
|
Quantity::Amperage,
|
||||||
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
|
FieldMatcher::build()
|
||||||
|
.set(DifVifKey("04FDD9FC02"))
|
||||||
|
);
|
||||||
|
|
||||||
|
addNumericFieldWithExtractor(
|
||||||
|
"amperage_at_phase_3",
|
||||||
|
"Amperage at phase L3.",
|
||||||
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
|
Quantity::Amperage,
|
||||||
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
|
FieldMatcher::build()
|
||||||
|
.set(DifVifKey("04FDD9FC03"))
|
||||||
|
);
|
||||||
|
|
||||||
|
addNumericFieldWithExtractor(
|
||||||
|
"voltage_at_phase_1",
|
||||||
|
"Voltage at phase L1.",
|
||||||
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
|
Quantity::Voltage,
|
||||||
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
|
FieldMatcher::build()
|
||||||
|
.set(DifVifKey("04FDC8FC01"))
|
||||||
|
);
|
||||||
|
|
||||||
|
addNumericFieldWithExtractor(
|
||||||
|
"voltage_at_phase_2",
|
||||||
|
"Voltage at phase L2.",
|
||||||
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
|
Quantity::Voltage,
|
||||||
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
|
FieldMatcher::build()
|
||||||
|
.set(DifVifKey("04FDC8FC02"))
|
||||||
|
);
|
||||||
|
|
||||||
|
addNumericFieldWithExtractor(
|
||||||
|
"voltage_at_phase_3",
|
||||||
|
"Voltage at phase L3.",
|
||||||
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
|
Quantity::Voltage,
|
||||||
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
|
FieldMatcher::build()
|
||||||
|
.set(DifVifKey("04FDC8FC03"))
|
||||||
|
);
|
||||||
|
|
||||||
|
addNumericFieldWithExtractor(
|
||||||
|
"raw_frequency",
|
||||||
|
"Frequency in 0.1 Hz",
|
||||||
|
DEFAULT_PRINT_PROPERTIES | PrintProperty::HIDE,
|
||||||
|
Quantity::Frequency,
|
||||||
|
VifScaling::None, DifSignedness::Signed,
|
||||||
|
FieldMatcher::build()
|
||||||
|
.set(DifVifKey("02FB2E"))
|
||||||
|
);
|
||||||
|
|
||||||
|
addNumericFieldWithCalculator(
|
||||||
|
"frequency",
|
||||||
|
"Frequency of AC.",
|
||||||
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
|
Quantity::Frequency,
|
||||||
|
"raw_frequency_hz / 10 counter");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test: Elen em24 66666666 NOKEY
|
// Test: Elen em24 66666666 NOKEY
|
||||||
// telegram=|35442D2C6666666633028D2070806A0520B4D378_0405F208000004FB82753F00000004853C0000000004FB82F53CCA01000001FD1722|
|
// telegram=|35442D2C6666666633028D2070806A0520B4D378_0405F208000004FB82753F00000004853C0000000004FB82F53CCA01000001FD1722|
|
||||||
// {"media":"electricity","meter":"em24","name":"Elen","id":"66666666","status":"I_3_OVERFLOW V_2_OVERFLOW","error":"I_3_OVERFLOW V_2_OVERFLOW","total_energy_consumption_kwh":229,"total_energy_production_kwh":0,"total_reactive_energy_consumption_kvarh":63,"total_reactive_energy_production_kvarh":458,"total_apparent_energy_consumption_kvah":237.507895,"total_apparent_energy_production_kvah":458,"timestamp":"1111-11-11T11:11:11Z"}
|
// {"media":"electricity","meter":"em24","name":"Elen","id":"66666666","status":"I_3_OVERFLOW V_2_OVERFLOW","error":"I_3_OVERFLOW V_2_OVERFLOW","total_energy_consumption_kwh":229,"total_energy_production_kwh":0,"total_reactive_energy_consumption_kvarh":63,"total_reactive_energy_production_kvarh":458,"total_apparent_energy_consumption_kvah":237.507895,"total_apparent_energy_production_kvah":458,"frequency_hz":null,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
// |Elen;66666666;229;0;63;458;237.507895;458;1111-11-11 11:11.11
|
// |Elen;66666666;229;0;63;458;237.507895;458;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
// Test: Elen2 em24 02020202 NOKEY
|
||||||
|
// telegram=|4144361C0202020200028C209A7A9A0030252F2F_04050100000004FB82750000000004FB82F53C00000000042A0000000001FD17002F2F2F2F2F2F2F2F2F2F2F2F2F|
|
||||||
|
// {"error": "","id": "02020202","media": "electricity","meter": "em24","name": "Elen2","power_kw": 0,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_apparent_energy_consumption_kvah": 0.1,"total_apparent_energy_production_kvah": null,"total_energy_consumption_kwh": 0.1,"total_reactive_energy_consumption_kvarh": 0,"total_reactive_energy_production_kvarh": 0,"frequency_hz":null}
|
||||||
|
// |Elen2;02020202;0.1;null;0;0;0.1;null;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
|
||||||
|
// telegram=|8144361C0202020200028C20357A351070252F2F_04050100000004FB82750000000004FB82F53C00000000042A0000000004FB140000000004FB943C0000000004FDD9FC010000000004FDD9FC020000000004FDD9FC030000000004FDC8FC011709000004FDC8FC02EC04000004FDC8FC03EC04000002FB2EF40101FD17002F2F2F|
|
||||||
|
// {"amperage_at_phase_1_a": 0,"amperage_at_phase_2_a": 0,"amperage_at_phase_3_a": 0,"error": "","frequency_hz": 50,"id": "02020202","media": "electricity","meter": "em24","name": "Elen2","power_kw": 0,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_apparent_energy_consumption_kvah": 0.1,"total_apparent_energy_production_kvah": null,"total_energy_consumption_kwh": 0.1,"total_reactive_energy_consumption_kvarh": 0,"total_reactive_energy_production_kvarh": 0,"voltage_at_phase_1_v": 232.7,"voltage_at_phase_2_v": 126,"voltage_at_phase_3_v": 126}
|
||||||
|
// |Elen2;02020202;0.1;null;0;0;0.1;null;1111-11-11 11:11.11
|
||||||
|
|
|
@ -42,7 +42,7 @@ namespace
|
||||||
"The total water consumption recorded by this meter.",
|
"The total water consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -53,7 +53,7 @@ namespace
|
||||||
"The target water consumption recorded at previous period.",
|
"The target water consumption recorded at previous period.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
|
|
@ -45,7 +45,7 @@ namespace
|
||||||
"The total energy consumption recorded by this meter.",
|
"The total energy consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -56,7 +56,7 @@ namespace
|
||||||
"The energy consumption recorded by this meter at the set date.",
|
"The energy consumption recorded by this meter at the set date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -68,7 +68,7 @@ namespace
|
||||||
"The active power consumption.",
|
"The active power consumption.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyPowerVIF)
|
.set(VIFRange::AnyPowerVIF)
|
||||||
|
@ -79,7 +79,7 @@ namespace
|
||||||
"The flow of water.",
|
"The flow of water.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Flow,
|
Quantity::Flow,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::VolumeFlow)
|
.set(VIFRange::VolumeFlow)
|
||||||
|
@ -90,7 +90,7 @@ namespace
|
||||||
"The maximum forward flow of water since the last set date?",
|
"The maximum forward flow of water since the last set date?",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Flow,
|
Quantity::Flow,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Maximum)
|
.set(MeasurementType::Maximum)
|
||||||
.set(VIFRange::VolumeFlow)
|
.set(VIFRange::VolumeFlow)
|
||||||
|
@ -101,7 +101,7 @@ namespace
|
||||||
"The forward temperature of the water.",
|
"The forward temperature of the water.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::FlowTemperature)
|
.set(VIFRange::FlowTemperature)
|
||||||
|
@ -112,7 +112,7 @@ namespace
|
||||||
"The return temperature of the water.",
|
"The return temperature of the water.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::ReturnTemperature)
|
.set(VIFRange::ReturnTemperature)
|
||||||
|
@ -123,7 +123,7 @@ namespace
|
||||||
"The temperature difference forward-return for the water.",
|
"The temperature difference forward-return for the water.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::AutoSigned,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::TemperatureDifference)
|
.set(VIFRange::TemperatureDifference)
|
||||||
|
@ -134,7 +134,7 @@ namespace
|
||||||
"The total amount of water that has passed through this meter.",
|
"The total amount of water that has passed through this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -145,7 +145,7 @@ namespace
|
||||||
"The amount of water that had passed through this meter at the set date.",
|
"The amount of water that had passed through this meter at the set date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -157,7 +157,7 @@ namespace
|
||||||
"The amount of water that has passed through subunit 1.",
|
"The amount of water that has passed through subunit 1.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -169,7 +169,7 @@ namespace
|
||||||
"The amount of water that had passed through the subunit 1 at the set date.",
|
"The amount of water that had passed through the subunit 1 at the set date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -182,7 +182,7 @@ namespace
|
||||||
"The current heat cost allocation for subunit 1.",
|
"The current heat cost allocation for subunit 1.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::HeatCostAllocation)
|
.set(VIFRange::HeatCostAllocation)
|
||||||
|
@ -194,7 +194,7 @@ namespace
|
||||||
"The heat cost allocation for subunit 1 at the target date.",
|
"The heat cost allocation for subunit 1 at the target date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::HeatCostAllocation)
|
.set(VIFRange::HeatCostAllocation)
|
||||||
|
@ -207,7 +207,7 @@ namespace
|
||||||
"The current heat cost allocation for subunit 2.",
|
"The current heat cost allocation for subunit 2.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::HeatCostAllocation)
|
.set(VIFRange::HeatCostAllocation)
|
||||||
|
@ -219,7 +219,7 @@ namespace
|
||||||
"The heat cost allocation for subunit 2 at the target date.",
|
"The heat cost allocation for subunit 2 at the target date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::HeatCostAllocation)
|
.set(VIFRange::HeatCostAllocation)
|
||||||
|
@ -237,7 +237,7 @@ namespace
|
||||||
.set(StorageNr(1))
|
.set(StorageNr(1))
|
||||||
);
|
);
|
||||||
|
|
||||||
addOptionalCommonFields("operating_time_h,on_time_h,meter_datetime");
|
addOptionalLibraryFields("operating_time_h,on_time_h,meter_datetime");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xff),
|
AlwaysTrigger, MaskBits(0xff),
|
||||||
"OK",
|
"OK",
|
||||||
{
|
{
|
||||||
|
@ -78,7 +78,7 @@ namespace
|
||||||
"The water consumption at the last billing period date.",
|
"The water consumption at the last billing period date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -95,7 +95,7 @@ namespace
|
||||||
info,
|
info,
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
|
|
@ -37,7 +37,7 @@ namespace
|
||||||
|
|
||||||
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
||||||
{
|
{
|
||||||
addOptionalCommonFields("fabrication_no,enhanced_id,location");
|
addOptionalLibraryFields("fabrication_no,enhanced_id,location");
|
||||||
|
|
||||||
addStringFieldWithExtractor(
|
addStringFieldWithExtractor(
|
||||||
"location_hex",
|
"location_hex",
|
||||||
|
@ -53,7 +53,7 @@ namespace
|
||||||
"The total energy consumption recorded by this meter.",
|
"The total energy consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -64,7 +64,7 @@ namespace
|
||||||
"Calculated sum of power consumption of all phases.",
|
"Calculated sum of power consumption of all phases.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyPowerVIF)
|
.set(VIFRange::AnyPowerVIF)
|
||||||
|
@ -75,7 +75,7 @@ namespace
|
||||||
"The total energy production recorded by this meter.",
|
"The total energy production recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -87,7 +87,7 @@ namespace
|
||||||
"The total energy consumption recorded by this meter on tariff 1.",
|
"The total energy consumption recorded by this meter on tariff 1.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -99,7 +99,7 @@ namespace
|
||||||
"The total energy consumption recorded by this meter on tariff 2.",
|
"The total energy consumption recorded by this meter on tariff 2.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -111,7 +111,7 @@ namespace
|
||||||
"Current power consumption phase 1.",
|
"Current power consumption phase 1.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("04A9FF01"))
|
.set(DifVifKey("04A9FF01"))
|
||||||
);
|
);
|
||||||
|
@ -121,7 +121,7 @@ namespace
|
||||||
"Current power consumption phase 2.",
|
"Current power consumption phase 2.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("04A9FF02"))
|
.set(DifVifKey("04A9FF02"))
|
||||||
);
|
);
|
||||||
|
@ -131,7 +131,7 @@ namespace
|
||||||
"Current power consumption phase 3.",
|
"Current power consumption phase 3.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Power,
|
Quantity::Power,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("04A9FF03"))
|
.set(DifVifKey("04A9FF03"))
|
||||||
);
|
);
|
||||||
|
|
|
@ -50,7 +50,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xffff),
|
AlwaysTrigger, MaskBits(0xffff),
|
||||||
"OK",
|
"OK",
|
||||||
{
|
{
|
||||||
|
@ -78,7 +78,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xffff),
|
AlwaysTrigger, MaskBits(0xffff),
|
||||||
"OK",
|
"OK",
|
||||||
{
|
{
|
||||||
|
@ -98,7 +98,7 @@ namespace
|
||||||
"The current heat cost allocation.",
|
"The current heat cost allocation.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::HeatCostAllocation)
|
.set(VIFRange::HeatCostAllocation)
|
||||||
|
@ -109,7 +109,7 @@ namespace
|
||||||
"The heat cost allocation at set date #.",
|
"The heat cost allocation at set date #.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::HeatCostAllocation)
|
.set(VIFRange::HeatCostAllocation)
|
||||||
|
@ -121,7 +121,7 @@ namespace
|
||||||
"Deprecated field.",
|
"Deprecated field.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::HeatCostAllocation)
|
.set(VIFRange::HeatCostAllocation)
|
||||||
|
|
|
@ -42,7 +42,7 @@ namespace
|
||||||
"The total water consumption recorded by this meter.",
|
"The total water consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -53,7 +53,7 @@ namespace
|
||||||
"The target water consumption recorded at previous period.",
|
"The target water consumption recorded at previous period.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
|
|
@ -40,7 +40,7 @@ namespace
|
||||||
addStringFieldWithExtractorAndLookup(
|
addStringFieldWithExtractorAndLookup(
|
||||||
"current_status",
|
"current_status",
|
||||||
"Status of meter.",
|
"Status of meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES
|
DEFAULT_PRINT_PROPERTIES
|
||||||
| PrintProperty::STATUS | PrintProperty::INCLUDE_TPL_STATUS,
|
| PrintProperty::STATUS | PrintProperty::INCLUDE_TPL_STATUS,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
|
@ -49,7 +49,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xffff),
|
AlwaysTrigger, MaskBits(0xffff),
|
||||||
"OK",
|
"OK",
|
||||||
{
|
{
|
||||||
|
@ -59,15 +59,15 @@ namespace
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
addOptionalCommonFields("fabrication_no");
|
addOptionalLibraryFields("fabrication_no");
|
||||||
addOptionalFlowRelatedFields("total_m3");
|
addOptionalLibraryFields("total_m3");
|
||||||
|
|
||||||
addNumericFieldWithExtractor(
|
addNumericFieldWithExtractor(
|
||||||
"consumption_at_set_date",
|
"consumption_at_set_date",
|
||||||
"The total water consumption at the most recent billing period date.",
|
"The total water consumption at the most recent billing period date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -89,7 +89,7 @@ namespace
|
||||||
"The total water consumption at the second most recent billing period date.",
|
"The total water consumption at the second most recent billing period date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -111,7 +111,7 @@ namespace
|
||||||
"Maximum water flow since date time.",
|
"Maximum water flow since date time.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Flow,
|
Quantity::Flow,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Maximum)
|
.set(MeasurementType::Maximum)
|
||||||
.set(VIFRange::VolumeFlow)
|
.set(VIFRange::VolumeFlow)
|
||||||
|
@ -133,7 +133,7 @@ namespace
|
||||||
"The total water consumption at the historic date.",
|
"The total water consumption at the historic date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -145,7 +145,7 @@ namespace
|
||||||
"Reference date for history.",
|
"Reference date for history.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::PointInTime,
|
Quantity::PointInTime,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Date)
|
.set(VIFRange::Date)
|
||||||
|
|
|
@ -105,7 +105,7 @@ namespace
|
||||||
leadingZeroString(month_prev) + "-" +
|
leadingZeroString(month_prev) + "-" +
|
||||||
leadingZeroString(day_prev) + "T02:00:00Z";
|
leadingZeroString(day_prev) + "T02:00:00Z";
|
||||||
|
|
||||||
setStringValue("previous_date", previous_date);
|
setStringValue("previous_date", previous_date, NULL);
|
||||||
|
|
||||||
string bytes = tostrprintf("%02x%02x", content[1], content[2]);
|
string bytes = tostrprintf("%02x%02x", content[1], content[2]);
|
||||||
string info = "*** "+bytes+" previous_date = %s";
|
string info = "*** "+bytes+" previous_date = %s";
|
||||||
|
@ -147,7 +147,7 @@ namespace
|
||||||
leadingZeroString(month_curr) + "-" +
|
leadingZeroString(month_curr) + "-" +
|
||||||
leadingZeroString(day_curr) + "T02:00:00Z";
|
leadingZeroString(day_curr) + "T02:00:00Z";
|
||||||
|
|
||||||
setStringValue("current_date", current_date);
|
setStringValue("current_date", current_date, NULL);
|
||||||
|
|
||||||
bytes = tostrprintf("%02x%02x", content[5], content[6]);
|
bytes = tostrprintf("%02x%02x", content[5], content[6]);
|
||||||
info = "*** "+bytes+" current_date = %s";
|
info = "*** "+bytes+" current_date = %s";
|
||||||
|
|
|
@ -50,7 +50,7 @@ namespace
|
||||||
"The current heat cost allocation.",
|
"The current heat cost allocation.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
);
|
);
|
||||||
|
@ -69,7 +69,7 @@ namespace
|
||||||
"Heat cost allocation at the most recent billing period date.",
|
"Heat cost allocation at the most recent billing period date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(StorageNr(1))
|
.set(StorageNr(1))
|
||||||
|
@ -89,7 +89,7 @@ namespace
|
||||||
"Heat cost allocation at the most recent billing period date.",
|
"Heat cost allocation at the most recent billing period date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(StorageNr(1))
|
.set(StorageNr(1))
|
||||||
|
@ -109,7 +109,7 @@ namespace
|
||||||
"Heat cost allocation at the 8 billing period date.",
|
"Heat cost allocation at the 8 billing period date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(StorageNr(8))
|
.set(StorageNr(8))
|
||||||
|
|
|
@ -51,7 +51,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xffffffff),
|
AlwaysTrigger, MaskBits(0xffffffff),
|
||||||
"OK",
|
"OK",
|
||||||
{
|
{
|
||||||
|
@ -71,7 +71,7 @@ namespace
|
||||||
"The total water consumption recorded by this meter.",
|
"The total water consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -82,7 +82,7 @@ namespace
|
||||||
"The total water consumption recorded at the beginning of this month.",
|
"The total water consumption recorded at the beginning of this month.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -104,7 +104,7 @@ namespace
|
||||||
"The current flow of water through the meter.",
|
"The current flow of water through the meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Flow,
|
Quantity::Flow,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::VolumeFlow)
|
.set(VIFRange::VolumeFlow)
|
||||||
|
@ -115,7 +115,7 @@ namespace
|
||||||
"The water temperature.",
|
"The water temperature.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Minimum)
|
.set(MeasurementType::Minimum)
|
||||||
.set(VIFRange::FlowTemperature)
|
.set(VIFRange::FlowTemperature)
|
||||||
|
@ -127,7 +127,7 @@ namespace
|
||||||
"The maximum water temperature.",
|
"The maximum water temperature.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Maximum)
|
.set(MeasurementType::Maximum)
|
||||||
.set(VIFRange::FlowTemperature)
|
.set(VIFRange::FlowTemperature)
|
||||||
|
@ -139,7 +139,7 @@ namespace
|
||||||
"The external temperature outside of the meter.",
|
"The external temperature outside of the meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Minimum)
|
.set(MeasurementType::Minimum)
|
||||||
.set(VIFRange::ExternalTemperature)
|
.set(VIFRange::ExternalTemperature)
|
||||||
|
@ -148,10 +148,10 @@ namespace
|
||||||
|
|
||||||
addNumericFieldWithExtractor(
|
addNumericFieldWithExtractor(
|
||||||
"max_flow",
|
"max_flow",
|
||||||
"The maxium flow recorded during previous period.",
|
"The maximum flow recorded during previous period.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Flow,
|
Quantity::Flow,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Maximum)
|
.set(MeasurementType::Maximum)
|
||||||
.set(VIFRange::VolumeFlow)
|
.set(VIFRange::VolumeFlow)
|
||||||
|
@ -163,7 +163,7 @@ namespace
|
||||||
"The minimum flow recorded during previous period.",
|
"The minimum flow recorded during previous period.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Flow,
|
Quantity::Flow,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Minimum)
|
.set(MeasurementType::Minimum)
|
||||||
.set(VIFRange::VolumeFlow)
|
.set(VIFRange::VolumeFlow)
|
||||||
|
@ -172,10 +172,10 @@ namespace
|
||||||
|
|
||||||
addNumericFieldWithExtractor(
|
addNumericFieldWithExtractor(
|
||||||
"max_external_temperature",
|
"max_external_temperature",
|
||||||
"The maxium temperature recorded during previous period.",
|
"The maximum temperature recorded during previous period.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Maximum)
|
.set(MeasurementType::Maximum)
|
||||||
.set(VIFRange::ExternalTemperature)
|
.set(VIFRange::ExternalTemperature)
|
||||||
|
@ -187,7 +187,7 @@ namespace
|
||||||
"The minimum flow recorded during previous period.",
|
"The minimum flow recorded during previous period.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Minimum)
|
.set(MeasurementType::Minimum)
|
||||||
.set(VIFRange::ExternalTemperature)
|
.set(VIFRange::ExternalTemperature)
|
||||||
|
@ -196,10 +196,10 @@ namespace
|
||||||
|
|
||||||
addNumericFieldWithExtractor(
|
addNumericFieldWithExtractor(
|
||||||
"max_flow",
|
"max_flow",
|
||||||
"The maxium flow recorded during previous period.",
|
"The maximum flow recorded during previous period.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Flow,
|
Quantity::Flow,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Maximum)
|
.set(MeasurementType::Maximum)
|
||||||
.set(VIFRange::VolumeFlow)
|
.set(VIFRange::VolumeFlow)
|
||||||
|
@ -216,7 +216,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"DRY",
|
"DRY",
|
||||||
Translate::Type::IndexToString,
|
Translate::MapType::IndexToString,
|
||||||
AlwaysTrigger, MaskBits(0x0070),
|
AlwaysTrigger, MaskBits(0x0070),
|
||||||
"",
|
"",
|
||||||
{
|
{
|
||||||
|
@ -243,7 +243,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"REVERSED",
|
"REVERSED",
|
||||||
Translate::Type::IndexToString,
|
Translate::MapType::IndexToString,
|
||||||
AlwaysTrigger, MaskBits(0x0380),
|
AlwaysTrigger, MaskBits(0x0380),
|
||||||
"",
|
"",
|
||||||
{
|
{
|
||||||
|
@ -270,7 +270,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"LEAKING",
|
"LEAKING",
|
||||||
Translate::Type::IndexToString,
|
Translate::MapType::IndexToString,
|
||||||
AlwaysTrigger, MaskBits(0x1c00),
|
AlwaysTrigger, MaskBits(0x1c00),
|
||||||
"",
|
"",
|
||||||
{
|
{
|
||||||
|
@ -297,7 +297,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"BURSTING",
|
"BURSTING",
|
||||||
Translate::Type::IndexToString,
|
Translate::MapType::IndexToString,
|
||||||
AlwaysTrigger, MaskBits(0xe000),
|
AlwaysTrigger, MaskBits(0xe000),
|
||||||
"",
|
"",
|
||||||
{
|
{
|
||||||
|
|
|
@ -70,10 +70,10 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger,
|
AlwaysTrigger,
|
||||||
AutoMask,
|
AutoMask,
|
||||||
"",
|
"OK",
|
||||||
{
|
{
|
||||||
{ 0x0001, "METER_HARDWARE_ERROR" },
|
{ 0x0001, "METER_HARDWARE_ERROR" },
|
||||||
{ 0x0002, "RTC_ERROR" },
|
{ 0x0002, "RTC_ERROR" },
|
||||||
|
@ -85,10 +85,10 @@ namespace
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS_SINGLE_PHASE",
|
"ERROR_FLAGS_SINGLE_PHASE",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
TriggerBits(0x01020000),
|
TriggerBits(0x01020000),
|
||||||
AutoMask,
|
AutoMask,
|
||||||
"",
|
"OK",
|
||||||
{
|
{
|
||||||
{ 0x0008, "DEVICE_NOT_CONFIGURED" },
|
{ 0x0008, "DEVICE_NOT_CONFIGURED" },
|
||||||
{ 0x0010, "INTERNAL_ERROR" },
|
{ 0x0010, "INTERNAL_ERROR" },
|
||||||
|
@ -99,10 +99,10 @@ namespace
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS_THREE_PHASE",
|
"ERROR_FLAGS_THREE_PHASE",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
TriggerBits(0x01010000),
|
TriggerBits(0x01010000),
|
||||||
AutoMask,
|
AutoMask,
|
||||||
"",
|
"OK",
|
||||||
{
|
{
|
||||||
{ 0x0008, "CALIBRATION_EEPROM_ERROR" },
|
{ 0x0008, "CALIBRATION_EEPROM_ERROR" },
|
||||||
{ 0x0010, "NETWORK_INTERFERENCE" },
|
{ 0x0010, "NETWORK_INTERFERENCE" },
|
||||||
|
@ -124,7 +124,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"INFO_FLAGS",
|
"INFO_FLAGS",
|
||||||
Translate::Type::IndexToString,
|
Translate::MapType::IndexToString,
|
||||||
AlwaysTrigger,
|
AlwaysTrigger,
|
||||||
AutoMask,
|
AutoMask,
|
||||||
"",
|
"",
|
||||||
|
@ -141,7 +141,7 @@ namespace
|
||||||
"The total energy consumption recorded by this meter.",
|
"The total energy consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -152,7 +152,7 @@ namespace
|
||||||
"The total energy consumption recorded by this meter.",
|
"The total energy consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -164,7 +164,7 @@ namespace
|
||||||
"Last day?",
|
"Last day?",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::PointInTime,
|
Quantity::PointInTime,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::DateTime)
|
.set(VIFRange::DateTime)
|
||||||
|
@ -176,7 +176,7 @@ namespace
|
||||||
"Last day energy consumption?",
|
"Last day energy consumption?",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -188,7 +188,7 @@ namespace
|
||||||
"Last day energy consumption for tariff?",
|
"Last day energy consumption for tariff?",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -201,7 +201,7 @@ namespace
|
||||||
"Device date time when telegram was sent.",
|
"Device date time when telegram was sent.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::PointInTime,
|
Quantity::PointInTime,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::DateTime)
|
.set(VIFRange::DateTime)
|
||||||
|
@ -212,7 +212,7 @@ namespace
|
||||||
"Voltage for single phase meter.",
|
"Voltage for single phase meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Voltage,
|
Quantity::Voltage,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Voltage)
|
.set(VIFRange::Voltage)
|
||||||
|
@ -224,7 +224,7 @@ namespace
|
||||||
"Voltage at phase L#.",
|
"Voltage at phase L#.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Voltage,
|
Quantity::Voltage,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Voltage)
|
.set(VIFRange::Voltage)
|
||||||
|
@ -236,7 +236,7 @@ namespace
|
||||||
"Amperage for single phase meter.",
|
"Amperage for single phase meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Amperage,
|
Quantity::Amperage,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Amperage)
|
.set(VIFRange::Amperage)
|
||||||
|
@ -248,7 +248,7 @@ namespace
|
||||||
"Amperage at phase L#.",
|
"Amperage at phase L#.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Amperage,
|
Quantity::Amperage,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Amperage)
|
.set(VIFRange::Amperage)
|
||||||
|
@ -260,7 +260,7 @@ namespace
|
||||||
"Raw input to frequency.",
|
"Raw input to frequency.",
|
||||||
DEFAULT_PRINT_PROPERTIES | PrintProperty::HIDE,
|
DEFAULT_PRINT_PROPERTIES | PrintProperty::HIDE,
|
||||||
Quantity::Frequency,
|
Quantity::Frequency,
|
||||||
VifScaling::None,
|
VifScaling::None, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(DifVifKey("02FB2D"))
|
.set(DifVifKey("02FB2D"))
|
||||||
);
|
);
|
||||||
|
|
|
@ -38,8 +38,8 @@ namespace
|
||||||
|
|
||||||
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
||||||
{
|
{
|
||||||
addOptionalCommonFields("actuality_duration_s");
|
addOptionalLibraryFields("actuality_duration_s");
|
||||||
addOptionalFlowRelatedFields("total_m3,target_m3,target_date");
|
addOptionalLibraryFields("total_m3,target_m3,target_date");
|
||||||
|
|
||||||
addStringField(
|
addStringField(
|
||||||
"status",
|
"status",
|
||||||
|
@ -77,7 +77,7 @@ namespace
|
||||||
|
|
||||||
if (type != 1)
|
if (type != 1)
|
||||||
{
|
{
|
||||||
setStringValue("mfct_status", tostrprintf("UKNOWN_MFCT_STATUS=%02x%02x%02x", type, a, b));
|
setStringValue("mfct_status", tostrprintf("UKNOWN_MFCT_STATUS=%02x%02x%02x", type, a, b), NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,9 +89,9 @@ namespace
|
||||||
if (a & 0x40) info += "BACKFLOW ";
|
if (a & 0x40) info += "BACKFLOW ";
|
||||||
|
|
||||||
if (info.size() > 0) info.pop_back();
|
if (info.size() > 0) info.pop_back();
|
||||||
setStringValue("mfct_status", info);
|
setStringValue("mfct_status", info, NULL);
|
||||||
|
|
||||||
setStringValue("power_mode", (b & 0x01) ? "SAVING" : "NORMAL");
|
setStringValue("power_mode", (b & 0x01) ? "SAVING" : "NORMAL", NULL);
|
||||||
|
|
||||||
double battery_semesters = (b >> 3); // Half years.
|
double battery_semesters = (b >> 3); // Half years.
|
||||||
setNumericValue("battery", Unit::Year, battery_semesters/2.0);
|
setNumericValue("battery", Unit::Year, battery_semesters/2.0);
|
||||||
|
@ -100,5 +100,5 @@ namespace
|
||||||
|
|
||||||
// Test: Wateroo gwfwater 20221031 NOKEY
|
// Test: Wateroo gwfwater 20221031 NOKEY
|
||||||
// telegram=|3144E61E31102220010E8C04F47ABE0420452F2F_037410000004133E0000004413FFFFFFFF426CFFFF0F0120012F2F2F2F2F|
|
// telegram=|3144E61E31102220010E8C04F47ABE0420452F2F_037410000004133E0000004413FFFFFFFF426CFFFF0F0120012F2F2F2F2F|
|
||||||
// {"actuality_duration_s": 16,"battery_y": 0,"id": "20221031","media": "bus/system component","meter": "gwfwater","name": "Wateroo","power_mode": "SAVING","status": "BATTERY_LOW POWER_LOW","target_date": "2128-03-31","target_m3": 4294967.295,"timestamp": "1111-11-11T11:11:11Z","total_m3": 0.062}
|
// {"actuality_duration_s": 16,"battery_y": 0,"id": "20221031","media": "bus/system component","meter": "gwfwater","name": "Wateroo","power_mode": "SAVING","status": "BATTERY_LOW POWER_LOW","target_date": "2128-03-31","target_m3": -0.001,"timestamp": "1111-11-11T11:11:11Z","total_m3": 0.062}
|
||||||
// |Wateroo;20221031;0.062;1111-11-11 11:11.11
|
// |Wateroo;20221031;0.062;1111-11-11 11:11.11
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2019-2023 Fredrik Öhrström (gpl-3.0-or-later)
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include"meters_common_implementation.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct Driver : public virtual MeterCommonImplementation
|
||||||
|
{
|
||||||
|
Driver(MeterInfo &mi, DriverInfo &di);
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool ok = registerDriver([](DriverInfo&di)
|
||||||
|
{
|
||||||
|
di.setName("hcae2");
|
||||||
|
di.setDefaultFields("name,id,current_consumption_hca,status,timestamp");
|
||||||
|
di.setMeterType(MeterType::HeatCostAllocationMeter);
|
||||||
|
di.addLinkMode(LinkMode::T1);
|
||||||
|
di.addDetection(MANUFACTURER_EFE, 0x08, 0x31);
|
||||||
|
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
|
||||||
|
});
|
||||||
|
|
||||||
|
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
||||||
|
{
|
||||||
|
addStringFieldWithExtractorAndLookup(
|
||||||
|
"status",
|
||||||
|
"Meter status from error flags and tpl status field.",
|
||||||
|
DEFAULT_PRINT_PROPERTIES |
|
||||||
|
PrintProperty::STATUS | PrintProperty::INCLUDE_TPL_STATUS,
|
||||||
|
FieldMatcher::build()
|
||||||
|
.set(MeasurementType::Instantaneous)
|
||||||
|
.set(VIFRange::ErrorFlags),
|
||||||
|
Translate::Lookup(
|
||||||
|
{
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"ERROR_FLAGS",
|
||||||
|
Translate::MapType::BitToString,
|
||||||
|
AlwaysTrigger, MaskBits(0xffff),
|
||||||
|
"OK",
|
||||||
|
{
|
||||||
|
{ 0x0001, "MEASUREMENT" },
|
||||||
|
{ 0x0002, "SABOTAGE" },
|
||||||
|
{ 0x0004, "BATTERY" },
|
||||||
|
{ 0x0008, "CS" },
|
||||||
|
{ 0x0010, "HF" },
|
||||||
|
{ 0x0020, "RESET" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
addNumericFieldWithExtractor(
|
||||||
|
"current_consumption",
|
||||||
|
"The current heat cost allocation.",
|
||||||
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
|
Quantity::HCA,
|
||||||
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
|
FieldMatcher::build()
|
||||||
|
.set(MeasurementType::Instantaneous)
|
||||||
|
.set(VIFRange::HeatCostAllocation)
|
||||||
|
);
|
||||||
|
|
||||||
|
addNumericFieldWithExtractor(
|
||||||
|
"consumption_at_set_date_{storage_counter}",
|
||||||
|
"The heat cost allocation at set date #.",
|
||||||
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
|
Quantity::HCA,
|
||||||
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
|
FieldMatcher::build()
|
||||||
|
.set(MeasurementType::Instantaneous)
|
||||||
|
.set(VIFRange::HeatCostAllocation)
|
||||||
|
.set(StorageNr(1),StorageNr(17))
|
||||||
|
);
|
||||||
|
|
||||||
|
addNumericFieldWithExtractor(
|
||||||
|
"consumption_at_set_date",
|
||||||
|
"Deprecated field.",
|
||||||
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
|
Quantity::HCA,
|
||||||
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
|
FieldMatcher::build()
|
||||||
|
.set(MeasurementType::Instantaneous)
|
||||||
|
.set(VIFRange::HeatCostAllocation)
|
||||||
|
.set(StorageNr(1))
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test: HeatMeter hcae2 88018801 NOKEY
|
||||||
|
// telegram=|7644C52501880188550872_01880188C5255508010000002F2F0B6E332211426E110182016E1102C2016E110382026E1104C2026E110582036E1106C2036E110782046E1108C2046E110982056E1110C2056E111182066E1112C2066E111382076E1114C2076E111582086E1116C2086E111702FD172100|
|
||||||
|
// {"media":"heat cost allocation","meter":"hcae2","name":"HeatMeter","id":"88018801","status":"MEASUREMENT RESET","current_consumption_hca":112233,"consumption_at_set_date_hca":273,"consumption_at_set_date_1_hca":273,"consumption_at_set_date_2_hca":529,"consumption_at_set_date_3_hca":785,"consumption_at_set_date_4_hca":1041,"consumption_at_set_date_5_hca":1297,"consumption_at_set_date_6_hca":1553,"consumption_at_set_date_7_hca":1809,"consumption_at_set_date_8_hca":2065,"consumption_at_set_date_9_hca":2321,"consumption_at_set_date_10_hca":4113,"consumption_at_set_date_11_hca":4369,"consumption_at_set_date_12_hca":4625,"consumption_at_set_date_13_hca":4881,"consumption_at_set_date_14_hca":5137,"consumption_at_set_date_15_hca":5393,"consumption_at_set_date_16_hca":5649,"consumption_at_set_date_17_hca":5905,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
// |HeatMeter;88018801;112233;MEASUREMENT RESET;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
|
||||||
|
// Test: HeatMeter2 hcae2 60200770 NOKEY
|
||||||
|
// telegram=|76442D4870072060550872700720602D485508280060052F2F_0B6E320100426E550082016E3500C2016E1F0082026E1F00C2026E130082036E1300C2036E130082046E1300C2046E120082056E7D01C2056E440182066E0601C2066EB80082076E7F00C2076E320082086E1C00C2086E1C0002FD170000
|
||||||
|
// {"media":"heat cost allocation","meter":"hcae2","name":"HeatMeter2","id":"60200770","status":"OK","current_consumption_hca":132,"consumption_at_set_date_4_hca":31,"consumption_at_set_date_5_hca":19,"consumption_at_set_date_6_hca":19,"consumption_at_set_date_2_hca":53,"consumption_at_set_date_14_hca":127,"consumption_at_set_date_8_hca":19,"consumption_at_set_date_1_hca":85,"consumption_at_set_date_10_hca":381,"consumption_at_set_date_17_hca":28,"consumption_at_set_date_13_hca":184,"consumption_at_set_date_11_hca":324,"consumption_at_set_date_3_hca":31,"consumption_at_set_date_9_hca":18,"consumption_at_set_date_15_hca":50,"consumption_at_set_date_16_hca":28,"consumption_at_set_date_7_hca":19,"consumption_at_set_date_12_hca":262,"consumption_at_set_date_hca":85,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
|
// |HeatMeter2;60200770;132;OK;1111-11-11 11:11.11
|
|
@ -39,7 +39,7 @@ namespace
|
||||||
{
|
{
|
||||||
setMfctTPLStatusBits(
|
setMfctTPLStatusBits(
|
||||||
Translate::Lookup()
|
Translate::Lookup()
|
||||||
.add(Translate::Rule("TPL_STS", Translate::Type::BitToString)
|
.add(Translate::Rule("TPL_STS", Translate::MapType::BitToString)
|
||||||
.set(MaskBits(0xe0))
|
.set(MaskBits(0xe0))
|
||||||
.set(DefaultMessage("OK"))
|
.set(DefaultMessage("OK"))
|
||||||
.add(Translate::Map(0x80 ,"SABOTAGE_ENCLOSURE", TestBit::Set))));
|
.add(Translate::Map(0x80 ,"SABOTAGE_ENCLOSURE", TestBit::Set))));
|
||||||
|
@ -55,7 +55,7 @@ namespace
|
||||||
"The total heating energy consumption recorded by this meter.",
|
"The total heating energy consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -67,7 +67,7 @@ namespace
|
||||||
"The date time when the recording was made.",
|
"The date time when the recording was made.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::PointInTime,
|
Quantity::PointInTime,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::DateTime)
|
.set(VIFRange::DateTime)
|
||||||
|
@ -78,7 +78,7 @@ namespace
|
||||||
"The total cooling energy consumption recorded by this meter.",
|
"The total cooling energy consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Energy,
|
Quantity::Energy,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::AnyEnergyVIF)
|
.set(VIFRange::AnyEnergyVIF)
|
||||||
|
@ -90,7 +90,7 @@ namespace
|
||||||
"Total heating volume of media.",
|
"Total heating volume of media.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -102,7 +102,7 @@ namespace
|
||||||
"Total cooling volume of media.",
|
"Total cooling volume of media.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -114,7 +114,7 @@ namespace
|
||||||
"Supply c1 volume.",
|
"Supply c1 volume.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -126,7 +126,7 @@ namespace
|
||||||
"Return c2 volume.",
|
"Return c2 volume.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -138,7 +138,7 @@ namespace
|
||||||
"The supply t1 pipe temperature.",
|
"The supply t1 pipe temperature.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::FlowTemperature)
|
.set(VIFRange::FlowTemperature)
|
||||||
|
@ -150,7 +150,7 @@ namespace
|
||||||
"The return t2 pipe temperature.",
|
"The return t2 pipe temperature.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Temperature,
|
Quantity::Temperature,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::ReturnTemperature)
|
.set(VIFRange::ReturnTemperature)
|
||||||
|
|
|
@ -45,7 +45,7 @@ namespace
|
||||||
"The current heat cost allocation.",
|
"The current heat cost allocation.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::HCA,
|
Quantity::HCA,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::HeatCostAllocation)
|
.set(VIFRange::HeatCostAllocation)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2019-2022 Fredrik Öhrström (gpl-3.0-or-later)
|
Copyright (C) 2019-2023 Fredrik Öhrström (gpl-3.0-or-later)
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -31,6 +31,7 @@ namespace
|
||||||
di.setMeterType(MeterType::WaterMeter);
|
di.setMeterType(MeterType::WaterMeter);
|
||||||
di.addLinkMode(LinkMode::T1);
|
di.addLinkMode(LinkMode::T1);
|
||||||
di.addDetection(MANUFACTURER_BMT, 0x06, 0x13);
|
di.addDetection(MANUFACTURER_BMT, 0x06, 0x13);
|
||||||
|
di.addDetection(MANUFACTURER_BMT, 0x06, 0x17);
|
||||||
di.addDetection(MANUFACTURER_BMT, 0x07, 0x13);
|
di.addDetection(MANUFACTURER_BMT, 0x07, 0x13);
|
||||||
di.addDetection(MANUFACTURER_BMT, 0x07, 0x15);
|
di.addDetection(MANUFACTURER_BMT, 0x07, 0x15);
|
||||||
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
|
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
|
||||||
|
@ -43,7 +44,7 @@ namespace
|
||||||
"The total water consumption recorded by this meter.",
|
"The total water consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -54,7 +55,7 @@ namespace
|
||||||
"Meter timestamp for measurement.",
|
"Meter timestamp for measurement.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::PointInTime,
|
Quantity::PointInTime,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::DateTime),
|
.set(VIFRange::DateTime),
|
||||||
|
@ -67,3 +68,8 @@ namespace
|
||||||
// telegram=|4E44B4098686868613077AF0004005_2F2F0C1366380000046D27287E2A0F150E00000000C10000D10000E60000FD00000C01002F0100410100540100680100890000A00000B30000002F2F2F2F2F2F|
|
// telegram=|4E44B4098686868613077AF0004005_2F2F0C1366380000046D27287E2A0F150E00000000C10000D10000E60000FD00000C01002F0100410100540100680100890000A00000B30000002F2F2F2F2F2F|
|
||||||
// {"media":"water","meter":"hydrodigit","name":"HydrodigitWater","id":"86868686","total_m3":3.866,"meter_datetime":"2019-10-30 08:39","timestamp":"1111-11-11T11:11:11Z"}
|
// {"media":"water","meter":"hydrodigit","name":"HydrodigitWater","id":"86868686","total_m3":3.866,"meter_datetime":"2019-10-30 08:39","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
// |HydrodigitWater;86868686;3.866;2019-10-30 08:39;1111-11-11 11:11.11
|
// |HydrodigitWater;86868686;3.866;2019-10-30 08:39;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
// Test: HydridigitWaterr hydrodigit 03245501 NOKEY
|
||||||
|
// telegram=|2444B4090155240317068C00487AC0000000_0C1335670000046D172EEA280F030000000000|
|
||||||
|
// {"id": "03245501","media": "warm water","meter": "hydrodigit","meter_datetime": "2023-08-10 14:23","name": "HydridigitWaterr","timestamp": "1111-11-11T11:11:11Z","total_m3": 6.735}
|
||||||
|
// |HydridigitWaterr;03245501;6.735;2023-08-10 14:23;1111-11-11 11:11.11
|
||||||
|
|
|
@ -44,8 +44,8 @@ namespace
|
||||||
|
|
||||||
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
||||||
{
|
{
|
||||||
addOptionalCommonFields("operating_time_h,actuality_duration_s,meter_datetime,customer");
|
addOptionalLibraryFields("operating_time_h,actuality_duration_s,meter_datetime,customer");
|
||||||
addOptionalFlowRelatedFields("flow_temperature_c,external_temperature_c");
|
addOptionalLibraryFields("flow_temperature_c,external_temperature_c");
|
||||||
|
|
||||||
addStringField(
|
addStringField(
|
||||||
"status",
|
"status",
|
||||||
|
@ -57,7 +57,7 @@ namespace
|
||||||
"The total water consumption recorded by this meter.",
|
"The total water consumption recorded by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -68,7 +68,7 @@ namespace
|
||||||
"The total water consumption recorded on tariff # by this meter.",
|
"The total water consumption recorded on tariff # by this meter.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -80,7 +80,7 @@ namespace
|
||||||
"The total water consumption recorded on tariff # by this meter at billing date.",
|
"The total water consumption recorded on tariff # by this meter at billing date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -93,7 +93,7 @@ namespace
|
||||||
"The current water flow.",
|
"The current water flow.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Flow,
|
Quantity::Flow,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::VolumeFlow));
|
.set(VIFRange::VolumeFlow));
|
||||||
|
@ -103,7 +103,7 @@ namespace
|
||||||
"The total water consumption recorded at date.",
|
"The total water consumption recorded at date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -115,7 +115,7 @@ namespace
|
||||||
"The last billing period date.",
|
"The last billing period date.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::PointInTime,
|
Quantity::PointInTime,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Date)
|
.set(VIFRange::Date)
|
||||||
|
@ -124,11 +124,11 @@ namespace
|
||||||
);
|
);
|
||||||
|
|
||||||
addNumericFieldWithExtractor(
|
addNumericFieldWithExtractor(
|
||||||
"total_at_date",
|
"target",
|
||||||
"Fix this! The total water consumption recorded at last day. Perhaps?",
|
"The total water consumption recorded at the end of last month.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -136,11 +136,11 @@ namespace
|
||||||
);
|
);
|
||||||
|
|
||||||
addNumericFieldWithExtractor(
|
addNumericFieldWithExtractor(
|
||||||
"at",
|
"target",
|
||||||
"Fix this! The last billing period date last day. Perhaps?",
|
"The end of last month.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::PointInTime,
|
Quantity::PointInTime,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::DateTime)
|
.set(VIFRange::DateTime)
|
||||||
|
@ -152,7 +152,7 @@ namespace
|
||||||
"Remaining battery life in years.",
|
"Remaining battery life in years.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Time,
|
Quantity::Time,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::RemainingBattery),
|
.set(VIFRange::RemainingBattery),
|
||||||
|
@ -163,14 +163,14 @@ namespace
|
||||||
// Test: HydrusWater hydrus 64646464 NOKEY
|
// Test: HydrusWater hydrus 64646464 NOKEY
|
||||||
// Comment:
|
// Comment:
|
||||||
// telegram=|4E44A5116464646470077AED004005_2F2F01FD08300C13741100007C1300000000FC101300000000FC201300000000726C00000B3B00000002FD748713025A6800C4016D3B177F2ACC011300020000|
|
// telegram=|4E44A5116464646470077AED004005_2F2F01FD08300C13741100007C1300000000FC101300000000FC201300000000726C00000B3B00000002FD748713025A6800C4016D3B177F2ACC011300020000|
|
||||||
// {"media":"water","meter":"hydrus","name":"HydrusWater","id":"64646464","total_m3":1.174,"flow_m3h":0,"flow_temperature_c":10.4,"remaining_battery_life_y":13.686797,"status":"OK","at_datetime":"2019-10-31 23:59","total_at_date_m3": 0.2,"timestamp":"1111-11-11T11:11:11Z"}
|
// {"media":"water","meter":"hydrus","name":"HydrusWater","id":"64646464","total_m3":1.174,"flow_m3h":0,"flow_temperature_c":10.4,"remaining_battery_life_y":13.686797,"status":"OK","target_datetime":"2019-10-31 23:59","target_m3": 0.2,"timestamp":"1111-11-11T11:11:11Z"}
|
||||||
// |HydrusWater;64646464;1.174;0.2;OK;1111-11-11 11:11.11
|
// |HydrusWater;64646464;1.174;null;OK;1111-11-11 11:11.11
|
||||||
|
|
||||||
// Test: HydrusVater hydrus 65656565 NOKEY
|
// Test: HydrusVater hydrus 65656565 NOKEY
|
||||||
// Comment:
|
// Comment:
|
||||||
// telegram=|3E44A5116565656570067AFB0030052F2F_0C13503400000DFD110A383731303134423032410B3B00000002FD74DC15C4016D3B178D29CC0113313400002F2F|
|
// telegram=|3E44A5116565656570067AFB0030052F2F_0C13503400000DFD110A383731303134423032410B3B00000002FD74DC15C4016D3B178D29CC0113313400002F2F|
|
||||||
// {"media":"warm water","meter":"hydrus","name":"HydrusVater","id":"65656565","flow_m3h":0,"customer": "A20B410178","total_m3":3.45,"total_at_date_m3":3.431,"remaining_battery_life_y":15.321328,"at_datetime":"2020-09-13 23:59","total_at_date_m3": 3.431,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
|
// {"media":"warm water","meter":"hydrus","name":"HydrusVater","id":"65656565","flow_m3h":0,"customer": "A20B410178","total_m3":3.45,"remaining_battery_life_y":15.321328,"target_datetime":"2020-09-13 23:59","target_m3": 3.431,"status":"OK","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
// |HydrusVater;65656565;3.45;3.431;OK;1111-11-11 11:11.11
|
// |HydrusVater;65656565;3.45;null;OK;1111-11-11 11:11.11
|
||||||
|
|
||||||
// Test: HydrusAES hydrus 64745666 NOKEY
|
// Test: HydrusAES hydrus 64745666 NOKEY
|
||||||
// Comment:
|
// Comment:
|
||||||
|
@ -189,3 +189,9 @@ namespace
|
||||||
// telegram=|1E4424238B06204790607A2A0010D8_0413DDC00000426CBF23441382BB0000|
|
// telegram=|1E4424238B06204790607A2A0010D8_0413DDC00000426CBF23441382BB0000|
|
||||||
// {"media":"warm water","meter":"hydrus","name":"HydrusIzarRSWarm","id":"60904720","total_m3":49.373,"total_at_date_m3":48.002,"at_date":"2021-03-31","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
|
// {"media":"warm water","meter":"hydrus","name":"HydrusIzarRSWarm","id":"60904720","total_m3":49.373,"total_at_date_m3":48.002,"at_date":"2021-03-31","status":"OK","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
// |HydrusIzarRSWarm;60904720;49.373;48.002;OK;1111-11-11 11:11.11
|
// |HydrusIzarRSWarm;60904720;49.373;48.002;OK;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
// Test: HydrusFoo hydrus 64641820 NOKEY
|
||||||
|
// Comment: Negative power values.
|
||||||
|
// telegram=|6344A5112018646470078C00D7900F002C256AB59B00F0F13032019092DE7A6A004007102F2F0C13896729004C1323462400CC101300000000CC201323462400426CDF2C0B3B0200F002FD742F0D025AC100C4016D3B17FE29CC01132841290001FD089F|
|
||||||
|
// {"at_date": "2022-12-31","target_datetime": "2023-09-30 23:59","flow_m3h": -0.002,"flow_temperature_c": 19.3,"id": "64641820","media": "water","meter": "hydrus","name": "HydrusFoo","remaining_battery_life_y": 9.240436,"status": "OK","timestamp": "1111-11-11T11:11:11Z","total_at_date_m3":244.623,"target_m3": 294.128,"total_m3": 296.789,"total_tariff1_at_date_m3": 0,"total_tariff2_at_date_m3": 244.623}
|
||||||
|
// |HydrusFoo;64641820;296.789;244.623;OK;1111-11-11 11:11.11
|
||||||
|
|
Plik diff jest za duży
Load Diff
|
@ -1,75 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2017-2022 Fredrik Öhrström (gpl-3.0-or-later)
|
|
||||||
Copyright (C) 2018 David Mallon (gpl-3.0-or-later)
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include"meters_common_implementation.h"
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
struct Driver : public virtual MeterCommonImplementation
|
|
||||||
{
|
|
||||||
Driver(MeterInfo &mi, DriverInfo &di);
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool ok = registerDriver([](DriverInfo&di)
|
|
||||||
{
|
|
||||||
di.setName("iperl");
|
|
||||||
di.setDefaultFields("name,id,total_m3,max_flow_m3h,timestamp");
|
|
||||||
di.setMeterType(MeterType::WaterMeter);
|
|
||||||
di.addLinkMode(LinkMode::T1);
|
|
||||||
di.addDetection(MANUFACTURER_SEN, 0x06, 0x68);
|
|
||||||
di.addDetection(MANUFACTURER_SEN, 0x07, 0x68);
|
|
||||||
di.addDetection(MANUFACTURER_SEN, 0x07, 0x7c);
|
|
||||||
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
|
|
||||||
});
|
|
||||||
|
|
||||||
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
|
||||||
{
|
|
||||||
addNumericFieldWithExtractor(
|
|
||||||
"total",
|
|
||||||
"The total water consumption recorded by this meter.",
|
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
|
||||||
Quantity::Volume,
|
|
||||||
VifScaling::Auto,
|
|
||||||
FieldMatcher::build()
|
|
||||||
.set(MeasurementType::Instantaneous)
|
|
||||||
.set(VIFRange::Volume)
|
|
||||||
);
|
|
||||||
|
|
||||||
addNumericFieldWithExtractor(
|
|
||||||
"max_flow",
|
|
||||||
"The maxium flow recorded during previous period.",
|
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
|
||||||
Quantity::Flow,
|
|
||||||
VifScaling::Auto,
|
|
||||||
FieldMatcher::build()
|
|
||||||
.set(MeasurementType::Instantaneous)
|
|
||||||
.set(VIFRange::VolumeFlow));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test: MoreWater iperl 12345699 NOKEY
|
|
||||||
// Comment: Test iPerl T1 telegram, that after decryption, has 2f2f markers.
|
|
||||||
// telegram=|1E44AE4C9956341268077A36001000_2F2F0413181E0000023B00002F2F2F2F|
|
|
||||||
// {"media":"water","meter":"iperl","name":"MoreWater","id":"12345699","total_m3":7.704,"max_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}
|
|
||||||
// |MoreWater;12345699;7.704;0;1111-11-11 11:11.11
|
|
||||||
|
|
||||||
// Test: WaterWater iperl 33225544 NOKEY
|
|
||||||
// Comment: Test iPerl T1 telegram not encrypted, which has no 2f2f markers.
|
|
||||||
// telegram=|1844AE4C4455223368077A55000000_041389E20100023B0000|
|
|
||||||
// {"media":"water","meter":"iperl","name":"WaterWater","id":"33225544","total_m3":123.529,"max_flow_m3h":0,"timestamp":"1111-11-11T11:11:11Z"}
|
|
||||||
// |WaterWater;33225544;123.529;0;1111-11-11 11:11.11
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright (C) 2022 Fredrik Öhrström (gpl-3.0-or-later)
|
Copyright (C) 2022-2023 Fredrik Öhrström (gpl-3.0-or-later)
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -30,8 +30,10 @@ namespace
|
||||||
di.setDefaultFields("name,id,total_m3,target_m3,timestamp");
|
di.setDefaultFields("name,id,total_m3,target_m3,timestamp");
|
||||||
di.setMeterType(MeterType::WaterMeter);
|
di.setMeterType(MeterType::WaterMeter);
|
||||||
di.addLinkMode(LinkMode::T1);
|
di.addLinkMode(LinkMode::T1);
|
||||||
|
di.addDetection(MANUFACTURER_ITW, 0x07, 0x00);
|
||||||
di.addDetection(MANUFACTURER_ITW, 0x07, 0x03);
|
di.addDetection(MANUFACTURER_ITW, 0x07, 0x03);
|
||||||
di.addDetection(MANUFACTURER_ITW, 0x07, 0x33);
|
di.addDetection(MANUFACTURER_ITW, 0x07, 0x33);
|
||||||
|
di.addDetection(MANUFACTURER_ITW, 0x16, 0x00);
|
||||||
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
|
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -43,8 +45,8 @@ namespace
|
||||||
|
|
||||||
addLinkMode(LinkMode::T1);
|
addLinkMode(LinkMode::T1);
|
||||||
|
|
||||||
addOptionalCommonFields("enhanced_id,meter_datetime");
|
addOptionalLibraryFields("enhanced_id,meter_datetime");
|
||||||
addOptionalFlowRelatedFields("total_m3,total_backward_m3,volume_flow_m3h");
|
addOptionalLibraryFields("total_m3,total_backward_m3,volume_flow_m3h");
|
||||||
|
|
||||||
addStringFieldWithExtractorAndLookup(
|
addStringFieldWithExtractorAndLookup(
|
||||||
"status",
|
"status",
|
||||||
|
@ -58,7 +60,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"ERROR_FLAGS",
|
"ERROR_FLAGS",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xffffff),
|
AlwaysTrigger, MaskBits(0xffffff),
|
||||||
"OK",
|
"OK",
|
||||||
{
|
{
|
||||||
|
@ -73,7 +75,7 @@ namespace
|
||||||
"The total water consumption recorded at the end of previous billing period.",
|
"The total water consumption recorded at the end of previous billing period.",
|
||||||
DEFAULT_PRINT_PROPERTIES,
|
DEFAULT_PRINT_PROPERTIES,
|
||||||
Quantity::Volume,
|
Quantity::Volume,
|
||||||
VifScaling::Auto,
|
VifScaling::Auto, DifSignedness::Signed,
|
||||||
FieldMatcher::build()
|
FieldMatcher::build()
|
||||||
.set(MeasurementType::Instantaneous)
|
.set(MeasurementType::Instantaneous)
|
||||||
.set(VIFRange::Volume)
|
.set(VIFRange::Volume)
|
||||||
|
@ -100,7 +102,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"WOOTA",
|
"WOOTA",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xffffffff),
|
AlwaysTrigger, MaskBits(0xffffffff),
|
||||||
"",
|
"",
|
||||||
{
|
{
|
||||||
|
@ -120,7 +122,7 @@ namespace
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
"WOOTB",
|
"WOOTB",
|
||||||
Translate::Type::BitToString,
|
Translate::MapType::BitToString,
|
||||||
AlwaysTrigger, MaskBits(0xffff),
|
AlwaysTrigger, MaskBits(0xffff),
|
||||||
"",
|
"",
|
||||||
{
|
{
|
||||||
|
@ -143,3 +145,14 @@ namespace
|
||||||
// telegram=|46449726560000183307725600001897263307AF0030052F2F_066D0E1015C82A000C13771252000C933C000000000B3B0400004C1361045200426CC12A03FD971C0000002F2F2F|
|
// telegram=|46449726560000183307725600001897263307AF0030052F2F_066D0E1015C82A000C13771252000C933C000000000B3B0400004C1361045200426CC12A03FD971C0000002F2F2F|
|
||||||
// {"media":"water","meter":"itron","name":"MoreWater","id":"18000056","meter_datetime":"2022-10-08 21:16:14","total_m3":521.277,"total_backward_m3":0,"volume_flow_m3h":0.004,"status":"OK","target_m3":520.461,"target_date":"2022-10-01","timestamp":"1111-11-11T11:11:11Z"}
|
// {"media":"water","meter":"itron","name":"MoreWater","id":"18000056","meter_datetime":"2022-10-08 21:16:14","total_m3":521.277,"total_backward_m3":0,"volume_flow_m3h":0.004,"status":"OK","target_m3":520.461,"target_date":"2022-10-01","timestamp":"1111-11-11T11:11:11Z"}
|
||||||
// |MoreWater;18000056;521.277;520.461;1111-11-11 11:11.11
|
// |MoreWater;18000056;521.277;520.461;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
// Test: AnyWater itron 20310959 NOKEY
|
||||||
|
// telegram=|384497265909312000077a930000a0041360B50100066d101295f427004413ac570100426cdf2c047f0000060c027f6c2a0e79000000000000|
|
||||||
|
// {"enhanced_id": "000000000000","id": "20310959","media": "water","meter": "itron","meter_datetime": "2023-07-20 21:18:16","name": "AnyWater","status": "OK","target_date": "2022-12-31","target_m3": 87.98,"timestamp": "1111-11-11T11:11:11Z","total_m3": 111.968,"unknown_a": "WOOTA_C060000","unknown_b": "WOOTB_2A6C"}
|
||||||
|
// |AnyWater;20310959;111.968;87.98;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
// Test: ColdWaterMeter itron 23362098 NOKEY
|
||||||
|
// Comment: Allmess cold water with Itron Module programmed with type 0x16
|
||||||
|
// telegram=|3A4497269820362300167AF60020A52F2F_04132E100000066D03260DE12B007413FEFEFEFE426C1F01047F1600060C027F9A2A0E79187103002300|
|
||||||
|
// {"enhanced_id": "002300037118", "id": "23362098", "media": "cold water", "meter": "itron", "meter_datetime": "2023-11-01 13:38:03", "name": "ColdWaterMeter", "status": "OK", "target_date": "2000-01-31", "timestamp": "1111-11-11T11:11:11Z", "total_m3": 4.142,"unknown_a": "WOOTA_C060016","unknown_b": "WOOTB_2A9A" }
|
||||||
|
// |ColdWaterMeter;23362098;4.142;null;1111-11-11 11:11.11
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2023 Fredrik Öhrström (gpl-3.0-or-later)
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include"meters_common_implementation.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct Driver : public virtual MeterCommonImplementation
|
||||||
|
{
|
||||||
|
Driver(MeterInfo &mi, DriverInfo &di);
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool ok = registerDriver([](DriverInfo&di)
|
||||||
|
{
|
||||||
|
di.setName("iwmtx5");
|
||||||
|
di.setDefaultFields("name,id,status,total_m3,timestamp");
|
||||||
|
|
||||||
|
di.setMeterType(MeterType::WaterMeter);
|
||||||
|
di.addLinkMode(LinkMode::T1);
|
||||||
|
di.addDetection(MANUFACTURER_BMT, 0x07, 0x18);
|
||||||
|
di.addDetection(MANUFACTURER_BMT, 0x06, 0x18);
|
||||||
|
di.setConstructor([](MeterInfo& mi, DriverInfo& di){ return shared_ptr<Meter>(new Driver(mi, di)); });
|
||||||
|
});
|
||||||
|
|
||||||
|
Driver::Driver(MeterInfo &mi, DriverInfo &di) : MeterCommonImplementation(mi, di)
|
||||||
|
{
|
||||||
|
addOptionalLibraryFields("meter_datetime");
|
||||||
|
addOptionalLibraryFields("total_m3");
|
||||||
|
|
||||||
|
addStringField(
|
||||||
|
"status",
|
||||||
|
"Status and error flags.",
|
||||||
|
PrintProperty::STATUS | PrintProperty::INCLUDE_TPL_STATUS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test: WaterWater iwmtx5 22917370 00000000000000000000000000000000
|
||||||
|
// telegram=|5144b4097073912218078c00247a0308400571e9615249ede52eaae09f61908f027c3877f3330ae9079528b23173ce124bcc255393e60b173c0a9f274c42dd92e4b23c14e8a41f042903358df01dd9268ad4|
|
||||||
|
// {"id": "22917370","media": "water","meter": "iwmtx5","meter_datetime": "2023-05-11 10:38:24","name": "WaterWater","status": "PERMANENT_ERROR","timestamp": "1111-11-11T11:11:11Z","total_m3": 0.025}
|
||||||
|
// |WaterWater;22917370;PERMANENT_ERROR;0.025;1111-11-11 11:11.11
|
||||||
|
|
||||||
|
// Test: WarmWater2 iwmtx5 23329344 NOKEY
|
||||||
|
// telegram=|4244B4094493322318068C005B7A1C0000000C13072000000F05170000000000000000000000000000000000000000009D0000C20000C20000C8000000000000000000|
|
||||||
|
// {"id": "23329344","media": "warm water","meter": "iwmtx5","name": "WarmWater2","status": "OK","timestamp": "1111-11-11T11:11:11Z","total_m3": 2.007}
|
||||||
|
// |WarmWater2;23329344;OK;2.007;1111-11-11 11:11.11
|
|
@ -214,11 +214,11 @@ namespace
|
||||||
// get the manufacture year
|
// get the manufacture year
|
||||||
uint8_t yy = atoi(digits.substr(0, 2).c_str());
|
uint8_t yy = atoi(digits.substr(0, 2).c_str());
|
||||||
int manufacture_year = yy > 70 ? (1900 + yy) : (2000 + yy); // Maybe to adjust in 2070, if this code stills lives :D
|
int manufacture_year = yy > 70 ? (1900 + yy) : (2000 + yy); // Maybe to adjust in 2070, if this code stills lives :D
|
||||||
setStringValue("manufacture_year", tostrprintf("%d", manufacture_year));
|
setStringValue("manufacture_year", tostrprintf("%d", manufacture_year), NULL);
|
||||||
|
|
||||||
// get the serial number
|
// get the serial number
|
||||||
uint32_t serial_number = atoi(digits.substr(2, digits.size()).c_str());
|
uint32_t serial_number = atoi(digits.substr(2, digits.size()).c_str());
|
||||||
setStringValue("serial_number", tostrprintf("%06d", serial_number));
|
setStringValue("serial_number", tostrprintf("%06d", serial_number), NULL);
|
||||||
|
|
||||||
// get letters
|
// get letters
|
||||||
uchar supplier_code = '@' + (((origin[9] & 0x0F) << 1) | (origin[8] >> 7));
|
uchar supplier_code = '@' + (((origin[9] & 0x0F) << 1) | (origin[8] >> 7));
|
||||||
|
@ -226,7 +226,7 @@ namespace
|
||||||
uchar diameter = '@' + (((origin[8] & 0x03) << 3) | (origin[7] >> 5));
|
uchar diameter = '@' + (((origin[8] & 0x03) << 3) | (origin[7] >> 5));
|
||||||
// build the prefix
|
// build the prefix
|
||||||
string prefix = tostrprintf("%c%02d%c%c", supplier_code, yy, meter_type, diameter);
|
string prefix = tostrprintf("%c%02d%c%c", supplier_code, yy, meter_type, diameter);
|
||||||
setStringValue("prefix", prefix);
|
setStringValue("prefix", prefix, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the remaining battery life (in year) and transmission period (in seconds)
|
// get the remaining battery life (in year) and transmission period (in seconds)
|
||||||
|
@ -252,7 +252,7 @@ namespace
|
||||||
uint8_t h0_month = decoded_content[10] & 0xF;
|
uint8_t h0_month = decoded_content[10] & 0xF;
|
||||||
uint8_t h0_day = decoded_content[9] & 0x1F;
|
uint8_t h0_day = decoded_content[9] & 0x1F;
|
||||||
|
|
||||||
setStringValue("last_month_measure_date", tostrprintf("%d-%02d-%02d", h0_year, h0_month%99, h0_day%99));
|
setStringValue("last_month_measure_date", tostrprintf("%d-%02d-%02d", h0_year, h0_month%99, h0_day%99), NULL);
|
||||||
|
|
||||||
// read the alarms:
|
// read the alarms:
|
||||||
IzarAlarms alarms {};
|
IzarAlarms alarms {};
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Ładowanie…
Reference in New Issue