mirror of
https://github.com/UtilitechAS/amsreader-firmware.git
synced 2026-01-12 00:02:53 +00:00
Merge branch 'main' into fix/mqtt_auto_reboot
This commit is contained in:
commit
60a6d76f6f
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@ -27,6 +27,7 @@ jobs:
|
||||
run: |
|
||||
sed -i 's/NO_AMS2MQTT_PRICE_KEY/AMS2MQTT_PRICE_KEY="${{secrets.AMS2MQTT_PRICE_KEY}}"/g' platformio.ini
|
||||
sed -i 's/NO_AMS2MQTT_PRICE_AUTHENTICATION/AMS2MQTT_PRICE_AUTHENTICATION="${{secrets.AMS2MQTT_PRICE_AUTHENTICATION}}"/g' platformio.ini
|
||||
sed -i 's/NO_AMS2MQTT_SC_KEY/AMS2MQTT_SC_KEY=\\"${{secrets.AMS2MQTT_SC_KEY}}\\"/g' platformio.ini
|
||||
sed -i 's/NO_ENERGY_SPEEDOMETER_USER/ENERGY_SPEEDOMETER_USER=\\"${{secrets.ENERGY_SPEEDOMETER_USER}}\\"/g' platformio.ini
|
||||
sed -i 's/NO_ENERGY_SPEEDOMETER_PASS/ENERGY_SPEEDOMETER_PASS=\\"${{secrets.ENERGY_SPEEDOMETER_PASS}}\\"/g' platformio.ini
|
||||
- name: Cache Python dependencies
|
||||
|
||||
2
.github/workflows/localazy.yml
vendored
2
.github/workflows/localazy.yml
vendored
@ -38,4 +38,4 @@ jobs:
|
||||
run: localazy download -k localazy-keys.json
|
||||
|
||||
- name: Upload translations to S3
|
||||
run: aws s3 sync ./localazy/language/ s3://amscloud-private/language/
|
||||
run: aws s3 sync ./localazy/language/ s3://${{ secrets.AWS_S3_BUCKET }}/language/
|
||||
|
||||
84
.github/workflows/prerelease.yml
vendored
Normal file
84
.github/workflows/prerelease.yml
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
name: Release candidate build and upload
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+'
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code from repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Get release version for filenames
|
||||
id: release_tag
|
||||
env:
|
||||
GITHUB_REF: ${{ github.ref }}
|
||||
run: echo ::set-output name=tag::$(echo ${GITHUB_REF:11})
|
||||
|
||||
- name: Create release with release notes
|
||||
id: create_release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: Release candidate v${{ steps.release_tag.outputs.tag }}
|
||||
prerelease: true
|
||||
|
||||
outputs:
|
||||
version: ${{ steps.release_tag.outputs.tag }}
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
|
||||
esp32s2:
|
||||
needs: prepare
|
||||
uses: ./.github/workflows/release-deploy-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp32s2
|
||||
version: ${{ needs.prepare.outputs.version }}
|
||||
upload_url: ${{ needs.prepare.outputs.upload_url }}
|
||||
subfolder: /rc
|
||||
esp32s3:
|
||||
needs: prepare
|
||||
uses: ./.github/workflows/release-deploy-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp32s3
|
||||
version: ${{ needs.prepare.outputs.version }}
|
||||
upload_url: ${{ needs.prepare.outputs.upload_url }}
|
||||
subfolder: /rc
|
||||
esp32c3:
|
||||
needs: prepare
|
||||
uses: ./.github/workflows/release-deploy-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp32c3
|
||||
version: ${{ needs.prepare.outputs.version }}
|
||||
upload_url: ${{ needs.prepare.outputs.upload_url }}
|
||||
subfolder: /rc
|
||||
esp32:
|
||||
needs: prepare
|
||||
uses: ./.github/workflows/release-deploy-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp32
|
||||
version: ${{ needs.prepare.outputs.version }}
|
||||
upload_url: ${{ needs.prepare.outputs.upload_url }}
|
||||
subfolder: /rc
|
||||
esp32solo:
|
||||
needs: prepare
|
||||
uses: ./.github/workflows/release-deploy-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp32solo
|
||||
version: ${{ needs.prepare.outputs.version }}
|
||||
upload_url: ${{ needs.prepare.outputs.upload_url }}
|
||||
subfolder: /rc
|
||||
esp8266:
|
||||
needs: prepare
|
||||
uses: ./.github/workflows/release-deploy-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp8266
|
||||
version: ${{ needs.prepare.outputs.version }}
|
||||
upload_url: ${{ needs.prepare.outputs.upload_url }}
|
||||
subfolder: /rc
|
||||
128
.github/workflows/release-deploy-env.yml
vendored
Normal file
128
.github/workflows/release-deploy-env.yml
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
name: Build with env and deploy
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
env:
|
||||
description: 'The environment to build for'
|
||||
required: true
|
||||
type: string
|
||||
upload_url:
|
||||
description: 'The upload URL for the release assets'
|
||||
required: true
|
||||
type: string
|
||||
version:
|
||||
description: 'The version tag for the release assets'
|
||||
required: true
|
||||
type: string
|
||||
subfolder:
|
||||
description: 'The subfolder in S3 to upload the binary to'
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
|
||||
jobs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code from repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v2
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: eu-north-1
|
||||
|
||||
- name: Cache Python dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('platformio.ini') }}
|
||||
|
||||
- name: Cache PlatformIO dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.pio/libdeps
|
||||
key: ${{ runner.os }}-pio-${{ hashFiles('platformio.ini') }}
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.9
|
||||
|
||||
- name: Inject secrets into ini file
|
||||
run: |
|
||||
sed -i 's/NO_AMS2MQTT_PRICE_KEY/AMS2MQTT_PRICE_KEY="${{secrets.AMS2MQTT_PRICE_KEY}}"/g' platformio.ini
|
||||
sed -i 's/NO_AMS2MQTT_PRICE_AUTHENTICATION/AMS2MQTT_PRICE_AUTHENTICATION="${{secrets.AMS2MQTT_PRICE_AUTHENTICATION}}"/g' platformio.ini
|
||||
sed -i 's/NO_AMS2MQTT_SC_KEY/AMS2MQTT_SC_KEY=\\"${{secrets.AMS2MQTT_SC_KEY}}\\"/g' platformio.ini
|
||||
sed -i 's/NO_ENERGY_SPEEDOMETER_USER/ENERGY_SPEEDOMETER_USER=\\"${{secrets.ENERGY_SPEEDOMETER_USER}}\\"/g' platformio.ini
|
||||
sed -i 's/NO_ENERGY_SPEEDOMETER_PASS/ENERGY_SPEEDOMETER_PASS=\\"${{secrets.ENERGY_SPEEDOMETER_PASS}}\\"/g' platformio.ini
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U platformio css_html_js_minify
|
||||
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '19.x'
|
||||
- name: Build with node
|
||||
run: |
|
||||
cd lib/SvelteUi/app
|
||||
npm ci
|
||||
npm run build
|
||||
cd -
|
||||
env:
|
||||
CI: false
|
||||
|
||||
- name: PlatformIO lib install
|
||||
env:
|
||||
GITHUB_TAG: v${{ inputs.version }}
|
||||
run: pio lib install
|
||||
|
||||
- name: Build firmware
|
||||
run: pio run -e ${{ inputs.env }}
|
||||
|
||||
- name: Create zip file
|
||||
run: /bin/sh scripts/${{ inputs.env }}/mkzip.sh
|
||||
|
||||
- name: Upload binary to release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ inputs.upload_url }}
|
||||
asset_path: .pio/build/${{ inputs.env }}/firmware.bin
|
||||
asset_name: ams2mqtt-${{ inputs.env }}-${{ inputs.version }}.bin
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
- name: Upload zip to release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ inputs.upload_url }}
|
||||
asset_path: ${{ inputs.env }}.zip
|
||||
asset_name: ams2mqtt-${{ inputs.env }}-${{ inputs.version }}.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Create MD5 checksum file
|
||||
run: md5sum .pio/build/${{ inputs.env }}/firmware.bin | cut -z -d ' ' -f 1 > firmware.md5
|
||||
|
||||
- name: Upload binary to S3
|
||||
run: aws s3 cp .pio/build/${{ inputs.env }}/firmware.bin s3://${{ secrets.AWS_S3_BUCKET }}/firmware${{ inputs.subfolder }}/ams2mqtt-${{ inputs.env }}-${{ inputs.version }}.bin
|
||||
|
||||
- name: Upload MD5 checksum to S3
|
||||
run: aws s3 cp firmware.md5 s3://${{ secrets.AWS_S3_BUCKET }}/firmware${{ inputs.subfolder }}/ams2mqtt-${{ inputs.env }}-${{ inputs.version }}.md5
|
||||
|
||||
- name: Upload bootloader to S3
|
||||
run: aws s3 cp .pio/build/${{ inputs.env }}/bootloader.bin s3://${{ secrets.AWS_S3_BUCKET }}/firmware${{ inputs.subfolder }}/ams2mqtt-${{ inputs.env }}-${{ inputs.version }}-bootloader.bin
|
||||
|
||||
- name: Upload partition table to S3
|
||||
run: aws s3 cp .pio/build/${{ inputs.env }}/partitions.bin s3://${{ secrets.AWS_S3_BUCKET }}/firmware${{ inputs.subfolder }}/ams2mqtt-${{ inputs.env }}-${{ inputs.version }}-partitions.bin
|
||||
|
||||
- name: Upload app0 to S3
|
||||
run: aws s3 cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin s3://${{ secrets.AWS_S3_BUCKET }}/firmware${{ inputs.subfolder }}/ams2mqtt-${{ inputs.env }}-${{ inputs.version }}-app0.bin
|
||||
269
.github/workflows/release.yml
vendored
269
.github/workflows/release.yml
vendored
@ -1,213 +1,78 @@
|
||||
name: Release
|
||||
name: Release build and upload
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code from repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Get release version for filenames
|
||||
id: release_tag
|
||||
env:
|
||||
GITHUB_REF: ${{ github.ref }}
|
||||
run: echo ::set-output name=tag::$(echo ${GITHUB_REF:11})
|
||||
- name: Get release version for code
|
||||
env:
|
||||
GITHUB_REF: ${{ github.ref }}
|
||||
run: echo "GITHUB_TAG=$(echo ${GITHUB_REF##*/})" >> $GITHUB_ENV
|
||||
- name: Check out code from repo
|
||||
uses: actions/checkout@v4
|
||||
- name: Get release version for filenames
|
||||
id: release_tag
|
||||
env:
|
||||
GITHUB_REF: ${{ github.ref }}
|
||||
run: echo ::set-output name=tag::$(echo ${GITHUB_REF:11})
|
||||
|
||||
- name: Inject secrets into ini file
|
||||
run: |
|
||||
sed -i 's/NO_AMS2MQTT_PRICE_KEY/AMS2MQTT_PRICE_KEY="${{secrets.AMS2MQTT_PRICE_KEY}}"/g' platformio.ini
|
||||
sed -i 's/NO_AMS2MQTT_PRICE_AUTHENTICATION/AMS2MQTT_PRICE_AUTHENTICATION="${{secrets.AMS2MQTT_PRICE_AUTHENTICATION}}"/g' platformio.ini
|
||||
sed -i 's/NO_AMS2MQTT_SC_KEY/AMS2MQTT_SC_KEY=\\"${{secrets.AMS2MQTT_SC_KEY}}\\"/g' platformio.ini
|
||||
sed -i 's/NO_ENERGY_SPEEDOMETER_USER/ENERGY_SPEEDOMETER_USER=\\"${{secrets.ENERGY_SPEEDOMETER_USER}}\\"/g' platformio.ini
|
||||
sed -i 's/NO_ENERGY_SPEEDOMETER_PASS/ENERGY_SPEEDOMETER_PASS=\\"${{secrets.ENERGY_SPEEDOMETER_PASS}}\\"/g' platformio.ini
|
||||
- name: Create release with release notes
|
||||
id: create_release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: Release v${{ steps.release_tag.outputs.tag }}
|
||||
generateReleaseNotes: true
|
||||
|
||||
- name: Cache Python dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('platformio.ini') }}
|
||||
- name: Cache PlatformIO dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.pio/libdeps
|
||||
key: ${{ runner.os }}-pio-${{ hashFiles('platformio.ini') }}
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -U platformio css_html_js_minify
|
||||
outputs:
|
||||
version: ${{ steps.release_tag.outputs.tag }}
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
|
||||
- name: Set up node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '19.x'
|
||||
- name: Build with node
|
||||
run: |
|
||||
cd lib/SvelteUi/app
|
||||
npm ci
|
||||
npm run build
|
||||
cd -
|
||||
env:
|
||||
CI: false
|
||||
|
||||
- name: PlatformIO lib install
|
||||
run: pio lib install
|
||||
|
||||
- name: Create release with release notes
|
||||
id: create_release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: Release v${{ steps.release_tag.outputs.tag }}
|
||||
generateReleaseNotes: true
|
||||
|
||||
- name: Build esp8266 firmware
|
||||
run: pio run -e esp8266
|
||||
- name: Create esp8266 zip file
|
||||
run: /bin/sh scripts/esp8266/mkzip.sh
|
||||
- name: Upload esp8266 binary to release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: .pio/build/esp8266/firmware.bin
|
||||
asset_name: ams2mqtt-esp8266-${{ steps.release_tag.outputs.tag }}.bin
|
||||
asset_content_type: application/octet-stream
|
||||
- name: Upload esp8266 zip to release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: esp8266.zip
|
||||
asset_name: ams2mqtt-esp8266-${{ steps.release_tag.outputs.tag }}.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Build esp32 firmware
|
||||
run: pio run -e esp32
|
||||
- name: Create esp32 zip file
|
||||
run: /bin/sh scripts/esp32/mkzip.sh
|
||||
- name: Upload esp32 binary to release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: .pio/build/esp32/firmware.bin
|
||||
asset_name: ams2mqtt-esp32-${{ steps.release_tag.outputs.tag }}.bin
|
||||
asset_content_type: application/octet-stream
|
||||
- name: Upload esp32 zip to release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: esp32.zip
|
||||
asset_name: ams2mqtt-esp32-${{ steps.release_tag.outputs.tag }}.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Build esp32s2 firmware
|
||||
run: pio run -e esp32s2
|
||||
- name: Create esp32s2 zip file
|
||||
run: /bin/sh scripts/esp32s2/mkzip.sh
|
||||
- name: Upload esp32s2 binary to release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: .pio/build/esp32s2/firmware.bin
|
||||
asset_name: ams2mqtt-esp32s2-${{ steps.release_tag.outputs.tag }}.bin
|
||||
asset_content_type: application/octet-stream
|
||||
- name: Upload esp32s2 zip to release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: esp32s2.zip
|
||||
asset_name: ams2mqtt-esp32s2-${{ steps.release_tag.outputs.tag }}.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Build esp32s3 firmware
|
||||
run: pio run -e esp32s3
|
||||
- name: Create esp32s3 zip file
|
||||
run: /bin/sh scripts/esp32s3/mkzip.sh
|
||||
- name: Upload esp32s3 binary to release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: .pio/build/esp32s3/firmware.bin
|
||||
asset_name: ams2mqtt-esp32s3-${{ steps.release_tag.outputs.tag }}.bin
|
||||
asset_content_type: application/octet-stream
|
||||
- name: Upload esp32s3 zip to release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: esp32s3.zip
|
||||
asset_name: ams2mqtt-esp32s3-${{ steps.release_tag.outputs.tag }}.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Build esp32solo firmware
|
||||
run: pio run -e esp32solo
|
||||
- name: Create esp32solo zip file
|
||||
run: /bin/sh scripts/esp32solo/mkzip.sh
|
||||
- name: Upload esp32solo binary to release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: .pio/build/esp32solo/firmware.bin
|
||||
asset_name: ams2mqtt-esp32solo-${{ steps.release_tag.outputs.tag }}.bin
|
||||
asset_content_type: application/octet-stream
|
||||
- name: Upload esp32solo zip to release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: esp32solo.zip
|
||||
asset_name: ams2mqtt-esp32solo-${{ steps.release_tag.outputs.tag }}.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: Build esp32c3 firmware
|
||||
run: pio run -e esp32c3
|
||||
- name: Create esp32c3 zip file
|
||||
run: /bin/sh scripts/esp32c3/mkzip.sh
|
||||
- name: Upload esp32c3 binary to release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: .pio/build/esp32c3/firmware.bin
|
||||
asset_name: ams2mqtt-esp32c3-${{ steps.release_tag.outputs.tag }}.bin
|
||||
asset_content_type: application/octet-stream
|
||||
- name: Upload esp32c3 zip to release
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: esp32c3.zip
|
||||
asset_name: ams2mqtt-esp32c3-${{ steps.release_tag.outputs.tag }}.zip
|
||||
asset_content_type: application/zip
|
||||
esp32s2:
|
||||
needs: prepare
|
||||
uses: ./.github/workflows/release-deploy-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp32s2
|
||||
version: ${{ needs.prepare.outputs.version }}
|
||||
upload_url: ${{ needs.prepare.outputs.upload_url }}
|
||||
esp32s3:
|
||||
needs: prepare
|
||||
uses: ./.github/workflows/release-deploy-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp32s3
|
||||
version: ${{ needs.prepare.outputs.version }}
|
||||
upload_url: ${{ needs.prepare.outputs.upload_url }}
|
||||
esp32c3:
|
||||
needs: prepare
|
||||
uses: ./.github/workflows/release-deploy-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp32c3
|
||||
version: ${{ needs.prepare.outputs.version }}
|
||||
upload_url: ${{ needs.prepare.outputs.upload_url }}
|
||||
esp32:
|
||||
needs: prepare
|
||||
uses: ./.github/workflows/release-deploy-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp32
|
||||
version: ${{ needs.prepare.outputs.version }}
|
||||
upload_url: ${{ needs.prepare.outputs.upload_url }}
|
||||
esp32solo:
|
||||
needs: prepare
|
||||
uses: ./.github/workflows/release-deploy-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp32solo
|
||||
version: ${{ needs.prepare.outputs.version }}
|
||||
upload_url: ${{ needs.prepare.outputs.upload_url }}
|
||||
esp8266:
|
||||
needs: prepare
|
||||
uses: ./.github/workflows/release-deploy-env.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
env: esp8266
|
||||
version: ${{ needs.prepare.outputs.version }}
|
||||
upload_url: ${{ needs.prepare.outputs.upload_url }}
|
||||
|
||||
@ -230,6 +230,16 @@ bool AmsFirmwareUpdater::fetchNextVersion() {
|
||||
http.setUserAgent("AMS-Firmware-Updater");
|
||||
http.addHeader(F("Cache-Control"), "no-cache");
|
||||
http.addHeader(F("x-AMS-version"), FirmwareVersion::VersionString);
|
||||
http.addHeader(F("x-AMS-STA-MAC"), WiFi.macAddress());
|
||||
http.addHeader(F("x-AMS-AP-MAC"), WiFi.softAPmacAddress());
|
||||
http.addHeader(F("x-AMS-chip-size"), String(ESP.getFlashChipSize()));
|
||||
http.addHeader(F("x-AMS-board-type"), String(hw->getBoardType(), 10));
|
||||
if(meterState->getMeterType() != AmsTypeAutodetect) {
|
||||
http.addHeader(F("x-AMS-meter-mfg"), String(meterState->getMeterType(), 10));
|
||||
}
|
||||
if(!meterState->getMeterModel().isEmpty()) {
|
||||
http.addHeader(F("x-AMS-meter-model"), meterState->getMeterModel());
|
||||
}
|
||||
int status = http.GET();
|
||||
if(status == 204) {
|
||||
String nextVersion = http.header("x-version");
|
||||
|
||||
9
lib/AmsJsonGenerator/include/AmsJsonGenerator.h
Normal file
9
lib/AmsJsonGenerator/include/AmsJsonGenerator.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "AmsDataStorage.h"
|
||||
|
||||
class AmsJsonGenerator {
|
||||
public:
|
||||
static void generateDayPlotJson(AmsDataStorage* ds, char* buf, size_t bufSize);
|
||||
static void generateMonthPlotJson(AmsDataStorage* ds, char* buf, size_t bufSize);
|
||||
};
|
||||
17
lib/AmsJsonGenerator/src/AmsJsonGenerator.cpp
Normal file
17
lib/AmsJsonGenerator/src/AmsJsonGenerator.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "AmsJsonGenerator.h"
|
||||
|
||||
void AmsJsonGenerator::generateDayPlotJson(AmsDataStorage* ds, char* buf, size_t bufSize) {
|
||||
uint16_t pos = snprintf_P(buf, bufSize, PSTR("{\"unit\":\"kwh\""));
|
||||
for(uint8_t i = 0; i < 24; i++) {
|
||||
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"i%02d\":%.3f,\"e%02d\":%.3f"), i, ds->getHourImport(i) / 1000.0, i, ds->getHourExport(i) / 1000.0);
|
||||
}
|
||||
snprintf_P(buf+pos, bufSize-pos, PSTR("}"));
|
||||
}
|
||||
|
||||
void AmsJsonGenerator::generateMonthPlotJson(AmsDataStorage* ds, char* buf, size_t bufSize) {
|
||||
uint16_t pos = snprintf_P(buf, bufSize, PSTR("{\"unit\":\"kwh\""));
|
||||
for(uint8_t i = 1; i < 32; i++) {
|
||||
pos += snprintf_P(buf+pos, bufSize-pos, PSTR(",\"i%02d\":%.3f,\"e%02d\":%.3f"), i, ds->getDayImport(i) / 1000.0, i, ds->getDayExport(i) / 1000.0);
|
||||
}
|
||||
snprintf_P(buf+pos, bufSize-pos, PSTR("}"));
|
||||
}
|
||||
@ -125,18 +125,6 @@ bool AmsMqttHandler::connect() {
|
||||
#endif
|
||||
debugger->printf_P(PSTR("Successfully connected to MQTT\n"));
|
||||
mqtt.onMessage(std::bind(&AmsMqttHandler::onMessage, this, std::placeholders::_1, std::placeholders::_2));
|
||||
if(strlen(mqttConfig.subscribeTopic) > 0) {
|
||||
#if defined(AMS_REMOTE_DEBUG)
|
||||
if (debugger->isActive(RemoteDebug::INFO))
|
||||
#endif
|
||||
debugger->printf_P(PSTR(" Subscribing to [%s]\n"), mqttConfig.subscribeTopic);
|
||||
if(!mqtt.subscribe(mqttConfig.subscribeTopic)) {
|
||||
#if defined(AMS_REMOTE_DEBUG)
|
||||
if (debugger->isActive(RemoteDebug::ERROR))
|
||||
#endif
|
||||
debugger->printf_P(PSTR(" Unable to subscribe to to [%s]\n"), mqttConfig.subscribeTopic);
|
||||
}
|
||||
}
|
||||
mqtt.publish(statusTopic, "online", true, 0);
|
||||
mqtt.loop();
|
||||
postConnect();
|
||||
@ -192,7 +180,7 @@ bool AmsMqttHandler::loop() {
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
delay(10);
|
||||
delay(10); // Needed to preserve power. After adding this, the voltage is super smooth on a HAN powered device
|
||||
yield();
|
||||
#if defined(ESP32)
|
||||
esp_task_wdt_reset();
|
||||
|
||||
@ -13,6 +13,12 @@
|
||||
#include "PriceService.h"
|
||||
|
||||
struct EnergyAccountingPeak {
|
||||
uint8_t day;
|
||||
uint8_t hour;
|
||||
uint16_t value;
|
||||
};
|
||||
|
||||
struct EnergyAccountingPeak6 {
|
||||
uint8_t day;
|
||||
uint16_t value;
|
||||
};
|
||||
@ -32,34 +38,19 @@ struct EnergyAccountingData {
|
||||
EnergyAccountingPeak peaks[5];
|
||||
};
|
||||
|
||||
struct EnergyAccountingData5 {
|
||||
struct EnergyAccountingData6 {
|
||||
uint8_t version;
|
||||
uint8_t month;
|
||||
uint16_t costYesterday;
|
||||
uint16_t costThisMonth;
|
||||
uint16_t costLastMonth;
|
||||
uint16_t incomeYesterday;
|
||||
uint16_t incomeThisMonth;
|
||||
uint16_t incomeLastMonth;
|
||||
EnergyAccountingPeak peaks[5];
|
||||
};
|
||||
|
||||
struct EnergyAccountingData4 {
|
||||
uint8_t version;
|
||||
uint8_t month;
|
||||
uint16_t costYesterday;
|
||||
uint16_t costThisMonth;
|
||||
uint16_t costLastMonth;
|
||||
EnergyAccountingPeak peaks[5];
|
||||
};
|
||||
|
||||
struct EnergyAccountingData2 {
|
||||
uint8_t version;
|
||||
uint8_t month;
|
||||
uint16_t maxHour;
|
||||
uint16_t costYesterday;
|
||||
uint16_t costThisMonth;
|
||||
uint16_t costLastMonth;
|
||||
int32_t costYesterday;
|
||||
int32_t costThisMonth;
|
||||
int32_t costLastMonth;
|
||||
int32_t incomeYesterday;
|
||||
int32_t incomeThisMonth;
|
||||
int32_t incomeLastMonth;
|
||||
uint32_t lastMonthImport;
|
||||
uint32_t lastMonthExport;
|
||||
uint8_t lastMonthAccuracy;
|
||||
EnergyAccountingPeak6 peaks[5];
|
||||
};
|
||||
|
||||
struct EnergyAccountingRealtimeData {
|
||||
@ -142,7 +133,7 @@ private:
|
||||
String currency = "";
|
||||
|
||||
void calcDayCost();
|
||||
bool updateMax(uint16_t val, uint8_t day);
|
||||
bool updateMax(uint16_t val, uint8_t day, uint8_t hour);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -72,15 +72,15 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
this->realtimeData->currentHour = local.Hour;
|
||||
this->realtimeData->currentDay = local.Day;
|
||||
if(!load()) {
|
||||
data = { 6, local.Month,
|
||||
data = { 7, local.Month,
|
||||
0, 0, 0, // Cost
|
||||
0, 0, 0, // Income
|
||||
0, 0, 0, // Last month import, export and accuracy
|
||||
0, 0, // Peak 1
|
||||
0, 0, // Peak 2
|
||||
0, 0, // Peak 3
|
||||
0, 0, // Peak 4
|
||||
0, 0 // Peak 5
|
||||
0, 0, 0, // Peak 1
|
||||
0, 0, 0, // Peak 2
|
||||
0, 0, 0, // Peak 3
|
||||
0, 0, 0, // Peak 4
|
||||
0, 0, 0 // Peak 5
|
||||
};
|
||||
}
|
||||
init = true;
|
||||
@ -97,7 +97,7 @@ bool EnergyAccounting::update(AmsData* amsData) {
|
||||
uint16_t val = round(ds->getHourImport(oneHrAgo.Hour) / 10.0);
|
||||
|
||||
breakTime(tz->toLocal(now-3600), oneHrAgoLocal);
|
||||
ret |= updateMax(val, oneHrAgoLocal.Day);
|
||||
ret |= updateMax(val, oneHrAgoLocal.Day, oneHrAgoLocal.Hour);
|
||||
|
||||
this->realtimeData->currentHour = local.Hour; // Need to be defined here so that day cost is correctly calculated
|
||||
if(local.Hour > 0) {
|
||||
@ -407,85 +407,29 @@ bool EnergyAccounting::load() {
|
||||
char buf[file.size()];
|
||||
file.readBytes(buf, file.size());
|
||||
|
||||
if(buf[0] == 6) {
|
||||
if(buf[0] == 7) {
|
||||
EnergyAccountingData* data = (EnergyAccountingData*) buf;
|
||||
memcpy(&this->data, data, sizeof(this->data));
|
||||
ret = true;
|
||||
} else if(buf[0] == 5) {
|
||||
EnergyAccountingData5* data = (EnergyAccountingData5*) buf;
|
||||
this->data = { 6, data->month,
|
||||
((uint32_t) data->costYesterday) * 10,
|
||||
((uint32_t) data->costThisMonth) * 100,
|
||||
((uint32_t) data->costLastMonth) * 100,
|
||||
((uint32_t) data->incomeYesterday) * 10,
|
||||
((uint32_t) data->incomeThisMonth) * 100,
|
||||
((uint32_t) data->incomeLastMonth) * 100,
|
||||
0,0,0, // Last month import, export and accuracy
|
||||
data->peaks[0].day, data->peaks[0].value,
|
||||
data->peaks[1].day, data->peaks[1].value,
|
||||
data->peaks[2].day, data->peaks[2].value,
|
||||
data->peaks[3].day, data->peaks[3].value,
|
||||
data->peaks[4].day, data->peaks[4].value
|
||||
};
|
||||
ret = true;
|
||||
} else if(buf[0] == 4) {
|
||||
EnergyAccountingData4* data = (EnergyAccountingData4*) buf;
|
||||
this->data = { 5, data->month,
|
||||
((uint32_t) data->costYesterday) * 10,
|
||||
((uint32_t) data->costThisMonth) * 100,
|
||||
((uint32_t) data->costLastMonth) * 100,
|
||||
0,0,0, // Income from production
|
||||
0,0,0, // Last month import, export and accuracy
|
||||
data->peaks[0].day, data->peaks[0].value,
|
||||
data->peaks[1].day, data->peaks[1].value,
|
||||
data->peaks[2].day, data->peaks[2].value,
|
||||
data->peaks[3].day, data->peaks[3].value,
|
||||
data->peaks[4].day, data->peaks[4].value
|
||||
};
|
||||
ret = true;
|
||||
} else if(buf[0] == 3) {
|
||||
EnergyAccountingData* data = (EnergyAccountingData*) buf;
|
||||
this->data = { 5, data->month,
|
||||
data->costYesterday * 10,
|
||||
} else if(buf[0] == 6) {
|
||||
EnergyAccountingData6* data = (EnergyAccountingData6*) buf;
|
||||
this->data = { 7, data->month,
|
||||
data->costYesterday,
|
||||
data->costThisMonth,
|
||||
data->costLastMonth,
|
||||
0,0,0, // Income from production
|
||||
0,0,0, // Last month import, export and accuracy
|
||||
data->peaks[0].day, data->peaks[0].value,
|
||||
data->peaks[1].day, data->peaks[1].value,
|
||||
data->peaks[2].day, data->peaks[2].value,
|
||||
data->peaks[3].day, data->peaks[3].value,
|
||||
data->peaks[4].day, data->peaks[4].value
|
||||
data->incomeYesterday,
|
||||
data->incomeThisMonth,
|
||||
data->incomeLastMonth,
|
||||
data->lastMonthImport,
|
||||
data->lastMonthExport,
|
||||
data->lastMonthAccuracy,
|
||||
data->peaks[0].day, 0, data->peaks[0].value,
|
||||
data->peaks[1].day, 0, data->peaks[1].value,
|
||||
data->peaks[2].day, 0, data->peaks[2].value,
|
||||
data->peaks[3].day, 0, data->peaks[3].value,
|
||||
data->peaks[4].day, 0, data->peaks[4].value
|
||||
};
|
||||
ret = true;
|
||||
} else {
|
||||
data = { 5, 0,
|
||||
0, 0, 0, // Cost
|
||||
0,0,0, // Income from production
|
||||
0,0,0, // Last month import, export and accuracy
|
||||
0, 0, // Peak 1
|
||||
0, 0, // Peak 2
|
||||
0, 0, // Peak 3
|
||||
0, 0, // Peak 4
|
||||
0, 0 // Peak 5
|
||||
};
|
||||
if(buf[0] == 2) {
|
||||
EnergyAccountingData2* data = (EnergyAccountingData2*) buf;
|
||||
this->data.month = data->month;
|
||||
this->data.costYesterday = data->costYesterday * 10;
|
||||
this->data.costThisMonth = data->costThisMonth;
|
||||
this->data.costLastMonth = data->costLastMonth;
|
||||
uint8_t b = 0;
|
||||
for(uint8_t i = sizeof(this->data); i < file.size(); i+=2) {
|
||||
this->data.peaks[b].day = b;
|
||||
memcpy(&this->data.peaks[b].value, buf+i, 2);
|
||||
b++;
|
||||
if(b >= config->hours || b >= 5) break;
|
||||
}
|
||||
ret = true;
|
||||
} else {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
@ -518,11 +462,12 @@ void EnergyAccounting::setData(EnergyAccountingData& data) {
|
||||
this->data = data;
|
||||
}
|
||||
|
||||
bool EnergyAccounting::updateMax(uint16_t val, uint8_t day) {
|
||||
bool EnergyAccounting::updateMax(uint16_t val, uint8_t day, uint8_t hour) {
|
||||
for(uint8_t i = 0; i < 5; i++) {
|
||||
if(data.peaks[i].day == day || data.peaks[i].day == 0) {
|
||||
if(val > data.peaks[i].value) {
|
||||
data.peaks[i].day = day;
|
||||
data.peaks[i].hour = hour;
|
||||
data.peaks[i].value = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -15,13 +15,13 @@
|
||||
class HomeAssistantMqttHandler : public AmsMqttHandler {
|
||||
public:
|
||||
#if defined(AMS_REMOTE_DEBUG)
|
||||
HomeAssistantMqttHandler(MqttConfig& mqttConfig, RemoteDebug* debugger, char* buf, uint8_t boardType, HomeAssistantConfig config, HwTools* hw, AmsFirmwareUpdater* updater) : AmsMqttHandler(mqttConfig, debugger, buf, updater) {
|
||||
HomeAssistantMqttHandler(MqttConfig& mqttConfig, RemoteDebug* debugger, char* buf, uint8_t boardType, HomeAssistantConfig config, HwTools* hw, AmsFirmwareUpdater* updater, char* hostname) : AmsMqttHandler(mqttConfig, debugger, buf, updater) {
|
||||
#else
|
||||
HomeAssistantMqttHandler(MqttConfig& mqttConfig, Stream* debugger, char* buf, uint8_t boardType, HomeAssistantConfig config, HwTools* hw) : AmsMqttHandler(mqttConfig, debugger, buf) {
|
||||
#endif
|
||||
this->boardType = boardType;
|
||||
this->hw = hw;
|
||||
setHomeAssistantConfig(config);
|
||||
setHomeAssistantConfig(config, hostname);
|
||||
};
|
||||
bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea, PriceService* ps);
|
||||
bool publishTemperatures(AmsConfiguration*, HwTools*);
|
||||
@ -36,7 +36,7 @@ public:
|
||||
|
||||
uint8_t getFormat();
|
||||
|
||||
void setHomeAssistantConfig(HomeAssistantConfig config);
|
||||
void setHomeAssistantConfig(HomeAssistantConfig config, char* hostname);
|
||||
private:
|
||||
uint8_t boardType;
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
#include <esp_task_wdt.h>
|
||||
#endif
|
||||
|
||||
void HomeAssistantMqttHandler::setHomeAssistantConfig(HomeAssistantConfig config) {
|
||||
void HomeAssistantMqttHandler::setHomeAssistantConfig(HomeAssistantConfig config, char* hostname) {
|
||||
l1Init = l2Init = l2eInit = l3Init = l3eInit = l4Init = l4eInit = rtInit = rteInit = pInit = sInit = rInit = fInit = false;
|
||||
|
||||
if(strlen(config.discoveryNameTag) > 0) {
|
||||
@ -34,14 +34,6 @@ void HomeAssistantMqttHandler::setHomeAssistantConfig(HomeAssistantConfig config
|
||||
deviceModel = boardTypeToString(boardType);
|
||||
manufacturer = boardManufacturerToString(boardType);
|
||||
|
||||
char hostname[32];
|
||||
#if defined(ESP8266)
|
||||
strcpy(hostname, WiFi.hostname().c_str());
|
||||
#elif defined(ESP32)
|
||||
strcpy(hostname, WiFi.getHostname());
|
||||
#endif
|
||||
|
||||
stripNonAscii((uint8_t*) hostname, 32, false);
|
||||
deviceUid = String(hostname); // Maybe configurable in the future?
|
||||
|
||||
if(strlen(config.discoveryHostname) > 0) {
|
||||
@ -764,9 +756,10 @@ bool HomeAssistantMqttHandler::publishRaw(String data) {
|
||||
|
||||
bool HomeAssistantMqttHandler::publishFirmware() {
|
||||
if(!fInit) {
|
||||
snprintf_P(json, BufferSize, PSTR("{\"name\":\"%sFirmware\",\"stat_t\":\"%s/firmware\",\"dev_cla\":\"firmware\",\"cmd_t\":\"%s\",\"pl_inst\":\"fwupgrade\"}"),
|
||||
snprintf_P(json, BufferSize, PSTR("{\"name\":\"%sFirmware\",\"stat_t\":\"%s/firmware\",\"uniq_id\":\"%s_fwupgrade\",\"dev_cla\":\"firmware\",\"cmd_t\":\"%s\",\"pl_inst\":\"fwupgrade\"}"),
|
||||
sensorNamePrefix.c_str(),
|
||||
pubTopic.c_str(),
|
||||
deviceUid.c_str(),
|
||||
subTopic.c_str()
|
||||
);
|
||||
fInit = mqtt.publish(updateTopic + "/" + deviceUid + "/config", json, true, 0);
|
||||
|
||||
@ -67,7 +67,9 @@ private:
|
||||
bool ledInvert, rgbInvert;
|
||||
uint8_t vccPin, vccGnd_r, vccVcc_r;
|
||||
float vccOffset, vccMultiplier;
|
||||
float vcc = 3.3; // Last known Vcc
|
||||
float maxVcc = 3.25; // Best to have this close to max as a start, in case Pow-U reboots and starts off with a low voltage, we dont want that to be perceived as max
|
||||
unsigned long lastVccRead = 0;
|
||||
|
||||
uint16_t analogRange = 1024;
|
||||
AdcConfig voltAdc, tempAdc;
|
||||
|
||||
@ -657,8 +657,12 @@ bool HwTools::writeLedPin(uint8_t color, uint8_t state) {
|
||||
}
|
||||
|
||||
bool HwTools::isVoltageOptimal(float range) {
|
||||
if(boardType >= 5 && boardType <= 7 && maxVcc > 2.8) { // Pow-*
|
||||
float vcc = getVcc();
|
||||
if(boardType >= 1 && boardType <= 8 && maxVcc > 2.8) { // BUS-Power boards
|
||||
unsigned long now = millis();
|
||||
if(now - lastVccRead > 250) {
|
||||
vcc = getVcc();
|
||||
lastVccRead = now;
|
||||
}
|
||||
if(vcc > 3.4 || vcc < 2.8) {
|
||||
maxVcc = 0; // Voltage is outside the operating range, we have to assume voltage is OK
|
||||
} else if(vcc > maxVcc) {
|
||||
|
||||
@ -12,14 +12,13 @@
|
||||
class JsonMqttHandler : public AmsMqttHandler {
|
||||
public:
|
||||
#if defined(AMS_REMOTE_DEBUG)
|
||||
JsonMqttHandler(MqttConfig& mqttConfig, RemoteDebug* debugger, char* buf, HwTools* hw, AmsFirmwareUpdater* updater) : AmsMqttHandler(mqttConfig, debugger, buf, updater) {
|
||||
this->hw = hw;
|
||||
};
|
||||
JsonMqttHandler(MqttConfig& mqttConfig, RemoteDebug* debugger, char* buf, HwTools* hw, AmsDataStorage* ds, AmsFirmwareUpdater* updater) : AmsMqttHandler(mqttConfig, debugger, buf, updater) {
|
||||
#else
|
||||
JsonMqttHandler(MqttConfig& mqttConfig, Stream* debugger, char* buf, HwTools* hw) : AmsMqttHandler(mqttConfig, debugger, buf) {
|
||||
this->hw = hw;
|
||||
};
|
||||
JsonMqttHandler(MqttConfig& mqttConfig, Stream* debugger, char* buf, HwTools* hw, AmsDataStorage* ds, AmsFirmwareUpdater* updater) : AmsMqttHandler(mqttConfig, debugger, buf, updater) {
|
||||
#endif
|
||||
this->hw = hw;
|
||||
this->ds = ds;
|
||||
};
|
||||
bool publish(AmsData* data, AmsData* previousState, EnergyAccounting* ea, PriceService* ps);
|
||||
bool publishTemperatures(AmsConfiguration*, HwTools*);
|
||||
bool publishPrices(PriceService*);
|
||||
@ -27,12 +26,16 @@ public:
|
||||
bool publishRaw(String data);
|
||||
bool publishFirmware();
|
||||
|
||||
bool postConnect();
|
||||
|
||||
void onMessage(String &topic, String &payload);
|
||||
|
||||
uint8_t getFormat();
|
||||
|
||||
private:
|
||||
HwTools* hw;
|
||||
AmsDataStorage* ds;
|
||||
|
||||
uint16_t appendJsonHeader(AmsData* data);
|
||||
uint16_t appendJsonFooter(EnergyAccounting* ea, uint16_t pos);
|
||||
bool publishList1(AmsData* data, EnergyAccounting* ea);
|
||||
|
||||
@ -8,6 +8,18 @@
|
||||
#include "FirmwareVersion.h"
|
||||
#include "hexutils.h"
|
||||
#include "Uptime.h"
|
||||
#include "AmsJsonGenerator.h"
|
||||
|
||||
bool JsonMqttHandler::postConnect() {
|
||||
if(!subTopic.isEmpty() && !mqtt.subscribe(subTopic)) {
|
||||
#if defined(AMS_REMOTE_DEBUG)
|
||||
if (debugger->isActive(RemoteDebug::ERROR))
|
||||
#endif
|
||||
debugger->printf_P(PSTR(" Unable to subscribe to to [%s]\n"), subTopic.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JsonMqttHandler::publish(AmsData* update, AmsData* previousState, EnergyAccounting* ea, PriceService* ps) {
|
||||
if(strlen(mqttConfig.publishTopic) == 0) {
|
||||
@ -67,14 +79,24 @@ uint16_t JsonMqttHandler::appendJsonFooter(EnergyAccounting* ea, uint16_t pos) {
|
||||
} else {
|
||||
memset(pf, 0, 4);
|
||||
}
|
||||
|
||||
String peaks = "";
|
||||
uint8_t peakCount = ea->getConfig()->hours;
|
||||
if(peakCount > 5) peakCount = 5;
|
||||
for(uint8_t i = 1; i <= peakCount; i++) {
|
||||
if(!peaks.isEmpty()) peaks += ",";
|
||||
peaks += String(ea->getPeak(i).value / 100.0, 2);
|
||||
}
|
||||
|
||||
return snprintf_P(json+pos, BufferSize-pos, PSTR("%s\"%sh\":%.2f,\"%sd\":%.1f,\"%st\":%d,\"%sx\":%.2f,\"%she\":%.2f,\"%sde\":%.1f%s"),
|
||||
return snprintf_P(json+pos, BufferSize-pos, PSTR("%s\"%sh\":%.3f,\"%sd\":%.2f,\"%sm\":%.1f,\"%st\":%d,\"%sx\":%.2f,\"%she\":%.3f,\"%sde\":%.2f,\"%sme\":%.1f,\"peaks\":[%s]%s"),
|
||||
strlen(pf) == 0 ? "},\"realtime\":{" : ",",
|
||||
pf,
|
||||
ea->getUseThisHour(),
|
||||
pf,
|
||||
ea->getUseToday(),
|
||||
pf,
|
||||
ea->getUseThisMonth(),
|
||||
pf,
|
||||
ea->getCurrentThreshold(),
|
||||
pf,
|
||||
ea->getMonthMax(),
|
||||
@ -82,6 +104,9 @@ uint16_t JsonMqttHandler::appendJsonFooter(EnergyAccounting* ea, uint16_t pos) {
|
||||
ea->getProducedThisHour(),
|
||||
pf,
|
||||
ea->getProducedToday(),
|
||||
pf,
|
||||
ea->getProducedThisMonth(),
|
||||
peaks.c_str(),
|
||||
strlen(pf) == 0 ? "}" : ""
|
||||
);
|
||||
}
|
||||
@ -447,11 +472,35 @@ bool JsonMqttHandler::publishFirmware() {
|
||||
}
|
||||
|
||||
void JsonMqttHandler::onMessage(String &topic, String &payload) {
|
||||
if(strlen(mqttConfig.publishTopic) == 0 || !mqtt.connected())
|
||||
return;
|
||||
|
||||
#if defined(AMS_REMOTE_DEBUG)
|
||||
if (debugger->isActive(RemoteDebug::INFO))
|
||||
#endif
|
||||
debugger->printf_P(PSTR("Received command [%s] to [%s]\n"), payload.c_str(), topic.c_str());
|
||||
|
||||
if(topic.equals(subTopic)) {
|
||||
#if defined(AMS_REMOTE_DEBUG)
|
||||
if (debugger->isActive(RemoteDebug::DEBUG))
|
||||
#endif
|
||||
debugger->printf_P(PSTR(" - this is our subscribed topic\n"));
|
||||
if(payload.equals("fwupgrade")) {
|
||||
if(strcmp(updater->getNextVersion(), FirmwareVersion::VersionString) != 0) {
|
||||
updater->setTargetVersion(updater->getNextVersion());
|
||||
}
|
||||
} else if(payload.equals("dayplot")) {
|
||||
char pubTopic[192];
|
||||
snprintf_P(pubTopic, 192, PSTR("%s/dayplot"), mqttConfig.publishTopic);
|
||||
AmsJsonGenerator::generateDayPlotJson(ds, json, BufferSize);
|
||||
bool ret = mqtt.publish(pubTopic, json);
|
||||
loop();
|
||||
} else if(payload.equals("monthplot")) {
|
||||
char pubTopic[192];
|
||||
snprintf_P(pubTopic, 192, PSTR("%s/monthplot"), mqttConfig.publishTopic);
|
||||
AmsJsonGenerator::generateMonthPlotJson(ds, json, BufferSize);
|
||||
bool ret = mqtt.publish(pubTopic, json);
|
||||
loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,22 @@
|
||||
#include "DataParser.h"
|
||||
#include "Cosem.h"
|
||||
|
||||
struct Lng2Data_3p {
|
||||
struct Lng2Data_3p_0b {
|
||||
CosemBasic header;
|
||||
CosemLongUnsigned u1;
|
||||
CosemLongUnsigned u2;
|
||||
CosemLongUnsigned u3;
|
||||
CosemLongUnsigned i1;
|
||||
CosemLongUnsigned i2;
|
||||
CosemLongUnsigned i3;
|
||||
CosemDLongUnsigned activeImport;
|
||||
CosemDLongUnsigned activeExport;
|
||||
CosemDLongUnsigned acumulatedImport;
|
||||
CosemDLongUnsigned accumulatedExport;
|
||||
CosemString meterId;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct Lng2Data_3p_0e {
|
||||
CosemBasic header;
|
||||
CosemLongUnsigned u1;
|
||||
CosemLongUnsigned u2;
|
||||
|
||||
@ -14,7 +14,33 @@ LNG2::LNG2(AmsData& meterState, const char* payload, uint8_t useMeterType, Meter
|
||||
meterType = AmsTypeLandisGyr;
|
||||
this->packageTimestamp = ctx.timestamp;
|
||||
|
||||
Lng2Data_3p* d = (Lng2Data_3p*) payload;
|
||||
Lng2Data_3p_0e* d = (Lng2Data_3p_0e*) payload;
|
||||
this->l1voltage = ntohs(d->u1.data);
|
||||
this->l2voltage = ntohs(d->u2.data);
|
||||
this->l3voltage = ntohs(d->u3.data);
|
||||
|
||||
this->l1current = ntohs(d->i1.data) / 100.0;
|
||||
this->l2current = ntohs(d->i2.data) / 100.0;
|
||||
this->l3current = ntohs(d->i3.data) / 100.0;
|
||||
|
||||
this->activeImportPower = ntohl(d->activeImport.data);
|
||||
this->activeExportPower = ntohl(d->activeExport.data);
|
||||
this->activeImportCounter = ntohl(d->acumulatedImport.data) / 1000.0;
|
||||
this->activeExportCounter = ntohl(d->accumulatedExport.data) / 1000.0;
|
||||
|
||||
char str[64];
|
||||
uint8_t str_len = getString((CosemData*) &d->meterId, str);
|
||||
if(str_len > 0) {
|
||||
this->meterId = String(str);
|
||||
}
|
||||
listType = 3;
|
||||
lastUpdateMillis = millis64();
|
||||
} else if(h->length == 0x0b) {
|
||||
apply(meterState);
|
||||
meterType = AmsTypeLandisGyr;
|
||||
this->packageTimestamp = ctx.timestamp;
|
||||
|
||||
Lng2Data_3p_0b* d = (Lng2Data_3p_0b*) payload;
|
||||
this->l1voltage = ntohs(d->u1.data);
|
||||
this->l2voltage = ntohs(d->u2.data);
|
||||
this->l3voltage = ntohs(d->u3.data);
|
||||
|
||||
2
lib/SvelteUi/app/dist/index.css
vendored
2
lib/SvelteUi/app/dist/index.css
vendored
File diff suppressed because one or more lines are too long
12
lib/SvelteUi/app/dist/index.js
vendored
12
lib/SvelteUi/app/dist/index.js
vendored
File diff suppressed because one or more lines are too long
@ -188,3 +188,22 @@ svg {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
border: 1px solid #ddd;
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.tooltip::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
margin-left: -9px;
|
||||
border-width: 9px;
|
||||
border-style: solid;
|
||||
border-color: #ddd transparent transparent transparent;
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import { Link } from "svelte-navigator";
|
||||
import { tooltip } from './tooltip';
|
||||
|
||||
export let config;
|
||||
|
||||
@ -61,7 +62,7 @@
|
||||
{#if !isNaN(yScale(tick.value))}
|
||||
<g class="tick tick-{tick.value} tick-{tick.color}" transform="translate(0, {yScale(tick.value)})">
|
||||
<line x2="100%"></line>
|
||||
<text y="-4" x={tick.align == 'right' ? '85%' : ''}>{tick.label}</text>
|
||||
<text y="-4" x={tick.align == 'right' ? '90%' : ''}>{tick.label}</text>
|
||||
</g>
|
||||
{/if}
|
||||
{/each}
|
||||
@ -83,7 +84,7 @@
|
||||
<g class='bars'>
|
||||
{#each config.points as point, i}
|
||||
{#if !isNaN(xScale(i)) && !isNaN(yScale(point.value))}
|
||||
<g>
|
||||
<g data-title="{point.title}" use:tooltip>
|
||||
{#if point.value !== undefined}
|
||||
<rect
|
||||
x="{xScale(i) + 2}"
|
||||
@ -102,9 +103,6 @@
|
||||
transform="translate({xScale(i) + barWidth/2} {yScale(point.value) > yScale(0) - labelOffset ? yScale(point.value) - labelOffset : yScale(point.value) + 10}) rotate({point.labelAngle ? point.labelAngle : barWidth < vertSwitch ? 90 : 0})"
|
||||
|
||||
>{point.label}</text>
|
||||
{#if point.title}
|
||||
<title>{point.title}</title>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</g>
|
||||
|
||||
@ -128,7 +128,7 @@
|
||||
{/if}
|
||||
{#if uiVisibility(sysinfo.ui.t, data.pr && (data.pr.startsWith("NO") || data.pr.startsWith("10YNO") || data.pr.startsWith('10Y1001A1001A4')))}
|
||||
<div class="cnt h-64">
|
||||
<TariffPeakChart title={translations.dashboard?.tariffpeak ?? "Tariff peaks"} tariffData={tariffData} translations={translations}/>
|
||||
<TariffPeakChart title={translations.dashboard?.tariffpeak ?? "Tariff peaks"} tariffData={tariffData} realtime={data.ea} translations={translations}/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if uiVisibility(sysinfo.ui.l, data.hm == 1)}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import { zeropad } from './Helpers.js';
|
||||
import { ampcol, zeropad } from './Helpers.js';
|
||||
import BarChart from './BarChart.svelte';
|
||||
|
||||
export let title;
|
||||
@ -12,6 +12,7 @@
|
||||
let min = 0;
|
||||
|
||||
export let tariffData;
|
||||
export let realtime;
|
||||
|
||||
$: {
|
||||
let i = 0;
|
||||
@ -24,16 +25,37 @@
|
||||
label: 0
|
||||
});
|
||||
|
||||
console.log(realtime);
|
||||
|
||||
if(tariffData && !isNaN(realtime?.h?.u)) {
|
||||
points.push({
|
||||
label: realtime.h.u.toFixed(2),
|
||||
value: realtime.h.u,
|
||||
title: realtime.h.u.toFixed(2) + ' kWh',
|
||||
color: ampcol(realtime.h.u/tariffData.c*100.0)
|
||||
});
|
||||
xTicks.push({
|
||||
label: translations.common?.now ?? "Now"
|
||||
});
|
||||
}
|
||||
|
||||
if(tariffData && tariffData.p) {
|
||||
for(i = 0; i < tariffData.p.length; i++) {
|
||||
let peak = tariffData.p[i];
|
||||
let daylabel = peak.d > 0 ? zeropad(peak.d) + "." + (translations.months ? translations.months?.[new Date().getMonth()] : zeropad(new Date().getMonth()+1)) : "-";
|
||||
let title = daylabel;
|
||||
if(!isNaN(peak.h))
|
||||
title = title + ' ' + zeropad(peak.h) + ':00';
|
||||
title = title + ': ' + peak.v.toFixed(2) + ' kWh';
|
||||
points.push({
|
||||
label: peak.v.toFixed(2),
|
||||
title: peak.v.toFixed(2) + ' kWh',
|
||||
value: peak.v,
|
||||
title: title,
|
||||
color: dark ? '#5c2da5' : '#7c3aed'
|
||||
});
|
||||
xTicks.push({
|
||||
label: peak.d > 0 ? zeropad(peak.d) + "." + (translations.months ? translations.months?.[new Date().getMonth()] : zeropad(new Date().getMonth()+1)) : "-"
|
||||
label: daylabel
|
||||
})
|
||||
max = Math.max(max, peak.v);
|
||||
}
|
||||
@ -71,7 +93,7 @@
|
||||
config = {
|
||||
title: title,
|
||||
dark: document.documentElement.classList.contains('dark'),
|
||||
padding: { top: 20, right: 35, bottom: 20, left: 35 },
|
||||
padding: { top: 20, right: 20, bottom: 20, left: 20 },
|
||||
y: {
|
||||
min: min,
|
||||
max: max,
|
||||
|
||||
14
lib/SvelteUi/app/src/lib/Tooltip.svelte
Normal file
14
lib/SvelteUi/app/src/lib/Tooltip.svelte
Normal file
@ -0,0 +1,14 @@
|
||||
<script>
|
||||
export let title;
|
||||
export let x;
|
||||
export let y;
|
||||
|
||||
let width;
|
||||
let height;
|
||||
</script>
|
||||
<div
|
||||
class="tooltip"
|
||||
style="top: {y - height - 10}px; left: {x - (width / 2)}px;"
|
||||
bind:clientHeight={height}
|
||||
bind:clientWidth={width}
|
||||
>{title}</div>
|
||||
41
lib/SvelteUi/app/src/lib/tooltip.js
Normal file
41
lib/SvelteUi/app/src/lib/tooltip.js
Normal file
@ -0,0 +1,41 @@
|
||||
import Tooltip from './Tooltip.svelte';
|
||||
|
||||
export function tooltip(element) {
|
||||
let title;
|
||||
let tooltipComponent;
|
||||
|
||||
function click(event) {
|
||||
if(tooltipComponent) tooltipComponent.$destroy();
|
||||
|
||||
title = element.dataset.title || element.getAttribute('title');
|
||||
var rect = element.getBoundingClientRect();
|
||||
|
||||
tooltipComponent = new Tooltip({
|
||||
props: {
|
||||
title: title,
|
||||
x: rect.left + window.scrollX + (rect.width / 2),
|
||||
y: rect.top + window.scrollY,
|
||||
},
|
||||
target: document.body,
|
||||
});
|
||||
}
|
||||
|
||||
function mouseLeave() {
|
||||
if(tooltipComponent) {
|
||||
setTimeout(() => {
|
||||
tooltipComponent.$destroy();
|
||||
tooltipComponent = null;
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
element.addEventListener('click', click);
|
||||
element.addEventListener('mouseleave', mouseLeave);
|
||||
|
||||
return {
|
||||
destroy() {
|
||||
element.removeEventListener('click', click);
|
||||
element.removeEventListener('mouseleave', mouseLeave);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@
|
||||
#include "FirmwareVersion.h"
|
||||
#include "base64.h"
|
||||
#include "hexutils.h"
|
||||
#include "AmsJsonGenerator.h"
|
||||
|
||||
#include "html/index_html.h"
|
||||
#include "html/index_css.h"
|
||||
@ -682,12 +683,7 @@ void AmsWebServer::dayplotJson() {
|
||||
if(ds == NULL) {
|
||||
notFound();
|
||||
} else {
|
||||
uint16_t pos = snprintf_P(buf, BufferSize, PSTR("{\"unit\":\"kwh\""));
|
||||
for(uint8_t i = 0; i < 24; i++) {
|
||||
pos += snprintf_P(buf+pos, BufferSize-pos, PSTR(",\"i%02d\":%.3f,\"e%02d\":%.3f"), i, ds->getHourImport(i) / 1000.0, i, ds->getHourExport(i) / 1000.0);
|
||||
}
|
||||
snprintf_P(buf+pos, BufferSize-pos, PSTR("}"));
|
||||
|
||||
AmsJsonGenerator::generateDayPlotJson(ds, buf, BufferSize);
|
||||
addConditionalCloudHeaders();
|
||||
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
|
||||
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
|
||||
@ -705,12 +701,7 @@ void AmsWebServer::monthplotJson() {
|
||||
if(ds == NULL) {
|
||||
notFound();
|
||||
} else {
|
||||
uint16_t pos = snprintf_P(buf, BufferSize, PSTR("{\"unit\":\"kwh\""));
|
||||
for(uint8_t i = 1; i < 32; i++) {
|
||||
pos += snprintf_P(buf+pos, BufferSize-pos, PSTR(",\"i%02d\":%.3f,\"e%02d\":%.3f"), i, ds->getDayImport(i) / 1000.0, i, ds->getDayExport(i) / 1000.0);
|
||||
}
|
||||
snprintf_P(buf+pos, BufferSize-pos, PSTR("}"));
|
||||
|
||||
AmsJsonGenerator::generateMonthPlotJson(ds, buf, BufferSize);
|
||||
addConditionalCloudHeaders();
|
||||
server.sendHeader(HEADER_CACHE_CONTROL, CACHE_CONTROL_NO_CACHE);
|
||||
server.sendHeader(HEADER_PRAGMA, PRAGMA_NO_CACHE);
|
||||
@ -2119,8 +2110,9 @@ void AmsWebServer::tariffJson() {
|
||||
String peaks;
|
||||
for(uint8_t x = 0;x < min((uint8_t) 5, eac->hours); x++) {
|
||||
EnergyAccountingPeak peak = ea->getPeak(x+1);
|
||||
int len = snprintf_P(buf, BufferSize, PSTR("{\"d\":%d,\"v\":%.2f}"),
|
||||
int len = snprintf_P(buf, BufferSize, PSTR("{\"d\":%d,\"h\":%d,\"v\":%.2f}"),
|
||||
peak.day,
|
||||
peak.hour,
|
||||
peak.value / 100.0
|
||||
);
|
||||
buf[len] = '\0';
|
||||
@ -2618,7 +2610,7 @@ void AmsWebServer::configFileDownload() {
|
||||
EnergyAccountingConfig eac;
|
||||
config->getEnergyAccountingConfig(eac);
|
||||
EnergyAccountingData ead = ea->getData();
|
||||
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("energyaccounting %d %d %.2f %.2f %.2f %.2f %.2f %.2f %d %.2f %d %.2f %d %.2f %d %.2f %d %.2f %.2f %.2f"),
|
||||
server.sendContent(buf, snprintf_P(buf, BufferSize, PSTR("energyaccounting %d %d %.2f %.2f %.2f %.2f %.2f %.2f %d %d %.2f %d %d %.2f %d %d %.2f %d %d %.2f %d %d %.2f %.2f %.2f"),
|
||||
ead.version,
|
||||
ead.month,
|
||||
ea->getCostYesterday(),
|
||||
@ -2628,14 +2620,19 @@ void AmsWebServer::configFileDownload() {
|
||||
ea->getIncomeThisMonth(),
|
||||
ea->getIncomeLastMonth(),
|
||||
ead.peaks[0].day,
|
||||
ead.peaks[0].hour,
|
||||
ead.peaks[0].value / 100.0,
|
||||
ead.peaks[1].day,
|
||||
ead.peaks[1].hour,
|
||||
ead.peaks[1].value / 100.0,
|
||||
ead.peaks[2].day,
|
||||
ead.peaks[2].hour,
|
||||
ead.peaks[2].value / 100.0,
|
||||
ead.peaks[3].day,
|
||||
ead.peaks[3].hour,
|
||||
ead.peaks[3].value / 100.0,
|
||||
ead.peaks[4].day,
|
||||
ead.peaks[4].hour,
|
||||
ead.peaks[4].value / 100.0,
|
||||
ea->getUseLastMonth(),
|
||||
ea->getProducedLastMonth()
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
extra_configs = platformio-user.ini
|
||||
|
||||
[common]
|
||||
lib_deps = EEPROM, LittleFS, DNSServer, 256dpi/MQTT@2.5.2, OneWireNg@0.13.3, DallasTemperature@4.0.4, https://github.com/gskjold/RemoteDebug.git, PaulStoffregen/Time@1.6.1, JChristensen/Timezone@1.2.4, bblanchon/ArduinoJson@7.0.4, FirmwareVersion, AmsConfiguration, AmsData, AmsDataStorage, HwTools, Uptime, AmsDecoder, PriceService, EnergyAccounting, AmsFirmwareUpdater, AmsMqttHandler, RawMqttHandler, JsonMqttHandler, DomoticzMqttHandler, HomeAssistantMqttHandler, PassthroughMqttHandler, RealtimePlot, ConnectionHandler, MeterCommunicators
|
||||
lib_deps = EEPROM, LittleFS, DNSServer, 256dpi/MQTT@2.5.2, OneWireNg@0.13.3, DallasTemperature@4.0.4, https://github.com/gskjold/RemoteDebug.git, PaulStoffregen/Time@1.6.1, JChristensen/Timezone@1.2.4, bblanchon/ArduinoJson@7.0.4, FirmwareVersion, AmsConfiguration, AmsData, AmsDataStorage, HwTools, Uptime, AmsDecoder, PriceService, EnergyAccounting, AmsFirmwareUpdater, AmsJsonGenerator, AmsMqttHandler, RawMqttHandler, JsonMqttHandler, DomoticzMqttHandler, HomeAssistantMqttHandler, PassthroughMqttHandler, RealtimePlot, ConnectionHandler, MeterCommunicators
|
||||
lib_ignore = OneWire
|
||||
extra_scripts =
|
||||
pre:scripts/addversion.py
|
||||
|
||||
@ -215,7 +215,6 @@ void connectToNetwork();
|
||||
void toggleSetupMode();
|
||||
void postConnect();
|
||||
void MQTT_connect();
|
||||
void handleNtpChange();
|
||||
void handleDataSuccess(AmsData* data);
|
||||
void handleTemperature(unsigned long now);
|
||||
void handleSystem(unsigned long now);
|
||||
@ -223,11 +222,27 @@ void handleButton(unsigned long now);
|
||||
void handlePriceService(unsigned long now);
|
||||
void handleClear(unsigned long now);
|
||||
void handleUiLanguage();
|
||||
void handleEnergyAccountingChanged();
|
||||
bool handleVoltageCheck();
|
||||
void handleEnergyAccounting();
|
||||
bool readHanPort();
|
||||
void errorBlink();
|
||||
|
||||
void handleUpdater();
|
||||
char ntpServerName[64] = "";
|
||||
void handleNtp();
|
||||
#if defined(ESP8266)
|
||||
void handleMdns();
|
||||
#endif
|
||||
#if defined(ESP32) && defined(ENERGY_SPEEDOMETER_PASS)
|
||||
void handleEnergySpeedometer();
|
||||
#endif
|
||||
#if defined(AMS_CLOUD)
|
||||
void handleCloud();
|
||||
#endif
|
||||
void handleMqtt();
|
||||
void handleWebserver();
|
||||
void handleSmartConfig();
|
||||
void handleMeterConfig();
|
||||
|
||||
uint8_t pulses = 0;
|
||||
void onPulse();
|
||||
|
||||
@ -498,7 +513,6 @@ void setup() {
|
||||
if(config.hasConfig()) {
|
||||
config.print(&Debug);
|
||||
connectToNetwork();
|
||||
handleNtpChange();
|
||||
ds.load();
|
||||
} else {
|
||||
debugI_P(PSTR("No configuration, booting AP"));
|
||||
@ -550,6 +564,9 @@ uint64_t lastErrorBlink = 0;
|
||||
unsigned long lastVoltageCheck = 0;
|
||||
int lastError = 0;
|
||||
|
||||
bool vccLevel1 = true;
|
||||
bool vccLevel2 = true;
|
||||
|
||||
void loop() {
|
||||
unsigned long now = millis();
|
||||
unsigned long start = now;
|
||||
@ -567,7 +584,7 @@ void loop() {
|
||||
errorBlink();
|
||||
}
|
||||
|
||||
// Only do normal stuff if we're not booted as AP
|
||||
// Only do normal stuff if we're not booted into setup mode
|
||||
if (!setupMode) {
|
||||
if (ch != NULL && !ch->isConnected()) {
|
||||
if(networkConnected) {
|
||||
@ -586,120 +603,50 @@ void loop() {
|
||||
if(!networkConnected) {
|
||||
postConnect();
|
||||
}
|
||||
if(config.isNtpChanged()) {
|
||||
handleNtpChange();
|
||||
}
|
||||
#if defined ESP8266
|
||||
if(mdnsEnabled) {
|
||||
start = millis();
|
||||
MDNS.update();
|
||||
end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to update mDNS"), millis()-start);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mqttEnabled || config.isMqttChanged()) {
|
||||
if(mqttHandler == NULL || !mqttHandler->connected() || config.isMqttChanged()) {
|
||||
if(mqttHandler != NULL && config.isMqttChanged()) {
|
||||
mqttHandler->disconnect();
|
||||
}
|
||||
MQTT_connect();
|
||||
config.ackMqttChange();
|
||||
}
|
||||
} else if(mqttHandler != NULL) {
|
||||
mqttHandler->disconnect();
|
||||
}
|
||||
handleUpdater();
|
||||
|
||||
#if defined(ESP32) && defined(ENERGY_SPEEDOMETER_PASS)
|
||||
if(sysConfig.energyspeedometer == 7) {
|
||||
if(!meterState.getMeterId().isEmpty()) {
|
||||
if(energySpeedometer == NULL) {
|
||||
uint16_t chipId;
|
||||
#if defined(ESP32)
|
||||
chipId = ( ESP.getEfuseMac() >> 32 ) % 0xFFFFFFFF;
|
||||
#else
|
||||
chipId = ESP.getChipId();
|
||||
#endif
|
||||
strcpy(energySpeedometerConfig.clientId, (String("ams") + String(chipId, HEX)).c_str());
|
||||
energySpeedometer = new JsonMqttHandler(energySpeedometerConfig, &Debug, (char*) commonBuffer, &hw, &updater);
|
||||
energySpeedometer->setCaVerification(false);
|
||||
}
|
||||
if(!energySpeedometer->connected()) {
|
||||
lwmqtt_err_t err = energySpeedometer->lastError();
|
||||
if(err > 0)
|
||||
debugE_P(PSTR("Energyspeedometer connector reporting error (%d)"), err);
|
||||
energySpeedometer->connect();
|
||||
energySpeedometer->publishSystem(&hw, ps, &ea);
|
||||
}
|
||||
energySpeedometer->loop();
|
||||
delay(10);
|
||||
}
|
||||
} else if(energySpeedometer != NULL) {
|
||||
if(energySpeedometer->connected()) {
|
||||
energySpeedometer->disconnect();
|
||||
energySpeedometer->loop();
|
||||
} else {
|
||||
delete energySpeedometer;
|
||||
energySpeedometer = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
try {
|
||||
// Only do these tasks if we have super-smooth voltage
|
||||
if(hw.isVoltageOptimal(0.1)) {
|
||||
handleNtp();
|
||||
#if defined(ESP8266)
|
||||
handleMdns();
|
||||
#endif
|
||||
#if defined(ESP32)
|
||||
#if defined(ENERGY_SPEEDOMETER_PASS)
|
||||
handleEnergySpeedometer();
|
||||
#endif
|
||||
#endif
|
||||
handlePriceService(now);
|
||||
} catch(const std::exception& e) {
|
||||
debugE_P(PSTR("Exception in PriceService loop (%s)"), e.what());
|
||||
}
|
||||
start = millis();
|
||||
ws.loop();
|
||||
end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to handle web"), millis()-start);
|
||||
#if defined(AMS_CLOUD)
|
||||
handleCloud();
|
||||
#endif
|
||||
handleUiLanguage();
|
||||
vccLevel1 = true;
|
||||
} else if(vccLevel1) {
|
||||
vccLevel1 = false;
|
||||
debugW_P(PSTR("Vcc below level 1"));
|
||||
}
|
||||
|
||||
if(mqttHandler != NULL) {
|
||||
start = millis();
|
||||
mqttHandler->loop();
|
||||
delay(10); // Needed to preserve power. After adding this, the voltage is super smooth on a HAN powered device
|
||||
end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to handle mqtt"), millis()-start);
|
||||
// Only do these task if we have smooth voltage
|
||||
if(hw.isVoltageOptimal(0.2)) {
|
||||
handleMqtt();
|
||||
vccLevel2 = true;
|
||||
} else if(vccLevel2) {
|
||||
vccLevel2 = false;
|
||||
debugW_P(PSTR("Vcc below level 2"));
|
||||
}
|
||||
|
||||
handleWebserver();
|
||||
|
||||
#if defined(ESP32)
|
||||
// At this point, if the voltage is not optimal, disconnect from WiFi to preserve power
|
||||
if(!hw.isVoltageOptimal(0.35)) {
|
||||
if(WiFi.getMode() == WIFI_STA) {
|
||||
debugW_P(PSTR("Vcc dropped below limit, disconnecting WiFi for 5 seconds to preserve power"));
|
||||
ch->disconnect(5000);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_CLOUDCONNECTOR_H)
|
||||
if(config.isCloudChanged()) {
|
||||
CloudConfig cc;
|
||||
if(config.getCloudConfig(cc) && cc.enabled) {
|
||||
if(cloud == NULL) {
|
||||
cloud = new CloudConnector(&Debug);
|
||||
}
|
||||
NtpConfig ntp;
|
||||
config.getNtpConfig(ntp);
|
||||
if(cloud->setup(cc, meterConfig, sysConfig, ntp, &hw, &rdc, ps)) {
|
||||
config.setCloudConfig(cc);
|
||||
}
|
||||
cloud->setConnectionHandler(ch);
|
||||
|
||||
PriceServiceConfig price;
|
||||
config.getPriceServiceConfig(price);
|
||||
cloud->setPriceConfig(price);
|
||||
|
||||
EnergyAccountingConfig *eac = ea.getConfig();
|
||||
cloud->setEnergyAccountingConfig(*eac);
|
||||
|
||||
ws.setCloud(cloud);
|
||||
} else if(cloud != NULL) {
|
||||
delete cloud;
|
||||
cloud = NULL;
|
||||
}
|
||||
config.ackCloudConfig();
|
||||
}
|
||||
if(cloud != NULL) {
|
||||
cloud->update(meterState, ea);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ZMART_CHARGE)
|
||||
@ -760,45 +707,255 @@ void loop() {
|
||||
debugW_P(PSTR("Used %dms to handle firmware updater"), end-start);
|
||||
}
|
||||
}
|
||||
#if defined(ESP32)
|
||||
if(now - lastVoltageCheck > 1000) {
|
||||
start = millis();
|
||||
handleVoltageCheck();
|
||||
end = millis();
|
||||
lastVoltageCheck = now;
|
||||
if(end-start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to handle language update"), end-start);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if(WiFi.smartConfigDone()) {
|
||||
debugI_P(PSTR("Smart config DONE!"));
|
||||
|
||||
NetworkConfig network;
|
||||
config.getNetworkConfig(network);
|
||||
strcpy(network.ssid, WiFi.SSID().c_str());
|
||||
strcpy(network.psk, WiFi.psk().c_str());
|
||||
network.mode = 1;
|
||||
network.mdns = true;
|
||||
config.setNetworkConfig(network);
|
||||
|
||||
SystemConfig sys;
|
||||
config.getSystemConfig(sys);
|
||||
sys.userConfigured = true;
|
||||
sys.dataCollectionConsent = 0;
|
||||
config.setSystemConfig(sys);
|
||||
config.save();
|
||||
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
}
|
||||
handleSmartConfig();
|
||||
if(dnsServer != NULL) {
|
||||
dnsServer->processNextRequest();
|
||||
}
|
||||
ws.loop();
|
||||
}
|
||||
|
||||
handleMeterConfig();
|
||||
handleEnergyAccounting();
|
||||
|
||||
try {
|
||||
start = millis();
|
||||
if(readHanPort() || now - meterState.getLastUpdateMillis() > 30000) {
|
||||
end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to read HAN port (true)"), millis()-start);
|
||||
}
|
||||
if(hw.isVoltageOptimal(0.1)) {
|
||||
handleTemperature(now);
|
||||
handleSystem(now);
|
||||
}
|
||||
hw.setBootSuccessful(true);
|
||||
} else {
|
||||
end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to read HAN port (false)"), millis()-start);
|
||||
}
|
||||
}
|
||||
if(millis() - meterState.getLastUpdateMillis() > 1800000 && !ds.isHappy(time(nullptr))) {
|
||||
handleClear(now);
|
||||
}
|
||||
} catch(const std::exception& e) {
|
||||
debugE_P(PSTR("Exception in readHanPort (%s)"), e.what());
|
||||
meterState.setLastError(METER_ERROR_EXCEPTION);
|
||||
}
|
||||
|
||||
delay(10); // Needed for auto modem sleep
|
||||
start = millis();
|
||||
#if defined(ESP32)
|
||||
esp_task_wdt_reset();
|
||||
#elif defined(ESP8266)
|
||||
ESP.wdtFeed();
|
||||
#endif
|
||||
yield();
|
||||
|
||||
end = millis();
|
||||
if(end-start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to feed WDT"), end-start);
|
||||
}
|
||||
|
||||
if(end-now > SLOW_PROC_TRIGGER_MS*2) {
|
||||
debugW_P(PSTR("loop() used %dms"), end-now);
|
||||
}
|
||||
}
|
||||
|
||||
void handleUpdater() {
|
||||
unsigned long start = millis();
|
||||
updater.loop();
|
||||
if(updater.isUpgradeInformationChanged()) {
|
||||
UpgradeInformation upinfo;
|
||||
updater.getUpgradeInformation(upinfo);
|
||||
config.setUpgradeInformation(upinfo);
|
||||
updater.ackUpgradeInformationChanged();
|
||||
if(mqttHandler != NULL)
|
||||
mqttHandler->publishFirmware();
|
||||
|
||||
if(upinfo.errorCode == AMS_UPDATE_ERR_SUCCESS_SIGNAL) {
|
||||
debugW_P(PSTR("Rebooting to firmware version %s"), upinfo.toVersion);
|
||||
upinfo.errorCode == AMS_UPDATE_ERR_SUCCESS_CONFIRMED;
|
||||
config.setUpgradeInformation(upinfo);
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
unsigned long end = millis();
|
||||
if(end-start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to handle firmware updater"), end-start);
|
||||
}
|
||||
}
|
||||
|
||||
void handleNtp() {
|
||||
if(config.isNtpChanged()) {
|
||||
NtpConfig ntp;
|
||||
if(config.getNtpConfig(ntp)) {
|
||||
tz = resolveTimezone(ntp.timezone);
|
||||
if(ntp.enable && strlen(ntp.server) > 0) {
|
||||
strcpy(ntpServerName, ntp.server);
|
||||
} else if(ntp.enable) {
|
||||
strcpy(ntpServerName, "pool.ntp.org");
|
||||
} else {
|
||||
memset(ntpServerName, 0, 64);
|
||||
}
|
||||
configTime(tz->toLocal(0), tz->toLocal(JULY1970)-JULY1970, ntpServerName, "", "");
|
||||
sntp_servermode_dhcp(ntp.enable && ntp.dhcp ? 1 : 0); // Not implemented on ESP32?
|
||||
ntpEnabled = ntp.enable;
|
||||
|
||||
ws.setTimezone(tz);
|
||||
ds.setTimezone(tz);
|
||||
ea.setTimezone(tz);
|
||||
ps->setTimezone(tz);
|
||||
}
|
||||
|
||||
config.ackNtpChange();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(ESP8266)
|
||||
void handleMdns() {
|
||||
if(mdnsEnabled) {
|
||||
unsigned long start = millis();
|
||||
MDNS.update();
|
||||
unsigned long end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to update mDNS"), millis()-start);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(ESP32) && defined(ENERGY_SPEEDOMETER_PASS)
|
||||
void handleEnergySpeedometer() {
|
||||
if(sysConfig.energyspeedometer == 7) {
|
||||
if(!meterState.getMeterId().isEmpty()) {
|
||||
if(energySpeedometer == NULL) {
|
||||
uint16_t chipId;
|
||||
#if defined(ESP32)
|
||||
chipId = ( ESP.getEfuseMac() >> 32 ) % 0xFFFFFFFF;
|
||||
#else
|
||||
chipId = ESP.getChipId();
|
||||
#endif
|
||||
strcpy(energySpeedometerConfig.clientId, (String("ams") + String(chipId, HEX)).c_str());
|
||||
energySpeedometer = new JsonMqttHandler(energySpeedometerConfig, &Debug, (char*) commonBuffer, &hw, &ds, &updater);
|
||||
energySpeedometer->setCaVerification(false);
|
||||
}
|
||||
if(!energySpeedometer->connected()) {
|
||||
lwmqtt_err_t err = energySpeedometer->lastError();
|
||||
if(err > 0)
|
||||
debugE_P(PSTR("Energyspeedometer connector reporting error (%d)"), err);
|
||||
energySpeedometer->connect();
|
||||
energySpeedometer->publishSystem(&hw, ps, &ea);
|
||||
}
|
||||
energySpeedometer->loop();
|
||||
delay(10);
|
||||
}
|
||||
} else if(energySpeedometer != NULL) {
|
||||
if(energySpeedometer->connected()) {
|
||||
energySpeedometer->disconnect();
|
||||
energySpeedometer->loop();
|
||||
} else {
|
||||
delete energySpeedometer;
|
||||
energySpeedometer = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(AMS_CLOUD)
|
||||
void handleCloud() {
|
||||
if(config.isCloudChanged()) {
|
||||
CloudConfig cc;
|
||||
if(config.getCloudConfig(cc) && cc.enabled) {
|
||||
if(cloud == NULL) {
|
||||
cloud = new CloudConnector(&Debug);
|
||||
}
|
||||
NtpConfig ntp;
|
||||
config.getNtpConfig(ntp);
|
||||
if(cloud->setup(cc, meterConfig, sysConfig, ntp, &hw, &rdc, ps)) {
|
||||
config.setCloudConfig(cc);
|
||||
}
|
||||
cloud->setConnectionHandler(ch);
|
||||
|
||||
PriceServiceConfig price;
|
||||
config.getPriceServiceConfig(price);
|
||||
cloud->setPriceConfig(price);
|
||||
|
||||
EnergyAccountingConfig *eac = ea.getConfig();
|
||||
cloud->setEnergyAccountingConfig(*eac);
|
||||
|
||||
ws.setCloud(cloud);
|
||||
} else if(cloud != NULL) {
|
||||
delete cloud;
|
||||
cloud = NULL;
|
||||
}
|
||||
config.ackCloudConfig();
|
||||
}
|
||||
if(cloud != NULL) {
|
||||
cloud->update(meterState, ea);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void handleMqtt() {
|
||||
if (mqttEnabled || config.isMqttChanged()) {
|
||||
if(mqttHandler == NULL || !mqttHandler->connected() || config.isMqttChanged()) {
|
||||
if(mqttHandler != NULL && config.isMqttChanged()) {
|
||||
mqttHandler->disconnect();
|
||||
}
|
||||
MQTT_connect();
|
||||
config.ackMqttChange();
|
||||
}
|
||||
} else if(mqttHandler != NULL) {
|
||||
mqttHandler->disconnect();
|
||||
}
|
||||
|
||||
if(mqttHandler != NULL) {
|
||||
unsigned long start = millis();
|
||||
mqttHandler->loop();
|
||||
unsigned long end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to handle mqtt"), millis()-start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleWebserver() {
|
||||
unsigned long start = millis();
|
||||
ws.loop();
|
||||
unsigned long end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to handle web"), millis()-start);
|
||||
}
|
||||
}
|
||||
|
||||
void handleSmartConfig() {
|
||||
if(WiFi.smartConfigDone()) {
|
||||
debugI_P(PSTR("Smart config DONE!"));
|
||||
|
||||
NetworkConfig network;
|
||||
config.getNetworkConfig(network);
|
||||
strcpy(network.ssid, WiFi.SSID().c_str());
|
||||
strcpy(network.psk, WiFi.psk().c_str());
|
||||
network.mode = 1;
|
||||
network.mdns = true;
|
||||
config.setNetworkConfig(network);
|
||||
|
||||
SystemConfig sys;
|
||||
config.getSystemConfig(sys);
|
||||
sys.userConfigured = true;
|
||||
sys.dataCollectionConsent = 0;
|
||||
config.setSystemConfig(sys);
|
||||
config.save();
|
||||
|
||||
delay(1000);
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
void handleMeterConfig() {
|
||||
if(config.isMeterChanged()) {
|
||||
config.getMeterConfig(meterConfig);
|
||||
if(meterConfig.source == METER_SOURCE_GPIO) {
|
||||
@ -872,54 +1029,10 @@ void loop() {
|
||||
ws.setMeterConfig(meterConfig.distributionSystem, meterConfig.mainFuse, meterConfig.productionCapacity);
|
||||
config.ackMeterChanged();
|
||||
}
|
||||
|
||||
if(config.isEnergyAccountingChanged()) {
|
||||
handleEnergyAccountingChanged();
|
||||
}
|
||||
try {
|
||||
start = millis();
|
||||
if(readHanPort() || now - meterState.getLastUpdateMillis() > 30000) {
|
||||
end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to read HAN port (true)"), millis()-start);
|
||||
}
|
||||
handleTemperature(now);
|
||||
handleSystem(now);
|
||||
hw.setBootSuccessful(true);
|
||||
} else {
|
||||
end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to read HAN port (false)"), millis()-start);
|
||||
}
|
||||
}
|
||||
if(millis() - meterState.getLastUpdateMillis() > 1800000 && !ds.isHappy(time(nullptr))) {
|
||||
handleClear(now);
|
||||
}
|
||||
} catch(const std::exception& e) {
|
||||
debugE_P(PSTR("Exception in readHanPort (%s)"), e.what());
|
||||
meterState.setLastError(METER_ERROR_EXCEPTION);
|
||||
}
|
||||
|
||||
delay(10); // Needed for auto modem sleep
|
||||
start = millis();
|
||||
#if defined(ESP32)
|
||||
esp_task_wdt_reset();
|
||||
#elif defined(ESP8266)
|
||||
ESP.wdtFeed();
|
||||
#endif
|
||||
yield();
|
||||
|
||||
end = millis();
|
||||
if(end-start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to feed WDT"), end-start);
|
||||
}
|
||||
|
||||
if(end-now > SLOW_PROC_TRIGGER_MS*2) {
|
||||
debugW_P(PSTR("loop() used %dms"), end-now);
|
||||
}
|
||||
}
|
||||
|
||||
void handleUiLanguage() {
|
||||
unsigned long start = millis();
|
||||
if(config.isUiLanguageChanged()) {
|
||||
debugD_P(PSTR("Language has changed"));
|
||||
if(LittleFS.begin()) {
|
||||
@ -969,6 +1082,10 @@ void handleUiLanguage() {
|
||||
|
||||
config.ackUiLanguageChange();
|
||||
}
|
||||
unsigned long end = millis();
|
||||
if(end-start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to handle language update"), end-start);
|
||||
}
|
||||
}
|
||||
|
||||
void handleClear(unsigned long now) {
|
||||
@ -982,42 +1099,18 @@ void handleClear(unsigned long now) {
|
||||
}
|
||||
}
|
||||
|
||||
void handleEnergyAccountingChanged() {
|
||||
EnergyAccountingConfig *eac = ea.getConfig();
|
||||
config.getEnergyAccountingConfig(*eac);
|
||||
ea.setup(&ds, eac);
|
||||
config.ackEnergyAccountingChange();
|
||||
#if defined(_CLOUDCONNECTOR_H)
|
||||
if(cloud != NULL) {
|
||||
cloud->setEnergyAccountingConfig(*eac);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
char ntpServerName[64] = "";
|
||||
|
||||
void handleNtpChange() {
|
||||
NtpConfig ntp;
|
||||
if(config.getNtpConfig(ntp)) {
|
||||
tz = resolveTimezone(ntp.timezone);
|
||||
if(ntp.enable && strlen(ntp.server) > 0) {
|
||||
strcpy(ntpServerName, ntp.server);
|
||||
} else if(ntp.enable) {
|
||||
strcpy(ntpServerName, "pool.ntp.org");
|
||||
} else {
|
||||
memset(ntpServerName, 0, 64);
|
||||
void handleEnergyAccounting() {
|
||||
if(config.isEnergyAccountingChanged()) {
|
||||
EnergyAccountingConfig *eac = ea.getConfig();
|
||||
config.getEnergyAccountingConfig(*eac);
|
||||
ea.setup(&ds, eac);
|
||||
config.ackEnergyAccountingChange();
|
||||
#if defined(AMS_CLOUD)
|
||||
if(cloud != NULL) {
|
||||
cloud->setEnergyAccountingConfig(*eac);
|
||||
}
|
||||
configTime(tz->toLocal(0), tz->toLocal(JULY1970)-JULY1970, ntpServerName, "", "");
|
||||
sntp_servermode_dhcp(ntp.enable && ntp.dhcp ? 1 : 0); // Not implemented on ESP32?
|
||||
ntpEnabled = ntp.enable;
|
||||
|
||||
ws.setTimezone(tz);
|
||||
ds.setTimezone(tz);
|
||||
ea.setTimezone(tz);
|
||||
ps->setTimezone(tz);
|
||||
#endif
|
||||
}
|
||||
|
||||
config.ackNtpChange();
|
||||
}
|
||||
|
||||
void handleSystem(unsigned long now) {
|
||||
@ -1058,17 +1151,6 @@ void handleSystem(unsigned long now) {
|
||||
}
|
||||
}
|
||||
|
||||
bool handleVoltageCheck() {
|
||||
if(!hw.isVoltageOptimal()) {
|
||||
if(WiFi.getMode() == WIFI_STA) {
|
||||
debugW_P(PSTR("Vcc dropped below limit, disconnecting WiFi for 5 seconds to preserve power"));
|
||||
ch->disconnect(5000);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void handleTemperature(unsigned long now) {
|
||||
unsigned long start, end;
|
||||
if(now - lastTemperatureRead > 15000) {
|
||||
@ -1088,51 +1170,55 @@ void handleTemperature(unsigned long now) {
|
||||
}
|
||||
|
||||
void handlePriceService(unsigned long now) {
|
||||
unsigned long start, end;
|
||||
if(ps != NULL && ntpEnabled) {
|
||||
start = millis();
|
||||
if(ps->loop() && mqttHandler != NULL) {
|
||||
end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to update prices"), millis()-start);
|
||||
}
|
||||
|
||||
try {
|
||||
unsigned long start, end;
|
||||
if(ps != NULL && ntpEnabled) {
|
||||
start = millis();
|
||||
mqttHandler->publishPrices(ps);
|
||||
end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to publish prices to MQTT"), millis()-start);
|
||||
}
|
||||
} else {
|
||||
end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to handle price API"), millis()-start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(config.isPriceServiceChanged()) {
|
||||
PriceServiceConfig price;
|
||||
if(config.getPriceServiceConfig(price) && price.enabled && strlen(price.area) > 0) {
|
||||
if(ps == NULL) {
|
||||
ps = new PriceService(&Debug);
|
||||
ea.setPriceService(ps);
|
||||
ws.setPriceService(ps);
|
||||
#if defined(_CLOUDCONNECTOR_H)
|
||||
if(cloud != NULL) {
|
||||
cloud->setPriceConfig(price);
|
||||
if(ps->loop() && mqttHandler != NULL) {
|
||||
end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to update prices"), millis()-start);
|
||||
}
|
||||
|
||||
start = millis();
|
||||
mqttHandler->publishPrices(ps);
|
||||
end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to publish prices to MQTT"), millis()-start);
|
||||
}
|
||||
} else {
|
||||
end = millis();
|
||||
if(end - start > SLOW_PROC_TRIGGER_MS) {
|
||||
debugW_P(PSTR("Used %dms to handle price API"), millis()-start);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
ps->setup(price);
|
||||
} else if(ps != NULL) {
|
||||
delete ps;
|
||||
ps = NULL;
|
||||
ws.setPriceService(NULL);
|
||||
}
|
||||
ws.setPriceSettings(price.area, price.currency);
|
||||
config.ackPriceServiceChange();
|
||||
ea.setCurrency(price.currency);
|
||||
|
||||
if(config.isPriceServiceChanged()) {
|
||||
PriceServiceConfig price;
|
||||
if(config.getPriceServiceConfig(price) && price.enabled && strlen(price.area) > 0) {
|
||||
if(ps == NULL) {
|
||||
ps = new PriceService(&Debug);
|
||||
ea.setPriceService(ps);
|
||||
ws.setPriceService(ps);
|
||||
#if defined(_CLOUDCONNECTOR_H)
|
||||
if(cloud != NULL) {
|
||||
cloud->setPriceConfig(price);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
ps->setup(price);
|
||||
} else if(ps != NULL) {
|
||||
delete ps;
|
||||
ps = NULL;
|
||||
ws.setPriceService(NULL);
|
||||
}
|
||||
ws.setPriceSettings(price.area, price.currency);
|
||||
config.ackPriceServiceChange();
|
||||
ea.setCurrency(price.currency);
|
||||
}
|
||||
} catch(const std::exception& e) {
|
||||
debugE_P(PSTR("Exception in PriceService loop (%s)"), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1204,7 +1290,7 @@ void connectToNetwork() {
|
||||
return;
|
||||
}
|
||||
lastConnectRetry = millis();
|
||||
if(!handleVoltageCheck()) {
|
||||
if(!hw.isVoltageOptimal(0.1) && (millis() - lastConnectRetry) < 60000) {
|
||||
debugW_P(PSTR("Voltage is not high enough to reconnect"));
|
||||
return;
|
||||
}
|
||||
@ -1358,20 +1444,18 @@ void handleDataSuccess(AmsData* data) {
|
||||
if(!setupMode && !hw.ledBlink(LED_GREEN, 1))
|
||||
hw.ledBlink(LED_INTERNAL, 1);
|
||||
|
||||
if(mqttHandler != NULL) {
|
||||
if(mqttHandler != NULL && hw.isVoltageOptimal(0.2)) {
|
||||
#if defined(ESP32)
|
||||
esp_task_wdt_reset();
|
||||
#elif defined(ESP8266)
|
||||
ESP.wdtFeed();
|
||||
#endif
|
||||
yield();
|
||||
if(mqttHandler->publish(data, &meterState, &ea, ps)) {
|
||||
delay(10);
|
||||
}
|
||||
mqttHandler->publish(data, &meterState, &ea, ps);
|
||||
}
|
||||
#if defined(ESP32) && defined(ENERGY_SPEEDOMETER_PASS)
|
||||
if(energySpeedometer != NULL && energySpeedometer->publish(&meterState, &meterState, &ea, ps)) {
|
||||
delay(10);
|
||||
if(energySpeedometer != NULL && hw.isVoltageOptimal(0.1)) {
|
||||
energySpeedometer->publish(&meterState, &meterState, &ea, ps);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1546,8 +1630,10 @@ void MQTT_connect() {
|
||||
case 4: {
|
||||
HomeAssistantConfig haconf;
|
||||
config.getHomeAssistantConfig(haconf);
|
||||
NetworkConfig network;
|
||||
ch->getCurrentConfig(network);
|
||||
HomeAssistantMqttHandler* hamh = (HomeAssistantMqttHandler*) &mqttHandler;
|
||||
hamh->setHomeAssistantConfig(haconf);
|
||||
hamh->setHomeAssistantConfig(haconf, network.hostname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1559,7 +1645,7 @@ void MQTT_connect() {
|
||||
case 0:
|
||||
case 5:
|
||||
case 6:
|
||||
mqttHandler = new JsonMqttHandler(mqttConfig, &Debug, (char*) commonBuffer, &hw, &updater);
|
||||
mqttHandler = new JsonMqttHandler(mqttConfig, &Debug, (char*) commonBuffer, &hw, &ds, &updater);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
@ -1573,7 +1659,9 @@ void MQTT_connect() {
|
||||
case 4:
|
||||
HomeAssistantConfig haconf;
|
||||
config.getHomeAssistantConfig(haconf);
|
||||
mqttHandler = new HomeAssistantMqttHandler(mqttConfig, &Debug, (char*) commonBuffer, sysConfig.boardType, haconf, &hw, &updater);
|
||||
NetworkConfig network;
|
||||
ch->getCurrentConfig(network);
|
||||
mqttHandler = new HomeAssistantMqttHandler(mqttConfig, &Debug, (char*) commonBuffer, sysConfig.boardType, haconf, &hw, &updater, network.hostname);
|
||||
break;
|
||||
case 255:
|
||||
mqttHandler = new PassthroughMqttHandler(mqttConfig, &Debug, (char*) commonBuffer, &updater);
|
||||
@ -2084,11 +2172,11 @@ void configFileParse() {
|
||||
0, 0, 0, // Cost
|
||||
0, 0, 0, // Income
|
||||
0, 0, 0, // Last month import, export and accuracy
|
||||
0, 0, // Peak 1
|
||||
0, 0, // Peak 2
|
||||
0, 0, // Peak 3
|
||||
0, 0, // Peak 4
|
||||
0, 0 // Peak 5
|
||||
0, 0, 0, // Peak 1
|
||||
0, 0, 0, // Peak 2
|
||||
0, 0, 0, // Peak 3
|
||||
0, 0, 0, // Peak 4
|
||||
0, 0, 0 // Peak 5
|
||||
};
|
||||
uint8_t peak = 0;
|
||||
uint64_t totalImport = 0, totalExport = 0;
|
||||
@ -2104,7 +2192,7 @@ void configFileParse() {
|
||||
} else if(i == 2) {
|
||||
float val = String(pch).toFloat();
|
||||
if(val > 0.0) {
|
||||
ead.peaks[0] = { 1, (uint16_t) (val*100) };
|
||||
ead.peaks[0] = { 1, 0, (uint16_t) (val*100) };
|
||||
}
|
||||
} else if(i == 3) {
|
||||
float val = String(pch).toFloat();
|
||||
@ -2116,7 +2204,6 @@ void configFileParse() {
|
||||
float val = String(pch).toFloat();
|
||||
ead.costLastMonth = val * 100;
|
||||
} else if(i >= 6 && i < 18) {
|
||||
uint8_t hour = i-6;
|
||||
{
|
||||
long val = String(pch).toInt();
|
||||
ead.peaks[peak].day = val;
|
||||
@ -2129,6 +2216,47 @@ void configFileParse() {
|
||||
}
|
||||
peak++;
|
||||
}
|
||||
} else if(ead.version < 7) {
|
||||
if(i == 1) {
|
||||
long val = String(pch).toInt();
|
||||
ead.month = val;
|
||||
} else if(i == 2) {
|
||||
float val = String(pch).toFloat();
|
||||
ead.costYesterday = val * 100;
|
||||
} else if(i == 3) {
|
||||
float val = String(pch).toFloat();
|
||||
ead.costThisMonth = val * 100;
|
||||
} else if(i == 4) {
|
||||
float val = String(pch).toFloat();
|
||||
ead.costLastMonth = val * 100;
|
||||
} else if(i == 5) {
|
||||
float val = String(pch).toFloat();
|
||||
ead.incomeYesterday= val * 100;
|
||||
} else if(i == 6) {
|
||||
float val = String(pch).toFloat();
|
||||
ead.incomeThisMonth = val * 100;
|
||||
} else if(i == 7) {
|
||||
float val = String(pch).toFloat();
|
||||
ead.incomeLastMonth = val * 100;
|
||||
} else if(i >= 8 && i < 18) {
|
||||
{
|
||||
long val = String(pch).toInt();
|
||||
ead.peaks[peak].day = val;
|
||||
}
|
||||
pch = strtok (NULL, " ");
|
||||
i++;
|
||||
{
|
||||
float val = String(pch).toFloat();
|
||||
ead.peaks[peak].value = val * 100;
|
||||
}
|
||||
peak++;
|
||||
} else if(i == 18) {
|
||||
float val = String(pch).toFloat();
|
||||
totalImport = val * 1000;
|
||||
} else if(i == 19) {
|
||||
float val = String(pch).toFloat();
|
||||
totalExport = val * 1000;
|
||||
}
|
||||
} else {
|
||||
if(i == 1) {
|
||||
long val = String(pch).toInt();
|
||||
@ -2151,14 +2279,19 @@ void configFileParse() {
|
||||
} else if(i == 7) {
|
||||
float val = String(pch).toFloat();
|
||||
ead.incomeLastMonth = val * 100;
|
||||
} else if(i >= 8 && i < 18) {
|
||||
uint8_t hour = i-8;
|
||||
} else if(i >= 8 && i < 23) {
|
||||
{
|
||||
long val = String(pch).toInt();
|
||||
ead.peaks[peak].day = val;
|
||||
}
|
||||
pch = strtok (NULL, " ");
|
||||
i++;
|
||||
{
|
||||
long val = String(pch).toInt();
|
||||
ead.peaks[peak].hour = val;
|
||||
}
|
||||
pch = strtok (NULL, " ");
|
||||
i++;
|
||||
{
|
||||
float val = String(pch).toFloat();
|
||||
ead.peaks[peak].value = val * 100;
|
||||
@ -2185,7 +2318,7 @@ void configFileParse() {
|
||||
ead.lastMonthImport = importUpdate;
|
||||
ead.lastMonthExport = exportUpdate;
|
||||
|
||||
ead.version = 6;
|
||||
ead.version = 7;
|
||||
ea.setData(ead);
|
||||
sEa = true;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user