├── .github
└── workflows
│ ├── close_inactive_issuses.yml
│ └── close_stale_ pull_requests.yml
├── .gitignore
├── .vscode
└── settings.json
├── LICENSE
├── NANO-CUL.md
├── NANOCUL-DOCKER.md
├── README.md
├── RTL232.md
├── __init__.py
├── app.py
├── config.py.dist
├── data
├── .gitkeep
├── 43430778-day-report.csv
├── 43430778-report.csv
├── 43430778.json
└── default_data.json
├── docs
├── .gitkeep
├── DS_IZAR_RADIO_COMPACT_INDUCTIVE_de_20170906_V2_0.pdf
├── DVB-T-DAB-FM- RTL2832U-FC0012 Chip.png
├── MA_IZAR_RADIO_COMPACT_INDUCTIVE_ml_20170529_V2_0.pdf
├── MA_Installation_guide_IZAR_RCi_02_2016_ml_20180314.pdf
├── RB3B_DVBT.png
├── SDR_STICK.png
├── build-rtl-wmbus.md
├── build-wmbusmeters.md
├── d1Mini868Mhz.png
├── d1min_cc1101.png
├── diehl_metering.jpg
├── esphome-watermeter.png
├── ha-card.png
├── ha-card2.png
├── homeassistant
│ ├── README.md
│ ├── card02.png
│ ├── card1.png
│ ├── config
│ │ ├── input_numbers
│ │ │ └── water.yaml
│ │ └── sensor
│ │ │ ├── smartmeter-water.yaml
│ │ │ └── smartmeter-watercosts.yaml
│ ├── details.png
│ ├── lovelace
│ │ ├── cards
│ │ │ └── smartmeter-water
│ │ │ │ ├── card-water-days.yaml
│ │ │ │ ├── card-water-overview.yaml
│ │ │ │ └── card-water-usage.yaml
│ │ └── views
│ │ │ └── smartmeter-water.yaml
│ ├── mqtt-data.json
│ ├── view_watermeter.png
│ ├── water_barcard.png
│ └── water_homekitpanel_card.png
├── liter-pro-stunde.png
├── logging.png
├── nanoCUL868.png
├── nanocul_v3
│ ├── CUL_V3_MBUS.hex
│ ├── README.md
│ └── nanocul.jpg
├── rpw.png
├── rtl-sdr4linux_quickstartguidev20.pdf
├── sampedata.json
├── top-nanocul.png
├── top-rtlsdr.png
├── wmbusdata.json
├── wmbusmeters-nano_cul.md
└── wmbusmeters-with-rtl-sdr.md
├── esphome
├── .gitignore
├── README.md
├── archive
│ └── wm-d1mini_izar-wmbus.yaml
├── atom_lite_watermeter.yaml
├── custom_components
│ ├── backup
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── backup.cpp
│ │ └── backup.h
│ ├── crash_info
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── crash_info.cpp
│ │ └── crash_info.h
│ ├── heapmon
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── heapmon.cpp
│ │ ├── heapmon.h
│ │ └── sensor.py
│ ├── influxdb
│ │ ├── .gitignore
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── influxdb_writer.cpp
│ │ └── influxdb_writer.h
│ ├── izar-wmbus
│ │ ├── README.md
│ │ ├── izar_utils.cpp
│ │ ├── izar_utils.h
│ │ ├── izar_wmbus.cpp
│ │ ├── izar_wmbus.h
│ │ ├── library.json
│ │ └── wmbus_t_cc1101_config.h
│ ├── izar_meter.h
│ ├── ping
│ │ ├── __init__.py
│ │ ├── ping.h
│ │ ├── ping_esp32.h
│ │ ├── ping_esp8266.h
│ │ ├── ping_sock.c
│ │ ├── ping_sock.h
│ │ └── sensor.py
│ ├── startup
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── sensor.py
│ │ ├── startup.cpp
│ │ └── startup.h
│ ├── syslog
│ │ ├── .gitignore
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── syslog_component.cpp
│ │ └── syslog_component.h
│ ├── water_statistics
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── sensor.py
│ │ ├── water_statistics.cpp
│ │ └── water_statistics.h
│ ├── wmbus
│ │ ├── __init__.py
│ │ ├── driver.h
│ │ ├── driver_amiplus.h
│ │ ├── driver_apator_08.h
│ │ ├── driver_apator_16_2.h
│ │ ├── driver_apatoreitn.h
│ │ ├── driver_bmeters.h
│ │ ├── driver_c5isf.h
│ │ ├── driver_compact5.h
│ │ ├── driver_elf.h
│ │ ├── driver_evo868.h
│ │ ├── driver_fhkvdataiii.h
│ │ ├── driver_hydrocalm3.h
│ │ ├── driver_hydrus.h
│ │ ├── driver_iperl.h
│ │ ├── driver_itron.h
│ │ ├── driver_izar.h
│ │ ├── driver_mkradio3.h
│ │ ├── driver_mkradio4.h
│ │ ├── driver_qheat.h
│ │ ├── driver_qwater.h
│ │ ├── driver_sharky774.h
│ │ ├── driver_topaseskr.h
│ │ ├── driver_ultrimis.h
│ │ ├── driver_unismart.h
│ │ ├── driver_vario451.h
│ │ ├── drivers.h
│ │ ├── sensor
│ │ │ └── __init__.py
│ │ ├── version.h
│ │ ├── wmbus.cpp
│ │ └── wmbus.h
│ └── wmbusgw
│ │ ├── __init__.py
│ │ ├── wmbusgw_component.cpp
│ │ └── wmbusgw_component.h
├── docs
│ ├── CC1101 SPI Adaptor_bottom.png
│ ├── CC1101 SPI Adaptor_top.png
│ ├── CC1101-868mhz-radio-module-pinout.jpg
│ ├── CC1101.png
│ ├── CC1101_Board.png
│ ├── D1MINI-ESP32.png
│ ├── E07-900M10S-CC1101.png
│ ├── E07-900M10S-CC1101_pins.png
│ ├── E07-900MBL-01.jpg
│ ├── Ebyte E07-900MM10S.txt
│ ├── IMAGE 2023-04-23 13:17:26.jpg
│ ├── analog_vs_wmbus.jpg
│ ├── az-delivery-devkit-v4.png
│ ├── cc1101_board_pins.png
│ ├── cc1101_board_pins.pxm
│ ├── cc1101_platine.png
│ ├── d1Mini-wemos.png
│ ├── d1Mini868Mhz.png
│ ├── d1Mini_pinlayout.png
│ ├── d1mini-esp32-cc1101.png
│ ├── d1mini-esp8266MOD-12F.png
│ ├── d1mini32_8266.png
│ ├── d1mini_CC1101.jpg
│ ├── d1miniesp32.png
│ ├── eshome_webui.png
│ ├── esp32-devkit_v1.png
│ ├── esp32-devkitv1.png
│ ├── esp32_cc1101.png
│ ├── esp32_devkit_v1.jpg
│ ├── esp32_devkit_v1_cc1011.png
│ ├── esphome-components-1.4.8.zip
│ ├── esphome.png
│ ├── esphome_nodemcu.png
│ ├── ha-Device.png
│ ├── ha_rssi.png
│ ├── ha_water-meter-esp.png
│ ├── ha_water.png
│ ├── ha_water_day.png
│ ├── ha_water_year.png
│ ├── ha_waterdisplay.png
│ ├── ha_wlan.png
│ ├── homeassistant_service_call.png
│ ├── homeassistat-water-meter-izar.png
│ ├── mdmini_v150.zip
│ ├── memory_usage.png
│ ├── nodemcu_cc1101.jpg
│ ├── nodemcu_v3.png
│ ├── postman_get_config.png
│ ├── schaltplan.png
│ ├── water-meter-d1mini.png
│ ├── water-meter-esp.png
│ ├── water-meter-izar.png
│ ├── water-meter2-esp.png
│ ├── watermeter-webserver3.png
│ ├── webserver_3_0.png
│ ├── wlan_lgi.png
│ └── wmbus_ szczepan.md
├── template_secrets.yaml
├── testcases
│ ├── README.md
│ ├── WLAN_ACCESS.png
│ ├── water-meter-dm32-test.bin
│ ├── water-meter-esp32-izar-test-dev.bin
│ ├── water-meter-esp32-izar-test.bin
│ ├── water-meter-esp32-izar-test2.bin
│ ├── water-meter-esp32-izar-test2.png
│ ├── water-meter-izar-test.bin
│ ├── wm-d1mini-izar-test.yaml
│ ├── wm-d1mini32-izar-test.yaml
│ ├── wm-esp32-izar-test.yaml
│ └── wm-esp32-izar-test2.yaml
├── version_3.md
├── water-meter-izar.md
├── water-meter-testcase.yaml
├── watermeter_d1mini.md
├── webserver
│ ├── README.md
│ ├── v1
│ │ ├── webserver-v1.css
│ │ ├── webserver-v1.js
│ │ ├── webserver-v1.min.css
│ │ └── webserver-v1.min.js
│ ├── v2
│ │ ├── home.svg
│ │ ├── index.html
│ │ ├── index.html.br
│ │ ├── index.html.gz
│ │ ├── logo.svg
│ │ ├── server_index.h
│ │ ├── www.js
│ │ ├── www.js.br
│ │ └── www.js.gz
│ ├── v3
│ │ ├── fonts
│ │ │ └── Roboto
│ │ │ │ ├── LICENSE.txt
│ │ │ │ ├── Roboto-Black.ttf
│ │ │ │ ├── Roboto-BlackItalic.ttf
│ │ │ │ ├── Roboto-Bold.ttf
│ │ │ │ ├── Roboto-BoldItalic.ttf
│ │ │ │ ├── Roboto-Italic.ttf
│ │ │ │ ├── Roboto-Light.ttf
│ │ │ │ ├── Roboto-LightItalic.ttf
│ │ │ │ ├── Roboto-Medium.ttf
│ │ │ │ ├── Roboto-MediumItalic.ttf
│ │ │ │ ├── Roboto-Regular.ttf
│ │ │ │ ├── Roboto-Thin.ttf
│ │ │ │ └── Roboto-ThinItalic.ttf
│ │ ├── webserver.css
│ │ ├── www.js
│ │ ├── www.js.br
│ │ └── www.js.gz
│ └── v4
│ │ └── www.js
├── wm-check-wmbus.yaml
├── wm-d1mini-simple.yaml
├── wm-d1mini-test.yaml
├── wm-d1mini.yaml
├── wm-d1mini32-simple.yaml
├── wm-d1mini32-test.yaml
├── wm-d1mini32.yaml
├── wm-esp32-simple.yaml
├── wm-esp32-test.yaml
├── wm-esp32.yaml
├── wm-nodemcu-test.yaml
└── wm-nodemcu.yaml
├── ha-watermeter.md
├── lib
├── __init__.py
├── calculator.py
└── logger.py
├── logs
├── .gitkeep
└── data.csv
├── requirements.txt
├── service.template
├── test.py
├── tests
├── .gitkeep
├── 43430778.json
├── test.py
└── wmbus.json
└── tools
└── simpleserver.py
/.github/workflows/close_inactive_issuses.yml:
--------------------------------------------------------------------------------
1 | name: Close inactive issues
2 | on:
3 | schedule:
4 | - cron: '30 1 * * *' # Run every day at 01:30
5 |
6 | jobs:
7 | close-issues:
8 |
9 | runs-on: ubuntu-latest
10 | permissions:
11 | issues: write
12 | pull-requests: write
13 |
14 | steps:
15 | - uses: actions/stale@v5
16 | with:
17 | days-before-issue-stale: 30
18 | days-before-issue-close: 14
19 | stale-issue-label: "stale"
20 | stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
21 | close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
22 | # Wenn diese Aktion niemals Issues schließen soll, lege diesen Wert auf -1 fest.
23 | days-before-pr-stale: 0
24 | days-before-pr-close: 0
25 | repo-token: ${{ secrets.GITHUB_TOKEN }}
26 |
--------------------------------------------------------------------------------
/.github/workflows/close_stale_ pull_requests.yml:
--------------------------------------------------------------------------------
1 | name: Close Stale Pull Requests
2 |
3 | on:
4 | schedule:
5 | - cron: '0 0 * * *' # Run every day at midnight
6 |
7 | jobs:
8 | close_stale_prs:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Close stale pull requests
12 | uses: actions/stale@v5
13 | with:
14 | stale-issue-message: 'This pull request has been automatically closed because it has been inactive for more than 14 days. Please reopen if you still intend to submit this pull request.'
15 | days-before-stale: 14
16 | days-before-close: 0
17 | stale-pr-message: 'This pull request has been marked as stale because it has been inactive for more than 14 days. Please update this pull request or it will be automatically closed in 7 days.'
18 | stale-pr-label: stale
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Project files
2 | settings.py
3 | .DS_Store
4 | .env
5 | .history
6 | config.py
7 | internal/
8 | data/43430778.json
9 |
10 | # Byte-compiled / optimized / DLL files
11 | __pycache__/
12 | *.py[cod]
13 | *$py.class
14 |
15 | # C extensions
16 | *.so
17 |
18 | # Distribution / packaging
19 | .Python
20 | build/
21 | develop-eggs/
22 | dist/
23 | downloads/
24 | eggs/
25 | .eggs/
26 | lib64/
27 | parts/
28 | sdist/
29 | var/
30 | wheels/
31 | pip-wheel-metadata/
32 | share/python-wheels/
33 | *.egg-info/
34 | .installed.cfg
35 | *.egg
36 | MANIFEST
37 |
38 | # PyInstaller
39 | # Usually these files are written by a python script from a template
40 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
41 | *.manifest
42 | *.spec
43 |
44 | # Installer logs
45 | pip-log.txt
46 | pip-delete-this-directory.txt
47 |
48 | # Unit test / coverage reports
49 | htmlcov/
50 | .tox/
51 | .nox/
52 | .coverage
53 | .coverage.*
54 | .cache
55 | nosetests.xml
56 | coverage.xml
57 | *.cover
58 | *.py,cover
59 | .hypothesis/
60 | .pytest_cache/
61 |
62 | # Translations
63 | *.mo
64 | *.pot
65 |
66 | # Django stuff:
67 | *.log
68 | local_settings.py
69 | db.sqlite3
70 | db.sqlite3-journal
71 |
72 | # Flask stuff:
73 | instance/
74 | .webassets-cache
75 |
76 | # Scrapy stuff:
77 | .scrapy
78 |
79 | # Sphinx documentation
80 | docs/_build/
81 |
82 | # PyBuilder
83 | target/
84 |
85 | # Jupyter Notebook
86 | .ipynb_checkpoints
87 |
88 | # IPython
89 | profile_default/
90 | ipython_config.py
91 |
92 | # pyenv
93 | .python-version
94 |
95 | # pipenv
96 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
97 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
98 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
99 | # install all needed dependencies.
100 | #Pipfile.lock
101 |
102 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
103 | __pypackages__/
104 |
105 | # Celery stuff
106 | celerybeat-schedule
107 | celerybeat.pid
108 |
109 | # SageMath parsed files
110 | *.sage.py
111 |
112 | # Environments
113 | .env
114 | .venv
115 | env/
116 | venv/
117 | ENV/
118 | env.bak/
119 | venv.bak/
120 |
121 | # Spyder project settings
122 | .spyderproject
123 | .spyproject
124 |
125 | # Rope project settings
126 | .ropeproject
127 |
128 | # mkdocs documentation
129 | /site
130 |
131 | # mypy
132 | .mypy_cache/
133 | .dmypy.json
134 | dmypy.json
135 |
136 | # Pyre type checker
137 | .pyre/
138 | data/43430778.json
139 | esphome/develop_esphome.code-workspace
140 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "python.pythonPath": "/usr/local/bin/python3",
3 | "files.associations": {
4 | "*.tcc": "cpp",
5 | "cctype": "cpp",
6 | "clocale": "cpp",
7 | "cmath": "cpp",
8 | "complex": "cpp",
9 | "cstdarg": "cpp",
10 | "cstddef": "cpp",
11 | "cstdio": "cpp",
12 | "cstdlib": "cpp",
13 | "cstring": "cpp",
14 | "ctime": "cpp",
15 | "cwchar": "cpp",
16 | "cwctype": "cpp",
17 | "bitset": "cpp",
18 | "deque": "cpp",
19 | "list": "cpp",
20 | "vector": "cpp",
21 | "exception": "cpp",
22 | "fstream": "cpp",
23 | "iomanip": "cpp",
24 | "iosfwd": "cpp",
25 | "iostream": "cpp",
26 | "istream": "cpp",
27 | "limits": "cpp",
28 | "memory": "cpp",
29 | "new": "cpp",
30 | "ostream": "cpp",
31 | "sstream": "cpp",
32 | "stdexcept": "cpp",
33 | "streambuf": "cpp",
34 | "array": "cpp",
35 | "cfenv": "cpp",
36 | "cinttypes": "cpp",
37 | "cstdint": "cpp",
38 | "functional": "cpp",
39 | "hashtable": "cpp",
40 | "random": "cpp",
41 | "tuple": "cpp",
42 | "type_traits": "cpp",
43 | "unordered_map": "cpp",
44 | "utility": "cpp",
45 | "typeinfo": "cpp",
46 | "atomic": "cpp",
47 | "bit": "cpp",
48 | "chrono": "cpp",
49 | "codecvt": "cpp",
50 | "compare": "cpp",
51 | "concepts": "cpp",
52 | "condition_variable": "cpp",
53 | "map": "cpp",
54 | "set": "cpp",
55 | "string": "cpp",
56 | "algorithm": "cpp",
57 | "iterator": "cpp",
58 | "memory_resource": "cpp",
59 | "numeric": "cpp",
60 | "optional": "cpp",
61 | "ratio": "cpp",
62 | "string_view": "cpp",
63 | "system_error": "cpp",
64 | "initializer_list": "cpp",
65 | "mutex": "cpp",
66 | "numbers": "cpp",
67 | "semaphore": "cpp",
68 | "stop_token": "cpp",
69 | "thread": "cpp",
70 | "typeindex": "cpp",
71 | "variant": "cpp"
72 | }
73 | }
--------------------------------------------------------------------------------
/NANO-CUL.md:
--------------------------------------------------------------------------------
1 | # wmbusmeters with NANO-CUL
2 |
3 | ## Raspbian Buster + wmbusmeters with NANO-CUL (mbus) 868 Mhz
4 |
5 | **NANO-CUL (mbus) 868** is a lot easier to use because it only requires `wmbusmeters`. The resources (memory) required are also less compared to the variant with RTL-SDR and rtl-wmbus.
6 |
7 |
8 | 
9 | Please not, that you do need the NANO-CUL (mbus) 868 Mhz for `wmbusmeters` **not for** `***\*FHEM\****`!! . smart-home-komponente.net can flash the firmware for `wmbusmeters`.
10 |
11 |
12 | ### Install dependencies
13 |
14 | - `wmbusmeters`
15 | Program receives and decodes WMBus telegrams
16 | https://github.com/weetmuts/wmbusmeters,
17 | build see: [build-wmbusmeters](docs/build-wmbusmeters.md)
18 |
19 | ### Install dependencies
20 |
21 | - `wmbusmeters`
22 | Program receives and decodes WMBus telegrams
23 | https://github.com/weetmuts/wmbusmeters,
24 | build see: [build-wmbusmeters](docs/build-wmbusmeters.md)
25 |
26 | ## Integration
27 |
28 | In the `wmbusmeters` reporting mode, data will be published to the MQTT broker topic "`tele/wasser/verbrauch`".
29 |
30 | This data can be subscribed to and processed by other applications. From this point forward your options are endless. Example for simple mode - publish all smartmeter display data:
31 |
32 | ```json
33 | {
34 | "media": "water",
35 | "meter": "izar",
36 | "name": "watermeter",
37 | "id": "1231150",
38 | "total_m3": 166.625,
39 | "last_month_total_m3": 162.614,
40 | "last_month_measure_date": "2020-07-01",
41 | "remaining_battery_life_y": 13,
42 | "current_alarms": "no_alarm",
43 | "previous_alarms": "no_alarm",
44 | "timestamp": "2020-07-16T11:32:38Z"
45 | }
46 | ```
47 |
48 | ### Continuous Daemon/Service
49 |
50 | You most probably want to execute the program **continuously in the background**. This can be done either by using the internal daemon or cron.
51 |
52 | **Attention:** Daemon mode must be enabled in the configuration file (default).
53 |
54 | 1. Systemd service - on systemd powered systems the **recommended** option
55 |
56 | ```bash
57 | sudo cp /opt/ha-watermeter/service.template /etc/systemd/system/ha-watermeter.service
58 |
59 | sudo systemctl daemon-reload
60 | sudo systemctl start ha-watermeter.service
61 | sudo systemctl status ha-watermeter.service
62 |
63 | sudo systemctl enable ha-watermeter.service
64 | ```
65 |
--------------------------------------------------------------------------------
/NANOCUL-DOCKER.md:
--------------------------------------------------------------------------------
1 | # Docker + wmbusmeters with NANO-CUL (mbus) 868 Mhz
2 | wmbusmeters docker is able to detect attachment and removal of wmbus dongles and to provide that functionality within docker image it must be started in privileged mode to have access to hosts /dev/ content.
3 |
4 | see: https://hub.docker.com/r/weetmuts/wmbusmeters
5 |
6 |
7 | 
8 |
9 | More Info see: [Testcase Flash CUL-Stick](docs/nanocul_v3/README.md)
10 |
11 | https://www.smart-home-komponente.de/nano-cul/nano-cul-868-extra/
12 |
13 |
14 | ## Docker wmbusmeters
15 |
16 | With this bash script, wmbusmeters is installed as a docker application
17 |
18 |
19 | ```bash
20 | #!/bin/bash
21 | # ----------------------------------
22 | # sudo sh wmbusmeters.sh
23 | # ----------------------------------
24 | DOCKER_APPSDIR=/apps/
25 | DOCKER_TIMEZONE=Europe/Berlin
26 | DOCKERIMAGE=weetmuts/wmbusmeters
27 | CONTAINERLABEL=wmbusmeters
28 | APPSDATA=$PWD${DOCKER_APPSDIR}${CONTAINERLABEL}
29 |
30 | echo "Create persistent data folder and log folder"
31 | mkdir -p ${APPSDATA} 2>&1
32 |
33 | echo "Try to remove previuos installation..."
34 | docker stop ${CONTAINERLABEL} >/dev/null 2>&1
35 | docker rm ${CONTAINERLABEL} >/dev/null 2>&1
36 |
37 | echo "Install Docker container ${CONTAINERLABEL}."
38 | docker run --detach --interactive \
39 | --privileged \
40 | --name=${CONTAINERLABEL} \
41 | --restart=always \
42 | --volume /etc/localtime:/etc/localtime:ro \
43 | --volume ${APPSDATA}:/wmbusmeters_data \
44 | --volume /dev/:/dev/ \
45 | ${DOCKERIMAGE}
46 |
47 | echo "Docker container ${CONTAINERLABEL} ready."
48 | ```
49 |
50 |
--------------------------------------------------------------------------------
/RTL232.md:
--------------------------------------------------------------------------------
1 | # Raspbian Buster + DVB-T receiver
2 |
3 |
4 | ### 
5 |
6 |
7 | **Andoer Tragbarer Mini Digitaler TV Stock USB 2.0 DVB-T+DAB+FM+RTL2832U+FC0012 Chip Unterstützung SDR**
8 |
9 | 
10 |
11 |
12 | Digitaler TV Stock USB 2.0 DVB-T + DAB + FM + RTL2832U + FC0012 see: [Amazon](https://amzn.eu/d/8NNDo6A).
13 |
14 | Details and Installation see: [wmbusmeters-with-rtl-sdr.md](docs/wmbusmeters-with-rtl-sdr.md)
15 |
16 |
17 | ### Requirements
18 |
19 | - Water meter with IZAR module (Diehl IZAR RC 868 I R4 PL),
20 | IZAR Radio Compact Hall is designed for mobile reading and fixed network remote reading of Diehl Metering meters.
21 |
22 | - Supported DVB-T receiver or Nano CUL Adapter,
23 | Andoer Tragbarer Mini Digitaler TV Stock USB 2.0 DVB-T + DAB + FM + RTL2832U + FC0012 Chip Unterstützung SDR Stimmer Empfänger
24 |
25 | - A computer. I’m using Raspbian Buster on a Raspberry Pi 3B+, but any Linux, macOS should work
26 |
27 |
28 | ## Integration
29 |
30 | In the `wmbusmeters` reporting mode, data will be published to the MQTT broker topic "`tele/wasser/verbrauch`".
31 |
32 | This data can be subscribed to and processed by other applications. From this point forward your options are endless. Example for simple mode - publish all smartmeter display data:
33 |
34 | ```json
35 | {
36 | "media": "water",
37 | "meter": "izar",
38 | "name": "watermeter",
39 | "id": "1231150",
40 | "total_m3": 166.625,
41 | "last_month_total_m3": 162.614,
42 | "last_month_measure_date": "2020-07-01",
43 | "remaining_battery_life_y": 13,
44 | "current_alarms": "no_alarm",
45 | "previous_alarms": "no_alarm",
46 | "timestamp": "2020-07-16T11:32:38Z"
47 | }
48 | ```
49 |
50 | ### Continuous Daemon/Service
51 |
52 | You most probably want to execute the program **continuously in the background**. This can be done either by using the internal daemon or cron.
53 |
54 | **Attention:** Daemon mode must be enabled in the configuration file (default).
55 |
56 | 1. Systemd service - on systemd powered systems the **recommended** option
57 |
58 | ```bash
59 | sudo cp /opt/ha-watermeter/service.template /etc/systemd/system/ha-watermeter.service
60 |
61 | sudo systemctl daemon-reload
62 | sudo systemctl start ha-watermeter.service
63 | sudo systemctl status ha-watermeter.service
64 |
65 | sudo systemctl enable ha-watermeter.service
66 | ```
67 |
68 |
69 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
1 | import config
2 | name = 'watermeter'
--------------------------------------------------------------------------------
/data/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/data/.gitkeep
--------------------------------------------------------------------------------
/data/43430778-day-report.csv:
--------------------------------------------------------------------------------
1 | watermeter,43430778,2020-07,2020-07-17T13:50:00Z,166.887,0.078,4.273,49.157,166.809,162.614,117.73,no_alarm,no_alarm
2 | watermeter,43430778,2020-07,2020-07-17T13:50:26Z,166.887,0.078,4.273,49.157,166.809,162.614,117.73,no_alarm,no_alarm
3 | watermeter,43430778,2020-07,2020-07-17T13:50:44Z,166.887,0.078,4.273,49.157,166.809,162.614,117.73,no_alarm,no_alarm
4 | watermeter,43430778,2020-07,2020-07-17T13:51:26Z,166.887,0.078,4.273,49.157,166.809,162.614,117.73,no_alarm,no_alarm
5 | watermeter,43430778,2020-07,2020-07-17T13:51:35Z,166.887,0.078,4.273,49.157,166.809,162.614,117.73,no_alarm,no_alarm
6 | watermeter,43430778,2020-07,2020-07-17T13:51:43Z,166.887,0.078,4.273,49.157,166.809,162.614,117.73,no_alarm,no_alarm
7 | watermeter,43430778,2020-07,2020-07-17T13:52:52Z,166.887,0.078,4.273,49.157,166.809,162.614,117.73,no_alarm,no_alarm
8 | watermeter,43430778,2020-07,2020-07-17T13:53:01Z,166.887,0.078,4.273,49.157,166.809,162.614,117.73,no_alarm,no_alarm
9 | watermeter,43430778,2020-07,2020-07-17T13:53:17Z,166.887,0.078,4.273,49.157,166.809,162.614,117.73,no_alarm,no_alarm
10 | watermeter,43430778,2020-07,2020-07-17T13:53:52Z,166.887,0.078,4.273,49.157,166.809,162.614,117.73,no_alarm,no_alarm
11 | watermeter,43430778,2020-07,2020-07-17T13:54:10Z,166.887,0.078,4.273,49.157,166.809,162.614,117.73,no_alarm,no_alarm
12 | watermeter,43430778,2020-07,2020-07-17T13:54:43Z,166.888,0.079,4.274,49.158,166.809,162.614,117.73,no_alarm,no_alarm
13 | watermeter,43430778,2020-07,2020-07-17T13:55:43Z,166.893,0.084,4.279,49.163,166.809,162.614,117.73,no_alarm,no_alarm
14 | watermeter,43430778,2020-07,2020-07-17T13:57:09Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
15 | watermeter,43430778,2020-07,2020-07-17T13:57:26Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
16 | watermeter,43430778,2020-07,2020-07-17T13:59:00Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
17 | watermeter,43430778,2020-07,2020-07-17T13:59:09Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
18 | watermeter,43430778,2020-07,2020-07-17T13:59:26Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
19 | watermeter,43430778,2020-07,2020-07-17T13:59:43Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
20 | watermeter,43430778,2020-07,2020-07-17T13:59:52Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
21 | watermeter,43430778,2020-07,2020-07-17T14:00:00Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
22 | watermeter,43430778,2020-07,2020-07-17T14:00:17Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
23 | watermeter,43430778,2020-07,2020-07-17T14:00:34Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
24 | watermeter,43430778,2020-07,2020-07-17T14:00:43Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
25 | watermeter,43430778,2020-07,2020-07-17T14:00:52Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
26 | watermeter,43430778,2020-07,2020-07-17T14:01:01Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
27 | watermeter,43430778,2020-07,2020-07-17T14:01:09Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
28 | watermeter,43430778,2020-07,2020-07-17T14:01:35Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
29 | watermeter,43430778,2020-07,2020-07-17T14:01:43Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,no_alarm
30 | watermeter,43430778,2020-07,2020-07-17T14:01:52Z,166.899,0.09,4.285,49.169,166.809,162.614,117.73,no_alarm,No alarm
31 | watermeter,43430778,2022-02,2022-02-11T08:22:10Z,343.913,0.016,2.734,226.183,343.897,341.179,117.73,no_alarm,No alarm
32 | watermeter,43430778,2022-02,2022-02-21T23:57:38Z,347.226,0.157,6.047,229.496,347.069,341.179,117.73,no_alarm,no_alarm
33 |
--------------------------------------------------------------------------------
/data/43430778-report.csv:
--------------------------------------------------------------------------------
1 | watermeter,43430778,2020-07-17,13:50:00,2020-07-17T13:50:00Z,166.887,162.614,117.73,no_alarm,no_alarm
2 | watermeter,43430778,2020-07-17,13:50:26,2020-07-17T13:50:26Z,166.887,162.614,117.73,no_alarm,no_alarm
3 | watermeter,43430778,2020-07-17,13:50:44,2020-07-17T13:50:44Z,166.887,162.614,117.73,no_alarm,no_alarm
4 | watermeter,43430778,2020-07-17,13:51:26,2020-07-17T13:51:26Z,166.887,162.614,117.73,no_alarm,no_alarm
5 | watermeter,43430778,2020-07-17,13:51:35,2020-07-17T13:51:35Z,166.887,162.614,117.73,no_alarm,no_alarm
6 | watermeter,43430778,2020-07-17,13:51:43,2020-07-17T13:51:43Z,166.887,162.614,117.73,no_alarm,no_alarm
7 | watermeter,43430778,2020-07-17,13:52:52,2020-07-17T13:52:52Z,166.887,162.614,117.73,no_alarm,no_alarm
8 | watermeter,43430778,2020-07-17,13:53:01,2020-07-17T13:53:01Z,166.887,162.614,117.73,no_alarm,no_alarm
9 | watermeter,43430778,2020-07-17,13:53:17,2020-07-17T13:53:17Z,166.887,162.614,117.73,no_alarm,no_alarm
10 | watermeter,43430778,2020-07-17,13:53:52,2020-07-17T13:53:52Z,166.887,162.614,117.73,no_alarm,no_alarm
11 | watermeter,43430778,2020-07-17,13:54:10,2020-07-17T13:54:10Z,166.887,162.614,117.73,no_alarm,no_alarm
12 | watermeter,43430778,2020-07-17,13:54:43,2020-07-17T13:54:43Z,166.888,162.614,117.73,no_alarm,no_alarm
13 | watermeter,43430778,2020-07-17,13:55:43,2020-07-17T13:55:43Z,166.893,162.614,117.73,no_alarm,no_alarm
14 | watermeter,43430778,2020-07-17,13:57:09,2020-07-17T13:57:09Z,166.899,162.614,117.73,no_alarm,no_alarm
15 | watermeter,43430778,2020-07-17,13:57:26,2020-07-17T13:57:26Z,166.899,162.614,117.73,no_alarm,no_alarm
16 | watermeter,43430778,2020-07-17,13:59:00,2020-07-17T13:59:00Z,166.899,162.614,117.73,no_alarm,no_alarm
17 | watermeter,43430778,2020-07-17,13:59:09,2020-07-17T13:59:09Z,166.899,162.614,117.73,no_alarm,no_alarm
18 | watermeter,43430778,2020-07-17,13:59:26,2020-07-17T13:59:26Z,166.899,162.614,117.73,no_alarm,no_alarm
19 | watermeter,43430778,2020-07-17,13:59:43,2020-07-17T13:59:43Z,166.899,162.614,117.73,no_alarm,no_alarm
20 | watermeter,43430778,2020-07-17,13:59:52,2020-07-17T13:59:52Z,166.899,162.614,117.73,no_alarm,no_alarm
21 | watermeter,43430778,2020-07-17,14:00:00,2020-07-17T14:00:00Z,166.899,162.614,117.73,no_alarm,no_alarm
22 | watermeter,43430778,2020-07-17,14:00:17,2020-07-17T14:00:17Z,166.899,162.614,117.73,no_alarm,no_alarm
23 | watermeter,43430778,2020-07-17,14:00:34,2020-07-17T14:00:34Z,166.899,162.614,117.73,no_alarm,no_alarm
24 | watermeter,43430778,2020-07-17,14:00:43,2020-07-17T14:00:43Z,166.899,162.614,117.73,no_alarm,no_alarm
25 | watermeter,43430778,2020-07-17,14:00:52,2020-07-17T14:00:52Z,166.899,162.614,117.73,no_alarm,no_alarm
26 | watermeter,43430778,2020-07-17,14:01:01,2020-07-17T14:01:01Z,166.899,162.614,117.73,no_alarm,no_alarm
27 | watermeter,43430778,2020-07-17,14:01:09,2020-07-17T14:01:09Z,166.899,162.614,117.73,no_alarm,no_alarm
28 | watermeter,43430778,2020-07-17,14:01:35,2020-07-17T14:01:35Z,166.899,162.614,117.73,no_alarm,no_alarm
29 | watermeter,43430778,2020-07-17,14:01:43,2020-07-17T14:01:43Z,166.899,162.614,117.73,no_alarm,no_alarm
30 | watermeter,43430778,2020-07-17,14:01:52,2020-07-17T14:01:52Z,166.899,162.614,117.73,no_alarm,No alarm
31 |
--------------------------------------------------------------------------------
/data/43430778.json:
--------------------------------------------------------------------------------
1 | {"name": "Wasserz\u00e4hler Haus", "device": "watermeter", "deviceid": "43430778", "date": "2022-02-22", "time": "08:53:37", "total_m3": 347.289, "ratio": 1.02, "m3": {"current": 0.0, "hour": 0.0, "day": 0.063, "month": 6.11, "year": 229.559}, "liter": {"current": 0.0, "hour": 0.0, "day": 63.0, "month": 6110.0, "year": 229559.0}, "last_total": {"hour": "08", "hour_m3": 347.289, "day": "2022-02-22", "day_m3": 347.226, "month": "2022-02", "month_m3": 341.179, "year": "2019-12-31", "year_m3": 117.73}, "wmbusmeter": {"media": "water", "meter": "izar", "name": "watermeter", "id": "18444343", "prefix": "", "serial_number": "000000", "total_m3": 347.289, "last_month_total_m3": 341.179, "last_month_measure_date": "2022-02-01", "remaining_battery_life_y": 11.5, "current_alarms": "no_alarm", "previous_alarms": "no_alarm", "transmit_period_s": 8, "manufacture_year": "0", "timestamp": "2022-02-22T08:53:37Z", "device": "cul", "rssi_dbm": -56}, "alarm": "no_alarm", "last_alarm": "no_alarm", "periode": "2022-02-22", "month": "2022-02", "year": "2022", "timestamp": "2022-02-22T08:53:37Z", "last_update": "2022-02-22 09:53:37.170982", "elapsed_time": "0:00:17", "icon": "mdi:billboard", "unit_of_measurement": "\u33a5", "last_reset": "1970-01-01T00:00:00+00:00", "state_class": "measurement", "device_class": "energy", "version": "1.0.2", "attribution": "Data provided by Peter Siebler", "first_run": false, "data_provider": "MACBOOK-PRO-PETER-WLAN.siebler.home"}
--------------------------------------------------------------------------------
/data/default_data.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Wasserzähler Haus",
3 | "device": "watermeter",
4 | "deviceid": "43430778",
5 | "m3": { "hour": 0.0, "day": 0.0, "month": 0.0, "year": 0.0 },
6 | "liter": { "hour": 0.0, "day": 0.0, "month": 0.0, "year": 0.0 },
7 | "alarms": "No alarm",
8 | "previous_alarms": "No alarm",
9 | "periode": "2020-07-15",
10 | "month": "2020-07",
11 | "year": "2020",
12 | "last_month_measure_date": "",
13 | "timestamp": "2020-07-15T05:23:12Z",
14 | "last_update": "2020-07-15T05:23:12Z",
15 | "icon": "mdi:billboard",
16 | "unit_of_measurement": "㎥",
17 | "version": "1.0.0",
18 | "attribution": "Data provided by Peter Siebler"
19 | }
20 |
--------------------------------------------------------------------------------
/docs/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/.gitkeep
--------------------------------------------------------------------------------
/docs/DS_IZAR_RADIO_COMPACT_INDUCTIVE_de_20170906_V2_0.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/DS_IZAR_RADIO_COMPACT_INDUCTIVE_de_20170906_V2_0.pdf
--------------------------------------------------------------------------------
/docs/DVB-T-DAB-FM- RTL2832U-FC0012 Chip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/DVB-T-DAB-FM- RTL2832U-FC0012 Chip.png
--------------------------------------------------------------------------------
/docs/MA_IZAR_RADIO_COMPACT_INDUCTIVE_ml_20170529_V2_0.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/MA_IZAR_RADIO_COMPACT_INDUCTIVE_ml_20170529_V2_0.pdf
--------------------------------------------------------------------------------
/docs/MA_Installation_guide_IZAR_RCi_02_2016_ml_20180314.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/MA_Installation_guide_IZAR_RCi_02_2016_ml_20180314.pdf
--------------------------------------------------------------------------------
/docs/RB3B_DVBT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/RB3B_DVBT.png
--------------------------------------------------------------------------------
/docs/SDR_STICK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/SDR_STICK.png
--------------------------------------------------------------------------------
/docs/build-rtl-wmbus.md:
--------------------------------------------------------------------------------
1 | # Builds and runs rtl-wmbus on GNU/Linux
2 |
3 | The primary purpose of rtl-wmbus is experimenting with digital signal processing and software radio. rtl-wmbus can be used on resource constrained devices such Raspberry Pi Zero or Raspberry PI B+ overclocked to 1GHz.
4 |
5 | rtl-wmbus is a software defined receiver for Wireless-M-Bus. It is written in plain C and uses RTL-SDR (https://github.com/osmocom/rtl-sdr) to interface with RTL2832-based hardware.
6 |
7 | The Osmocom RTL-SDR library must be installed before you can build rtl-wmbus. See http://sdr.osmocom.org/trac/wiki/rtl-sdr for more information.
8 |
9 | To install rtl-wmbus, download, unpack the source code and go to the top level directory:
10 |
11 | ```bash
12 | # $ git clone https://github.com/xaelsouth/rtl-wmbus.git
13 | $ git clone https://github.com/weetmuts/rtl-wmbus.git
14 | $ cd rtl-wmbus
15 | # $ make
16 | # $ make debug # (no optimization at all, with debug options)
17 | $ make release # (-O3 optimized version, without any debugging options
18 | $ cp rtl-wmbus/build/rtl_wmbus /usr/bin/rtl_wmbus
19 |
20 | ```
21 |
22 | ## Test rtl-wmbus
23 |
24 | ```bash
25 | $ ls -l /dev/rtlsdr*
26 | >> lrwxrwxrwx 1 root root 15 Jul 19 05:59 /dev/rtlsdr_5 -> bus/usb/001/004
27 |
28 | $ cd build
29 | $ rtl_sdr -f 868.95M -s 1.6e6 - | rtl_wmbus
30 | $ rtl_sdr -f 868.95M -s 1600000 - 2 | rtl_wmbus
31 | $ rtl_sdr -f 868.95M -s 1.6e6 - 2>/dev/null | rtl_wmbus
32 |
33 | >>> Results
34 | Found 1 device(s):
35 | 0: Realtek, RTL2838UHIDIR, SN: 00000001
36 |
37 | Using device 0: Generic RTL2832U OEM
38 | Found Fitipower FC0012 tuner
39 | Sampling at 1600000 S/s.
40 | Tuned to 868950000 Hz.
41 | Tuner gain set to automatic.
42 | Reading samples in async mode...
43 | Allocating 15 zero-copy buffers
44 | T1;0;0;2020-07-19 05:28:57.000;46;95;43410778;0x1944a511780741434418ff411a0013109684c3a89f7017569381
45 | T1;0;0;2020-07-19 05:28:59.000;53;107;FF430778;0x1944a511780743ffff18a2611a001312f3e9669a7e9d9c8381d2
46 | T1;0;0;2020-07-19 05:29:00.000;61;60;FFFF0785;0x19ff24238507ffffff18a261ffffffff1cbe74ff32ffff72ffff
47 | T1;0;0;2020-07-19 05:29:05.000;59;100;43FF0778;0x1944a5117807ff43ffffa251ff00131081abff03bea3ce8d8a83
48 | T1;0;0;2020-07-19 05:29:08.000;69;77;43430778;0x1944a51178074343ffffa2711a001312e4c62f315f4e455898d0
49 | T1;0;0;2020-07-19 05:29:09.000;34;66;FFFFFFFF;0x1944ff23ffffffffff18a2ffffffff11ffff3dffffff70a9ffff
50 | T1;0;0;2020-07-19 05:29:14.000;51;84;43410778;0x1944a511780741434418a261ff001310b8da50fedcd7a4e0a184
51 | T1;0;0;2020-07-19 05:29:17.000;46;179;43FF0778;0x1944a5117807ff4344ffa2011a001312810ad360ba754859d7dd
52 | T1;0;0;2020-07-19 05:29:18.000;42;68;FF8907FF;0x19ffffffff0789ffff18ff01ffffff116b5dfffff664ffffffff
53 | T1;0;0;2020-07-19 05:29:22.000;48;52;43FF0778;0x19ffa5117807ff434418a2711a001310aff51955fd04ffffff01
54 | T1;0;0;2020-07-19 05:29:26.000;53;169;43430778;0x1944a511780743434418a2111a00131296259acb9bff9182cedf
55 | T1;0;0;2020-07-19 05:29:27.000;45;61;FFFF07FF;0x19ff2423ff07ffff4418a2ffff0013117cffffbbd7b7ffffffff
56 | T1;1;1;2020-07-19 05:29:31.000;57;167;43410778;0x1944a511780741434418a2011a001310ca39e504183f703af78b
57 | T1;0;0;2020-07-19 05:29:34.000;67;135;43430778;0x1944a511780743434418a221ff001312af544036f9d2fbefe5d8
58 | T1;0;0;2020-07-19 05:29:36.000;40;80;FFFF07FF;0x1944ffffff07ffffffffa2210600ffffffffffffffc3ffffffff
59 | T1;0;0;2020-07-19 05:29:40.000;49;178;43410778;0x1944a511780741434418a211ffff1310dd16acaf39eca9e1ee89
60 | ```
61 |
62 |
63 |
64 | ## For more information see:
65 |
66 | ```txt
67 | https://github.com/xaelsouth/rtl-wmbus
68 |
69 | ## Adjust length byte after DLL CRC:s are removed.
70 | When trimming away the crc bytes, update the first len byte.
71 |
72 | Previously wmbusmeters was not so picky, and it worked anyway,
73 | but now wmbusmeters actually check the internal payload crc:s and
74 | other lengths. So an incorrect len byte fails.
75 |
76 | The hardware dongles like im871a, amb8465 do update the
77 | len byte as well.
78 |
79 | https://github.com/weetmuts/rtl-wmbus
80 | ```
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/docs/d1Mini868Mhz.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/d1Mini868Mhz.png
--------------------------------------------------------------------------------
/docs/d1min_cc1101.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/d1min_cc1101.png
--------------------------------------------------------------------------------
/docs/diehl_metering.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/diehl_metering.jpg
--------------------------------------------------------------------------------
/docs/esphome-watermeter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/esphome-watermeter.png
--------------------------------------------------------------------------------
/docs/ha-card.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/ha-card.png
--------------------------------------------------------------------------------
/docs/ha-card2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/ha-card2.png
--------------------------------------------------------------------------------
/docs/homeassistant/card02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/homeassistant/card02.png
--------------------------------------------------------------------------------
/docs/homeassistant/card1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/homeassistant/card1.png
--------------------------------------------------------------------------------
/docs/homeassistant/config/input_numbers/water.yaml:
--------------------------------------------------------------------------------
1 | ## ####################################################################################################
2 | ## calulation for a month
3 | ## (m3_water * cost_m3_wasser) + (m3_water * cost_m3abwasser) + cost_watermeterfee + cost_waterbasefee
4 | ## ####################################################################################################
5 | cost_m3_wasser:
6 | name: Wasser €/m3
7 | icon: mdi:currency-eur
8 | initial: 5.4176
9 | unit_of_measurement: "€"
10 | min: 5.00
11 | max: 10.00
12 | mode: box
13 |
14 | cost_m3wasser:
15 | name: Wasserbezugsgebühr
16 | icon: mdi:currency-eur
17 | initial: 0.88
18 | unit_of_measurement: "€"
19 | min: 0.80
20 | max: 1.50
21 | mode: box
22 |
23 | cost_m3abwasser:
24 | name: Kanalbenützungsgebühr
25 | icon: mdi:currency-eur
26 | initial: 3.38
27 | unit_of_measurement: "€"
28 | min: 3.00
29 | max: 5.00
30 | mode: box
31 |
32 | cost_watermeterfee:
33 | name: Zählermiete / Monat
34 | icon: mdi:currency-eur
35 | initial: 2.157
36 | unit_of_measurement: "€"
37 | min: 2.000
38 | max: 3.500
39 | mode: box
40 |
41 | cost_waterbasefee:
42 | name: Wassergrundgebühr / Monat
43 | icon: mdi:currency-eur
44 | initial: 4.917
45 | unit_of_measurement: "€"
46 | min: 4.800
47 | max: 6.500
48 | mode: box
49 |
50 | max_m3_day:
51 | name: Wasser m3 pro Tag
52 | icon: mdi:counter
53 | initial: 0.60
54 | unit_of_measurement: "\u33A5"
55 | min: .200
56 | max: 3.00
57 | mode: box
58 |
59 | max_m3_month:
60 | name: Wasser m3 pro Monat
61 | icon: mdi:counter
62 | initial: 15.00
63 | unit_of_measurement: "\u33A5"
64 | min: 5.00
65 | max: 20.00
66 | mode: box
67 |
68 | max_m3_year:
69 | name: Wasser m3 pro Jahr
70 | icon: mdi:counter
71 | initial: 102.000
72 | unit_of_measurement: "\u33A5"
73 | min: 80.00
74 | max: 200.00
75 | mode: box
76 |
--------------------------------------------------------------------------------
/docs/homeassistant/config/sensor/smartmeter-watercosts.yaml:
--------------------------------------------------------------------------------
1 |
2 | - platform: template
3 |
4 | sensors:
5 |
6 | ## calculate the water costs
7 |
8 | water_cost_current:
9 | friendly_name: "Wasser aktuell"
10 | value_template: >-
11 | {{ (states("sensor.wasserverbrauch")|float * states.input_number.cost_m3_wasser.state|float)|round(2)}}
12 | unit_of_measurement: "€"
13 |
14 | water_cost_hour:
15 | friendly_name: "Wasser pro Stunde"
16 | value_template: >-
17 | {{ (states("sensor.wasser_pro_stunde")|float * states.input_number.cost_m3_wasser.state|float)|round(2)}}
18 | unit_of_measurement: "€"
19 |
20 | water_cost_day:
21 | friendly_name: "Wasser pro Tag"
22 | value_template: >-
23 | {{ (states("sensor.wasser_pro_tag")|float * states.input_number.cost_m3_wasser.state|float)|round(2)}}
24 | unit_of_measurement: "€"
25 |
26 | water_cost_month:
27 | friendly_name: "Wasser pro Monat"
28 | value_template: >-
29 | {{ (states("sensor.wasser_pro_monat")|float * states.input_number.cost_m3_wasser.state|float)|round(2)}}
30 | unit_of_measurement: "€"
31 |
32 | water_cost_jahr:
33 | friendly_name: "Wasser pro Jahr"
34 | value_template: >-
35 | {{ (states("sensor.wasser_pro_jahr")|float * states.input_number.cost_m3_wasser.state|float)|round(2)}}
36 | unit_of_measurement: "€"
37 |
38 |
--------------------------------------------------------------------------------
/docs/homeassistant/details.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/homeassistant/details.png
--------------------------------------------------------------------------------
/docs/homeassistant/lovelace/cards/smartmeter-water/card-water-days.yaml:
--------------------------------------------------------------------------------
1 | type: "custom:mini-graph-card"
2 | style: |-
3 | ha-card{
4 | transform: scale(0.96);
5 | background-size: 100% 100%;
6 | }
7 | entities:
8 | - entity: sensor.wasser_liter_tag
9 | name: Tagesverbrauch
10 | aggregate_func: last
11 | state_adaptive_color: true
12 |
13 | name: Wasser / Tag _
14 | font_size: 85
15 | font_size_header: 18
16 | align_icon: left
17 | align_state: center
18 | align_header: left
19 | height: 186
20 | hours_to_show: 168
21 | hour24: true
22 | group_by: date
23 | unit: "Liter / Tag"
24 | icon: mdi:waves
25 | line_width: 1
26 | more_info: true
27 | lower_bound: 0.00
28 | upper_bound: 400.00
29 | show:
30 | icon_adaptive_color: true
31 | name_adaptive_color: true
32 | extrema: true
33 | graph: bar
34 |
35 | color_thresholds:
36 | - value: 50
37 | color: "#2196F3"
38 | - value: 100
39 | color: "#0D47A1"
40 | - value: 200
41 | color: "#1976D2"
42 | - value: 300
43 | color: "#42A5F5"
44 | - value: 400
45 | color: "#90CAF9"
--------------------------------------------------------------------------------
/docs/homeassistant/lovelace/cards/smartmeter-water/card-water-overview.yaml:
--------------------------------------------------------------------------------
1 | type: "custom:mini-graph-card"
2 | style: |-
3 | ha-card{
4 | transform: scale(0.96);
5 | background-size: 100% 100%;
6 | }
7 | entities:
8 | - entity: sensor.wasser_liter_h
9 | name: Aktuell
10 | aggregate_func: max
11 | name: Wasser / Stunde _
12 | font_size: 85
13 | font_size_header: 18
14 | align_icon: left
15 | align_state: center
16 | align_header: left
17 | height: 186
18 | hours_to_show: 48
19 | hour24: true
20 | group_by: hour
21 | unit: "Liter / h"
22 | icon: mdi:waves
23 | line_width: 1
24 | more_info: true
25 | # lower_bound: 0.00
26 | # upper_bound: 50.00
27 | show:
28 | icon_adaptive_color: true
29 | name_adaptive_color: true
30 | name: true
31 | icon: true
32 | state: true
33 | fill: fade
34 | legend: true
35 | extrema: true
36 | graph: bar
37 | color_thresholds:
38 | - value: 5
39 | color: "#2196F3"
40 | - value: 10
41 | color: "#0D47A1"
42 | - value: 15
43 | color: "#1976D2"
44 | - value: 20
45 | color: "#42A5F5"
46 | - value: 30
47 | color: "#90CAF9"
--------------------------------------------------------------------------------
/docs/homeassistant/lovelace/cards/smartmeter-water/card-water-usage.yaml:
--------------------------------------------------------------------------------
1 | ## #################################
2 | ## Card water usage
3 | ## #################################
4 | type: "custom:bar-card"
5 | entities:
6 | - entity: sensor.wasser_liter_h
7 | name: Jetzt
8 | min: 0.0
9 | max: 100.0
10 | - entity: sensor.wasser_liter_tag
11 | name: Heute
12 | min: 0.0
13 | max: 420.0
14 | - entity: sensor.wasser_liter_monat
15 | name: Monat
16 | min: 0.0
17 | max: 5200.0
18 | - entity: sensor.wasser_liter_jahr
19 | name: Jahr
20 | min: 0.0
21 | max: 120000.0
22 | title: Übersicht Wasserverbrauch
23 | title_icon: mdi:waves
24 | direction: up
25 | height: 150px
26 | width: 75%
27 | stack: horizontal
28 | animation: auto
29 | positions:
30 | icon: inside
31 | indicator: inside
32 | name: outside
33 | title: outside
34 | style: |
35 | ha-card{
36 | transform: scale(0.96);
37 | background-size: 100% 100%;
38 | }
39 | .card-header{
40 | padding: 6px 20px;
41 | font-size: 1.4em;
42 | text-align: left;
43 | color: #007aff;
44 | }
45 | .graph__legend { flex-wrap: nowrap; }
46 | #states { padding: 24px 24px; }
47 | bar-card-value { font-size: 0.9em }
48 | bar-card-current, bar-card-backgroundbar, bar-card-currentbar {
49 | border-radius: 0 0 1.6em 1.6em;
50 | }
51 |
--------------------------------------------------------------------------------
/docs/homeassistant/mqtt-data.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Wasserz\u00e4hler Haus",
3 | "device": "watermeter",
4 | "deviceid": "43430778",
5 | "date": "2021-03-13",
6 | "time": "13:45:46",
7 | "total_m3": 234.489,
8 | "m3": {
9 | "current": 0.0,
10 | "hour": 0.028,
11 | "day": 0.204,
12 | "month": 3.64,
13 | "year": 20.216
14 | },
15 | "liter": {
16 | "current": 0.0,
17 | "hour": 28.0,
18 | "day": 204.0,
19 | "month": 3640.0,
20 | "year": 20216.0
21 | },
22 | "last_total": {
23 | "hour": "13",
24 | "hour_m3": 234.461,
25 | "day": "2021-03-13",
26 | "day_m3": 234.285,
27 | "month": "2021-03",
28 | "month_m3": 230.849,
29 | "year": "2021",
30 | "year_m3": 214.273
31 | },
32 | "alarm": "no_alarm",
33 | "last_alarm": "no_alarm",
34 | "periode": "2021-03-13",
35 | "month": "2021-03",
36 | "year": "2021",
37 | "timestamp": "2021-03-13T13:45:46Z",
38 | "last_update": "2021-03-13 14:45:46.914795",
39 | "elapsed_time": "0:00:17",
40 | "icon": "mdi:billboard",
41 | "unit_of_measurement": "\u33a5",
42 | "version": "1.0.0",
43 | "attribution": "Data provided by Peter Siebler",
44 | "data_provider": "smarthome.siebler.home"
45 | }
46 |
--------------------------------------------------------------------------------
/docs/homeassistant/view_watermeter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/homeassistant/view_watermeter.png
--------------------------------------------------------------------------------
/docs/homeassistant/water_barcard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/homeassistant/water_barcard.png
--------------------------------------------------------------------------------
/docs/homeassistant/water_homekitpanel_card.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/homeassistant/water_homekitpanel_card.png
--------------------------------------------------------------------------------
/docs/liter-pro-stunde.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/liter-pro-stunde.png
--------------------------------------------------------------------------------
/docs/logging.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/logging.png
--------------------------------------------------------------------------------
/docs/nanoCUL868.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/nanoCUL868.png
--------------------------------------------------------------------------------
/docs/nanocul_v3/nanocul.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/nanocul_v3/nanocul.jpg
--------------------------------------------------------------------------------
/docs/rpw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/rpw.png
--------------------------------------------------------------------------------
/docs/rtl-sdr4linux_quickstartguidev20.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/rtl-sdr4linux_quickstartguidev20.pdf
--------------------------------------------------------------------------------
/docs/sampedata.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Wasserz\u00e4hler Haus",
3 | "device": "watermeter",
4 | "deviceid": "43430778",
5 | "date": "2020-07-15",
6 | "time": "13:54:45",
7 | "total_m3": 166.426,
8 | "m3": {
9 | "current": 0.0,
10 | "hour": 0.004,
11 | "day": 1.029,
12 | "month": 3.812,
13 | "year": 48.696
14 | },
15 | "liter": {
16 | "current": 0.0,
17 | "hour": 4.0,
18 | "day": 1029.0,
19 | "month": 3812.0,
20 | "year": 48696.0
21 | },
22 | "last_total": {
23 | "hour": "13",
24 | "hour_m3": 166.422,
25 | "day": "2020-07-15",
26 | "day_m3": 165.397,
27 | "month": "2020-07",
28 | "month_m3": 162.614,
29 | "year": "2020-01-01",
30 | "year_m3": 117.73
31 | },
32 | "alarm": "no_alarm",
33 | "last_alarm": "no_alarm",
34 | "periode": "2020-07-15",
35 | "month": "2020-07",
36 | "year": "2020",
37 | "timestamp": "2020-07-15T13:54:45Z",
38 | "last_update": "2020-07-15 15:54:45.287626",
39 | "elapsed_time": "0:00:09",
40 | "icon": "mdi:billboard",
41 | "unit_of_measurement": "\u33a5",
42 | "version": "1.0.0",
43 | "attribution": "Data provided by Peter Siebler",
44 | "data_provider": "ubuntu server"
45 | }
--------------------------------------------------------------------------------
/docs/top-nanocul.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/top-nanocul.png
--------------------------------------------------------------------------------
/docs/top-rtlsdr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/docs/top-rtlsdr.png
--------------------------------------------------------------------------------
/docs/wmbusdata.json:
--------------------------------------------------------------------------------
1 | {
2 | "media": "water",
3 | "meter": "izar",
4 | "name": "watermeter",
5 | "id": "1231150",
6 | "total_m3": 166.625,
7 | "last_month_total_m3": 162.614,
8 | "last_month_measure_date": "2020-07-01",
9 | "remaining_battery_life_y": 13,
10 | "current_alarms": "no_alarm",
11 | "previous_alarms": "no_alarm",
12 | "timestamp": "2020-07-16T11:32:38Z"
13 | }
--------------------------------------------------------------------------------
/docs/wmbusmeters-nano_cul.md:
--------------------------------------------------------------------------------
1 | # wmbusmeters version: 0.9.34 with NANO-CUL (mbus) 868 Mhz
2 |
3 | 
4 |
5 |
6 |
7 | NANO-CUL (mbus) 868 is a lot easier to use because it only requires `wmbusmeters`.
8 | The resources (memory) required are also less compared to the variant with RTL-SDR and rtl-wmbus.
9 | #### RTL-SDR and rtl-wmbus: 32 % CPU
10 | 
11 |
12 | The tests also show that the signal processing is better and thus the range for receiving the data is increased.
13 | #### NANO-CUL (mbus) 868: 0.7 % CPU !
14 | 
15 |
16 | `nano CUL 868 mit Externer Magnetfußantenne` , see: https://www.smart-home-komponente.de/
17 |
18 | ## Update system
19 |
20 | The following is required before installation:
21 |
22 | ```bash
23 | $ sudo apt-get update
24 | $ sudo apt-get install build-essential libncurses-dev cmake automake make
25 | $ sudo apt-get install -y mosquitto mosquitto-clients librtlsdr-dev libxml2-dev
26 | ```
27 |
28 | ## Build wmbusmeters
29 |
30 | The program receives and decodes C1,T1 or S1 telegrams (using the wireless mbus protocol) to acquire utility meter readings. The readings can then be published using MQTT, curled to a REST api, inserted into a database or stored in a log file.
31 |
32 | ```bash
33 | $ git clone https://github.com/weetmuts/wmbusmeters.git
34 | $ cd wmbusmeters
35 | $ make && make test
36 |
37 | $ make install
38 |
39 | ## optional, only if debuging
40 | $ make DEBUG=true
41 |
42 | $ lsusb
43 | ## --> Bus 001 Device 004: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
44 |
45 | $ ls -l /dev/serial/by-id
46 | ## --> lrwxrwxrwx 1 root root 13 Feb 8 09:33 usb-SHK_NANO_CUL_868-if00-port0 -> ../../ttyUSB0
47 |
48 | $ ls -l /dev/ttyUSB0
49 | ## >> wmbusmeters 188, 0 Jul 21 13:50 ttyUSB0
50 |
51 | $ dmesg | grep ttyUSB0
52 | ##[ 491.198770] usb 2-5: FTDI USB Serial Device converter now attached to ttyUSB0
53 | ##[ 2413.835006] ftdi_sio ttyUSB0: FTDI USB Serial Device converter now ##disconnected from ttyUSB0
54 | ##[ 2419.879307] usb 2-5: FTDI USB Serial Device converter now attached to ttyUSB0
55 |
56 |
57 | # get the id for the watermeter
58 | $ wmbusmeters --listento=t1 /dev/ttyUSB0
59 |
60 | ```
61 |
62 | Result from the test: `wmbusmeters --listento=t1 /dev/ttyUSB0`
63 |
64 | ```verilog
65 | Results:
66 |
67 | No meters configured. Printing id:s of all telegrams heard!
68 |
69 | Received telegram from: 43410778
70 | manufacturer: (DME) DIEHL Metering, Germany
71 | device type: Pressure meter
72 | Received telegram from: 15300778
73 | manufacturer: (DME) DIEHL Metering, Germany
74 | device type: A/D converter
75 | Received telegram from: 43430778
76 | manufacturer: (DME) DIEHL Metering, Germany
77 | device type: Pressure meter
78 | ```
79 |
80 |
81 |
82 | ## Debug and test installed wmbusmeters
83 |
84 | ```bash
85 | wmbusmeters --listento=t1 --debug --logtelegrams /dev/ttyUSB0 diehl izar 43410778 NOKEY
86 |
87 | >> (izar) 0a: a2 tpl-ci-field (Mfct specific)
88 | diehl 43410778 87.511 m3 84.654 m3 2020-07-01 13 y no_alarm no_alarm 2020-07-21 14:08.40
89 |
90 |
91 | ```
92 |
93 |
94 |
95 | ## Configuration wmbusmeters
96 |
97 | Check the config file `nano /etc/wmbusmeters.conf` and edit the device to point to your dongle.
98 |
99 | ```ini
100 | loglevel=normal
101 | device=/dev/ttyUSB0
102 | logtelegrams=true
103 | format=json
104 | meterfiles=/var/log/wmbusmeters/meter_readings
105 | meterfilesaction=overwrite
106 | meterfilesnaming=name
107 | meterfilestimestamp=hour
108 | logfile=/var/log/wmbusmeters/wmbusmeters.log
109 | shell=/usr/bin/mosquitto_pub -h mbs.siebler.home -p 1883 -u "username" -P "password" -t tele/testsensor/verbrauch -m "$METER_JSON"
110 | ```
111 |
112 |
113 |
114 | Then add a meter file in `nano /etc/wmbusmeters.d/diehl`
115 |
116 | ```ini
117 | name=watermeter
118 | type=izar
119 | id=43430778
120 | key=
121 | ```
122 |
123 |
124 |
125 | ## Service wmbusmeters
126 |
127 | ```bash
128 | $ sudo systemctl daemon-reload
129 |
130 | $ systemctl start wmbusmeters
131 | $ systemctl status wmbusmeters
132 | $ systemctl enable wmbusmeters
133 | $ systemctl stop wmbusmeters
134 | $ systemctl restart wmbusmeters
135 |
136 | ```
137 |
138 |
--------------------------------------------------------------------------------
/esphome/.gitignore:
--------------------------------------------------------------------------------
1 | # Gitignore settings for ESPHome
2 | # This is an example and may include too much for your use-case.
3 | # You can modify this file to suit your needs.
4 | /.esphome/
5 | /secrets.yaml
6 |
--------------------------------------------------------------------------------
/esphome/custom_components/backup/README.md:
--------------------------------------------------------------------------------
1 | # Backup
2 |
3 | Save your ESPHome device configuration in firmware and and recover it if you lost source files. Just download it from `http://yourdevice.local/config.yaml`.
4 |
5 | > WARNING: You should backup your all your files, this solution only for emergency purpose. I am not responsible for the loss or inability to recover data.
6 |
7 | > WARNING: Stored configuration is the same as shown by `esphome config` command. It is fully worked but not the same as your original sources.
8 |
9 | > WARNING: Command line substitutions, custom components and includes are not supported yet!
10 |
11 | The configuration is very simple. Look at a sample below:
12 | ```yaml
13 | # Full example of configuration entry
14 | ...
15 | external_components:
16 | - source:
17 | type: git
18 | url: https://github.com/zdzichu6969/esphome_components
19 | components: [ backup ]
20 | refresh: 0s
21 | ...
22 | backup:
23 | auth:
24 | username: !secret web_username
25 | password: !secret web_password
26 | force_update: false
27 | ```
28 |
29 | ## Configuration variables:
30 | * **auth** (*Optional*): Enables basic authentication with username and password.
31 | * **username** (**Required**, string): The username to use for authentication.
32 | * **password** (**Required**, string): The password to check for authentication.
33 | * **force_update** (*Optional*, boolean): Unnecessary but might be needed when you changes in separated files are not detected properly. Default is False.
34 |
--------------------------------------------------------------------------------
/esphome/custom_components/backup/__init__.py:
--------------------------------------------------------------------------------
1 | from io import BytesIO
2 | from gzip import GzipFile
3 | from pathlib import Path
4 | import logging
5 | import esphome.codegen as cg
6 | import esphome.config_validation as cv
7 | from esphome.config import strip_default_ids
8 | from esphome.cpp_generator import ArrayInitializer
9 | from esphome.yaml_util import dump, load_yaml
10 | from esphome.core import CORE, coroutine_with_priority, ID
11 | from esphome.components import web_server_base
12 | from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
13 | from esphome.const import (
14 | CONF_AUTH,
15 | CONF_FORCE_UPDATE,
16 | CONF_ID,
17 | CONF_PASSWORD,
18 | CONF_RAW_DATA_ID,
19 | CONF_USERNAME,
20 | )
21 |
22 | _LOGGER = logging.getLogger(__name__)
23 |
24 | AUTO_LOAD = ["web_server_base", "web_server"]
25 |
26 | CONF_BACKUP = "backup"
27 |
28 | backup_ns = cg.esphome_ns.namespace("backup")
29 | Backup = backup_ns.class_("Backup", cg.Component)
30 |
31 | CONFIG_SCHEMA = cv.Schema(
32 | {
33 | cv.GenerateID(): cv.declare_id(Backup),
34 | cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(
35 | web_server_base.WebServerBase
36 | ),
37 | cv.Optional(CONF_AUTH): cv.Schema(
38 | {
39 | cv.Required(CONF_USERNAME): cv.string_strict,
40 | cv.Required(CONF_PASSWORD): cv.string_strict,
41 | }
42 | ),
43 | cv.Optional(CONF_FORCE_UPDATE, default=False): cv.boolean,
44 | }
45 | ).extend(cv.COMPONENT_SCHEMA)
46 |
47 |
48 | def _dump_config():
49 | return dump(strip_default_ids(load_yaml(CORE.config_path)))
50 |
51 |
52 | @coroutine_with_priority(40.0)
53 | async def to_code(config):
54 | paren = await cg.get_variable(config[CONF_WEB_SERVER_BASE_ID])
55 |
56 | var = cg.new_Pvariable(config[CONF_ID], paren)
57 | await cg.register_component(var, config)
58 |
59 | if CONF_AUTH in config:
60 | username = config[CONF_AUTH][CONF_USERNAME]
61 | if username:
62 | cg.add(var.set_username(username))
63 | password = config[CONF_AUTH][CONF_PASSWORD]
64 | if password:
65 | cg.add(var.set_password(password))
66 |
67 | gz_io = BytesIO()
68 | with GzipFile(
69 | fileobj=gz_io,
70 | mode="wb",
71 | compresslevel=9,
72 | mtime=None
73 | if config[CONF_FORCE_UPDATE]
74 | else Path(CORE.config_path).stat().st_mtime,
75 | ) as f:
76 | f.write(bytes(_dump_config(), encoding="utf8"))
77 |
78 | gz_config = gz_io.getvalue()
79 |
80 | arr = cg.progmem_array(
81 | ID(
82 | "backup_data_43c31a8938664f3a97597d916cb5c2ba",
83 | is_declaration=True,
84 | type=cg.uint8,
85 | ),
86 | ArrayInitializer(*[o for o in gz_config]),
87 | )
88 | cg.add(var.set_config(arr, len(gz_config)))
89 |
90 | _LOGGER.info(f"Backup config will take: {len(gz_config)} bytes")
91 |
--------------------------------------------------------------------------------
/esphome/custom_components/backup/backup.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include "esphome/core/log.h"
3 | #include "esphome/core/application.h"
4 | #include "esphome/core/util.h"
5 | #include "esphome/components/json/json_util.h"
6 |
7 | #include "StreamString.h"
8 |
9 | #include
10 |
11 | #include "backup.h"
12 |
13 | namespace esphome {
14 | namespace backup {
15 |
16 | static const char *const TAG = "backup";
17 | static const char *const URL = "/config.yaml";
18 |
19 | void Backup::setup() {
20 | ESP_LOGCONFIG(TAG, "Setting up backup handler...");
21 | this->base_->init();
22 | this->base_->get_server()->on(URL, HTTP_GET, [this](AsyncWebServerRequest *request) {
23 | if (this->using_auth() && !request->authenticate(this->username_, this->password_)) {
24 | return request->requestAuthentication();
25 | }
26 | auto response = request->beginResponse_P(200, "plain/text;charset=UTF-8", this->data_, this->size_);
27 | response->addHeader("Content-Encoding", "gzip");
28 | request->send(response);
29 | });
30 | }
31 |
32 | void Backup::dump_config() {
33 | ESP_LOGCONFIG(TAG, "Backup:");
34 | ESP_LOGCONFIG(TAG, " URL path is %s", URL);
35 | if (this->using_auth()) {
36 | ESP_LOGCONFIG(TAG, " Basic authentication enabled");
37 | }
38 | }
39 |
40 | } // namespace backup
41 | } // namespace esphome
42 |
--------------------------------------------------------------------------------
/esphome/custom_components/backup/backup.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "esphome/core/component.h"
4 | #include "esphome/core/controller.h"
5 | #include "esphome/components/web_server_base/web_server_base.h"
6 |
7 | #include
8 |
9 | namespace esphome {
10 | namespace backup {
11 |
12 | class Backup : public Component {
13 | public:
14 | explicit Backup(web_server_base::WebServerBase *base) : base_(base) {}
15 |
16 | void setup() override;
17 | void dump_config() override;
18 | float get_setup_priority() const override { return setup_priority::WIFI - 1.0f; }
19 |
20 | void set_config(const uint8_t *data, size_t size) {
21 | this->data_ = data;
22 | this->size_ = size;
23 | }
24 |
25 | void set_username(const char *username) { this->username_ = username; }
26 |
27 | void set_password(const char *password) { this->password_ = password; }
28 |
29 | bool using_auth() { return this->username_ != nullptr && this->password_ != nullptr; }
30 |
31 | protected:
32 | web_server_base::WebServerBase *base_;
33 | const uint8_t *data_{nullptr};
34 | size_t size_{0};
35 | const char *username_{nullptr};
36 | const char *password_{nullptr};
37 | };
38 |
39 | } // namespace backup
40 | } // namespace esphome
41 |
--------------------------------------------------------------------------------
/esphome/custom_components/crash_info/README.md:
--------------------------------------------------------------------------------
1 | # Crash Info
2 |
3 | An ESP8266 remote crash detector.
4 | With enabled indicator you can monitor crashes right from your Home Assistant.
5 |
6 | ## Advanced usage
7 | Connect to you ESP8266 and find crash backtrace information in the log.
8 | Copy stack frames and decode with `xtensa-lx106-elf-addr2line -aipfC -e $elf ...` command.
9 |
10 | > By default crash information stored in RTC memory and do not survive power loss. You can change this bahaviour by store_in_flash by be carefull it is may not work properly due dynamic memory usage in this procedure.
11 |
12 | ```yaml
13 | # Example configuration entry.
14 | ...
15 | external_components:
16 | - source:
17 | type: git
18 | url: https://github.com/zdzichu6969/esphome_components
19 | components: [ crash_info ]
20 | refresh: 0s
21 | ...
22 | crash_info:
23 | id: crash_info_obj
24 | # Optional, uint32. The number of stack frames to be saved.
25 | max_stack_frames_size: 10
26 | # Optional, uint32. Minimum address of stack frame to be saved. Default: 0x40000000.
27 | min_stack_frames_addr: 0x40000000
28 | # Optional, uint32. Maximum address of stack frame to be saved. Default: 0x50000000.
29 | max_stack_frames_addr: 0x50000000
30 | # Optional, binary_sensor. Crash indicator.
31 | indicator:
32 | name: $name Crash state
33 | # Optional, boolean. Store backtrace in FLASH or RTC. Default: false.
34 | store_in_flash: false
35 | # Optional, uint. Break line after this number of frames. Default: 4
36 | frames_in_line: 4
37 |
38 | # Add button to reset state of crash.
39 | button:
40 | - platform: template
41 | name: $name Reset crash state
42 | on_press:
43 | lambda: id(crash_info_obj).reset();
44 |
45 | # Add sntp or homeassistant time platform to enable saving crash time.
46 | time:
47 | - platform: sntp
48 | timezone: UTC-3
49 | id: time_sntp_obj
50 |
51 | ```
52 |
--------------------------------------------------------------------------------
/esphome/custom_components/crash_info/__init__.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import esphome.codegen as cg
3 | import esphome.config_validation as cv
4 | from esphome.core import CORE
5 | from esphome.cpp_types import Component
6 | from esphome.components import binary_sensor
7 | from esphome.const import (
8 | DEVICE_CLASS_PROBLEM,
9 | ENTITY_CATEGORY_DIAGNOSTIC,
10 | PLATFORM_ESP8266,
11 | CONF_ID,
12 | )
13 |
14 | _LOGGER = logging.getLogger(__name__)
15 |
16 | ESP_PLATFORMS = [PLATFORM_ESP8266]
17 | AUTO_LOAD = ["binary_sensor"]
18 |
19 | CONF_MAX_STACK_FRAMES_SIZE = "max_stack_frames_size"
20 | CONF_MIN_STACK_FRAMES_ADDR = "min_stack_frames_addr"
21 | CONF_MAX_STACK_FRAMES_ADDR = "max_stack_frames_addr"
22 | CONF_INDICATOR = "indicator"
23 | CONF_STORE_IN_FLASH = "store_in_flash"
24 | CONF_STORE_FREE_HEAP = "store_free_heap"
25 | CONF_FRAMES_IN_LINE = "frames_in_line"
26 |
27 | MAX_STACK_FRAMES = 120
28 |
29 | crash_info_ns = cg.esphome_ns.namespace("crash_info")
30 | CrashInfo = crash_info_ns.class_("CrashInfo", Component)
31 |
32 | CONFIG_SCHEMA = cv.Schema(
33 | {
34 | cv.GenerateID(): cv.declare_id(CrashInfo),
35 | cv.Optional(CONF_MAX_STACK_FRAMES_SIZE, default=10): cv.All(
36 | cv.positive_int, cv.int_range(4, MAX_STACK_FRAMES)
37 | ),
38 | cv.Optional(CONF_MIN_STACK_FRAMES_ADDR, default=0x40000000): cv.All(
39 | cv.positive_int, cv.int_range(min=1, max=0x70000000)
40 | ),
41 | cv.Optional(CONF_MAX_STACK_FRAMES_ADDR, default=0x50000000): cv.All(
42 | cv.positive_int, cv.int_range(min=1, max=0x70000000)
43 | ),
44 | cv.Optional(CONF_INDICATOR): binary_sensor.binary_sensor_schema(
45 | device_class=DEVICE_CLASS_PROBLEM,
46 | entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
47 | ),
48 | cv.Optional(CONF_STORE_IN_FLASH, default=False): cv.boolean,
49 | cv.Optional(CONF_STORE_FREE_HEAP, default=False): cv.boolean,
50 | cv.Optional(CONF_FRAMES_IN_LINE, default=4): cv.All(
51 | cv.positive_int, cv.int_range(min=1, max=MAX_STACK_FRAMES)
52 | ),
53 | }
54 | )
55 |
56 |
57 | async def to_code(config):
58 | var = cg.new_Pvariable(config[CONF_ID])
59 | await cg.register_component(var, config)
60 |
61 | if CONF_INDICATOR in config:
62 | conf = config[CONF_INDICATOR]
63 | sens = cg.new_Pvariable(conf[CONF_ID])
64 | await binary_sensor.register_binary_sensor(sens, conf)
65 | cg.add(var.set_indicator(sens))
66 |
67 | max_stack_frames_size = config[CONF_MAX_STACK_FRAMES_SIZE]
68 | cg.add_define("CRASH_INFO_MAX_STACK_FRAMES_SIZE", max_stack_frames_size)
69 | min_stack_frames_addr = config[CONF_MIN_STACK_FRAMES_ADDR]
70 | cg.add_define("CRASH_INFO_MIN_STACK_FRAMES_ADDR", min_stack_frames_addr)
71 | max_stack_frames_addr = config[CONF_MAX_STACK_FRAMES_ADDR]
72 | cg.add_define("CRASH_INFO_MAX_STACK_FRAMES_ADDR", max_stack_frames_addr)
73 | frames_in_line = config[CONF_FRAMES_IN_LINE]
74 | cg.add_define("CRASH_INFO_FRAMES_IN_LINE", frames_in_line)
75 |
76 | if config[CONF_STORE_IN_FLASH]:
77 | cg.add_define("CRASH_INFO_STORE_IN_FLASH")
78 |
79 | num_bytes = 1 + 1 + (config[CONF_MAX_STACK_FRAMES_SIZE] * 4)
80 | extended_with = []
81 |
82 | if config[CONF_STORE_FREE_HEAP]:
83 | cg.add_define("CRASH_INFO_STORE_FREE_HEAP")
84 | num_bytes += 2
85 | extended_with.append("free_heap")
86 |
87 | if "time" in CORE.loaded_integrations:
88 | num_bytes += 8
89 | extended_with.append("time")
90 |
91 | if len(extended_with) > 0:
92 | extended_with = f" (extended with {', '.join(extended_with)})"
93 | else:
94 | extended_with = ""
95 |
96 | save_type = "FLASH" if config[CONF_STORE_IN_FLASH] else "RTC"
97 | _LOGGER.info(
98 | "Crash info will take %u bytes of %s memory%s",
99 | num_bytes,
100 | save_type,
101 | extended_with,
102 | )
103 |
--------------------------------------------------------------------------------
/esphome/custom_components/crash_info/crash_info.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "esphome/core/component.h"
4 | #include "esphome/core/preferences.h"
5 | #include "esphome/core/defines.h"
6 | #include "esphome/components/binary_sensor/binary_sensor.h"
7 |
8 | // maximum number of stack frames saved to RTC or FLASH.
9 | #ifndef CRASH_INFO_MAX_STACK_FRAMES_SIZE
10 | #define CRASH_INFO_MAX_STACK_FRAMES_SIZE 10
11 | #endif
12 | // minimum stack frame address.
13 | #ifndef CRASH_INFO_MIN_STACK_FRAMES_ADDR
14 | #define CRASH_INFO_MIN_STACK_FRAMES_ADDR 0x40000000
15 | #endif
16 | // maximum stack frame address.
17 | #ifndef CRASH_INFO_MAX_STACK_FRAMES_ADDR
18 | #define CRASH_INFO_MAX_STACK_FRAMES_ADDR 0x50000000
19 | #endif
20 | // number of frames to show in line
21 | #ifndef CRASH_INFO_FRAMES_IN_LINE
22 | #define CRASH_INFO_FRAMES_IN_LINE 4
23 | #endif
24 |
25 | namespace esphome {
26 | namespace crash_info {
27 |
28 | class CrashInfo : public Component {
29 | public:
30 | CrashInfo();
31 | float get_setup_priority() const override { return setup_priority::LATE; }
32 | void setup() override;
33 | void dump_config() override;
34 |
35 | void set_indicator(binary_sensor::BinarySensor *indicator) { this->indicator_ = indicator; }
36 |
37 | void reset();
38 |
39 | void save_crash_info(uint8_t reason, uint8_t exccause, const uint32_t *stack, size_t stack_size);
40 |
41 | uint32_t get_max_stack_frames_size() const { return CRASH_INFO_MAX_STACK_FRAMES_SIZE; }
42 | uint32_t get_min_stack_frames_addr() const { return CRASH_INFO_MIN_STACK_FRAMES_ADDR; }
43 | uint32_t get_max_stack_frames_addr() const { return CRASH_INFO_MAX_STACK_FRAMES_ADDR; }
44 | size_t get_frames_in_line() const { return CRASH_INFO_FRAMES_IN_LINE; }
45 |
46 | bool is_store_in_flash() const {
47 | #ifdef CRASH_INFO_STORE_IN_FLASH
48 | return true;
49 | #else
50 | return false;
51 | #endif
52 | }
53 |
54 | std::string get_stack_frames();
55 | uint16_t get_free_heap();
56 |
57 | protected:
58 | ESPPreferenceObject rtc_;
59 | binary_sensor::BinarySensor *indicator_{};
60 | };
61 |
62 | } // namespace crash_info
63 | } // namespace esphome
64 |
--------------------------------------------------------------------------------
/esphome/custom_components/heapmon/README.md:
--------------------------------------------------------------------------------
1 | # heap space monitor
2 |
3 | A sensor to show the available heap space.
4 |
5 | Example:
6 | ```yaml
7 | sensor:
8 | - platform: heapmon
9 | id: heapspace
10 | name: "Free Space"
11 | ```
12 |
--------------------------------------------------------------------------------
/esphome/custom_components/heapmon/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zibous/ha-watermeter/85a72753f238345cfd16fbfdd78204cf673dee2b/esphome/custom_components/heapmon/__init__.py
--------------------------------------------------------------------------------
/esphome/custom_components/heapmon/heapmon.cpp:
--------------------------------------------------------------------------------
1 | #include "heapmon.h"
2 | #include "esphome/core/log.h"
3 |
4 | #ifdef USE_ESP_IDF
5 | #include
6 | #include
7 | #endif
8 |
9 | #ifdef USE_ARDUINO
10 | #include
11 | #endif
12 |
13 | namespace esphome {
14 | namespace debug {
15 |
16 | static const char *TAG = "heapmon";
17 |
18 | void HeapMonitor::update() {
19 | uint32_t free_heap;
20 | #ifdef USE_ARDUINO
21 | free_heap = ESP.getFreeHeap();
22 | #elif defined(USE_ESP_IDF)
23 | free_heap = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
24 | #endif
25 |
26 | // ESP.getFreeHeap() - returns total amount of available memory
27 | // ESP.getHeapFragmentation() - returns an indication of how badly the heap is fragmented. The bigger, the worse - over 50 is very bad.
28 | // ESP.getMaxFreeBlockSize() - the biggest block you can allocate.
29 |
30 | ESP_LOGD(TAG, "Free Heap Size: %u bytes", free_heap);
31 | this->publish_state(free_heap);
32 | }
33 |
34 | } // namespace debug
35 | } // namespace esphome
36 |
--------------------------------------------------------------------------------
/esphome/custom_components/heapmon/heapmon.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "esphome/core/component.h"
4 | #include "esphome/components/sensor/sensor.h"
5 |
6 | namespace esphome {
7 | namespace debug {
8 |
9 | class HeapMonitor : public sensor::Sensor, public PollingComponent {
10 | public:
11 | void update() override;
12 | float get_setup_priority() const override { return setup_priority::LATE; };
13 | };
14 |
15 | } // namespace debug
16 | } // namespace esphome
17 |
--------------------------------------------------------------------------------
/esphome/custom_components/heapmon/sensor.py:
--------------------------------------------------------------------------------
1 | import esphome.config_validation as cv
2 | import esphome.codegen as cg
3 | from esphome.components import sensor
4 | from esphome.const import CONF_ID, ICON_GAUGE
5 |
6 | UNIT_BYTE = "B"
7 |
8 | debug_ns = cg.esphome_ns.namespace('debug')
9 | HeapMonitor = debug_ns.class_('HeapMonitor', cg.PollingComponent)
10 | CONFIG_SCHEMA = sensor.sensor_schema(
11 | HeapMonitor,
12 | unit_of_measurement=UNIT_BYTE,
13 | icon=ICON_GAUGE,
14 | accuracy_decimals=0
15 | ).extend(cv.polling_component_schema('60s'))
16 |
17 | async def to_code(config):
18 | var = cg.new_Pvariable(config[CONF_ID])
19 | await cg.register_component(var, config)
20 | await sensor.register_sensor(var, config)
21 |
--------------------------------------------------------------------------------
/esphome/custom_components/influxdb/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | *~
3 |
--------------------------------------------------------------------------------
/esphome/custom_components/influxdb/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019 Espen Skjelnes Johnsen
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
19 | OR OTHER DEALINGS IN THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/esphome/custom_components/influxdb/README.md:
--------------------------------------------------------------------------------
1 | # InfluxDB custom component for ESPHome
2 |
3 | This custom component allows you to send sensor updates from ESPHome to an
4 | InfluxDB database with UDP.
5 |
6 |
7 | ## Installation
8 |
9 | Clone this repository into `custom_components/influxdb` from the directory
10 | where your ESPHome configuration is stored.
11 |
12 | ```bash
13 | git clone https://github.com/la7dja/esphome-influxdb custom_components/influxdb
14 | ```
15 |
16 | ## Usage
17 |
18 | Add `influxdb` section to your ESPHome configuration file.
19 |
20 | ### Example configuration
21 |
22 | ```yaml
23 | influxdb:
24 | host: "influxdb-host"
25 | sensors:
26 | meter_id:
27 | ignore: True
28 | ams_temperature:
29 | measurement: 'temperature'
30 | tags:
31 | room: kitchen
32 | ```
33 |
34 | ### Configuration variables
35 |
36 | * **host** (Required, string): Hostname or IP for the InfluxDB server
37 | * **port** (Optional, int): Port number the InfluxDB server is listening on. Defaults to 8089.
38 | * **max\_packet_size** (Optional, int): Max size of UDP packets. Defaults to 500.
39 | * **send_timeout** (Optional, time): Time to wait before sending UDP packets which have not been filled to max size. Defaults to 100ms.
40 | * **publish_all** (Optional, boolean): If true publish updates from all sensors unless explicit ignored in per sensor configuration. If false only publish updates from sensors explicit configured. Defaults to True.
41 | * **tags** (Optional, mapping): Mapping of tag keys and values. Defaults to 'node: '.
42 | * **sensors** (Optional, mapping): Per sensor configuration. Keys are sensor IDs. All types of sensors are included in this mapping, there is no distinquin bettwi float, binary and text sensors.
43 |
44 | #### Sensor configuration variables
45 |
46 | * **ignore** (Optional, boolean): Whether or not to include updates for this sensor. Defaults to True.
47 | * **measurement** (Optional, string): Name of measurements with update from this sensor. Defaults to the sanitized name of the sensor.
48 | * **tags** (Optional, mapping): Additional tags added for this sensor.
49 |
--------------------------------------------------------------------------------
/esphome/custom_components/influxdb/__init__.py:
--------------------------------------------------------------------------------
1 | import esphome.codegen as cg
2 | import esphome.config_validation as cv
3 | from esphome.const import CONF_ID, CONF_PORT
4 | from esphome.core import coroutine_with_priority
5 | from esphome.core import CORE
6 |
7 | influxdb_ns = cg.esphome_ns.namespace('influxdb')
8 | InfluxDBWriter = influxdb_ns.class_('InfluxDBWriter', cg.Component, cg.Controller)
9 |
10 | CONF_HOST= 'host'
11 | CONF_SEND_TIMEOUT= 'send_timeout'
12 | CONF_MAX_PACKET_SIZE= 'max_packet_size'
13 | CONF_TAGS = 'tags'
14 | CONF_PUBLISH_ALL = 'publish_all'
15 | CONF_SENSORS = 'sensors'
16 | CONF_IGNORE = 'ignore'
17 | CONF_MEASUREMENT = 'measurement'
18 |
19 |
20 | SENSOR_SCHEMA = cv.Schema({
21 | cv.validate_id_name:
22 | cv.Schema({
23 | cv.Optional(CONF_IGNORE, default=False): cv.boolean,
24 | cv.Optional(CONF_MEASUREMENT): cv.string,
25 | cv.Optional(CONF_TAGS, default={}): cv.Schema({
26 | cv.string: cv.string
27 | })
28 | })
29 | })
30 |
31 | CONFIG_SCHEMA = cv.Schema({
32 | cv.GenerateID(): cv.declare_id(InfluxDBWriter),
33 | cv.Required(CONF_HOST): cv.domain,
34 | cv.Optional(CONF_PORT, default=8089): cv.port,
35 | cv.Optional(CONF_SEND_TIMEOUT, default='100ms'): cv.positive_time_period_milliseconds,
36 | cv.Optional(CONF_MAX_PACKET_SIZE, default=500): cv.positive_not_null_int,
37 | cv.Optional(CONF_PUBLISH_ALL, default=True): cv.boolean,
38 | cv.Optional(CONF_TAGS, default={'node': CORE.name}): cv.Schema({
39 | cv.valid_name: cv.valid_name
40 | }),
41 | cv.Optional(CONF_SENSORS, default={}): SENSOR_SCHEMA,
42 | }).extend(cv.COMPONENT_SCHEMA)
43 |
44 |
45 | @coroutine_with_priority(40)
46 | def to_code(config):
47 | var = cg.new_Pvariable(config[CONF_ID])
48 | yield cg.register_component(var, config)
49 |
50 | cg.add(var.set_host(config[CONF_HOST]))
51 | cg.add(var.set_port(config[CONF_PORT]))
52 | cg.add(var.set_max_packet_size(config[CONF_MAX_PACKET_SIZE]))
53 | cg.add(var.set_send_timeout(config[CONF_SEND_TIMEOUT]))
54 | cg.add(var.set_publish_all(config[CONF_PUBLISH_ALL]))
55 | cg.add(var.set_tags(''.join(',{}={}'.format(tag, value) for tag, value in config[CONF_TAGS].items())))
56 | for sensor_id, sensor_config in config[CONF_SENSORS].items():
57 | if sensor_config[CONF_IGNORE] == False:
58 | tags = ''.join(',{}={}'.format(tag, value) for tag, value in {**config[CONF_TAGS] , **sensor_config[CONF_TAGS]}.items())
59 | if 'measurement' in sensor_config:
60 | measurement = f"\"{sensor_config[CONF_MEASUREMENT]}\""
61 | else:
62 | measurement = f"{sensor_id}->get_object_id()"
63 | cg.add(var.add_setup_callback(cg.RawExpression(f"[]() -> Nameable* {{ {sensor_id}->add_on_state_callback([](float state) {{ {config[CONF_ID]}->on_sensor_update({sensor_id}, {measurement}, \"{tags}\", state); }}); return {sensor_id}; }}")))
64 | else:
65 | cg.add(var.add_setup_callback(cg.RawExpression(f"[]() -> Nameable* {{ return {sensor_id}; }}")))
66 |
67 | cg.add_define('USE_INFLUXDB')
68 | cg.add_global(influxdb_ns.using)
69 |
--------------------------------------------------------------------------------
/esphome/custom_components/influxdb/influxdb_writer.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "esphome/core/log.h"
4 | #include "esphome/core/application.h"
5 | #include "influxdb_writer.h"
6 |
7 | #ifdef USE_LOGGER
8 | #include "esphome/components/logger/logger.h"
9 | #endif
10 |
11 | namespace esphome {
12 | namespace influxdb {
13 |
14 | static const char *TAG = "influxdb";
15 |
16 | void InfluxDBWriter::setup() {
17 | ESP_LOGCONFIG(TAG, "Setting up InfluxDB Writer...");
18 | std::vector objs;
19 | for(auto fun: setup_callbacks)
20 | objs.push_back(fun());
21 |
22 | if (publish_all) {
23 | #ifdef USE_BINARY_SENSOR
24 | for (auto *obj : App.get_binary_sensors()) {
25 | if (!obj->is_internal() && std::none_of(objs.begin(), objs.end(), [&obj](Nameable *o) {return o == obj;}))
26 | obj->add_on_state_callback([this, obj](bool state) { this->on_sensor_update(obj, obj->get_object_id(), tags, state); });
27 | }
28 | #endif
29 | #ifdef USE_SENSOR
30 | for (auto *obj : App.get_sensors()) {
31 | if (!obj->is_internal() && std::none_of(objs.begin(), objs.end(), [&obj](Nameable *o) {return o == obj;}))
32 | obj->add_on_state_callback([this, obj](float state) { this->on_sensor_update(obj, obj->get_object_id(), tags, state); });
33 | }
34 | #endif
35 | #ifdef USE_TEXT_SENSOR
36 | for (auto *obj : App.get_text_sensors()) {
37 | if (!obj->is_internal() && std::none_of(objs.begin(), objs.end(), [&obj](Nameable *o) {return o == obj;}))
38 | obj->add_on_state_callback([this, obj](std::string state) { this->on_sensor_update(obj, obj->get_object_id(), tags, state); });
39 | }
40 | #endif
41 | }
42 | }
43 |
44 | void InfluxDBWriter::loop() {
45 | if (packet_size > 0 && millis() >= packet_timeout) {
46 | udp.endPacket();
47 | packet_size = 0;
48 | }
49 | }
50 |
51 | void InfluxDBWriter::write(std::string measurement, std::string tags, const std::string value, bool is_string)
52 | {
53 | std::string line = measurement + tags + " value=" + (is_string ? ("\"" + value + "\"") : value);
54 |
55 | if (line.size() > max_packet_size) {
56 | ESP_LOGW(TAG, "Line too large to fit in UDP packet: %s", line.c_str());
57 | return;
58 | }
59 |
60 | if (packet_size + line.size() + 1 > max_packet_size) {
61 | udp.endPacket();
62 | packet_size = 0;
63 | }
64 |
65 | if (packet_size == 0) {
66 | packet_timeout = millis() + send_timeout;
67 | udp.beginPacket(host.c_str(), port);
68 | } else
69 | udp.write('\n');
70 |
71 | udp.write(line.data(), line.size());
72 | packet_size += line.size();
73 | }
74 |
75 | void InfluxDBWriter::dump_config() {
76 | ESP_LOGCONFIG(TAG, "InfluxDB Writer:");
77 | ESP_LOGCONFIG(TAG, " Address: %s:%u", host.c_str(), port);
78 | }
79 |
80 | #ifdef USE_BINARY_SENSOR
81 | void InfluxDBWriter::on_sensor_update(binary_sensor::BinarySensor *obj, std::string measurement, std::string tags, bool state) {
82 | write(measurement, tags, state ? "t" : "f", false);
83 | }
84 | #endif
85 |
86 | #ifdef USE_SENSOR
87 | void InfluxDBWriter::on_sensor_update(sensor::Sensor *obj, std::string measurement, std::string tags, float state) {
88 | write(measurement, tags, to_string(state), false);
89 | }
90 | #endif
91 |
92 | #ifdef USE_TEXT_SENSOR
93 | void InfluxDBWriter::on_sensor_update(text_sensor::TextSensor *obj, std::string measurement, std::string tags, std::string state) {
94 | write(measurement, tags, state, true);
95 | }
96 | #endif
97 |
98 | } // namespace api
99 | } // namespace esphome
100 |
--------------------------------------------------------------------------------
/esphome/custom_components/influxdb/influxdb_writer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include "esphome/core/component.h"
7 | #include "esphome/core/controller.h"
8 | #include "esphome/core/defines.h"
9 | #include "esphome/core/log.h"
10 |
11 | namespace esphome {
12 | namespace influxdb {
13 |
14 | // struct SensorConfig {
15 | // bool ignore;
16 | // std::string measurement;
17 | // std:vector> tags;
18 | // }
19 |
20 | class InfluxDBWriter : public Component {
21 | public:
22 | InfluxDBWriter() : packet_size(0) { };
23 | void setup() override;
24 | void loop() override;
25 | void dump_config() override;
26 | #ifdef USE_BINARY_SENSOR
27 | void on_sensor_update(binary_sensor::BinarySensor *obj, std::string measurement, std::string tags, bool state);
28 | #endif
29 | #ifdef USE_SENSOR
30 | void on_sensor_update(sensor::Sensor *obj, std::string measurement, std::string tags, float state);
31 | #endif
32 | #ifdef USE_TEXT_SENSOR
33 | void on_sensor_update(text_sensor::TextSensor *obj, std::string measurement, std::string tags, std::string state);
34 | #endif
35 |
36 | void set_host(std::string host) { this->host = host; };
37 | void set_port(uint16_t port) { this->port = port; };
38 | void set_send_timeout(int timeout) { send_timeout = timeout; };
39 | void set_max_packet_size(int size) { max_packet_size = size; };
40 | void set_tags(std::string tags) { this->tags = tags; };
41 | void set_publish_all(bool all) { publish_all = all; };
42 | void add_setup_callback(std::function fun) { setup_callbacks.push_back(fun); };
43 |
44 | protected:
45 | void write(std::string measurement, std::string tags, const std::string value, bool is_string);
46 |
47 | uint16_t port;
48 | std::string host;
49 | int max_packet_size;
50 | int send_timeout;
51 | std::string tags;
52 | bool publish_all;
53 |
54 | std::vector> setup_callbacks;
55 |
56 | WiFiUDP udp;
57 | int packet_size;
58 | int packet_timeout;
59 | };
60 |
61 | } // namespace api
62 | } // namespace esphome
63 |
--------------------------------------------------------------------------------
/esphome/custom_components/izar-wmbus/README.md:
--------------------------------------------------------------------------------
1 | # Izar water meter reader (ESP8266 and ESP32 -based)
2 | The idea behind the project is to read Izar water meter current consumption
3 |
4 | Implementation is naive, and hardcoded in many places. It was meant to be simple ;)
5 |
6 | ## Required components
7 | - NodeMCU (however any ESP8266-based device should work fine)
8 | - CC1101 module (ideally with an antena tuned to 868MHz *)
9 |
10 | _Note_: While CC1101 chip is versatile and may by configured programatically to use different frequency, some PCB components has to be selected for better performance. Therefore, please pay attention to get the right one.More info here: https://wiki.fhem.de/wiki/Selbstbau_CUL
11 |
12 | I was able to use CC1101 board tuned for 433MHz correctly, though ;)
13 |
14 | ## Usage
15 |
16 | ```
17 | IzarWmbus reader;
18 |
19 | void setup() {
20 | //somewhere in the setup
21 | reader.init(0);
22 | }
23 |
24 |
25 | IzarResultData data;
26 |
27 | void loop() {
28 | FetchResult result = reader.fetchPacket(&data);
29 | if (result == FETCH_SUCCESSFUL) {
30 | // we have new package!
31 | }
32 | }
33 |
34 | ```
35 |
36 |
37 | ## Wiring
38 | ### ESP8266
39 | ```
40 | CC1101 -> NodeMCU
41 | ==================
42 | SCK -> D5
43 | MISO -> D6
44 | MOSI -> D7
45 | CSN -> D8
46 | VCC -> 3V
47 | GND -> GND
48 | ```
49 |
50 | ### ESP32-CAM
51 | My main challenge with wiring ESP32-CAM was use of the default SPI connection by camera module, so I had to divert to other pin set
52 | ```
53 |
54 | CC1101 -> ESP32(left side)
55 | ============================
56 | 5V
57 | GND
58 | MISO -> GPIO12
59 | MOSI -> GPIO13
60 | CSN -> GPIO15
61 | SCK -> GPIO14
62 | GPIO2
63 | GPIO4
64 |
65 |
66 | (right side)
67 | VCC -> 3V3
68 | GDN -> GND
69 | ```
70 |
71 | More details about wiring in library this tool is using for connecting with CC1101: https://github.com/LSatan/SmartRC-CC1101-Driver-Lib
72 |
73 | ### CC1101 868Mhz pinout
74 | (connectors on the left, ANT on the right)
75 |
76 | ```
77 | VCC -----------------
78 | GND | |
79 | MOSI | |
80 | SCK | CC1101 | GND
81 | MISO | | ANT
82 | GDO2 | | GND
83 | GDO0 | crystal |
84 | CSN |_______________-
85 | ```
86 |
87 | ## Building and usage
88 | The library is based on PlatformIO and is build through PlatformIO's toolchain.
89 |
90 | ## Related materials:
91 | - Application note on using CC1101 with Wirless MBUS: https://www.ti.com/lit/an/swra234a/swra234a.pdf
92 | - CULFW source code: https://github.com/heliflieger/a-culfw/
93 | - App filtering WMBUS packets from RTL: https://github.com/xaelsouth/rtl-wmbus
94 | - Almighty WMBUS meters decoders library: https://github.com/weetmuts/wmbusmeters
95 |
--------------------------------------------------------------------------------
/esphome/custom_components/izar-wmbus/izar_utils.cpp:
--------------------------------------------------------------------------------
1 | #include "izar_utils.h"
2 |
3 | #include
4 |
5 | #define CRC_POLYNOM 0x3D65
6 |
7 |
8 | const uint8_t decoder[] = {
9 | 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
10 | 0xFF,0xFF,0xFF,0x03,0xFF,0x01,0x02,0xFF,
11 | 0xFF,0xFF,0xFF,0x07,0xFF,0xFF,0x00,0xFF,
12 | 0xFF,0x05,0x06,0xFF,0x04,0xFF,0xFF,0xFF,
13 | 0xFF,0xFF,0xFF,0x0B,0xFF,0x09,0x0A,0xFF,
14 | 0xFF,0x0F,0xFF,0xFF,0x08,0xFF,0xFF,0xFF,
15 | 0xFF,0x0D,0x0E,0xFF,0x0C,0xFF,0xFF,0xFF,
16 | 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
17 | };
18 |
19 | uint32_t uintFromBytes(uint8_t* data) {
20 | uint32_t result = data[0] << 24;
21 | result += data[1] << 16;
22 | result += data[2] << 8;
23 | result += data[3];
24 | return result;
25 | }
26 |
27 | uint32_t uintFromBytesLittleEndian(uint8_t* data) {
28 | uint32_t result = data[3] << 24;
29 | result += data[2] << 16;
30 | result += data[1] << 8;
31 | result += data[0];
32 | return result;
33 | }
34 |
35 | uint16_t uint16FromBytes(uint8_t* data) {
36 | uint16_t result = data[0] << 8;
37 | result += data[1];
38 | return result;
39 | }
40 |
41 | uint32_t hashShiftKey(uint32_t key) {
42 | for (uint8_t i = 0; i < 8; i++) {
43 | uint8_t bit = GET_BIT(key, 1) ^ GET_BIT(key, 2) ^ GET_BIT(key, 11) ^
44 | GET_BIT(key, 31);
45 | key <<= 1;
46 | key |= bit;
47 | }
48 | return key;
49 | }
50 |
51 | uint8_t decode3of6Single(uint8_t* encoded, uint8_t* decoded) {
52 | uint8_t data[4];
53 |
54 | data[0] = decoder[encoded[2] & 0x3F];
55 | data[1] = decoder[((encoded[2] & 0xC0) >> 6) | ((encoded[1] & 0x0F) << 2)];
56 | data[2] = decoder[((encoded[1] & 0xF0) >> 4) | ((encoded[0] & 0x03) << 4)];
57 | data[3] = decoder[((encoded[0] & 0xFC) >> 2)];
58 |
59 | for (uint8_t i = 0; i < 4; i++) {
60 | if (data[i] == 0xFF) {
61 | return -1;
62 | }
63 | }
64 |
65 | // - Shift the encoded values into a byte buffer -
66 | decoded[0] = (data[3] << 4) | (data[2]);
67 | decoded[1] = (data[1] << 4) | (data[0]);
68 |
69 | return 0;
70 | }
71 |
72 | uint8_t decrypt(uint8_t* encoded, uint8_t len, uint8_t* decoded) {
73 | if (len < 15) {
74 | return 0;
75 | }
76 |
77 | uint32_t key = 0xdfd109e8;
78 | key ^= uintFromBytes(encoded + 2);
79 | key ^= uintFromBytes(encoded + 6);
80 | key ^= uintFromBytes(encoded + 10);
81 |
82 | const uint8_t size = len - 15;
83 | for (uint8_t i = 0; i < size; i++) {
84 | key = hashShiftKey(key);
85 | decoded[i] = encoded[i + 15] ^ (key & 0xFF);
86 | }
87 |
88 | if (decoded[0] != 0x4B) {
89 | return 0;
90 | }
91 |
92 | return size;
93 | }
94 |
95 | uint16_t crc16(uint16_t crcVal, uint8_t dataByte) {
96 | for (int i = 0; i < 8; i++) {
97 | if (((crcVal & 0x8000) >> 8) ^ (dataByte & 0x80)) {
98 | crcVal = (crcVal << 1) ^ CRC_POLYNOM;
99 | } else {
100 | crcVal = (crcVal << 1);
101 | }
102 |
103 | dataByte <<= 1;
104 | }
105 |
106 | return crcVal;
107 | }
--------------------------------------------------------------------------------
/esphome/custom_components/izar-wmbus/izar_utils.h:
--------------------------------------------------------------------------------
1 | #ifndef IZAR_UTILS
2 | #define IZAR_UTILS
3 |
4 | #include
5 |
6 | #define GET_BIT(var, pos) ((var >> pos) & 0x01)
7 |
8 | uint32_t uintFromBytes(uint8_t* data);
9 | uint32_t uintFromBytesLittleEndian(uint8_t* data);
10 | uint16_t uint16FromBytes(uint8_t* data);
11 | uint8_t decrypt(uint8_t* encoded, uint8_t len, uint8_t* decoded);
12 | uint8_t decode3of6Single(uint8_t* encoded, uint8_t* decoded);
13 | uint16_t crc16(uint16_t crcVal, uint8_t dataByte);
14 |
15 | #endif
16 |
17 |
--------------------------------------------------------------------------------
/esphome/custom_components/izar-wmbus/izar_wmbus.h:
--------------------------------------------------------------------------------
1 | #ifndef IZAR_WMBUS
2 | #define IZAR_WMBUS
3 | #include
4 | #include