From e5cf1d209664c3d471afeaef0d7be870039fdb78 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Mon, 4 Dec 2017 19:38:44 +0100 Subject: [PATCH 01/82] Removed timestamp and changed ouput to JSON format * Removed timestamp from output * Changed output to JSON format --- sparsnas_decode.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index c222831..7dc452e 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -153,12 +153,12 @@ class SignalDetector { struct tm * timeinfo; timeinfo = localtime(&mytime); - m += sprintf(m, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d] ", timeinfo->tm_year + 1900, - timeinfo->tm_mon + 1, - timeinfo->tm_mday, - timeinfo->tm_hour, - timeinfo->tm_min, - timeinfo->tm_sec); +// m += sprintf(m, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d] ", timeinfo->tm_year + 1900, +// timeinfo->tm_mon + 1, +// timeinfo->tm_mday, +// timeinfo->tm_hour, +// timeinfo->tm_min, +// timeinfo->tm_sec); uint8_t dec[32]; @@ -186,7 +186,7 @@ class SignalDetector { int pulse = (dec[13] << 24 | dec[14] << 16 | dec[15] << 8 | dec[16]); int battery = dec[17]; float watt = (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect); - m += sprintf(m, "%5d: %7.1f W. %d.%.3d kWh. Batt %d%%. FreqErr: %.2f", seq, watt, pulse/1000, pulse%1000, battery, freq); + m += sprintf(m, "{\"Sequence\":%5d,\"Watt\":%7.1f,\"kWh\":%d.%.3d,\"battery\":%d,\"FreqErr\":%.2f}", seq, watt, pulse/1000, pulse%1000, battery, freq); if (testing && crc == packet_crc) { error_sum += fabs(freq); From 1d006da040dd9362cc80ab6ded0e6d5acf80dfe4 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Mon, 4 Dec 2017 20:17:24 +0100 Subject: [PATCH 02/82] Updated information Added information for installation of Raspberry pi and other preparations. Example configuration for Home Assistant --- README.md | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 141 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ea72780..3f8d4d6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +A BIG thanks to the "Svenska Hemautomationsgruppen" on Facebook and a special superduper thanks to the core coder @strigeus! +https://github.com/strigeus/sparsnas_decoder + +This is a fork is adapted for output in JSON format to a MQTT broker. This is a decoder for IKEA SPARSNÄS. =================================== @@ -6,10 +10,56 @@ It uses RTL-SDR running on a Raspberry Pi to demodulate the FSK signal, and deco The packet data is encrypted using your sender's ID. The sender ID is the last 6 digits of the serial number located under the battery. -How to build: +Prerequisites: +------------- +Tested on a Raspberry Pi 3 with Raspbian Stretch with a NooElec Micro 3 USB SDR dongle. You should be able to use any RTL-SDR compatible dongle. + +An IKEA Sparsnäs unit (duuh) + +Make sure you have updated your Raspbian installation, then +``` +sudo apt-get install libusb-1.0 rtl-sdr g++ git mosquitto-clients +``` +Using your favorite text editor, create a new file named /etc/modprobe.d/no-rtl.conf and put the following text in the file. You need to run that text editor as sudo (i.e. 'sudo vi' or 'sudo nano' etc) to write to the modprobe.d directory: +``` +sudo nano /etc/modprobe.d/no-rtl.conf +``` +``` +blacklist dvb_usb_rtl28xxu +blacklist rtl2832 +blacklist rtl2830 +``` +Reboot your pi with the USB dongle installed in one of the USB ports. + +Test your dongle +``` +rtl_test +``` +Outcome should look something like this: +``` +pi@raspberrypi:~ $ rtl_test +Found 1 device(s): + 0: Realtek, RTL2838UHIDIR, SN: 00000001 + +Using device 0: Generic RTL2832U OEM +Found Rafael Micro R820T tuner +Supported gain values (29): 0.0 0.9 1.4 2.7 3.7 7.7 8.7 12.5 14.4 15.7 16.6 19.7 20.7 22.9 25.4 28.0 29.7 32.8 33.8 36.4 37.2 38.6 40.2 42.1 43.4 43.9 44.5 48.0 49.6 +[R82XX] PLL not locked! +Sampling at 2048000 S/s. + +Info: This tool will continuously read from the device, and report if +samples get lost. If you observe no further output, everything is fine. + +Reading samples in async mode... +lost at least 24 bytes +``` + +How to build the decoder: ------------- +Take a photo/make a note of your transmitter ID number underneeth the battery lid of the transmitter (not the display) -First edit sparsnas_decode.cpp to setup your sender id. Then: +First edit sparsnas_decode.cpp to setup your sender id. +Then: ```g++ -o sparsnas_decode -O3 sparsnas_decode.cpp``` @@ -27,12 +77,99 @@ Wait about a minute for it to finish. It prints something like: Edit your sparsnas_decode.cpp with those values and rebuild it according to the How to build step above. -How to run: +Perform a testrun: ----------- ```rtl_sdr -f 868000000 -s 1024000 -g 40 - | ./sparsnas_decode``` +Create a bash script to send IKEA Sparsnäs output to MQTT broker: +----------- +``` +pi@rpitest:~ $ nano sparsnas.sh +``` +``` +#!/bin/bash +rtl_sdr -f 868000000 -s 1024000 -g 40 - | sparsnas_decode 2>&1 | /usr/bin/mosquitto_pub -h 192.168.x.x -u username -P password -i sparsnas -l -t "home/sparsnas" & +``` + +Make the bash script executable +```chmod +x sparsnas.sh``` + +Copy the binary file to /usr/sbin +```sudo cp sparsnas_decode /usr/sbin``` + +Test the script: +```./sparsnas.sh``` + +Make it run on startup +```sudo nano /etc/rc.local``` +``` +#!/bin/sh -e +# +# rc.local +# +# This script is executed at the end of each multiuser runlevel. +# Make sure that the script will "exit 0" on success or any other +# value on error. +# +# In order to enable or disable this script just change the execution +# bits. +# +# By default this script does nothing. + +# Print the IP address +_IP=$(hostname -I) || true +if [ "$_IP" ]; then + printf "My IP address is %s\n" "$_IP" +fi + +/home/pi/sparsnas.sh + +exit 0 +``` + +Check status and traffic on your MQTT broker: +``` +mosquitto_sub -v -h 192.168.x.x -u username -P password -t '#' +``` +Example of outcome: +``` +core-ssh:~# mosquitto_sub -v -h 192.168.x.x -u username -P password -t '#' +home/sparsnas {"Sequence":35851,"Watt": 7372.8,"kWh":18889.219,"battery":100,"FreqErr":0.57} +``` + +Create a sensor in Home Assistant (example code, configuration.yaml): +---------------------------------- +``` +mqtt: + broker: 127.0.0.1 + port: 1883 + client_id: home-assistant-1 + username: username + password: password + discovery: true + discovery_prefix: homeassistant + +sensor: + - platform: mqtt + state_topic: "home/sparsnas" + name: "Sparsnäs energy consumption momentary" + unit_of_measurement: "W" + value_template: '{{ value_json.Watt | round(1) }}' + - platform: mqtt + state_topic: "home/sparsnas" + name: "Sparsnäs energy consumption over time" + unit_of_measurement: "kWh" + value_template: '{{ value_json.kWh | round(1) }}' + - platform: mqtt + state_topic: "home/sparsnas" + name: "Sparsnäs Battery remaining" + unit_of_measurement: "%" + value_template: '{{ value_json.battery | round(1) }}' +``` + + -Technical details: +Technical details on the decoder: ------------------ SPARSNÄS uses a CC1101 chip configured for GFSK modulation at 868MHz. The two FSK symbol frequencies once downconverted to baseband are roughly 67kHz and 105kHz. (EDIT: It seems like with a different RTL-SDR the baseband frequencies are 12.5khz and 50khz. Maybe my first RTL-SDR was inaccurate). The packet length is 18 bytes, including the length byte, and a 16-bit CRC is appended to the end. The CC1101 sync word is 0xD201. The packet payload byte 5 to 17 are encrypted using a key derived from the sender device's ID. From 19e120239b9e6fcc98958027a607c484d7cc8cfc Mon Sep 17 00:00:00 2001 From: tubalainen Date: Mon, 4 Dec 2017 20:25:01 +0100 Subject: [PATCH 03/82] Updates URL to facebook group --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3f8d4d6..faf4329 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -A BIG thanks to the "Svenska Hemautomationsgruppen" on Facebook and a special superduper thanks to the core coder @strigeus! +A BIG thanks to the https://www.facebook.com/groups/SHgruppen on Facebook and a special superduper thanks to the core coder @strigeus! https://github.com/strigeus/sparsnas_decoder This is a fork is adapted for output in JSON format to a MQTT broker. From ad5f68b5f27db4f2bdc94a767ca6221558540ee7 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Thu, 28 Dec 2017 09:53:39 +0100 Subject: [PATCH 04/82] Added MQTT info, mothly automation and node-red parsing Thanks all for the contrib! --- README.md | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index faf4329..64935b3 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,10 @@ Tested on a Raspberry Pi 3 with Raspbian Stretch with a NooElec Micro 3 USB SDR An IKEA Sparsnäs unit (duuh) +An MQTT broker installed, see this video for support/help to get it going: https://www.youtube.com/watch?v=VaWdvVVYU3A + +You can also pipe your MQTT data via node-red to InfluxDB for diplaying in for example Grafana, @naestrom have done a great job creating a node-red flow, please find more info here: https://github.com/Naesstrom/sparsnas_mqtt_nodered_influxdb + Make sure you have updated your Raspbian installation, then ``` sudo apt-get install libusb-1.0 rtl-sdr g++ git mosquitto-clients @@ -137,7 +141,7 @@ core-ssh:~# mosquitto_sub -v -h 192.168.x.x -u username -P password -t '#' home/sparsnas {"Sequence":35851,"Watt": 7372.8,"kWh":18889.219,"battery":100,"FreqErr":0.57} ``` -Create a sensor in Home Assistant (example code, configuration.yaml): +Create a sensor and monthly automation for Home Assistant (example code, configuration.yaml): ---------------------------------- ``` mqtt: @@ -165,6 +169,49 @@ sensor: name: "Sparsnäs Battery remaining" unit_of_measurement: "%" value_template: '{{ value_json.battery | round(1) }}' + +automation old: + - trigger: + platform: time + at: '00:00:01' + condition: + condition: and + conditions: + - condition: template + value_template: '{{ now().day() | string == "1" }}' + - condition: or + conditions: + - condition: template + value_template: '{{ now().month() | string == "1" }}' + - condition: template + value_template: '{{ now().month() | string == "2" }}' + - condition: template + value_template: '{{ now().month() | string == "3" }}' + - condition: template + value_template: '{{ now().month() | string == "4" }}' + - condition: template + value_template: '{{ now().month() | string == "5" }}' + - condition: template + value_template: '{{ now().month() | string == "6" }}' + - condition: template + value_template: '{{ now().month() | string == "7" }}' + - condition: template + value_template: '{{ now().month() | string == "8" }}' + - condition: template + value_template: '{{ now().month() | string == "9" }}' + - condition: template + value_template: '{{ now().month() | string == "10" }}' + - condition: template + value_template: '{{ now().month() | string == "11" }}' + - condition: template + value_template: '{{ now().month() | string == "12" }}' + action: + service: mqtt.publish + data: + topic: 'template/kwh' + payload_template: "{{ states('sensor.total_kwh') }}" + retain: 'true' + alias: "Automation för månadsförbrukning Sparsnäs" ``` From 2968dd08e23f360030271ec792e18840a946e1f5 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Thu, 28 Dec 2017 09:54:22 +0100 Subject: [PATCH 05/82] minor adjustments --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 64935b3..b363c52 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ An MQTT broker installed, see this video for support/help to get it going: https You can also pipe your MQTT data via node-red to InfluxDB for diplaying in for example Grafana, @naestrom have done a great job creating a node-red flow, please find more info here: https://github.com/Naesstrom/sparsnas_mqtt_nodered_influxdb +The installation: +------------- Make sure you have updated your Raspbian installation, then ``` sudo apt-get install libusb-1.0 rtl-sdr g++ git mosquitto-clients From 3a86c41c498b9324a541fa743aa0880978a92e60 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Thu, 28 Dec 2017 09:58:48 +0100 Subject: [PATCH 06/82] minor update --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b363c52..1f6a9d8 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,7 @@ sensor: unit_of_measurement: "%" value_template: '{{ value_json.battery | round(1) }}' +# Thanks to @bhaap for the monthly automation below automation old: - trigger: platform: time From 28188f3d32e8758e794ced99ed8c5b57e65ac252 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Thu, 28 Dec 2017 20:28:39 +0100 Subject: [PATCH 07/82] Added trouble shooting information .. --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1f6a9d8..37688b3 100644 --- a/README.md +++ b/README.md @@ -253,10 +253,10 @@ float watt = (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect); Note: Due to how the encryption works, it's possible for two different SPARSNÄS with unique sender_id to confuse each other. When decrypting a packet from sender B with the A key, it's possible that the resulting packet gets a sender_id equal to A, which means A will display incorrectly decrypted data from the B device. -Example usage +Example usage -- Also for troubleshooting ------------- ``` -ludde@raspberrypi ~/sparsnas $ sudo rtl_sdr -f 868000000 -s 1024000 -g 40 - | ./sparsnas_decode +pi@raspberrypi:~ $ rtl_sdr -f 868000000 -s 1024000 -g 40 - | sparsnas_decode Found 1 device(s): 0: Realtek, RTL2838UHIDIR, SN: 00000001 @@ -267,6 +267,5 @@ Sampling at 1024000 S/s. Tuned to 868000000 Hz. Tuner gain set to 40.20 dB. Reading samples in async mode... -[2016-12-05 21:29:35] 12711: 362.3 W. 43.339 kWh. Batt 99%. -[2016-12-05 21:29:50] 12712: 362.6 W. 43.341 kWh. Batt 99%. +{"Sequence":43096,"Watt": 7256.7,"kWh":21483.315,"battery":100,"FreqErr":0.83} ``` From 18089d24d40901d8c8784d9c50937069d17cd31a Mon Sep 17 00:00:00 2001 From: tubalainen Date: Thu, 28 Dec 2017 20:37:24 +0100 Subject: [PATCH 08/82] =?UTF-8?q?Adding=20information=20on=20git=20clone?= =?UTF-8?q?=20and=20path=C2=B4s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just to clarify --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 37688b3..c1eeb38 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,12 @@ The installation: ------------- Make sure you have updated your Raspbian installation, then ``` +cd ~ sudo apt-get install libusb-1.0 rtl-sdr g++ git mosquitto-clients +git clone https://github.com/tubalainen/sparsnas_decoder ``` +The decoder is now cloned into /home/pi/sparsnas_decoder + Using your favorite text editor, create a new file named /etc/modprobe.d/no-rtl.conf and put the following text in the file. You need to run that text editor as sudo (i.e. 'sudo vi' or 'sudo nano' etc) to write to the modprobe.d directory: ``` sudo nano /etc/modprobe.d/no-rtl.conf @@ -89,6 +93,8 @@ Perform a testrun: Create a bash script to send IKEA Sparsnäs output to MQTT broker: ----------- +The script will reside in /home/pi/ + ``` pi@rpitest:~ $ nano sparsnas.sh ``` @@ -101,7 +107,7 @@ Make the bash script executable ```chmod +x sparsnas.sh``` Copy the binary file to /usr/sbin -```sudo cp sparsnas_decode /usr/sbin``` +```sudo cp /home/pi/sparsnas_decoder/sparsnas_decode /usr/sbin``` Test the script: ```./sparsnas.sh``` From dbf64de239f1fbf3cc229884cc7c3fce39ea372f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20N=C3=A4sstr=C3=B6m?= Date: Sat, 30 Dec 2017 13:41:12 +0100 Subject: [PATCH 09/82] Added sleep command To make sure that the mqtt broker is started added a sleep command before starting publishing to mqtt. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c1eeb38..d843c90 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,9 @@ pi@rpitest:~ $ nano sparsnas.sh ``` ``` #!/bin/bash +#To make sure that the mqtt broker is started, make the script wait 5 minutes. +sleep 5m +#Start sending information to mosquitto_pub rtl_sdr -f 868000000 -s 1024000 -g 40 - | sparsnas_decode 2>&1 | /usr/bin/mosquitto_pub -h 192.168.x.x -u username -P password -i sparsnas -l -t "home/sparsnas" & ``` From dd15d9a0e4f4aaa7f194a733c1fead4228ac0309 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Thu, 4 Jan 2018 17:15:56 +0100 Subject: [PATCH 10/82] Fixed quotations in the JSON output The output JSON values was not quoted. --- sparsnas_decode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index 7dc452e..ad92966 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -186,7 +186,7 @@ class SignalDetector { int pulse = (dec[13] << 24 | dec[14] << 16 | dec[15] << 8 | dec[16]); int battery = dec[17]; float watt = (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect); - m += sprintf(m, "{\"Sequence\":%5d,\"Watt\":%7.1f,\"kWh\":%d.%.3d,\"battery\":%d,\"FreqErr\":%.2f}", seq, watt, pulse/1000, pulse%1000, battery, freq); + m += sprintf(m, "{\"Sequence\":\"%5d\",\"Watt\":\"%7.1f\",\"kWh\":\"%d.%.3d\",\"battery\":\"%d\",\"FreqErr\":\"%.2f\"}", seq, watt, pulse/1000, pulse%1000, battery, freq); if (testing && crc == packet_crc) { error_sum += fabs(freq); From 457629be4ab1e229e9d658272f0ee7e2dab6f23c Mon Sep 17 00:00:00 2001 From: tubalainen Date: Thu, 4 Jan 2018 17:25:51 +0100 Subject: [PATCH 11/82] Updated information on multiple transmitters --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index d843c90..8915d51 100644 --- a/README.md +++ b/README.md @@ -225,6 +225,21 @@ automation old: retain: 'true' alias: "Automation för månadsförbrukning Sparsnäs" ``` +Use two or more Sparsnäs at the same location (Experts only): +------------------ +It is possible to use twoor more Sparsnäs at the same time. +You will need to compile one sparsnas_decode per Sparsnäs Energy meter. The same sdr_rtl instance can be used. + +Edit your sparsnas.sh to look something like the following after you have compiled and copied all your instances to /usr/sbin/. +``` +#!/bin/bash + +rtl_sdr -f 868000000 -s 1024000 -g 40 - | pee 'sparsnas_decode 2>&1' 'sparsnas_decode_bilen 2>&1' | /usr/bin/mosquitto_pub -h 192.168.21.10 -u username -P sniper -i sparsnas -l -t "home/sparsnas" & +``` + +If the "#define frequencies" are too close you will run into problems. + +If you do not want to spam your MQTT with "BAD" messages you would need to remove or comment out the "BAD" statements in the .cpp file above the JSON printout "elseif" From dbc7f07cdecb05a1f31eb64be41d5b74b22b4a21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Lindstr=C3=B6m?= Date: Tue, 9 Jan 2018 14:38:29 +0100 Subject: [PATCH 12/82] Update README.md Added automation for showing todays kWh and the template sensors used to show the data from the automations. --- README.md | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/README.md b/README.md index 8915d51..4965054 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,21 @@ sensor: name: "Sparsnäs Battery remaining" unit_of_measurement: "%" value_template: '{{ value_json.battery | round(1) }}' + - platform: mqtt + name: "template kwh sensor dag" + state_topic: "template/kwh/day" + - platform: template + sensors: + kwh_current_month: + friendly_name: "Förbrukning denna månad" + unit_of_measurement: "kWh" + value_template: >- + {{ (float(states.sensor.total_kwh.state) - float(states.sensor.template_kwh_sensor.state)) | round(1) }} + kwh_today: + friendly_name: "Förbrukning idag" + unit_of_measurement: "kWh" + value_template: >- + {{ (float(states.sensor.total_kwh.state) - float(states.sensor.template_kwh_sensor_dag.state)) | round(1) }} # Thanks to @bhaap for the monthly automation below automation old: @@ -224,6 +239,84 @@ automation old: payload_template: "{{ states('sensor.total_kwh') }}" retain: 'true' alias: "Automation för månadsförbrukning Sparsnäs" + + - alias: "Spara kWh varje dag" + trigger: + platform: time + at: '00:00:01' + condition: + condition: or + conditions: + - condition: template + value_template: '{{ now().day() | string == "1" }}' + - condition: template + value_template: '{{ now().day() | string == "2" }}' + - condition: template + value_template: '{{ now().day() | string == "3" }}' + - condition: template + value_template: '{{ now().day() | string == "4" }}' + - condition: template + value_template: '{{ now().day() | string == "5" }}' + - condition: template + value_template: '{{ now().day() | string == "6" }}' + - condition: template + value_template: '{{ now().day() | string == "7" }}' + - condition: template + value_template: '{{ now().day() | string == "8" }}' + - condition: template + value_template: '{{ now().day() | string == "9" }}' + - condition: template + value_template: '{{ now().day() | string == "10" }}' + - condition: template + value_template: '{{ now().day() | string == "11" }}' + - condition: template + value_template: '{{ now().day() | string == "12" }}' + - condition: template + value_template: '{{ now().day() | string == "13" }}' + - condition: template + value_template: '{{ now().day() | string == "14" }}' + - condition: template + value_template: '{{ now().day() | string == "15" }}' + - condition: template + value_template: '{{ now().day() | string == "16" }}' + - condition: template + value_template: '{{ now().day() | string == "17" }}' + - condition: template + value_template: '{{ now().day() | string == "18" }}' + - condition: template + value_template: '{{ now().day() | string == "19" }}' + - condition: template + value_template: '{{ now().day() | string == "20" }}' + - condition: template + value_template: '{{ now().day() | string == "21" }}' + - condition: template + value_template: '{{ now().day() | string == "22" }}' + - condition: template + value_template: '{{ now().day() | string == "23" }}' + - condition: template + value_template: '{{ now().day() | string == "24" }}' + - condition: template + value_template: '{{ now().day() | string == "25" }}' + - condition: template + value_template: '{{ now().day() | string == "26" }}' + - condition: template + value_template: '{{ now().day() | string == "27" }}' + - condition: template + value_template: '{{ now().day() | string == "28" }}' + - condition: template + value_template: '{{ now().day() | string == "29" }}' + - condition: template + value_template: '{{ now().day() | string == "30" }}' + - condition: template + value_template: '{{ now().day() | string == "31" }}' + action: + service: mqtt.publish + data: + topic: 'template/kwh/day' + payload_template: "{{ states('sensor.total_kwh') }}" + retain: 'true' + + ``` Use two or more Sparsnäs at the same location (Experts only): ------------------ From c7f2d21121f09f26cd2c57df0470975dbb6433f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Lindstr=C3=B6m?= Date: Tue, 9 Jan 2018 14:54:07 +0100 Subject: [PATCH 13/82] Update sparsnas_decode.cpp Removed data_[4] in if statement for BAD: This was causing alot of errors for my sparsnas. --- sparsnas_decode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index ad92966..60afe79 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -176,7 +176,7 @@ class SignalDetector { uint32_t rcv_sensor_id = dec[5] << 24 | dec[6] << 16 | dec[7] << 8 | dec[8]; - if (data_[0] != 0x11 || data_[1] != (SENSOR_ID & 0xFF) || data_[3] != 0x07 || data_[4] != 0x0E || rcv_sensor_id != SENSOR_ID) { + if (data_[0] != 0x11 || data_[1] != (SENSOR_ID & 0xFF) || data_[3] != 0x07 || rcv_sensor_id != SENSOR_ID) { m += sprintf(m, "Bad: "); for (int i = 0; i < 18; i++) m += sprintf(m, "%.2X ", data_[i]); From b8e58497122e63e58aac95626b1e525947c52eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Lindstr=C3=B6m?= Date: Tue, 9 Jan 2018 15:31:03 +0100 Subject: [PATCH 14/82] Update sparsnas_decode.cpp temporary fix for higher effect numbers. noticed when i was drawing more power that the calculation for watt was incorrect. So added a if statement to only atleast make it work in my case. more research in when the different calculations are needed is needed --- sparsnas_decode.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index 60afe79..63215f2 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -185,7 +185,10 @@ class SignalDetector { int effect = (dec[11] << 8 | dec[12]); int pulse = (dec[13] << 24 | dec[14] << 16 | dec[15] << 8 | dec[16]); int battery = dec[17]; - float watt = (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect); + float watt = effect * 24; + if(watt > 10000){ + watt = (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect); + } m += sprintf(m, "{\"Sequence\":\"%5d\",\"Watt\":\"%7.1f\",\"kWh\":\"%d.%.3d\",\"battery\":\"%d\",\"FreqErr\":\"%.2f\"}", seq, watt, pulse/1000, pulse%1000, battery, freq); if (testing && crc == packet_crc) { From ade03448edafea4586ea7f16f529ea2165d880f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Lindstr=C3=B6m?= Date: Tue, 9 Jan 2018 17:35:49 +0100 Subject: [PATCH 15/82] Update sparsnas_decode.cpp Change break value --- sparsnas_decode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index 63215f2..c921623 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -186,7 +186,7 @@ class SignalDetector { int pulse = (dec[13] << 24 | dec[14] << 16 | dec[15] << 8 | dec[16]); int battery = dec[17]; float watt = effect * 24; - if(watt > 10000){ + if(watt > 3999){ watt = (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect); } m += sprintf(m, "{\"Sequence\":\"%5d\",\"Watt\":\"%7.1f\",\"kWh\":\"%d.%.3d\",\"battery\":\"%d\",\"FreqErr\":\"%.2f\"}", seq, watt, pulse/1000, pulse%1000, battery, freq); From 6d4fc8770902059a2cec2ea344b4fbaeed3740fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Lindstr=C3=B6m?= Date: Wed, 10 Jan 2018 11:23:01 +0100 Subject: [PATCH 16/82] Update README.md Fixed template code. --- README.md | 91 ++++++++----------------------------------------------- 1 file changed, 13 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index 4965054..ac8a372 100644 --- a/README.md +++ b/README.md @@ -205,33 +205,33 @@ automation old: condition: and conditions: - condition: template - value_template: '{{ now().day() | string == "1" }}' + value_template: '{{ now().day | string == "1" }}' - condition: or conditions: - condition: template - value_template: '{{ now().month() | string == "1" }}' + value_template: '{{ now().month | string == "1" }}' - condition: template - value_template: '{{ now().month() | string == "2" }}' + value_template: '{{ now().month | string == "2" }}' - condition: template - value_template: '{{ now().month() | string == "3" }}' + value_template: '{{ now().month | string == "3" }}' - condition: template - value_template: '{{ now().month() | string == "4" }}' + value_template: '{{ now().month | string == "4" }}' - condition: template - value_template: '{{ now().month() | string == "5" }}' + value_template: '{{ now().month | string == "5" }}' - condition: template - value_template: '{{ now().month() | string == "6" }}' + value_template: '{{ now().month | string == "6" }}' - condition: template - value_template: '{{ now().month() | string == "7" }}' + value_template: '{{ now().month | string == "7" }}' - condition: template - value_template: '{{ now().month() | string == "8" }}' + value_template: '{{ now().month | string == "8" }}' - condition: template - value_template: '{{ now().month() | string == "9" }}' + value_template: '{{ now().month | string == "9" }}' - condition: template - value_template: '{{ now().month() | string == "10" }}' + value_template: '{{ now().month | string == "10" }}' - condition: template - value_template: '{{ now().month() | string == "11" }}' + value_template: '{{ now().month | string == "11" }}' - condition: template - value_template: '{{ now().month() | string == "12" }}' + value_template: '{{ now().month | string == "12" }}' action: service: mqtt.publish data: @@ -244,71 +244,6 @@ automation old: trigger: platform: time at: '00:00:01' - condition: - condition: or - conditions: - - condition: template - value_template: '{{ now().day() | string == "1" }}' - - condition: template - value_template: '{{ now().day() | string == "2" }}' - - condition: template - value_template: '{{ now().day() | string == "3" }}' - - condition: template - value_template: '{{ now().day() | string == "4" }}' - - condition: template - value_template: '{{ now().day() | string == "5" }}' - - condition: template - value_template: '{{ now().day() | string == "6" }}' - - condition: template - value_template: '{{ now().day() | string == "7" }}' - - condition: template - value_template: '{{ now().day() | string == "8" }}' - - condition: template - value_template: '{{ now().day() | string == "9" }}' - - condition: template - value_template: '{{ now().day() | string == "10" }}' - - condition: template - value_template: '{{ now().day() | string == "11" }}' - - condition: template - value_template: '{{ now().day() | string == "12" }}' - - condition: template - value_template: '{{ now().day() | string == "13" }}' - - condition: template - value_template: '{{ now().day() | string == "14" }}' - - condition: template - value_template: '{{ now().day() | string == "15" }}' - - condition: template - value_template: '{{ now().day() | string == "16" }}' - - condition: template - value_template: '{{ now().day() | string == "17" }}' - - condition: template - value_template: '{{ now().day() | string == "18" }}' - - condition: template - value_template: '{{ now().day() | string == "19" }}' - - condition: template - value_template: '{{ now().day() | string == "20" }}' - - condition: template - value_template: '{{ now().day() | string == "21" }}' - - condition: template - value_template: '{{ now().day() | string == "22" }}' - - condition: template - value_template: '{{ now().day() | string == "23" }}' - - condition: template - value_template: '{{ now().day() | string == "24" }}' - - condition: template - value_template: '{{ now().day() | string == "25" }}' - - condition: template - value_template: '{{ now().day() | string == "26" }}' - - condition: template - value_template: '{{ now().day() | string == "27" }}' - - condition: template - value_template: '{{ now().day() | string == "28" }}' - - condition: template - value_template: '{{ now().day() | string == "29" }}' - - condition: template - value_template: '{{ now().day() | string == "30" }}' - - condition: template - value_template: '{{ now().day() | string == "31" }}' action: service: mqtt.publish data: From ef9eedfe9d16223f7c1aa862809a6a5b8bcaeb52 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Wed, 10 Jan 2018 13:07:05 +0100 Subject: [PATCH 17/82] Updated "Bad" printout to JSON format To avoid Home Assistant from generating parsing errors. Example: 2018-01-10 12:54:14 ERROR (MainThread) [homeassistant.helpers.template] Error parsing value: 'value_json' is undefined (value: Bad: 11 5E 2E 07 0F A2 98 19 19 49 0C F7 4A 47 D2 03 81 78 , template: {{ value_json.Watt | round(1) }}) --- sparsnas_decode.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index c921623..163b4c4 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -177,9 +177,10 @@ class SignalDetector { uint32_t rcv_sensor_id = dec[5] << 24 | dec[6] << 16 | dec[7] << 8 | dec[8]; if (data_[0] != 0x11 || data_[1] != (SENSOR_ID & 0xFF) || data_[3] != 0x07 || rcv_sensor_id != SENSOR_ID) { - m += sprintf(m, "Bad: "); - for (int i = 0; i < 18; i++) - m += sprintf(m, "%.2X ", data_[i]); +// m += sprintf(m, "Bad: "); + m += sprintf(m, "{\"BadRxOrAdditionalNearBySparsnas\":\"true\"}"); +// for (int i = 0; i < 18; i++) +// m += sprintf(m, "%.2X ", data_[i]); } else { int seq = (dec[9] << 8 | dec[10]); int effect = (dec[11] << 8 | dec[12]); From 7403cae88cb584ce3a89954b84836c66bfb09d46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Lindstr=C3=B6m?= Date: Wed, 10 Jan 2018 13:46:53 +0100 Subject: [PATCH 18/82] Update README.md Add template sensor for kwh --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index ac8a372..38c327d 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,9 @@ sensor: - platform: mqtt name: "template kwh sensor dag" state_topic: "template/kwh/day" + - platform: mqtt + name: "template kwh sensor" + state_topic: "template/kwh" - platform: template sensors: kwh_current_month: From c8b2049c264cb373c67a28d514ca160d38ea1fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Lindstr=C3=B6m?= Date: Wed, 10 Jan 2018 13:48:30 +0100 Subject: [PATCH 19/82] Update sparsnas_decode.cpp Added variable for different PULSES_PER_KWH in the sprint --- sparsnas_decode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index 163b4c4..3246a5a 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -190,7 +190,7 @@ class SignalDetector { if(watt > 3999){ watt = (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect); } - m += sprintf(m, "{\"Sequence\":\"%5d\",\"Watt\":\"%7.1f\",\"kWh\":\"%d.%.3d\",\"battery\":\"%d\",\"FreqErr\":\"%.2f\"}", seq, watt, pulse/1000, pulse%1000, battery, freq); + m += sprintf(m, "{\"Sequence\":\"%5d\",\"Watt\":\"%7.1f\",\"kWh\":\"%d.%.3d\",\"battery\":\"%d\",\"FreqErr\":\"%.2f\"}", seq, watt, pulse/PULSES_PER_KWH, pulse%PULSES_PER_KWH, battery, freq); if (testing && crc == packet_crc) { error_sum += fabs(freq); From f6b94aed729279029c17c8699cb422e193f5c2f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Lindstr=C3=B6m?= Date: Wed, 10 Jan 2018 14:09:47 +0100 Subject: [PATCH 20/82] Update sparsnas_decode.cpp Fixed json formatting for bad: --- sparsnas_decode.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index 3246a5a..bf19e03 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -177,10 +177,11 @@ class SignalDetector { uint32_t rcv_sensor_id = dec[5] << 24 | dec[6] << 16 | dec[7] << 8 | dec[8]; if (data_[0] != 0x11 || data_[1] != (SENSOR_ID & 0xFF) || data_[3] != 0x07 || rcv_sensor_id != SENSOR_ID) { -// m += sprintf(m, "Bad: "); - m += sprintf(m, "{\"BadRxOrAdditionalNearBySparsnas\":\"true\"}"); -// for (int i = 0; i < 18; i++) -// m += sprintf(m, "%.2X ", data_[i]); + if (data_[0] != 0x11 || data_[1] != (SENSOR_ID & 0xFF) || data_[3] != 0x07 || rcv_sensor_id != SENSOR_ID) { + m += sprintf(m, "{\"Bad\":\""); + for (int i = 0; i < 18; i++) + m += sprintf(m, "%.2X ", data_[i]); + m += sprintf(m, "\"}"); } else { int seq = (dec[9] << 8 | dec[10]); int effect = (dec[11] << 8 | dec[12]); From 253b38769c8112a434e6159fc61bff0a8d7c2b3c Mon Sep 17 00:00:00 2001 From: tubalainen Date: Wed, 10 Jan 2018 15:30:39 +0100 Subject: [PATCH 21/82] Cleaned the sensor naming The code has been tried, "copy pasted" into configuration.yaml and confirmed working. Thanks to @bhaap again for the great automations! <3 --- README.md | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 38c327d..86e2a5e 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,7 @@ sensor: name: "Sparsnäs energy consumption momentary" unit_of_measurement: "W" value_template: '{{ value_json.Watt | round(1) }}' +# Note that the sensor below will be resetted if you remove/reinstall the batteries in the Sparsnäs transmitter - platform: mqtt state_topic: "home/sparsnas" name: "Sparsnäs energy consumption over time" @@ -181,23 +182,28 @@ sensor: unit_of_measurement: "%" value_template: '{{ value_json.battery | round(1) }}' - platform: mqtt - name: "template kwh sensor dag" + state_topic: "home/sparsnas" + name: "Sparsnäs Frequency Error" + unit_of_measurement: "%" + value_template: '{{ value_json.FreqErr }}' + - platform: mqtt + name: "Sparsnäs template kwh sensor day" state_topic: "template/kwh/day" - platform: mqtt - name: "template kwh sensor" - state_topic: "template/kwh" + name: "Sparsnäs template kwh sensor month" + state_topic: "template/kwh/month" - platform: template sensors: kwh_current_month: - friendly_name: "Förbrukning denna månad" + friendly_name: "Sparsnäs current month" unit_of_measurement: "kWh" value_template: >- - {{ (float(states.sensor.total_kwh.state) - float(states.sensor.template_kwh_sensor.state)) | round(1) }} + {{ (float(states.sensor.sparsnas_energy_consumption_over_time.state) - float(states.sensor.sparsnas_template_kwh_sensor_month.state)) | round(1) }} kwh_today: - friendly_name: "Förbrukning idag" + friendly_name: "Sparsnäs current day" unit_of_measurement: "kWh" value_template: >- - {{ (float(states.sensor.total_kwh.state) - float(states.sensor.template_kwh_sensor_dag.state)) | round(1) }} + {{ (float(states.sensor.sparsnas_energy_consumption_over_time.state) - float(states.sensor.sparsnastemplate_kwh_sensor_day.state)) | round(1) }} # Thanks to @bhaap for the monthly automation below automation old: @@ -238,12 +244,12 @@ automation old: action: service: mqtt.publish data: - topic: 'template/kwh' - payload_template: "{{ states('sensor.total_kwh') }}" + topic: 'template/kwh/month' + payload_template: "{{ states('sensor.sparsnas_energy_consumption_over_time') }}" retain: 'true' - alias: "Automation för månadsförbrukning Sparsnäs" + alias: "Sparsnäs monthly" - - alias: "Spara kWh varje dag" + - alias: "Sparsnäs daily" trigger: platform: time at: '00:00:01' @@ -251,11 +257,10 @@ automation old: service: mqtt.publish data: topic: 'template/kwh/day' - payload_template: "{{ states('sensor.total_kwh') }}" + payload_template: "{{ states('sensor.sparsnas_energy_consumption_over_time') }}" retain: 'true' - - ``` + Use two or more Sparsnäs at the same location (Experts only): ------------------ It is possible to use twoor more Sparsnäs at the same time. From 6c74e520aa7973309a438a32c8aa6242335e6f98 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Wed, 10 Jan 2018 15:48:30 +0100 Subject: [PATCH 22/82] Typo mistake --- sparsnas_decode.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index bf19e03..3f79777 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -176,8 +176,7 @@ class SignalDetector { uint32_t rcv_sensor_id = dec[5] << 24 | dec[6] << 16 | dec[7] << 8 | dec[8]; - if (data_[0] != 0x11 || data_[1] != (SENSOR_ID & 0xFF) || data_[3] != 0x07 || rcv_sensor_id != SENSOR_ID) { - if (data_[0] != 0x11 || data_[1] != (SENSOR_ID & 0xFF) || data_[3] != 0x07 || rcv_sensor_id != SENSOR_ID) { + if (data_[0] != 0x11 || data_[1] != (SENSOR_ID & 0xFF) || data_[3] != 0x07 || rcv_sensor_id != SENSOR_ID) { m += sprintf(m, "{\"Bad\":\""); for (int i = 0; i < 18; i++) m += sprintf(m, "%.2X ", data_[i]); From 12f730c9c16efb458a0f1d7e0f184fca420611e5 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Wed, 10 Jan 2018 20:53:47 +0100 Subject: [PATCH 23/82] Minor changes .. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 86e2a5e..b5f5ce6 100644 --- a/README.md +++ b/README.md @@ -270,7 +270,7 @@ Edit your sparsnas.sh to look something like the following after you have compil ``` #!/bin/bash -rtl_sdr -f 868000000 -s 1024000 -g 40 - | pee 'sparsnas_decode 2>&1' 'sparsnas_decode_bilen 2>&1' | /usr/bin/mosquitto_pub -h 192.168.21.10 -u username -P sniper -i sparsnas -l -t "home/sparsnas" & +rtl_sdr -f 868000000 -s 1024000 -g 40 - | pee 'sparsnas_decode 2>&1' 'sparsnas_decode_bilen 2>&1' | /usr/bin/mosquitto_pub -h 192.168.xx.xx -u username -P password -i sparsnas -l -t "home/sparsnas" & ``` If the "#define frequencies" are too close you will run into problems. From be7d2e60f22122daffabbbaae6ae5bdbd4307554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Lindstr=C3=B6m?= Date: Wed, 10 Jan 2018 22:41:05 +0100 Subject: [PATCH 24/82] Update sparsnas_decode.cpp Update again to fix the power calculation. --- sparsnas_decode.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index 3f79777..fab6a87 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -186,11 +186,13 @@ class SignalDetector { int effect = (dec[11] << 8 | dec[12]); int pulse = (dec[13] << 24 | dec[14] << 16 | dec[15] << 8 | dec[16]); int battery = dec[17]; - float watt = effect * 24; - if(watt > 3999){ + float watt; +// Note that data_[4] cycles between 0-3 when you first put in the batterys in the sender. So please wait atleast 1 hour before trusting this next bit to work correct. + if(data_[4]^0x0f == 1){ watt = (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect); - } - m += sprintf(m, "{\"Sequence\":\"%5d\",\"Watt\":\"%7.1f\",\"kWh\":\"%d.%.3d\",\"battery\":\"%d\",\"FreqErr\":\"%.2f\"}", seq, watt, pulse/PULSES_PER_KWH, pulse%PULSES_PER_KWH, battery, freq); + } else if(data_[4]^0x0f == 2){ + watt = effect * 24; + } m += sprintf(m, "{\"Sequence\":\"%5d\",\"Watt\":\"%7.1f\",\"kWh\":\"%d.%.3d\",\"battery\":\"%d\",\"FreqErr\":\"%.2f\"}", seq, watt, pulse/PULSES_PER_KWH, pulse%PULSES_PER_KWH, battery, freq); if (testing && crc == packet_crc) { error_sum += fabs(freq); From 43bbff10d645abfa07f255358f55e1a3df14653c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Lindstr=C3=B6m?= Date: Wed, 10 Jan 2018 22:46:13 +0100 Subject: [PATCH 25/82] Update sparsnas_decode.cpp Fixed formatting --- sparsnas_decode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index fab6a87..120e381 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -192,8 +192,8 @@ class SignalDetector { watt = (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect); } else if(data_[4]^0x0f == 2){ watt = effect * 24; - } m += sprintf(m, "{\"Sequence\":\"%5d\",\"Watt\":\"%7.1f\",\"kWh\":\"%d.%.3d\",\"battery\":\"%d\",\"FreqErr\":\"%.2f\"}", seq, watt, pulse/PULSES_PER_KWH, pulse%PULSES_PER_KWH, battery, freq); - + } + m += sprintf(m, "{\"Sequence\":\"%5d\",\"Watt\":\"%7.1f\",\"kWh\":\"%d.%.3d\",\"battery\":\"%d\",\"FreqErr\":\"%.2f\"}", seq, watt, pulse/PULSES_PER_KWH, pulse%PULSES_PER_KWH, battery, freq); if (testing && crc == packet_crc) { error_sum += fabs(freq); error_sum_count += 1; From d21e57212b84fae701f8315bcb5ed1751662b977 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Fri, 12 Jan 2018 19:49:08 +0100 Subject: [PATCH 26/82] Update with ESP8266 information and decoding --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b5f5ce6..bef6440 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ https://github.com/strigeus/sparsnas_decoder This is a fork is adapted for output in JSON format to a MQTT broker. +If you want to implement the same thing as below into an ESP8266 please see the following project: https://github.com/bphermansson/EspSparsnasGateway + This is a decoder for IKEA SPARSNÄS. =================================== From be7dd2e430ba2ebbb3232500d96c2f0461f419cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Lindstr=C3=B6m?= Date: Sun, 14 Jan 2018 13:27:40 +0100 Subject: [PATCH 27/82] Update sparsnas_decode.cpp Fix data4 calculation dependency. --- sparsnas_decode.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index 120e381..fa24f9e 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -186,13 +186,12 @@ class SignalDetector { int effect = (dec[11] << 8 | dec[12]); int pulse = (dec[13] << 24 | dec[14] << 16 | dec[15] << 8 | dec[16]); int battery = dec[17]; - float watt; -// Note that data_[4] cycles between 0-3 when you first put in the batterys in the sender. So please wait atleast 1 hour before trusting this next bit to work correct. - if(data_[4]^0x0f == 1){ + float watt = effect * 24; + int data4 = data_[4]^0x0f; +// Note that data_[4] cycles between 0-3 when you first put in the batterys in t$ + if(data4 == 1){ watt = (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect); - } else if(data_[4]^0x0f == 2){ - watt = effect * 24; - } + } m += sprintf(m, "{\"Sequence\":\"%5d\",\"Watt\":\"%7.1f\",\"kWh\":\"%d.%.3d\",\"battery\":\"%d\",\"FreqErr\":\"%.2f\"}", seq, watt, pulse/PULSES_PER_KWH, pulse%PULSES_PER_KWH, battery, freq); if (testing && crc == packet_crc) { error_sum += fabs(freq); From 439da118c1112ff2e4b27a5c174c192e5fb32cd0 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Mon, 15 Jan 2018 15:04:40 +0100 Subject: [PATCH 28/82] Fixed typo in automation example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bef6440..6d38a70 100644 --- a/README.md +++ b/README.md @@ -205,7 +205,7 @@ sensor: friendly_name: "Sparsnäs current day" unit_of_measurement: "kWh" value_template: >- - {{ (float(states.sensor.sparsnas_energy_consumption_over_time.state) - float(states.sensor.sparsnastemplate_kwh_sensor_day.state)) | round(1) }} + {{ (float(states.sensor.sparsnas_energy_consumption_over_time.state) - float(states.sensor.sparsnas_template_kwh_sensor_day.state)) | round(1) }} # Thanks to @bhaap for the monthly automation below automation old: From b8a04ba1a0dae3e9d3365a9cf55793f3c2072bed Mon Sep 17 00:00:00 2001 From: tubalainen Date: Wed, 24 Jan 2018 16:12:31 +0100 Subject: [PATCH 29/82] Ported automation old to "new syntax" for automation Done! --- README.md | 105 ++++++++++++++++++++++++++---------------------------- 1 file changed, 51 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 6d38a70..3f8aa74 100644 --- a/README.md +++ b/README.md @@ -207,60 +207,57 @@ sensor: value_template: >- {{ (float(states.sensor.sparsnas_energy_consumption_over_time.state) - float(states.sensor.sparsnas_template_kwh_sensor_day.state)) | round(1) }} -# Thanks to @bhaap for the monthly automation below -automation old: - - trigger: - platform: time - at: '00:00:01' - condition: - condition: and - conditions: - - condition: template - value_template: '{{ now().day | string == "1" }}' - - condition: or - conditions: - - condition: template - value_template: '{{ now().month | string == "1" }}' - - condition: template - value_template: '{{ now().month | string == "2" }}' - - condition: template - value_template: '{{ now().month | string == "3" }}' - - condition: template - value_template: '{{ now().month | string == "4" }}' - - condition: template - value_template: '{{ now().month | string == "5" }}' - - condition: template - value_template: '{{ now().month | string == "6" }}' - - condition: template - value_template: '{{ now().month | string == "7" }}' - - condition: template - value_template: '{{ now().month | string == "8" }}' - - condition: template - value_template: '{{ now().month | string == "9" }}' - - condition: template - value_template: '{{ now().month | string == "10" }}' - - condition: template - value_template: '{{ now().month | string == "11" }}' - - condition: template - value_template: '{{ now().month | string == "12" }}' - action: - service: mqtt.publish - data: - topic: 'template/kwh/month' - payload_template: "{{ states('sensor.sparsnas_energy_consumption_over_time') }}" - retain: 'true' - alias: "Sparsnäs monthly" - - - alias: "Sparsnäs daily" - trigger: - platform: time - at: '00:00:01' - action: - service: mqtt.publish - data: - topic: 'template/kwh/day' - payload_template: "{{ states('sensor.sparsnas_energy_consumption_over_time') }}" - retain: 'true' +# Thanks to @bhaap and @naestrom for the monthly and daily automation below +automation: +- action: + - data: + payload_template: '{{ states(''sensor.sparsnas_energy_consumption_over_time'') + }}' + retain: 'true' + topic: template/kwh/month + service: mqtt.publish + alias: Sparsnäs monthly consumption + condition: + - condition: template + value_template: '{{ now().month() | string == "1" }}' + - condition: template + value_template: '{{ now().month() | string == "2" }}' + - condition: template + value_template: '{{ now().month() | string == "3" }}' + - condition: template + value_template: '{{ now().month() | string == "4" }}' + - condition: template + value_template: '{{ now().month() | string == "5" }}' + - condition: template + value_template: '{{ now().month() | string == "6" }}' + - condition: template + value_template: '{{ now().month() | string == "7" }}' + - condition: template + value_template: '{{ now().month() | string == "8" }}' + - condition: template + value_template: '{{ now().month() | string == "9" }}' + - condition: template + value_template: '{{ now().month() | string == "10" }}' + - condition: template + value_template: '{{ now().month() | string == "11" }}' + - condition: template + value_template: '{{ now().month() | string == "12" }}' + id: '1516722867362' + trigger: + - at: 00:00:01 + platform: time +- action: + - data: + payload_template: "{{ states('sensor.sparsnas_energy_consumption_over_time')}}" + retain: 'true' + topic: template/kwh/day + service: mqtt.publish + alias: Sparsnäs daily consumption + condition: [] + id: '1516806539856' + trigger: + - at: 00:00:02 + platform: time ``` Use two or more Sparsnäs at the same location (Experts only): From edb18f429db7960112c79ed122f06b17b06b5646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Lindstr=C3=B6m?= Date: Thu, 1 Feb 2018 11:01:08 +0100 Subject: [PATCH 30/82] Update README.md Fix month automation. --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 3f8aa74..ecb9029 100644 --- a/README.md +++ b/README.md @@ -219,29 +219,29 @@ automation: alias: Sparsnäs monthly consumption condition: - condition: template - value_template: '{{ now().month() | string == "1" }}' + value_template: '{{ now().month | string == "1" }}' - condition: template - value_template: '{{ now().month() | string == "2" }}' + value_template: '{{ now().month | string == "2" }}' - condition: template - value_template: '{{ now().month() | string == "3" }}' + value_template: '{{ now().month | string == "3" }}' - condition: template - value_template: '{{ now().month() | string == "4" }}' + value_template: '{{ now().month | string == "4" }}' - condition: template - value_template: '{{ now().month() | string == "5" }}' + value_template: '{{ now().month | string == "5" }}' - condition: template - value_template: '{{ now().month() | string == "6" }}' + value_template: '{{ now().month | string == "6" }}' - condition: template - value_template: '{{ now().month() | string == "7" }}' + value_template: '{{ now().month | string == "7" }}' - condition: template - value_template: '{{ now().month() | string == "8" }}' + value_template: '{{ now().month | string == "8" }}' - condition: template - value_template: '{{ now().month() | string == "9" }}' + value_template: '{{ now().month | string == "9" }}' - condition: template - value_template: '{{ now().month() | string == "10" }}' + value_template: '{{ now().month | string == "10" }}' - condition: template - value_template: '{{ now().month() | string == "11" }}' + value_template: '{{ now().month | string == "11" }}' - condition: template - value_template: '{{ now().month() | string == "12" }}' + value_template: '{{ now().month | string == "12" }}' id: '1516722867362' trigger: - at: 00:00:01 From 2191b33dc698733f21718150df6a4fef1df8269b Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Mon, 5 Feb 2018 08:56:34 +0100 Subject: [PATCH 31/82] initial support to run in docker --- docker/Dockerfile | 22 +++++++++++++++++++++ docker/README.md | 40 ++++++++++++++++++++++++++++++++++++++ docker/sparsnas_decoder.sh | 40 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 docker/Dockerfile create mode 100644 docker/README.md create mode 100755 docker/sparsnas_decoder.sh diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..6aed6e4 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,22 @@ +# derive from debian jessie image +FROM debian:jessie + +MAINTAINER Fredrik Erlandsson + +RUN apt-get update && \ + apt-get install -y rtl-sdr g++ git mosquitto-clients nano && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /tmp + +RUN echo 'blacklist dvb_usb_rtl28xxu' > /etc/modprobe.d/raspi-blacklist.conf && \ + git clone git://github.com/tubalainen/sparsnas_decoder && \ + g++ -o sparsnas_decoder/sparsnas_decode -O3 sparsnas_decoder/sparsnas_decode.cpp + +WORKDIR / + +RUN echo 'rtl_sdr -f 868000000 -s 1024000 -g 10 - | sparsnas_decode 2>&1 | /usr/bin/mosquitto_pub -h 127.0.0.1 -u username -P password -i sparsnas -l -t "home/sparsnas"' >> /etc/rc.local + + + diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..0f75274 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,40 @@ +# Docker support + +This dockerfile makes it possible to run `rtl-sdr` and `sparsnas_decode` as a docker (currently only tested on a x86 Synology NAS). + +## Usage + +Due to the way `sparsnas_decode` is constructed each of the steps of configuration requires a new compile of `sparsnas_decode`. + +1. Build the docker environment: + `sudo docker build --rm -t rtlsdr-sparsnas .` + +2. Run the newly created docker image (this will spawn a shell in the new container): + + `sudo docker run -it --device=/dev/bus/usb --name=sparsnas rtlsdr-sparsnas:latest /bin/bash` + +3. From the shell in the container follow the guidance from the main README + until you have a working version that outputs values via mqtt. Copy the compiled + `sparsnas_decode` to `/usr/bin` (`cp /tmp/sparsnas_decoder/sparsnas_decode /usr/bin`) + and modify `/etc/rc.local` to include ip, username and password of your running + mqtt-broker. + +4. Remove the /tmp/sparsnas_decoder folder, including the file `sparsnas.raw` (`rm -rf /tmp/sparsnas_decoder`). + +5. From another shell (outside the container) save the state of the image as a new docker-image + + `sudo docker commit $(sudo docker ps |grep sparsnas |awk '{print $1}') rtlsdr-sparsnas:configured` + +6. Exit the shell in the containter. + +7. Start the newly created image and enjoy :smile: + + `sudo docker run --rm -it --device=/dev/bus/usb -t sparsnas --restart=always rtlsdr-sparsnas:configured /etc/rc.local` + + +### Todo + +* Verify that everything is working :stuck_out_tongue_winking_eye:. +* Rewrite `sparsnas-decode.cpp` to support argument of senderId and frequencies. +* Make the process more automatic (perhaps by only requiring the sender-id). +* Switch to using `alpine` as the base for the container (to keep it smaller). diff --git a/docker/sparsnas_decoder.sh b/docker/sparsnas_decoder.sh new file mode 100755 index 0000000..331d6d8 --- /dev/null +++ b/docker/sparsnas_decoder.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +echo "Work in progress." +exit 0 + +SPARSNAS=/usr/bin/sparsnas_decode +RTL_SDR=rtl_sdr -f 868000000 -s 1024000 -g 10 - +SENDER_ID=$1 +HOST=$2 +USER=$3 +PASS=$4 + +while true; do + if [ -e /etc/sparsnas.conf ]; then + source /etc/sparsnas.conf + $RTL_SDR | $SPARSNAS -i $SENDER_ID -l $LOWER_ FREQUENCY -u $UPPER_FREQUENCY 2>&1 |\ + /usr/bin/mosquitto_pub -h $HOST -u $USER -P $PASSWORD -i sparsnas -l -t "home/sparsnas + else + # We have not configured sparsnas, try to find frequencies. + $RTL_SDR > /tmp/sparsnas.raw & + TASK_PID=$! + sleep 30 + kill $TASK_PID + + # sparsnas_decode.cpp should write FREQUENCES to stdout not stderr. + $SPARSNAS /tmp/sparsnas.raw --find-frequencies > /tmp/sparsnas_freq + source /tmp/sparsnas_freq + if [ -z "$UPPER_FREQUENCY" ]; then + echo "Could not find frequencies." + exit 0 + else + echo "SENDER_ID=$1 +HOST=$2 +USER=$3 +PASS=$4 +LOWER_ FREQUENCY=$LOWER_FREQUENCY +UPPER_FREQUENCY=$UPPER_FREQUENCY" > /etc/sparsnas.conf + fi + fi +done From 3300f53961df3c8c9b4225235ee74750aa23a1be Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Tue, 6 Feb 2018 10:03:27 +0100 Subject: [PATCH 32/82] verified setting, thanks to @martikainen87 --- docker/Dockerfile | 7 ++++--- docker/README.md | 17 ++++++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 6aed6e4..3dfae5f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -6,17 +6,18 @@ MAINTAINER Fredrik Erlandsson RUN apt-get update && \ apt-get install -y rtl-sdr g++ git mosquitto-clients nano && \ apt-get clean && \ - rm -rf /var/lib/apt/lists/* + rm -rf /var/lib/apt/lists/* /usr/share/doc* /usr/share/man WORKDIR /tmp RUN echo 'blacklist dvb_usb_rtl28xxu' > /etc/modprobe.d/raspi-blacklist.conf && \ git clone git://github.com/tubalainen/sparsnas_decoder && \ - g++ -o sparsnas_decoder/sparsnas_decode -O3 sparsnas_decoder/sparsnas_decode.cpp + g++ -o sparsnas_decoder/sparsnas_decode -O3 sparsnas_decoder/sparsnas_decode.cpp WORKDIR / -RUN echo 'rtl_sdr -f 868000000 -s 1024000 -g 10 - | sparsnas_decode 2>&1 | /usr/bin/mosquitto_pub -h 127.0.0.1 -u username -P password -i sparsnas -l -t "home/sparsnas"' >> /etc/rc.local +RUN sed -i "14 d" /etc/rc.local && \ + echo 'rtl_sdr -f 868000000 -s 1024000 -g 10 - | sparsnas_decode 2>&1 | /usr/bin/mosquitto_pub -h 127.0.0.1 -u username -P password -i sparsnas -l -t "home/sparsnas"' >> /etc/rc.local diff --git a/docker/README.md b/docker/README.md index 0f75274..d702cc7 100644 --- a/docker/README.md +++ b/docker/README.md @@ -21,20 +21,23 @@ Due to the way `sparsnas_decode` is constructed each of the steps of configurati 4. Remove the /tmp/sparsnas_decoder folder, including the file `sparsnas.raw` (`rm -rf /tmp/sparsnas_decoder`). -5. From another shell (outside the container) save the state of the image as a new docker-image +5. Exit the shell in the docker (`ctrl-d` or `exit`) and save the state of the image as a new docker-image - `sudo docker commit $(sudo docker ps |grep sparsnas |awk '{print $1}') rtlsdr-sparsnas:configured` + ``` + sudo docker commit $(docker ps -a |grep sparsnas |awk '{print $1}') rtlsdr-sparsnas:configured + ``` -6. Exit the shell in the containter. +6. Start the newly created image and enjoy :smile: -7. Start the newly created image and enjoy :smile: - - `sudo docker run --rm -it --device=/dev/bus/usb -t sparsnas --restart=always rtlsdr-sparsnas:configured /etc/rc.local` + ``` + sudo docker rm sparsnas + sudo docker run -d --device=/dev/bus/usb --security-opt seccomp=unconfined \ + --name=sparsnas --restart=always rtlsdr-sparsnas:configured /etc/rc.local + ``` ### Todo -* Verify that everything is working :stuck_out_tongue_winking_eye:. * Rewrite `sparsnas-decode.cpp` to support argument of senderId and frequencies. * Make the process more automatic (perhaps by only requiring the sender-id). * Switch to using `alpine` as the base for the container (to keep it smaller). From 43055d15f7bcfe9f8133c7180b9656848319918d Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Thu, 8 Feb 2018 14:32:18 +0100 Subject: [PATCH 33/82] docker support with automatic configuration --- Dockerfile | 34 +++++++ docker.md | 19 ++++ docker/Dockerfile | 23 ----- docker/README.md | 43 --------- docker/sparsnas_decoder.sh | 40 -------- sparsnas.sh | 29 ++++++ sparsnas_decode.cpp | 188 ++++++++++++++++++++++--------------- 7 files changed, 196 insertions(+), 180 deletions(-) create mode 100644 Dockerfile create mode 100644 docker.md delete mode 100644 docker/Dockerfile delete mode 100644 docker/README.md delete mode 100755 docker/sparsnas_decoder.sh create mode 100755 sparsnas.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a983ea4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +# docker build --build-arg SENSOR_ID=1234 --build-arg PULSES_PER_KWH=10000 \ +# --build-arg MQTT_ARG="-h 192.168.x.x -u username -P password -i sparsnas". + +FROM alpine:edge as BUILD_ENV + +COPY ./sparsnas_decode.cpp /build/ + +RUN apk add --no-cache g++ && \ + g++ -o /build/sparsnas_decode -static-libgcc -static-libstdc++ -O2 /build/sparsnas_decode.cpp + +FROM alpine:edge + +ARG SENSOR_ID +ENV SPARSNAS_SENSOR_ID=$SENSOR_ID +ARG PULSES_PER_KWH=1000 +ENV SPARSNAS_PULSES_PER_KWH=$PULSES_PER_KWH +ARG SPARSNAS_FREQ_MIN +ARG SPARSNAS_FREQ_MAX +ARG MQTT_ARG +ENV MQTT_ARG=$MQTT_ARG + +COPY --from=BUILD_ENV /build/sparsnas_decode /usr/bin/ +COPY sparsnas.sh / + +RUN apk add --no-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ --allow-untrusted \ + rtl-sdr \ + mosquitto-clients && \ + \ + if [ "$SPARSNAS_FREQ_MIN" -a "$SPARSNAS_FREQ_MAX" ]; then \ + echo "export SPARSNAS_FREQ_MIN=${SPARSNAS_FREQ_MIN}" >> /etc/sparsnas.conf && \ + echo "export SPARSNAS_FREQ_MAX=${SPARSNAS_FREQ_MAX}" >> /etc/sparsnas.conf; \ + fi + +ENTRYPOINT ["/sparsnas.sh"] diff --git a/docker.md b/docker.md new file mode 100644 index 0000000..5b557b4 --- /dev/null +++ b/docker.md @@ -0,0 +1,19 @@ +# Docker support instructions + + +1. Build docker container as follows. The `SENSOR_ID` is required and is the unique + id of your sensor, `PULSES_PER_KWH` and `MQTT_ARG` are optional (but highly recommended). + +``` +docker build -t sparsnas --build-arg SENSOR_ID=1234 --build-arg PULSES_PER_KWH=10000 \ + --build-arg MQTT_ARG="-h 192.168.x.x -u username -P password -i sparsnas" \ + https://github.com/fredrike/sparsnas_decoder.git#dockerVersion +``` + +2. Run the container. + +``` +docker run --device=/dev/bus/usb --name=sparsnas --restart=always sparsnas:latest +``` + +3. Sit back and enjoy :smile:. diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 3dfae5f..0000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -# derive from debian jessie image -FROM debian:jessie - -MAINTAINER Fredrik Erlandsson - -RUN apt-get update && \ - apt-get install -y rtl-sdr g++ git mosquitto-clients nano && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* /usr/share/doc* /usr/share/man - -WORKDIR /tmp - -RUN echo 'blacklist dvb_usb_rtl28xxu' > /etc/modprobe.d/raspi-blacklist.conf && \ - git clone git://github.com/tubalainen/sparsnas_decoder && \ - g++ -o sparsnas_decoder/sparsnas_decode -O3 sparsnas_decoder/sparsnas_decode.cpp - -WORKDIR / - -RUN sed -i "14 d" /etc/rc.local && \ - echo 'rtl_sdr -f 868000000 -s 1024000 -g 10 - | sparsnas_decode 2>&1 | /usr/bin/mosquitto_pub -h 127.0.0.1 -u username -P password -i sparsnas -l -t "home/sparsnas"' >> /etc/rc.local - - - diff --git a/docker/README.md b/docker/README.md deleted file mode 100644 index d702cc7..0000000 --- a/docker/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Docker support - -This dockerfile makes it possible to run `rtl-sdr` and `sparsnas_decode` as a docker (currently only tested on a x86 Synology NAS). - -## Usage - -Due to the way `sparsnas_decode` is constructed each of the steps of configuration requires a new compile of `sparsnas_decode`. - -1. Build the docker environment: - `sudo docker build --rm -t rtlsdr-sparsnas .` - -2. Run the newly created docker image (this will spawn a shell in the new container): - - `sudo docker run -it --device=/dev/bus/usb --name=sparsnas rtlsdr-sparsnas:latest /bin/bash` - -3. From the shell in the container follow the guidance from the main README - until you have a working version that outputs values via mqtt. Copy the compiled - `sparsnas_decode` to `/usr/bin` (`cp /tmp/sparsnas_decoder/sparsnas_decode /usr/bin`) - and modify `/etc/rc.local` to include ip, username and password of your running - mqtt-broker. - -4. Remove the /tmp/sparsnas_decoder folder, including the file `sparsnas.raw` (`rm -rf /tmp/sparsnas_decoder`). - -5. Exit the shell in the docker (`ctrl-d` or `exit`) and save the state of the image as a new docker-image - - ``` - sudo docker commit $(docker ps -a |grep sparsnas |awk '{print $1}') rtlsdr-sparsnas:configured - ``` - -6. Start the newly created image and enjoy :smile: - - ``` - sudo docker rm sparsnas - sudo docker run -d --device=/dev/bus/usb --security-opt seccomp=unconfined \ - --name=sparsnas --restart=always rtlsdr-sparsnas:configured /etc/rc.local - ``` - - -### Todo - -* Rewrite `sparsnas-decode.cpp` to support argument of senderId and frequencies. -* Make the process more automatic (perhaps by only requiring the sender-id). -* Switch to using `alpine` as the base for the container (to keep it smaller). diff --git a/docker/sparsnas_decoder.sh b/docker/sparsnas_decoder.sh deleted file mode 100755 index 331d6d8..0000000 --- a/docker/sparsnas_decoder.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -echo "Work in progress." -exit 0 - -SPARSNAS=/usr/bin/sparsnas_decode -RTL_SDR=rtl_sdr -f 868000000 -s 1024000 -g 10 - -SENDER_ID=$1 -HOST=$2 -USER=$3 -PASS=$4 - -while true; do - if [ -e /etc/sparsnas.conf ]; then - source /etc/sparsnas.conf - $RTL_SDR | $SPARSNAS -i $SENDER_ID -l $LOWER_ FREQUENCY -u $UPPER_FREQUENCY 2>&1 |\ - /usr/bin/mosquitto_pub -h $HOST -u $USER -P $PASSWORD -i sparsnas -l -t "home/sparsnas - else - # We have not configured sparsnas, try to find frequencies. - $RTL_SDR > /tmp/sparsnas.raw & - TASK_PID=$! - sleep 30 - kill $TASK_PID - - # sparsnas_decode.cpp should write FREQUENCES to stdout not stderr. - $SPARSNAS /tmp/sparsnas.raw --find-frequencies > /tmp/sparsnas_freq - source /tmp/sparsnas_freq - if [ -z "$UPPER_FREQUENCY" ]; then - echo "Could not find frequencies." - exit 0 - else - echo "SENDER_ID=$1 -HOST=$2 -USER=$3 -PASS=$4 -LOWER_ FREQUENCY=$LOWER_FREQUENCY -UPPER_FREQUENCY=$UPPER_FREQUENCY" > /etc/sparsnas.conf - fi - fi -done diff --git a/sparsnas.sh b/sparsnas.sh new file mode 100755 index 0000000..d2c1b96 --- /dev/null +++ b/sparsnas.sh @@ -0,0 +1,29 @@ +#!/bin/sh + + +SPARSNAS="/usr/bin/sparsnas_decode" +RTL_SDR="rtl_sdr -f 868000000 -s 1024000 -g 10 -" + +if [ -e /etc/sparsnas.conf ]; then + . /etc/sparsnas.conf +fi + +for i in `seq 2`; do + if [ "$SPARSNAS_FREQ_MIN" -a "$SPARSNAS_FREQ_MAX" ]; then + $RTL_SDR | $SPARSNAS 2>&1 | \ + /usr/bin/mosquitto_pub -l -t 'home/sparsnas' ${MQTT_ARG:--h 127.0.0.1 -i sparsnas} + else + echo "We have not configured sparsnas, try to find frequencies." + $RTL_SDR > /tmp/sparsnas.raw & TASK_PID=$!; sleep 25; kill $TASK_PID &>/dev/null + $SPARSNAS /tmp/sparsnas.raw --find-frequencies > /tmp/sparsnas_freq + rm /tmp/sparsnas.raw + . /tmp/sparsnas_freq + if [ "$SPARSNAS_FREQ_MIN" -a "$SPARSNAS_FREQ_MAX" ]; then + echo "export SPARSNAS_FREQ_MIN=${SPARSNAS_FREQ_MIN}" >> /etc/sparsnas.conf && \ + echo "export SPARSNAS_FREQ_MAX=${SPARSNAS_FREQ_MAX}" >> /etc/sparsnas.conf; + else + echo "Could not find frequencies." + exit 1 + fi + fi +done diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index fa24f9e..93d650d 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -1,34 +1,32 @@ -#include -#include -#define _USE_MATH_DEFINES 1 -#include +#include +#include +#include #include -#include -#include -#include +#include + +// This is the number of pulse per kWh consumed of your elecricity meter. +// In Sweden at least, the standard seems to be 1 pulse per Wh, i.e. 1000 pulses per kWh +int PULSES_PER_KWH=1000; -////////////////////////////////////// -#define PULSES_PER_KWH 1000 // These are the last 6 digits from the serial number of the sender. // The serial number is located under the battery. // The full serial number looks like "400 666 111" -#define SENSOR_ID 666111 +int SENSOR_ID; // It seems like different RTL-SDR tune to slightly different frequencies // Or I'm not really sure what's up, but the 0 and 1 frequencies differ -// between different RTL-SDR and/or sparsnäs. You can have a look at the +// between different RTL-SDR and/or sparsnäs. You can have a look at the // signal in a wave file editor and you can measure the wavelengths of the // sine waves and put in appropriate values here. //#define FREQUENCIES {12500.0,50000.0} -#define FREQUENCIES {67500.0,105000.0} -//#define FREQUENCIES {20000.0,60000.0} +float frequencies[2]; //={67500.0,105000.0}; + -////////////////////////////////////// -FILE *outfile; -int testing; +FILE *outfile; +int testing=0; // Implementation of Complex numbers, cause std::complex is stupid and doesn't inline properly. @@ -155,7 +153,7 @@ class SignalDetector { // m += sprintf(m, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d] ", timeinfo->tm_year + 1900, // timeinfo->tm_mon + 1, -// timeinfo->tm_mday, +// timeinfo->tm_mday, // timeinfo->tm_hour, // timeinfo->tm_min, // timeinfo->tm_sec); @@ -176,11 +174,11 @@ class SignalDetector { uint32_t rcv_sensor_id = dec[5] << 24 | dec[6] << 16 | dec[7] << 8 | dec[8]; - if (data_[0] != 0x11 || data_[1] != (SENSOR_ID & 0xFF) || data_[3] != 0x07 || rcv_sensor_id != SENSOR_ID) { + if (data_[0] != 0x11 || data_[1] != (SENSOR_ID & 0xFF) || data_[3] != 0x07 || rcv_sensor_id != SENSOR_ID) { m += sprintf(m, "{\"Bad\":\""); for (int i = 0; i < 18; i++) m += sprintf(m, "%.2X ", data_[i]); - m += sprintf(m, "\"}"); + m += sprintf(m, "\""); } else { int seq = (dec[9] << 8 | dec[10]); int effect = (dec[11] << 8 | dec[12]); @@ -191,15 +189,15 @@ class SignalDetector { // Note that data_[4] cycles between 0-3 when you first put in the batterys in t$ if(data4 == 1){ watt = (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect); - } - m += sprintf(m, "{\"Sequence\":\"%5d\",\"Watt\":\"%7.1f\",\"kWh\":\"%d.%.3d\",\"battery\":\"%d\",\"FreqErr\":\"%.2f\"}", seq, watt, pulse/PULSES_PER_KWH, pulse%PULSES_PER_KWH, battery, freq); + } + m += sprintf(m, "{\"Sequence\":\"%5d\",\"Watt\":\"%7.1f\",\"kWh\":\"%d.%.3d\",\"battery\":\"%d\",\"FreqErr\":\"%.2f\"", seq, watt, pulse/PULSES_PER_KWH, pulse%PULSES_PER_KWH, battery, freq); if (testing && crc == packet_crc) { error_sum += fabs(freq); error_sum_count += 1; } } - m += sprintf(m, (crc == packet_crc) ? "\n" : "CRC ERR\n"); + m += sprintf(m, (crc == packet_crc) ? ",\"CRC\":\"ok\"}\n" : ",\"CRC\": \"ERR\"}\n"); if (!testing) { fprintf(stderr, "%s", mesg); @@ -234,7 +232,7 @@ int run_for_frequencies(FILE *f, FILE *logfile, float F1, float F2) { bool last_signal = false; int last_sigtime = 0; - + const float PERFECT_PULSE_LEN = 26.6666666f * S / 1024000.0; const int MIN_PULSE_LEN = 12 * S / 1024000.0; const int MAX_PULSE_LEN = 42 * S / 1024000.0; @@ -285,7 +283,7 @@ int run_for_frequencies(FILE *f, FILE *logfile, float F1, float F2) { if (signal != last_signal) { int pulse_len = (unsigned)j - last_sigtime; - + if (pulse_len >= MIN_PULSE_LEN && (sd.has_some_sync() || pulse_len < MAX_PULSE_LEN)) { if (signal) avg_err = -avg_err; @@ -313,68 +311,110 @@ int run_for_frequencies(FILE *f, FILE *logfile, float F1, float F2) { } -int main(int argc, char **argv) -{ - FILE *f = stdin; - if (argc >= 2) { - f = fopen(argv[1], "rb"); - if (!f) { - fprintf(stderr, "Failed load!\n"); - return 1; - } -// for loading wav files -// fseek(f, 44, SEEK_SET); - } - - testing = (argc >= 3 && strcmp(argv[2], "--find-frequencies") == 0); +int run_calibration(FILE *f){ + testing = 1; + float range_min = -100000, range_max = 100000, step = 5000; + float invalid_f1 = 1e100, best_f1; - outfile = fopen("sparsnas.log", "a"); + do { + float best_error = 1e100; + best_f1 = invalid_f1; + + for(float f1 = range_min; f1 <= range_max; f1 += step) { + fseek(f, 0, SEEK_SET); + + fprintf(stderr, "Trying %.0f hz...\n", f1); + + error_sum = 0; + error_sum_count = 0; + run_for_frequencies(f, NULL, f1, f1 + 40000.0f); + + if (error_sum_count != 0) { + float error = error_sum / error_sum_count; + if (error < best_error) { + fprintf(stderr, "Freq %.0f gives error %f\n", f1, error); + best_error = error; + best_f1 = f1; + } + } + } - FILE *logfile = NULL;// fopen("logfile.pcm", "wb"); + if (best_f1 == invalid_f1) { + fprintf(stderr, "Nothing found...\n"); + return 1; + } + range_min = best_f1 - step * 0.5f; + range_max = best_f1 + step * 0.5f; + step /= 10.0f; + } while (step >= 10.0f); - if (!testing) { - float frequencies[] = FREQUENCIES; - run_for_frequencies(f, logfile, frequencies[0], frequencies[1]); - } else { - float range_min = -100000, range_max = 100000, step = 5000; - float invalid_f1 = 1e100, best_f1; + fprintf(stderr, "#define FREQUENCIES {%f, %f}\n", best_f1, best_f1 + 40000.0f); + printf("export SPARSNAS_FREQ_MIN=%f\nexport SPARSNAS_FREQ_MAX=%f\n" , best_f1, best_f1 + 40000.0f); + return 0; +} - do { - float best_error = 1e100; - best_f1 = invalid_f1; - for(float f1 = range_min; f1 <= range_max; f1 += step) { - fseek(f, 0, SEEK_SET); +int get_env_int(const char * env_var_name,int * buf) { - fprintf(stderr, "Trying %.0f hz...\n", f1); + if (const char *env_p = std::getenv(env_var_name)) + if (sscanf(env_p,"%d",buf)) + return 1; + return 0; +} - error_sum = 0; - error_sum_count = 0; - run_for_frequencies(f, NULL, f1, f1 + 40000.0f); - if (error_sum_count != 0) { - float error = error_sum / error_sum_count; - if (error < best_error) { - fprintf(stderr, "Freq %.0f gives error %f\n", f1, error); - best_error = error; - best_f1 = f1; - } +int main(int argc, char **argv) +{ + int tmp; + FILE *f = stdin; + if (argc >= 2) { + f = fopen(argv[1], "rb"); + if (!f) { + fprintf(stderr, "Failed load!\n"); + return 1; } - } + } - if (best_f1 == invalid_f1) { - fprintf(stderr, "Nothing found...\n"); - return 0; - } - range_min = best_f1 - step * 0.5f; - range_max = best_f1 + step * 0.5f; - step /= 10.0f; - } while (step >= 10.0f); + //Get SENSOR_ID from environment + if (get_env_int("SPARSNAS_SENSOR_ID",&tmp)) + SENSOR_ID = tmp; + else { + fprintf(stderr, "SPARSNAS_SENSOR_ID not defined or incorrect. Aborting!\n"); + return 1; + } - fprintf(stderr, "#define FREQUENCIES {%f, %f}\n", best_f1, best_f1 + 40000.0f); - } + if (argc >= 3 && strcmp(argv[2], "--find-frequencies") == 0) + return run_calibration(f); + + + //Get the parameters from environment + if (get_env_int("SPARSNAS_PULSES_PER_KWH",&tmp)) + PULSES_PER_KWH = tmp; + + if (get_env_int("SPARSNAS_FREQ_MIN",&tmp)) + frequencies[0] = tmp; + else { + fprintf(stderr, "SPARSNAS_FREQ_MIN not defined or incorrect. Aborting!\n"); + return 1; + } + + if (get_env_int("SPARSNAS_FREQ_MAX",&tmp)) + frequencies[1] = tmp; + else { + fprintf(stderr, "SPARSNAS_FREQ_MAX not defined or incorrect. Aborting!\n"); + return 1; + } + + FILE *logfile; + if (const char *env_p = std::getenv("SPARSNAS_LOG")) + logfile=fopen(env_p,"a"); + else + logfile = NULL; + + //Run the main program + run_for_frequencies(f, logfile, frequencies[0], frequencies[1]); - return 0; + return 0; } From 603a21dbdbebe3ce32654369071adcdbaee6076c Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Fri, 9 Feb 2018 08:36:27 +0100 Subject: [PATCH 34/82] changed the gain of rtl_sdr --- sparsnas.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sparsnas.sh b/sparsnas.sh index d2c1b96..a2fcdc2 100755 --- a/sparsnas.sh +++ b/sparsnas.sh @@ -2,7 +2,7 @@ SPARSNAS="/usr/bin/sparsnas_decode" -RTL_SDR="rtl_sdr -f 868000000 -s 1024000 -g 10 -" +RTL_SDR="rtl_sdr -f 868000000 -s 1024000 -g 40 -" if [ -e /etc/sparsnas.conf ]; then . /etc/sparsnas.conf From 0a32045793e0f8be7730df6ab4075e0b69768ddc Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Fri, 9 Feb 2018 12:35:57 +0100 Subject: [PATCH 35/82] exit with pipe errors (set -e) --- sparsnas.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/sparsnas.sh b/sparsnas.sh index a2fcdc2..a37e07b 100755 --- a/sparsnas.sh +++ b/sparsnas.sh @@ -1,5 +1,6 @@ #!/bin/sh +set -e SPARSNAS="/usr/bin/sparsnas_decode" RTL_SDR="rtl_sdr -f 868000000 -s 1024000 -g 40 -" From 03c065cfef868de083744205da3daee00da167bb Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Fri, 9 Feb 2018 12:40:33 +0100 Subject: [PATCH 36/82] export all variables to subsequent commands (set -a) --- sparsnas.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sparsnas.sh b/sparsnas.sh index a37e07b..a292f06 100755 --- a/sparsnas.sh +++ b/sparsnas.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -e +set -ae SPARSNAS="/usr/bin/sparsnas_decode" RTL_SDR="rtl_sdr -f 868000000 -s 1024000 -g 40 -" From 76304e2872044929e80a6d86b9b87e29269a24a2 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Sun, 11 Feb 2018 17:27:12 +0100 Subject: [PATCH 37/82] Added https://github.com/kodarn/Sparsnas https://github.com/kodarn/Sparsnas --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index ecb9029..e16636d 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ This is a fork is adapted for output in JSON format to a MQTT broker. If you want to implement the same thing as below into an ESP8266 please see the following project: https://github.com/bphermansson/EspSparsnasGateway +If you want to get into the bits and bytes of the system please see: +https://github.com/kodarn/Sparsnas + This is a decoder for IKEA SPARSNÄS. =================================== From 7329d402db3fdab2334eba20dd517e0ac38cad67 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Wed, 14 Feb 2018 10:21:26 +0100 Subject: [PATCH 38/82] better json output, errors to stderr & function fix --- sparsnas_decode.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index 93d650d..ba33b28 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -138,6 +138,7 @@ class SignalDetector { void add_fail(float freq) { char mesg[1024]; + int bad = false; shift_ = 0; found_sync_ = 0; @@ -175,11 +176,13 @@ class SignalDetector { uint32_t rcv_sensor_id = dec[5] << 24 | dec[6] << 16 | dec[7] << 8 | dec[8]; if (data_[0] != 0x11 || data_[1] != (SENSOR_ID & 0xFF) || data_[3] != 0x07 || rcv_sensor_id != SENSOR_ID) { + bad = true; m += sprintf(m, "{\"Bad\":\""); for (int i = 0; i < 18; i++) m += sprintf(m, "%.2X ", data_[i]); m += sprintf(m, "\""); } else { + bad = false; int seq = (dec[9] << 8 | dec[10]); int effect = (dec[11] << 8 | dec[12]); int pulse = (dec[13] << 24 | dec[14] << 16 | dec[15] << 8 | dec[16]); @@ -190,17 +193,18 @@ class SignalDetector { if(data4 == 1){ watt = (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect); } - m += sprintf(m, "{\"Sequence\":\"%5d\",\"Watt\":\"%7.1f\",\"kWh\":\"%d.%.3d\",\"battery\":\"%d\",\"FreqErr\":\"%.2f\"", seq, watt, pulse/PULSES_PER_KWH, pulse%PULSES_PER_KWH, battery, freq); + m += sprintf(m, "{\"Sequence\": %5d,\"Watt\": %7.2f,\"kWh\": %d.%.3d,\"battery\": %d,\"FreqErr\": %.2f", seq, watt, pulse/PULSES_PER_KWH, pulse%PULSES_PER_KWH, battery, freq); if (testing && crc == packet_crc) { error_sum += fabs(freq); error_sum_count += 1; } } - m += sprintf(m, (crc == packet_crc) ? ",\"CRC\":\"ok\"}\n" : ",\"CRC\": \"ERR\"}\n"); + m += sprintf(m, (crc == packet_crc) ? ",\"CRC\":\"ok\"" : ",\"CRC\": \"ERR\""); + m += sprintf(m, ",\"Sensor\":%6d}\n", SENSOR_ID); if (!testing) { - fprintf(stderr, "%s", mesg); + bad ? fprintf(stderr, "%s", mesg) : printf("%s", mesg); if (outfile) { fprintf(outfile, "%s", mesg); fflush(outfile); @@ -216,7 +220,7 @@ class SignalDetector { uint32_t bits_; }; -int run_for_frequencies(FILE *f, FILE *logfile, float F1, float F2) { +void run_for_frequencies(FILE *f, FILE *logfile, float F1, float F2) { uint8_t buf[16384]; SignalDetector sd; From a7cd5a8755181fab313e3f5e698dcbac68f73153 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Wed, 14 Feb 2018 10:38:03 +0100 Subject: [PATCH 39/82] fix of compilation errors --- sparsnas_decode.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index ba33b28..47fcb1b 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -6,13 +6,13 @@ // This is the number of pulse per kWh consumed of your elecricity meter. // In Sweden at least, the standard seems to be 1 pulse per Wh, i.e. 1000 pulses per kWh -int PULSES_PER_KWH=1000; +uint32_t PULSES_PER_KWH=1000; // These are the last 6 digits from the serial number of the sender. // The serial number is located under the battery. // The full serial number looks like "400 666 111" -int SENSOR_ID; +uint32_t SENSOR_ID; // It seems like different RTL-SDR tune to slightly different frequencies // Or I'm not really sure what's up, but the 0 and 1 frequencies differ @@ -224,8 +224,8 @@ void run_for_frequencies(FILE *f, FILE *logfile, float F1, float F2) { uint8_t buf[16384]; SignalDetector sd; - Complex hist1[27] = { 0 }; - Complex hist2[27] = { 0 }; + Complex hist1[27] = { {0} }; + Complex hist2[27] = { {0} }; Complex sum1 = {0, 0}; Complex sum2 = {0, 0}; From 7788d25a42aea15e4d231e6faa31c347764b6e5c Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Wed, 14 Feb 2018 13:47:33 +0100 Subject: [PATCH 40/82] support for mqtt_publish --- sparsnas_decode.cpp | 80 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 16 deletions(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index 47fcb1b..a447dcc 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -3,16 +3,17 @@ #include #include #include +#include // This is the number of pulse per kWh consumed of your elecricity meter. // In Sweden at least, the standard seems to be 1 pulse per Wh, i.e. 1000 pulses per kWh -uint32_t PULSES_PER_KWH=1000; +int PULSES_PER_KWH=1000; // These are the last 6 digits from the serial number of the sender. // The serial number is located under the battery. // The full serial number looks like "400 666 111" -uint32_t SENSOR_ID; +int SENSOR_ID; // It seems like different RTL-SDR tune to slightly different frequencies // Or I'm not really sure what's up, but the 0 and 1 frequencies differ @@ -22,7 +23,15 @@ uint32_t SENSOR_ID; //#define FREQUENCIES {12500.0,50000.0} float frequencies[2]; //={67500.0,105000.0}; +// MQTT connection +struct mosquitto *mosq = NULL; +// MQTT server connection parameters +char MQTT_HOSTNAME[64]; +uint32_t MQTT_PORT = 1883; +char MQTT_USERNAME[64]; +char MQTT_PASSWORD[64]; +char MQTT_TOPIC[64]; FILE *outfile; @@ -147,18 +156,6 @@ class SignalDetector { uint16_t packet_crc = data_[18] << 8 | data_[19]; char *m = mesg; - time_t mytime; - mytime = time(NULL); - struct tm * timeinfo; - timeinfo = localtime(&mytime); - -// m += sprintf(m, "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d] ", timeinfo->tm_year + 1900, -// timeinfo->tm_mon + 1, -// timeinfo->tm_mday, -// timeinfo->tm_hour, -// timeinfo->tm_min, -// timeinfo->tm_sec); - uint8_t dec[32]; uint8_t enc_key[5]; @@ -173,7 +170,7 @@ class SignalDetector { for(size_t i = 0; i < 13; i++) dec[5 + i] = data_[5 + i] ^ enc_key[i % 5]; - uint32_t rcv_sensor_id = dec[5] << 24 | dec[6] << 16 | dec[7] << 8 | dec[8]; + int rcv_sensor_id = dec[5] << 24 | dec[6] << 16 | dec[7] << 8 | dec[8]; if (data_[0] != 0x11 || data_[1] != (SENSOR_ID & 0xFF) || data_[3] != 0x07 || rcv_sensor_id != SENSOR_ID) { bad = true; @@ -204,7 +201,18 @@ class SignalDetector { m += sprintf(m, ",\"Sensor\":%6d}\n", SENSOR_ID); if (!testing) { - bad ? fprintf(stderr, "%s", mesg) : printf("%s", mesg); + if (mosq && !bad) { + int ret = mosquitto_publish (mosq, NULL, MQTT_TOPIC, strlen(mesg) - 1, mesg, 0, false); + if (ret) { + fprintf(stderr, "Can't publish to Mosquitto server\n"); + // Tear down the connecton and exit. + mosquitto_disconnect (mosq); + mosquitto_destroy (mosq); + mosquitto_lib_cleanup(); + exit(-1); + } + } else + bad ? fprintf(stderr, "%s", mesg) : printf("%s", mesg); if (outfile) { fprintf(outfile, "%s", mesg); fflush(outfile); @@ -417,6 +425,46 @@ int main(int argc, char **argv) else logfile = NULL; + memset(MQTT_HOSTNAME, '\0', sizeof(MQTT_HOSTNAME)); + if (const char *env_p = std::getenv("MQTT_HOST")) + strncpy(MQTT_HOSTNAME, env_p, sizeof(MQTT_HOSTNAME)-1); + else + strncpy(MQTT_HOSTNAME, "localhost", sizeof(MQTT_HOSTNAME)-1); + + if (get_env_int("MQTT_PORT",&tmp)) + MQTT_PORT = tmp; + + memset(MQTT_TOPIC, '\0', sizeof(MQTT_TOPIC)); + if (const char *env_p = std::getenv("MQTT_TOPIC")) + strncpy(MQTT_TOPIC, env_p, sizeof(MQTT_TOPIC)-1); + else + sprintf(MQTT_TOPIC, "sparsnas/%d", SENSOR_ID); + + memset(MQTT_USERNAME, '\0', sizeof(MQTT_USERNAME)); + if (const char *env_p = std::getenv("MQTT_USERNAME")) + strncpy(MQTT_USERNAME, env_p, sizeof(MQTT_USERNAME)-1); + + memset(MQTT_PASSWORD, '\0', sizeof(MQTT_PASSWORD)); + if (const char *env_p = std::getenv("MQTT_PASSWORD")) + strncpy(MQTT_PASSWORD, env_p, sizeof(MQTT_PASSWORD)-1); + + // Initialize the Mosquitto library + mosquitto_lib_init(); + + // Create a new Mosquitto runtime instance with a random client ID, + mosq = mosquitto_new (NULL, true, NULL); + + if (mosq) { + //Set username and password (will be ignored of MQTT_USERNAME=NULL) + mosquitto_username_pw_set (mosq, MQTT_USERNAME, MQTT_PASSWORD); + int ret = mosquitto_connect (mosq, MQTT_HOSTNAME, MQTT_PORT, 30); + if (ret) { + fprintf (stderr, "Can't connect to Mosquitto server\n"); + mosq = NULL; + } + } else + fprintf (stderr, "Can't initialize Mosquitto library\n"); + //Run the main program run_for_frequencies(f, logfile, frequencies[0], frequencies[1]); From 01534c8e8c7b9bc37e34822b7509f2d9e74cbde8 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Wed, 14 Feb 2018 13:50:14 +0100 Subject: [PATCH 41/82] support for multiple senders and docker mqtt support --- Dockerfile | 36 ++++++++++----------- docker.md | 13 +++++--- sparsnas.sh | 91 +++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 94 insertions(+), 46 deletions(-) diff --git a/Dockerfile b/Dockerfile index a983ea4..f67d68b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,34 +1,34 @@ -# docker build --build-arg SENSOR_ID=1234 --build-arg PULSES_PER_KWH=10000 \ -# --build-arg MQTT_ARG="-h 192.168.x.x -u username -P password -i sparsnas". +# docker build --build-arg SENSORS="12 10000" \ +# --build-arg MQTT_HOST=192.168.x.x --build-arg MQTT_PORT \ +# --build-arg=MQTT_USERNAME=username --build-arg=MQTT_PASSWORD FROM alpine:edge as BUILD_ENV COPY ./sparsnas_decode.cpp /build/ -RUN apk add --no-cache g++ && \ - g++ -o /build/sparsnas_decode -static-libgcc -static-libstdc++ -O2 /build/sparsnas_decode.cpp +RUN apk add --no-cache g++ mosquitto-dev && \ + g++ -o /build/sparsnas_decode -O2 -Wall /build/sparsnas_decode.cpp -lmosquitto FROM alpine:edge -ARG SENSOR_ID -ENV SPARSNAS_SENSOR_ID=$SENSOR_ID -ARG PULSES_PER_KWH=1000 -ENV SPARSNAS_PULSES_PER_KWH=$PULSES_PER_KWH -ARG SPARSNAS_FREQ_MIN -ARG SPARSNAS_FREQ_MAX -ARG MQTT_ARG -ENV MQTT_ARG=$MQTT_ARG +ARG SENSORS +ARG MQTT_HOST +ARG MQTT_PORT +ARG MQTT_USERNAME +ARG MQTT_PASSWORD +ENV MQTT_HOST=${MQTT_HOST:-localhost} +ENV MQTT_PORT=${MQTT_PORT:-1883} +ENV MQTT_USERNAME=$MQTT_USERNAME +ENV MQTT_PASSWORD=$MQTT_PASSWORD COPY --from=BUILD_ENV /build/sparsnas_decode /usr/bin/ COPY sparsnas.sh / RUN apk add --no-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ --allow-untrusted \ rtl-sdr \ - mosquitto-clients && \ - \ - if [ "$SPARSNAS_FREQ_MIN" -a "$SPARSNAS_FREQ_MAX" ]; then \ - echo "export SPARSNAS_FREQ_MIN=${SPARSNAS_FREQ_MIN}" >> /etc/sparsnas.conf && \ - echo "export SPARSNAS_FREQ_MAX=${SPARSNAS_FREQ_MAX}" >> /etc/sparsnas.conf; \ - fi + mosquitto-libs++ \ + zsh + +RUN if [ "$SENSORS" ]; then sed -i "s/^SENSORS=.*/SENSORS=($SENSORS)/" /sparsnas_multiple.sh; fi ENTRYPOINT ["/sparsnas.sh"] diff --git a/docker.md b/docker.md index 5b557b4..1ee9945 100644 --- a/docker.md +++ b/docker.md @@ -1,16 +1,19 @@ # Docker support instructions -1. Build docker container as follows. The `SENSOR_ID` is required and is the unique - id of your sensor, `PULSES_PER_KWH` and `MQTT_ARG` are optional (but highly recommended). +1. Build docker container as follows. The `SENSORS` is required and is the unique + id of your sensors in the form `sensor_id pulses_per_kwh`. The various `MQTT_`args are + optional (but highly recommended to enable communication to your mqtt_broker). ``` -docker build -t sparsnas --build-arg SENSOR_ID=1234 --build-arg PULSES_PER_KWH=10000 \ - --build-arg MQTT_ARG="-h 192.168.x.x -u username -P password -i sparsnas" \ +docker build -t sparsnas --build-arg SENSORS="1234 1000" \ + --build-arg MQTT_HOST=192.168.x.x --build-arg MQTT_PORT \ + --build-arg=MQTT_USERNAME=username --build-arg=MQTT_PASSWORD \ https://github.com/fredrike/sparsnas_decoder.git#dockerVersion ``` -2. Run the container. +2. Run the container (it is possible to set the arguments above at runtime as + `-e MQTT_HOST=192.168.1.2` if you like). ``` docker run --device=/dev/bus/usb --name=sparsnas --restart=always sparsnas:latest diff --git a/sparsnas.sh b/sparsnas.sh index a292f06..fd517af 100755 --- a/sparsnas.sh +++ b/sparsnas.sh @@ -1,30 +1,75 @@ -#!/bin/sh +#!/bin/zsh -set -ae +#Associatve array +typeset -A SENSORS +SENSORS=(722270 1000 602064 10000) #(id_1 pulses_1 id_2 pulses_2) -SPARSNAS="/usr/bin/sparsnas_decode" -RTL_SDR="rtl_sdr -f 868000000 -s 1024000 -g 40 -" +RTL_SDR=(/usr/bin/rtl_sdr -f 868000000 -s 1024000 -g 40 -) +#RTL_SDR=(cat /sparsnas.raw) +SPARSNAS_DECODE="/usr/bin/sparsnas_decode" -if [ -e /etc/sparsnas.conf ]; then - . /etc/sparsnas.conf -fi +FIND_FREQ="SPARSNAS_SENSOR_ID=_SENSE_ $SPARSNAS_DECODE /tmp/sparsnas.raw --find-frequencies > /tmp/_SENSE_.freq" +DECODE="> >(SPARSNAS_SENSOR_ID=_SENSE_ PULSES_PER_KWH=_pulse_ SPARSNAS_FREQ_MIN=_min_ SPARSNAS_FREQ_MAX=_max_ /usr/bin/sparsnas_decode)" + +[ "${(k)SENSORS}" ] || exit 0 + +#Build find freq-cmd.. +function findfreq () { + CMD="" + for i in ${(k)SENSORS}; do + CMD="$CMD ${CMD:+ &} `echo $FIND_FREQ | sed "s/_SENSE_/$i/g"`" + done + + echo $CMD +} + +#Build decode-cmd. +function decode () { + CMD="" + for i in ${(k)SENSORS}; do + _c=$(echo $DECODE | \ + sed -e "s/_SENSE_/$i/g" -e "s/_pulse_/$PULSES[$i]/" \ + -e "s/_min_/$(awk -F= '/MIN/ {print $2}' < /tmp/$i.freq)/" \ + -e "s/_max_/$(awk -F= '/MAX/ {print $2}' < /tmp/$i.freq)/") + CMD="$CMD $_c" + done -for i in `seq 2`; do - if [ "$SPARSNAS_FREQ_MIN" -a "$SPARSNAS_FREQ_MAX" ]; then - $RTL_SDR | $SPARSNAS 2>&1 | \ - /usr/bin/mosquitto_pub -l -t 'home/sparsnas' ${MQTT_ARG:--h 127.0.0.1 -i sparsnas} - else - echo "We have not configured sparsnas, try to find frequencies." - $RTL_SDR > /tmp/sparsnas.raw & TASK_PID=$!; sleep 25; kill $TASK_PID &>/dev/null - $SPARSNAS /tmp/sparsnas.raw --find-frequencies > /tmp/sparsnas_freq - rm /tmp/sparsnas.raw - . /tmp/sparsnas_freq - if [ "$SPARSNAS_FREQ_MIN" -a "$SPARSNAS_FREQ_MAX" ]; then - echo "export SPARSNAS_FREQ_MIN=${SPARSNAS_FREQ_MIN}" >> /etc/sparsnas.conf && \ - echo "export SPARSNAS_FREQ_MAX=${SPARSNAS_FREQ_MAX}" >> /etc/sparsnas.conf; + COMMAND="$RTL_SDR $CMD" + echo $COMMAND +} + +function frequencies () { + for sensor in ${(k)SENSORS}; do + grep -e SPARSNAS_FREQ_MAX -e SPARSNAS_FREQ_MIN /tmp/${sensor}.freq >& /dev/null || return 1 + done + return 0 +} + + +if ( frequencies ); then + echo "Frequencies have already been detected" + CMD=`decode` + # echo $CMD + eval $CMD +else + echo "Need to find frequencies" + timeout -t 45 $RTL_SDR > /tmp/sparsnas.raw + #ls -l /tmp/sparsnas.raw + F=`findfreq` + # echo $F + eval $F + sleep 1 + rm /tmp/sparsnas.raw + if ( frequencies ); then + CMD=`decode` + # echo $CMD + eval $CMD + else + if [ ${#SENSORS[@]} -gt 1 ]; then + echo "Could not find the sensors please verify that the sensor ids are: ${(k)SENSORS}" else - echo "Could not find frequencies." - exit 1 + echo "Could not find the sensor please verify that the sensor id is: ${(k)SENSORS}" fi + exit -1 fi -done +fi From b3b52555d2f04a8e112274adc4ef7f112b6b8391 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Wed, 14 Feb 2018 14:16:21 +0100 Subject: [PATCH 42/82] Example dashboard for grafana --- grafana/readme.md | 5 + grafana/sparsnas.png | Bin 0 -> 123891 bytes "grafana/sparsn\303\244s.json" | 371 +++++++++++++++++++++++++++++++++ 3 files changed, 376 insertions(+) create mode 100644 grafana/readme.md create mode 100644 grafana/sparsnas.png create mode 100644 "grafana/sparsn\303\244s.json" diff --git a/grafana/readme.md b/grafana/readme.md new file mode 100644 index 0000000..463b266 --- /dev/null +++ b/grafana/readme.md @@ -0,0 +1,5 @@ +# Example panel for grafana + +The json-file will create a dashboard in grafana that expects data from indluxDB pushed by Home Assistant like the one below. + + diff --git a/grafana/sparsnas.png b/grafana/sparsnas.png new file mode 100644 index 0000000000000000000000000000000000000000..71f014cdf24017d139123b6dc4307b4a337fe165 GIT binary patch literal 123891 zcmeFYbyQYgzc2a#(v3*BARsL%od$?>Hz*-3-QA53EnT8WNOyKU zuLR%F?Vf2lz+jknp+5+))b}JX7!B;Xw1leb`|WuTSJm+*v76Hw?sw$N1d_E~&tA~< zN20Qff0W6u4m#1~5c!Ry>S~pZp+1BVI3s$UxN^T*W_l%>rVZ=TJ5P|D@B7^U~T-ZJQEbUUO8 ze5SYuu4YR3^HCVRy}iD8i0SF+0^U~w(bQtm6=p*#yA69U9Vc^spL>?gp4)>bf@7V9 zfp3KSQzkLlWo3Bs^73Z~F+P#i)qL=zByt3IFE5d5oB8nYa12Kmm;3zu#FKh1ltgUW zfk#J<+w%>?3S!qh+s;Dz-si6cC~&TNe6CPWc4kBB9M)G1ZhjgJ&(BBw>`Ri84GW=i zM{`)~_fy6Ya@p8Sl7QWoBFgG`E1y2Zry<=49o@}{s^4!u{ zeB_L`Gu0$PgxFRLJO@wQY&o+%y-S)x}qZI#SxNtBwJy5ELMot2Y=7$bHg@IWDhVl-(WjlW~r z&hThsB!H^Vu;Vb(eUsVqWZEoQ*zJ+&;QO$|L?Q|icf#Z2W9VKZ3k%V?Ph)bxZjKcy zJe8Bf{P^ip`RhI+us{8oV$`txIfo&my@e*2FPBMoR%Ip6${yE*2?HISEW@ABMD)HG z%ala(%gCV0-C;2}$AB%h+-$Wh9k|A3WJDWWZ)zlS8X+!to$587twek%F7C{h3gPrP zv4UPcDk|#3$B&XWHtgV4VpCGWz%%6L=XarfKRi6-a^BW_FX~0+bG1s10K*kMqp@i` zc*2ywzu$qI+uTfL+jLB{+j4so>#gthGeNV#nGJ-NyN3s=a6ud5QE)H{tX1T6E`Y=9 zmmJI&Jf5SI69SCA=}1}l_Udq$2iw}(djH(>Y{1q#pd$o#_4;(N=j!~>{$yM0>U_ut z1=gR&kISM_B5i5OGVxi5_;*FHLovv;qChAQm3$|+kn!~CabTj?M=G$^{uFL5qxL(n zR&4$`Ut$AKe3;$UnSFnT2zio$_hT}0a{C{>_&?Ux5Mj*+QMOB;YZfHc)DrNsP%GBK zR&zfZ0I5;QMJT*cF(it)wGMv&reDZKfP&buzru8&eew3j1q$}w{(fl?(53^aK{~cg z_k6`Peb!RU_D7yllcwBet*x2DZnAINOll9h$q`_y-BBIH&HKpPvo%9g-U2+pC5Y#ORJmR4C#=74w)!KLmFyUVDkrxyicX{9sl?pYhIw{McZ zJ9-EXrKGbn9}&B5=)r2wDz48N5)QQ}1yNa#@rQqVAndlIUt=S~!@l4tmbRxWK+O1a z6_dk2DS^GmZ4?p|EVEq{CA@dfXs*^ina`T+WOv>YD-+j7Lsj(-?H#ys@}t)n&DT4& z#D9&D$ons^|95!VAa;M!Jl<% za_Z~JV2T@ESt*yPk96}vz#sB`_ zQXqIK^X6CPJ}3?MsA5pJCO%7nfCjY%9cEXjTl@PP3j*x%ra8f z3dbz4mf4w@T9dgZNbD*xFH)=Fs?-_X!rjNgZc2N4EW zh?BYw*dVPy07KzD=Y6?z_Eo`~fFSawL+mQxpla;b9pBj2u^s1-ZI7<-dz#f&qy_S6 z!KtZas;a8uXnv($JC?~J9w%iNLLlc^^&3gRQkcJa)1IHCAKn!~9AfQqeLQLM{ezN# z^EL?%m2hZ({|nC@OM?z1TwxgOY|y1Sz_PT72)5rNc7qR9V31&V)tUEL)R&-YGdec5 zeC%$&^|@wc^HV7bEH6JlYNMP0AD;m;o9?rpBuH4ofP#VtkyIixXx$)=_5-M|U~Ft` zP>Phl8UJ|aG#0$?l|U!dJlUsit}n}-w$wqSwb|6HZj9t^Y;Q}Onm*XAU5%D@i2;`q z{K=pZWEQY}1gck|vA(!TCS9Cy%4^j(*cJuiM0LjC7> z(jdlTw+=Ox=<&p2wKci(?p)Zyg8rOc2lm;*X)|it0MXdcT&2Zbs0kor62V|IGc#qz zT^MstlX^zhEjP!dacODfTSos389{9NjRM6xX^U46cAHN1VXb~hsQbVBICAUiNZFGx zp?cgMLrs11zGl&zoZ9=0md|R+u=2BB1JPfJN$BH%n||&g!}dUQ z*WJ4H{ff+MERc%gT3TA2){8#3UX7Q#4O3-CNZ!Yv43>N2?z^cTixUAmes*btQ9~;XH{V3xXz^ zFJB1E2H&I4DuB#|Eu9^#!hFAdldYY%AtWRWto-@&rx94b?n9_hU+%R~pU%5bgIW;c zgvEurQM2IAa&y^oOD-ZJBH*-1D0;DRA9Nm}m1@xBnOIqQsECP(RzQc^cNIpuiQJj3 zF#$bis@DGg1Nqdzl@(*Ad_@%%q}EKfOi*G#=1a}^w|M>?FlF^TPPW_T>KteTVX*fC z_H^`o17E+kfs#e{AkCVOTvk>#5`Q)!Apw*+JHd1#Yl->!`468zcdZSim3v>iU+ucI zbo?4CG}(3ON4QAP&<_KH4j3s!G^^g+E2&+<@C0Qhvi5V&s)V|F;(@@~u{gp7Xt(8{ z+(YwF@7J#gb~nVTN=kkuCG3?oH74KS%-~*6boBHn|4Eq~!pXV0NH77B@XtHG8(9OR zM@kCm7d>@#qWXr0`@6Y&JX?mGyKn0xXD)g&<&xNC+}#D)8@3;TEYAkB>soVag&kB`3=#_>*%i^I3S)h{K6 zrqgc;EL`qK8?k%HKBOQ}nV6XoU;^ncw`Zy_!H?ht+VY;i%Q`dkHak>0(N(DP6y(i<%ircn@g~h&mlaV#}u9T?)0x*T!EVwg-l$6w{ z%~eA~LvxtwhjW9Hws!d7;7cBUev{4bMIbn3oSb+Cg@lxJbjaf+hQOHM?p}W&AR^*5 z9JdYyhsJw1x0q_+Jmqwi>z>6Uvuf_qGunHy%T#JSu@p5+iOA@R0VB`c@sidfgXHN~r zzmUl-87(bR5LYyT;!mES{Qmu05j4hflOAKR^X1h2G{$5r8EI(|@845EODvj+3kJEf z!_3=APz?xS!{5ILY8ZHpf3V_r+0}(7BxGl2BYgh+*%h=1$zW3HM<(4^!dLqppfqrU zNyMr$2@Y>vJ;-WooI~s8=H}+|zB-HL`zQG2pT`b&6XV*4-0=x4Vk7{k2s9WmZV_bG&3@j%hw z0I?l1H>b<0S5N3G*3s;B>74uJ0;=8ThwH0xPpR5cc`X%nb*+kZMDYs8!ko4ymcKG) z@^qh}`bdC)8!M3Ku6C-^HMg`h`3EGzSHAW2*$?ne24d?C_4Rdt57__4Qo6L@vDXE5 zs0=hYgwvnS4I>j1Ln6#}ehfVD=iszokV!(iD#4j_OnM!nSv%yY(WK0+g zq?l&8G4gKxR!Ld4!R^H)s5nsvAHXeC4jpb$zpPR`5IJ~}(*Vs|(gC2O`s`FqMwT|| zb5MEiOF;b@zsBqK`t$&B3dSA5*q{@3gE?`3EzQ<=Bu9qp`UODt3ZmzouDYPoUtM{s zf}F3Ee=iW%_84EuKp7D)Y!~^@J4p0P%~AfRei~hC?dJ9v^qUZiu>x8^I=IZ)qn>O} zbLV9qf*de9-W-RzA{gJJDIOy-Gr+Pwe#C(4;F~vZBxk^AT^u4M4WA1%^n`|sSNOy{ zs9r-EBFRE7?17=4#~KC(G41V=tM*1lw1e*-BgOP^FL~G4E=E%dIU52%l+0s65FQbc z4VRRU@}YVyBZCZ~v#YDC*lKfrjL=Z!B!9!eKc9@=fis+zmUeK^L%j+(itzfV|EI18 zo3i4A!{HFP*VEH8x3%qoGpj7Ut&6O#uNQ<^pKB&$*u#-|8c?fpXhYp_+!etxK*Qbh zcMoLdAKbr>1pZ(5>e3`=9r!h;?oCs54vgw0x?v-^PeVM1e!k5HL&tyHut>`|Kv9qq z<>TJC^pN8fKKb5hLQPsy$6j z1;>*uMIF{&2p{%y(^`*c9t2exXRZeQ=_3d0<K@<=CVZt<5DdE`NnOvDL(1I_ zO({e@r_4E+nE_&yaC57}k2EtiH3le{)>>NHF+&xDVg=ecp0@!1Ktp)#wxuS5J6PtD zx;3&_rl!G_psgBTo`6a9c&oI9>TrFi=YEW^KdZmRr2is~Z(wQy?7`>HQGicSx^qLf%S!MR+^h&@z$5b#y|q;B+UH2(~+6VYpHYoV8}mpe5FV4P;nv)-Ah!k-18n0)dE*D~}n{2mKk zH8C;qD(Eb@7CN)a&%CWK!>dDb*6Cy9mF=++z z{Xmw+mux{W*~3Gq84P@Yf`m}#>6`;Dn3n8TI?)MPU@P>(U3fZ^7i>l=0lW? zJumI>mdvwEX_xyJbB`?XOPCwE{v>ladFdo(=_L7RVq#)2AQWS(0b}=!`2Y}ohM@b$ zDzAY0(F2GJI{*SYRu5?S`T2i524HGqcQ^XX0c`9;78ZLD8Mz?6K|nrraNve=EEj@u zro9e{?0Vq<=jb|5zh-*)P!d%3egwgg+PnA+TnRbT7NDBQ;(*x`RA`*@NihLj8&p`$ zyfgXRw{K&?)TLunhm49#bqDt9)vG|{RZwA!f`$U-Z1_KZ{5TAl2|z)CWWL93+Asb; z45T0=7}r)`gW(0<3CDOL1u@yO#wOj7ZMF8RIo`MNa_kb;)}Ie*^Yf8+c6OMUm_lp4 z?2yYrB7<@WCAf}txL@J;~6bb+F{;!Ody$oOPmM4pUIFsd+}djTjZV`0GzCd-kT znQ)f{H&hKhr+Y9%P{ARh;bdhtvViOzF2yr8&O7`!9xK{U$P;5G24)xBY33Zf3> zljq~%E`8#Yy&!nzqeagiU93zW?XxF*qS|4yUS620Dtdz1WgjCgEj8?V>$3b=G=9Sd zD_|u@2hX#`HNXnsAR7T{}$!WmnMnFfyNra}` zFNpe8U?d1q_MMO0(C>Wl9Kly+1p=(akj^k-h^RIkRJLMegWPrY-gxPHTfoyyfw7(( z+>1F3x_~CHXO;Q^`?0hOgQ+Al+CVrz5tCBt?X|(k$*3*CJT^#dc;{mf?g5G-RA}-Q zKipR^!tc*9^QEF}c#Z+dg8`BI?{7sEQ(=x8J>JPS&feYvy zWPzIp;$v15{aFrSyy0~04UjTJSZYi zmZ?{424IvhO}c1fI#$7SK%p@G5l_ehk6g2+wW~H{0z~@&TL&N#p0BX%Gbw)PB|~^j3a1Y_Wk_`tm`X=Z)dn# zq<=?y}g1lPP2XdNeOJ~wyeu!7|BV3G5{_5gR(OO$&1%%!~rw)wa)7|+9NXe5+Wc>qc0cGPfYdu@N z+7PRdfBBpi%brrz|uq7ZssTPf!0Btigx4BNIW?b;J$2qv-jM ztHvQ9Q39G4w8%$!)f-TU(QN zN;#BSeA0=MmzNiU0H-atUd6g~1RRa~h>iNTojY4wTS}Um=ECf(tepVt1KADb>hWxi zE#N7H%IsI4Ky)Uc8Wm-;3kpzt#dJh1Cs@IYhg8@m7^D!S@mc%B2_S_Ll1m!)nuV-9K=UjvW?i-4xBMezMuv)+ zfH7CVb)SaH>qrT(?PM^+ifKhF$V#Xg$Q*Z zFO#*o#ezxi^=k&elqxAJBfgc;=%E$m$8A1<}dAAHVkc(*OHDiJmxs4k48shc_sJKf}@A_Pw(Dx(_OxlS^d&u zasf{X+wIOetM5rt(|5^OU$x9SYJ-6iBm%Kp2x+X#UIOb+69M zXhX~tHuP>~fQN;|*Rze>7|G`Kw{?nYYC&LIOiIoHDQpjvVnBH1#L*RNy2&u(s1-~- zLq=;W!iVyV0lt5mSy^`*-(59xLhYW+XoKL0AZ&>sB{tMq` zHzv=IU3*K)ZMpNbM(_89?*}!eB@xK7JITMU~cuGGb~s zoju`wYEZ49o-&&-Y2Av>S+tQh;b>)hvU-hQX8v zh;8zO@zRzXcSyO85&n%fQLGgJ_-&I}KX6^KP7MyA81l;DsekD@ARKX7&+wS&00tk> zz@S(0{AN`j*JehSH}4NrwnF50K5M20Pr$$s_#j!LrZ$-|Xllo~ShF0FMZFm9Qs3U5 zOW)`EIJP$GM1QF{W)uux*e9q}M8g$_VPCQ86T04mN-V3E*vBYKnk@HzBRV$!8G1f? zUla)~ovfR32%f(RoHZKiIf`1=y`}MrsomK&gyi><0|udTmCwYCfMZ`H47FaVJXUr3 zm(P*q(TKuwrZY){O2=bwO9?~1Pv>+I1UxU@(BY?*TK+B_i2QI zCk>@6{8DD}Z?*4wZNNnwb{{;KdSBKhk^{CP)MT|VCZVoD$KWKMck;cOF~b!RsF(ay zg@uKy;~ECMRMY}?Fpie1N2zIP%K#cK1>i#H+UU{^LarbtATH?1>`av|)|aAjf=5@q{Q$PXD2u`jX`fxUN7a zB|!EaP@Y{K6^eBMVg8`c;D#X6>x2{rNOKy%m%q9MeZbM>X~yoVb3=!}J&@E!*PI)q zGAbN@Dd&MW_|nG?$yRA4?bh_vO$d@(`K3sXWz~5l8Hc(4m5C|YvHI&ZewJ&wZX-eCMYV1Zb2cFj zPn66`dNb^&ejwdOS$8ZLsF02~|L z-dqFCZ>rvj1rTjONuk>v0@fh#5CGz0e}w}r;2));*PathyM!`lJ!xI+rpK-6M}{&s zM1eWkir>UKMPxYkmHpyJeFTBz0N2es@|h^yKW_j492!GBMX8}R9si_Z0@)gT8ykfI zYt^lZI{)7R;pnpS^@DaWn=+A2-)2`K>fXOghE;7uZ#lnC5zMNv1Yc#5@lQ$?YBF&)r2ZfbX(M1@EOH1}CV)^5q0H?Ne_t0%8!X*Y zTZL8CQ1A}NetZ%f?}oxX$3s%uJ})nD*os-GQ$!2$)`iot{<12+Q?LB31x`@f!Hn`y zQ*rpMOP+ae-jRaGaxYoVj~uzy?h!8a9%xlC#`jjn-yYWHA}pe(;X$L-{G4K(C9p~Nm054y&5wx`*>Z?eyoM*jz>5Gs>V0UY zEzw2)A#udWnUQ>X+(XWgxHg?pj0{|GF9u>}S}W&ULjEtTP_b0%MMDVBZ`U;m6#^hP z_$4#b%ZO(-(AFuJHQx!T)ggyq4-=hU!$l);3v~w($;#mJH$u#OLMuKYuw=>QID%jc zzRGO+4h+)S6a9Jr@Ef%`1o2a?b^i%)d0hb^rs?pz%^5sOjF%CeGZY@CyD;+3L;>w(W37Y=UQ z3aS|4{7&&YWzQ%otian&rbsCKadqZ|s?xu7VqIl5;GLHTf5gNgKulFgTipV9m9>BlQ`}0~=4_%`rHJ>g~FG`j7mr zn;F9XFZc}iGVhRicrvXmR_Eux@V(jky+M4xXeCiA!cKlU3iR8!*U~R&w9PRt0&E+j zO2nBjp88x0-fX2>63pGNPlh9^wz!cs4=J>}8w7B^H$cT$ppXnsx=Vw}?; z@xX4=N+qX3-2P8>!XW*%GHw=yIrGgV+hwTdmFa^Ij9<5ebRKg3JREG}e?fwLD}Cgb zCfP#Wo5AhM&B%b_c^=FP@}RW*r`so-+(X&*gpA40|P18^@(pud3XbQ*H&|wF^Y)A)R!j`$J*xC z3S^H3ho-;!lm5x5F2-+&_BN(9xZ|1pA(g*GF&*dL!2Kz#L|qWkX;vtX{8k;%X?f7( zrFBhX3FNSUUtp)e8`0mMfu@?HDcQT5eqHd6!HHjzEjE-eDVhuC@9xZz%|Mz25 zm!~kax}vGjZCmB!~k#*Sqmtz&s$O~7Iwv(pyByPdxxeT@w$zk` zD3zHa!C&fn#Vkkq;rq~`|2RmL(NUUN?Yr~$WH~<-KavXI{?A0kx=e4@sX=ppp>jh0d zt5}gfB$$2;b{)o;(Yn$V5#Dkkvve}y;_ia04=BcE(Tf=93*yes)%fNd z+LcTNnd{CC2f#5v62tQFVQgk*3>YqAtARU{4(J30UZ7#*zB&_m!QEp5O!BUcagT0I z_iC%A^8EdMP1pFP?L^&L__z#FL;xFrP)}MULHvc9?pNtV=622QgN)721ZqB`Nq%;D zM~x?u@dVo1D2_Px3U2zg8%NdbF~p!(qi?7@H0W{t7f zKL4A8iZ7{;_k9N~T{x5S3(J$_{8il_I3MX-@LHy}-RC6p6c^8!5A#2Cgr8fg~bi--z`+0+l0bYWRQHyC^95yGs(k z(L}f)w{7%8rE-Ol^V@XxQ2S6gHC$Y{Lt;-1CMKr+T0g0)a0bF@3-D=dPn9D zB9Q8T(L+v}++6+PLM-8ql-UR9jp52X+y&O>(j@n|DBQz~lZ%xNGn$lrcLS_!<)Ex^ zcb5vObn!rquIX(wrdkoTT@a3>6e0pVE)d{w_n~ndvMos>9?<+3PUdnEOn#83@+q|! zA#moR{F|n%Qt$#AOAB74ASU&_t^^_@mpHYwf fpdi6~l*G9!u|UaLJ+46>-9-J@ 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

2lEIVkgy)=9P-V8<+oSI;^90~2bV_F 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 L-O7_T^8fz;;CGn@ literal 0 HcmV?d00001 diff --git "a/grafana/sparsn\303\244s.json" "b/grafana/sparsn\303\244s.json" new file mode 100644 index 0000000..4972c8f --- /dev/null +++ "b/grafana/sparsn\303\244s.json" @@ -0,0 +1,371 @@ +{ + "__inputs": [ + { + "name": "DS_HOMEASSISTANT", + "label": "HomeAssistant", + "description": "", + "type": "datasource", + "pluginId": "influxdb", + "pluginName": "InfluxDB" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "4.1.1" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "influxdb", + "name": "InfluxDB", + "version": "1.0.0" + } + ], + "annotations": { + "list": [] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "hideControls": false, + "id": null, + "links": [], + "rows": [ + { + "collapse": false, + "height": 250, + "panels": [ + { + "aliasColors": { + "Power usage": "#E24D42", + "Power usage (avg)": "#2F575E" + }, + "bars": false, + "datasource": "${DS_HOMEASSISTANT}", + "fill": 0, + "height": "500px", + "hideTimeOverride": false, + "id": 1, + "interval": "", + "legend": { + "alignAsTable": false, + "avg": true, + "current": true, + "max": false, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "/kWh/", + "bars": true, + "lines": false, + "yaxis": 2 + }, + { + "alias": "Power usage (avg)", + "linewidth": 3 + } + ], + "span": 12, + "stack": false, + "steppedLine": false, + "targets": [ + { + "alias": "Daily kWh", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "1d" + ], + "type": "time" + }, + { + "params": [ + "0" + ], + "type": "fill" + } + ], + "measurement": "kWh", + "policy": "default", + "query": "SELECT non_negative_derivative(top(\"value\", 10), 1d) FROM \"kWh\" WHERE \"entity_id\" = 'sparsnas_energy_consumption_over_time' AND $timeFilter GROUP BY time(1d) fill(0)", + "rawQuery": true, + "refId": "A", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [ + "10" + ], + "type": "top" + }, + { + "params": [ + "1d" + ], + "type": "non_negative_derivative" + } + ] + ], + "tags": [ + { + "key": "entity_id", + "operator": "=", + "value": "sparsnas_energy_consumption_over_time" + } + ] + }, + { + "alias": "Power usage", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "$interval" + ], + "type": "time" + }, + { + "params": [ + "linear" + ], + "type": "fill" + } + ], + "hide": false, + "measurement": "W", + "policy": "default", + "refId": "B", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + }, + { + "params": [ + "alias" + ], + "type": "alias" + } + ] + ], + "tags": [ + { + "key": "entity_id", + "operator": "=", + "value": "sparsnas_energy_consumption_momentary" + } + ] + }, + { + "alias": "Power usage (avg)", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "30m" + ], + "type": "time" + }, + { + "params": [ + "linear" + ], + "type": "fill" + } + ], + "hide": false, + "measurement": "W", + "policy": "default", + "refId": "C", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [ + { + "key": "entity_id", + "operator": "=", + "value": "sparsnas_energy_consumption_momentary" + } + ] + }, + { + "alias": "Power usage (avg)", + "dsType": "influxdb", + "groupBy": [ + { + "params": [ + "30m" + ], + "type": "time" + }, + { + "params": [ + "linear" + ], + "type": "fill" + } + ], + "hide": false, + "measurement": "W", + "policy": "default", + "query": "SELECT mean(\"value\") FROM \"W\" WHERE \"entity_id\" = 'sparsnas_energy_consumption_momentary' AND $timeFilter GROUP BY time(30m) fill(linear)", + "rawQuery": true, + "refId": "D", + "resultFormat": "time_series", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + } + ] + ], + "tags": [ + { + "key": "entity_id", + "operator": "=", + "value": "sparsnas_energy_consumption_momentary" + } + ] + } + ], + "thresholds": [], + "timeFrom": "1w", + "timeShift": null, + "title": "Power usage", + "tooltip": { + "shared": true, + "sort": 1, + "value_type": "individual" + }, + "transparent": true, + "type": "graph", + "xaxis": { + "mode": "time", + "name": null, + "show": true, + "values": [ + "current" + ] + }, + "yaxes": [ + { + "format": "watt", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "watth", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "repeat": null, + "repeatIteration": null, + "repeatRowId": null, + "showTitle": false, + "title": "Dashboard Row", + "titleSize": "h6" + } + ], + "schemaVersion": 14, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "Sparsnäs", + "version": 2 +} \ No newline at end of file From 407b539bf573b7a59a38b7c32977a122f67161c2 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Wed, 14 Feb 2018 14:27:19 +0100 Subject: [PATCH 43/82] package example for Home Assistant --- home-assistant/panel-focus.png | Bin 0 -> 103473 bytes home-assistant/panel.png | Bin 0 -> 46339 bytes home-assistant/readme.md | 8 +++ home-assistant/sparsnas_mqtt.yaml | 116 ++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 home-assistant/panel-focus.png create mode 100644 home-assistant/panel.png create mode 100644 home-assistant/readme.md create mode 100644 home-assistant/sparsnas_mqtt.yaml diff --git a/home-assistant/panel-focus.png b/home-assistant/panel-focus.png new file mode 100644 index 0000000000000000000000000000000000000000..570e39151186d02e3cafb61914792d316ca46838 GIT binary patch literal 103473 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

VjFABg+10{P3mHrEw-&&d`d=Ie3QjNx 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 literal 0 HcmV?d00001 diff --git a/home-assistant/panel.png b/home-assistant/panel.png new file mode 100644 index 0000000000000000000000000000000000000000..5a6a34c3eff2e6bd9960f02f6e06a876e67e10d2 GIT binary patch literal 46339 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

G=H*z9s0DOTo2D4a2by^mOA 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 E1J#}pY5)KL literal 0 HcmV?d00001 diff --git a/home-assistant/readme.md b/home-assistant/readme.md new file mode 100644 index 0000000..555fd45 --- /dev/null +++ b/home-assistant/readme.md @@ -0,0 +1,8 @@ +# Example package for [Home Assistant](https://home-assistant.io/) + +The file `sparsnas_mqtt.yaml` is a package for Home Assistant's [package configuration feature](https://home-assistant.io/docs/configuration/packages/). Requires [Custom UI](https://github.com/andrey-git/home-assistant-custom-ui) for optimal layout. + +Once loaded the package will create the following panel in Home Assistant. + + + diff --git a/home-assistant/sparsnas_mqtt.yaml b/home-assistant/sparsnas_mqtt.yaml new file mode 100644 index 0000000..cec9830 --- /dev/null +++ b/home-assistant/sparsnas_mqtt.yaml @@ -0,0 +1,116 @@ +sensor: + - platform: mqtt + state_topic: "home/sparsnas" + name: "Sparsnäs energy consumption momentary" + unit_of_measurement: "W" + value_template: '{{ value_json.Watt | round(1) }}' + icon: mdi:flash-circle +# Note that the sensor below will be resetted if you remove/reinstall the batteries in the Sparsnäs transmitter + - platform: mqtt + state_topic: "home/sparsnas" + name: "Sparsnäs energy consumption over time" + unit_of_measurement: "kWh" + value_template: '{{ value_json.kWh | round(1) }}' + hidden: true + icon: mdi:flash-circle + - platform: mqtt + state_topic: "home/sparsnas" + name: "Sparsnäs Battery remaining" + unit_of_measurement: "%" + value_template: '{{ value_json.battery | round(1) }}' + hidden: true + icon: mdi:battery-outline + - platform: mqtt + state_topic: "home/sparsnas" + name: "Sparsnäs Frequency Error" + unit_of_measurement: "%" + value_template: '{{ value_json.FreqErr }}' + hidden: true + icon: mdi:signal + - platform: mqtt + name: "Sparsnäs template kwh sensor day" + state_topic: "template/kwh/day" + icon: mdi:flash-circle + hidden: true + - platform: mqtt + name: "Sparsnäs template kwh sensor month" + state_topic: "template/kwh/month" + icon: mdi:flash-circle + hidden: true + - platform: template + sensors: + kwh_current_month: + friendly_name: "Sparsnäs current month" + unit_of_measurement: "kWh" + value_template: >- + {{ (float(states.sensor.sparsnas_energy_consumption_over_time.state) - float(states.sensor.sparsnas_template_kwh_sensor_month.state)) | round(1) }} + kwh_today: + friendly_name: "Sparsnäs current day" + unit_of_measurement: "kWh" + value_template: >- + {{ (float(states.sensor.sparsnas_energy_consumption_over_time.state) - float(states.sensor.sparsnas_template_kwh_sensor_day.state)) | round(1) }} + +# Thanks to @bhaap and @naestrom for the monthly and daily automation below +automation: +- action: + - data: + payload_template: '{{ states(''sensor.sparsnas_energy_consumption_over_time'') + }}' + retain: 'true' + topic: template/kwh/month + service: mqtt.publish + alias: Sparsnäs monthly consumption + condition: + - condition: template + value_template: '{{ now().month() | string == "1" }}' + - condition: template + value_template: '{{ now().month() | string == "2" }}' + - condition: template + value_template: '{{ now().month() | string == "3" }}' + - condition: template + value_template: '{{ now().month() | string == "4" }}' + - condition: template + value_template: '{{ now().month() | string == "5" }}' + - condition: template + value_template: '{{ now().month() | string == "6" }}' + - condition: template + value_template: '{{ now().month() | string == "7" }}' + - condition: template + value_template: '{{ now().month() | string == "8" }}' + - condition: template + value_template: '{{ now().month() | string == "9" }}' + - condition: template + value_template: '{{ now().month() | string == "10" }}' + - condition: template + value_template: '{{ now().month() | string == "11" }}' + - condition: template + value_template: '{{ now().month() | string == "12" }}' + id: '1516722867362' + trigger: + - at: 00:00:01 + platform: time +- action: + - data: + payload_template: "{{ states('sensor.sparsnas_energy_consumption_over_time')}}" + retain: 'true' + topic: template/kwh/day + service: mqtt.publish + alias: Sparsnäs daily consumption + condition: [] + id: '1516806539856' + trigger: + - at: 00:00:02 + platform: time + +group: + sparsnas: + name: Energy Consumption + view: no + icon: mdi:flash-circle + entities: + - sensor.sparsnas_energy_consumption_momentary + - sensor.sparsnas_energy_consumption_over_time + - sensor.sparsnas_battery_remaining + - sensor.sparsnas_frequency_error + - sensor.kwh_today + - sensor.kwh_current_month From d9af93abc0a4f1e969347ff8b73804afc8c8a39f Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Wed, 14 Feb 2018 17:01:17 +0100 Subject: [PATCH 44/82] fix in Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f67d68b..9cc74b5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,6 +29,6 @@ RUN apk add --no-cache --repository http://dl-3.alpinelinux.org/alpine/edge/test mosquitto-libs++ \ zsh -RUN if [ "$SENSORS" ]; then sed -i "s/^SENSORS=.*/SENSORS=($SENSORS)/" /sparsnas_multiple.sh; fi +RUN if [ "$SENSORS" ]; then sed -i "s/^SENSORS=.*/SENSORS=($SENSORS)/" /sparsnas.sh; fi ENTRYPOINT ["/sparsnas.sh"] From 803e56e2d109f083c9de5bbdd796e86d83be322d Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Thu, 15 Feb 2018 09:01:50 +0100 Subject: [PATCH 45/82] use busybox version of timeout --- sparsnas.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sparsnas.sh b/sparsnas.sh index fd517af..4bf039b 100755 --- a/sparsnas.sh +++ b/sparsnas.sh @@ -53,7 +53,7 @@ if ( frequencies ); then eval $CMD else echo "Need to find frequencies" - timeout -t 45 $RTL_SDR > /tmp/sparsnas.raw + busybox timeout -t 45 $RTL_SDR > /tmp/sparsnas.raw #ls -l /tmp/sparsnas.raw F=`findfreq` # echo $F From f2ae44be8cba664f7d9206270a909629f60802dd Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Thu, 15 Feb 2018 18:15:42 +0100 Subject: [PATCH 46/82] added pipefail to sparsnas.sh --- sparsnas.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sparsnas.sh b/sparsnas.sh index 4bf039b..50d44cd 100755 --- a/sparsnas.sh +++ b/sparsnas.sh @@ -1,5 +1,7 @@ #!/bin/zsh +set -o pipefail + #Associatve array typeset -A SENSORS SENSORS=(722270 1000 602064 10000) #(id_1 pulses_1 id_2 pulses_2) From 04faf5af2aadede45fa6cc99a2a434dde937904b Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Fri, 16 Feb 2018 21:58:34 +0100 Subject: [PATCH 47/82] stupid error in pulses env --- sparsnas.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sparsnas.sh b/sparsnas.sh index 50d44cd..6ea9c5d 100755 --- a/sparsnas.sh +++ b/sparsnas.sh @@ -11,7 +11,7 @@ RTL_SDR=(/usr/bin/rtl_sdr -f 868000000 -s 1024000 -g 40 -) SPARSNAS_DECODE="/usr/bin/sparsnas_decode" FIND_FREQ="SPARSNAS_SENSOR_ID=_SENSE_ $SPARSNAS_DECODE /tmp/sparsnas.raw --find-frequencies > /tmp/_SENSE_.freq" -DECODE="> >(SPARSNAS_SENSOR_ID=_SENSE_ PULSES_PER_KWH=_pulse_ SPARSNAS_FREQ_MIN=_min_ SPARSNAS_FREQ_MAX=_max_ /usr/bin/sparsnas_decode)" +DECODE="> >(SPARSNAS_SENSOR_ID=_SENSE_ SPARSNAS_PULSES_PER_KWH=_pulse_ SPARSNAS_FREQ_MIN=_min_ SPARSNAS_FREQ_MAX=_max_ /usr/bin/sparsnas_decode)" [ "${(k)SENSORS}" ] || exit 0 @@ -30,7 +30,7 @@ function decode () { CMD="" for i in ${(k)SENSORS}; do _c=$(echo $DECODE | \ - sed -e "s/_SENSE_/$i/g" -e "s/_pulse_/$PULSES[$i]/" \ + sed -e "s/_SENSE_/$i/g" -e "s/_pulse_/$SENSORS[$i]/" \ -e "s/_min_/$(awk -F= '/MIN/ {print $2}' < /tmp/$i.freq)/" \ -e "s/_max_/$(awk -F= '/MAX/ {print $2}' < /tmp/$i.freq)/") CMD="$CMD $_c" From 877828f2901a6785f615e6771646f9ba2da054b0 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Sun, 25 Feb 2018 08:39:54 +0100 Subject: [PATCH 48/82] Updated documentation --- README.md | 269 +++++------------------------ oldDoc/README.md | 330 ++++++++++++++++++++++++++++++++++++ help.png => oldDoc/help.png | Bin 3 files changed, 373 insertions(+), 226 deletions(-) create mode 100644 oldDoc/README.md rename help.png => oldDoc/help.png (100%) diff --git a/README.md b/README.md index 3f8aa74..68ad9fd 100644 --- a/README.md +++ b/README.md @@ -24,24 +24,37 @@ You can also pipe your MQTT data via node-red to InfluxDB for diplaying in for e The installation: ------------- -Make sure you have updated your Raspbian installation, then -``` +It is highly recommended to use the [docker](https://docker.com) version described [here](docker.md). + +The following instructions are for you that for some reason don't want to use docker, and assumes a debian or ubuntu based environment. + +```sh +sudo apt-get update && apt-get install -y git g++ libmosquitto-dev libmosquitto1 rtl-sdr zsh busybox cd ~ -sudo apt-get install libusb-1.0 rtl-sdr g++ git mosquitto-clients -git clone https://github.com/tubalainen/sparsnas_decoder +git clone https://github.com/fredrike/sparsnas_decoder +cd sparsnas_decoder +git checkout dockerVersion + +# Build sparsnas_decode and copy the binary to /usr/bin +g++ -o sparsnas_decode -std=gnu++11 -O2 sparsnas_decode.cpp -lmosquitto +sudo cp sparsnas_decode /usr/bin/ ``` -The decoder is now cloned into /home/pi/sparsnas_decoder +The decoder is now cloned, compiled and copied to `/usr/bin/sparsnas_decoder` + Using your favorite text editor, create a new file named /etc/modprobe.d/no-rtl.conf and put the following text in the file. You need to run that text editor as sudo (i.e. 'sudo vi' or 'sudo nano' etc) to write to the modprobe.d directory: -``` + +```sh sudo nano /etc/modprobe.d/no-rtl.conf ``` + ``` blacklist dvb_usb_rtl28xxu blacklist rtl2832 blacklist rtl2830 ``` -Reboot your pi with the USB dongle installed in one of the USB ports. + +Reboot with the USB dongle installed in one of the USB ports. Test your dongle ``` @@ -66,60 +79,35 @@ Reading samples in async mode... lost at least 24 bytes ``` -How to build the decoder: +How to configure the decoder: ------------- Take a photo/make a note of your transmitter ID number underneeth the battery lid of the transmitter (not the display) -First edit sparsnas_decode.cpp to setup your sender id. -Then: -```g++ -o sparsnas_decode -O3 sparsnas_decode.cpp``` - - -How to calibrate to your sender frequency: ------------------------------------------- -```rtl_sdr -f 868000000 -s 1024000 -g 40 - > sparsnas.raw``` - -Wait about 20 seconds. Press Ctrl-C. +Edit sparsnas.sh to setup your sender id. You are allowed to have unlimitied number of senders and the format is `transmitter_id pulses/kWh` followed by a space. -```./sparsnas_decode sparsnas.raw --find-frequencies``` +This command will do it for you :smile: (adding the transmitters 722270 and 602064 with 1000/10000 pulses/kWh respectively). -Wait about a minute for it to finish. It prints something like: - -```#define FREQUENCIES {65050.000000, 105050.000000}``` - -Edit your sparsnas_decode.cpp with those values and rebuild it according to the How to build step above. +```sh +SENSORS="722270 1000 602064 10000"; sed -i "s/^SENSORS=.*/SENSORS=(${SENSORS})/" sparsnas.sh +``` -Perform a testrun: ------------ -```rtl_sdr -f 868000000 -s 1024000 -g 40 - | ./sparsnas_decode``` +Test the script: +```sh +./sparsnas.sh +``` -Create a bash script to send IKEA Sparsnäs output to MQTT broker: ------------ -The script will reside in /home/pi/ +To make the decoder report values over MQTT the MQTT variables needs to be configured as follows: +```sh +MQTT_HOST=192.168.x.x MQTT_PORT=1883 MQTT_USERNAME=username MQTT_PASSWORD=password ./sparsnas.sh ``` -pi@rpitest:~ $ nano sparsnas.sh -``` -``` -#!/bin/bash -#To make sure that the mqtt broker is started, make the script wait 5 minutes. -sleep 5m -#Start sending information to mosquitto_pub -rtl_sdr -f 868000000 -s 1024000 -g 40 - | sparsnas_decode 2>&1 | /usr/bin/mosquitto_pub -h 192.168.x.x -u username -P password -i sparsnas -l -t "home/sparsnas" & -``` - -Make the bash script executable -```chmod +x sparsnas.sh``` -Copy the binary file to /usr/sbin -```sudo cp /home/pi/sparsnas_decoder/sparsnas_decode /usr/sbin``` -Test the script: -```./sparsnas.sh``` +To make `sparsnas_decoder` run on startup (modify the `MQTT_*` variables according to your configuration and make sure you get the right path (`/home/pi/`) to `sparsnas.sh`) -Make it run on startup ```sudo nano /etc/rc.local``` -``` + +```sh #!/bin/sh -e # # rc.local @@ -139,192 +127,21 @@ if [ "$_IP" ]; then printf "My IP address is %s\n" "$_IP" fi -/home/pi/sparsnas.sh +MQTT_HOST=192.168.x.x MQTT_PORT=1883 MQTT_USERNAME=username MQTT_PASSWORD=password /home/pi/sparsnas_decoder/sparsnas.sh exit 0 ``` Check status and traffic on your MQTT broker: -``` +```sh mosquitto_sub -v -h 192.168.x.x -u username -P password -t '#' ``` Example of outcome: -``` -core-ssh:~# mosquitto_sub -v -h 192.168.x.x -u username -P password -t '#' -home/sparsnas {"Sequence":35851,"Watt": 7372.8,"kWh":18889.219,"battery":100,"FreqErr":0.57} -``` -Create a sensor and monthly automation for Home Assistant (example code, configuration.yaml): ----------------------------------- -``` -mqtt: - broker: 127.0.0.1 - port: 1883 - client_id: home-assistant-1 - username: username - password: password - discovery: true - discovery_prefix: homeassistant - -sensor: - - platform: mqtt - state_topic: "home/sparsnas" - name: "Sparsnäs energy consumption momentary" - unit_of_measurement: "W" - value_template: '{{ value_json.Watt | round(1) }}' -# Note that the sensor below will be resetted if you remove/reinstall the batteries in the Sparsnäs transmitter - - platform: mqtt - state_topic: "home/sparsnas" - name: "Sparsnäs energy consumption over time" - unit_of_measurement: "kWh" - value_template: '{{ value_json.kWh | round(1) }}' - - platform: mqtt - state_topic: "home/sparsnas" - name: "Sparsnäs Battery remaining" - unit_of_measurement: "%" - value_template: '{{ value_json.battery | round(1) }}' - - platform: mqtt - state_topic: "home/sparsnas" - name: "Sparsnäs Frequency Error" - unit_of_measurement: "%" - value_template: '{{ value_json.FreqErr }}' - - platform: mqtt - name: "Sparsnäs template kwh sensor day" - state_topic: "template/kwh/day" - - platform: mqtt - name: "Sparsnäs template kwh sensor month" - state_topic: "template/kwh/month" - - platform: template - sensors: - kwh_current_month: - friendly_name: "Sparsnäs current month" - unit_of_measurement: "kWh" - value_template: >- - {{ (float(states.sensor.sparsnas_energy_consumption_over_time.state) - float(states.sensor.sparsnas_template_kwh_sensor_month.state)) | round(1) }} - kwh_today: - friendly_name: "Sparsnäs current day" - unit_of_measurement: "kWh" - value_template: >- - {{ (float(states.sensor.sparsnas_energy_consumption_over_time.state) - float(states.sensor.sparsnas_template_kwh_sensor_day.state)) | round(1) }} - -# Thanks to @bhaap and @naestrom for the monthly and daily automation below -automation: -- action: - - data: - payload_template: '{{ states(''sensor.sparsnas_energy_consumption_over_time'') - }}' - retain: 'true' - topic: template/kwh/month - service: mqtt.publish - alias: Sparsnäs monthly consumption - condition: - - condition: template - value_template: '{{ now().month() | string == "1" }}' - - condition: template - value_template: '{{ now().month() | string == "2" }}' - - condition: template - value_template: '{{ now().month() | string == "3" }}' - - condition: template - value_template: '{{ now().month() | string == "4" }}' - - condition: template - value_template: '{{ now().month() | string == "5" }}' - - condition: template - value_template: '{{ now().month() | string == "6" }}' - - condition: template - value_template: '{{ now().month() | string == "7" }}' - - condition: template - value_template: '{{ now().month() | string == "8" }}' - - condition: template - value_template: '{{ now().month() | string == "9" }}' - - condition: template - value_template: '{{ now().month() | string == "10" }}' - - condition: template - value_template: '{{ now().month() | string == "11" }}' - - condition: template - value_template: '{{ now().month() | string == "12" }}' - id: '1516722867362' - trigger: - - at: 00:00:01 - platform: time -- action: - - data: - payload_template: "{{ states('sensor.sparsnas_energy_consumption_over_time')}}" - retain: 'true' - topic: template/kwh/day - service: mqtt.publish - alias: Sparsnäs daily consumption - condition: [] - id: '1516806539856' - trigger: - - at: 00:00:02 - platform: time -``` - -Use two or more Sparsnäs at the same location (Experts only): ------------------- -It is possible to use twoor more Sparsnäs at the same time. -You will need to compile one sparsnas_decode per Sparsnäs Energy meter. The same sdr_rtl instance can be used. - -Edit your sparsnas.sh to look something like the following after you have compiled and copied all your instances to /usr/sbin/. -``` -#!/bin/bash - -rtl_sdr -f 868000000 -s 1024000 -g 40 - | pee 'sparsnas_decode 2>&1' 'sparsnas_decode_bilen 2>&1' | /usr/bin/mosquitto_pub -h 192.168.xx.xx -u username -P password -i sparsnas -l -t "home/sparsnas" & -``` - -If the "#define frequencies" are too close you will run into problems. - -If you do not want to spam your MQTT with "BAD" messages you would need to remove or comment out the "BAD" statements in the .cpp file above the JSON printout "elseif" - - - -Technical details on the decoder: ------------------- -SPARSNÄS uses a CC1101 chip configured for GFSK modulation at 868MHz. The two FSK symbol frequencies once downconverted to baseband are roughly 67kHz and 105kHz. (EDIT: It seems like with a different RTL-SDR the baseband frequencies are 12.5khz and 50khz. Maybe my first RTL-SDR was inaccurate). The packet length is 18 bytes, including the length byte, and a 16-bit CRC is appended to the end. The CC1101 sync word is 0xD201. The packet payload byte 5 to 17 are encrypted using a key derived from the sender device's ID. - -The encryption key is a repeating XOR key: -``` - const uint32_t sensor_id_sub = SENSOR_ID - 0x5D38E8CB; - enc_key[0] = (uint8_t)(sensor_id_sub >> 24); - enc_key[1] = (uint8_t)(sensor_id_sub); - enc_key[2] = (uint8_t)(sensor_id_sub >> 8); - enc_key[3] = 0x47; - enc_key[4] = (uint8_t)(sensor_id_sub >> 16); -```` - -Packet format (big endian): -``` -0: uint8_t length; // Always 0x11 -1: uint8_t sender_id_lo; // Lowest byte of sender ID -2: uint8_t unknown; // Not sure -3: uint8_t major_version; // Always 0x07 - the major version number of the sender. -4: uint8_t minor_version; // Always 0x0E - the minor version number of the sender. -5: uint32_t sender_id; // ID of sender -9: uint16_t time; // Time in units of 15 seconds. -11:uint16_t effect; // Current effect usage -13:uint32_t pulses; // Total number of pulses -17:uint8_t battery; // Battery level, 0-100. -``` -This is how to convert the 'effect' field into Watt: -``` -float watt = (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect); -``` - -Note: Due to how the encryption works, it's possible for two different SPARSNÄS with unique sender_id to confuse each other. When decrypting a packet from sender B with the A key, it's possible that the resulting packet gets a sender_id equal to A, which means A will display incorrectly decrypted data from the B device. - -Example usage -- Also for troubleshooting -------------- +```json +core-ssh:~# mosquitto_sub -v -h 192.168.x.x -u username -P password -t '#' +sparsnas/602064 {"Sequence": 47318,"Watt": 2424.00,"kWh": 4207.933,"battery": 100,"FreqErr": 0.70,"CRC":"ok","Sensor":602064} +sparsnas/722270 {"Sequence": 77,"Watt": 3245.07,"kWh": 0.483,"battery": 100,"FreqErr": 0.40,"CRC":"ok","Sensor":722270} ``` -pi@raspberrypi:~ $ rtl_sdr -f 868000000 -s 1024000 -g 40 - | sparsnas_decode -Found 1 device(s): - 0: Realtek, RTL2838UHIDIR, SN: 00000001 -Using device 0: Generic RTL2832U OEM -Found Rafael Micro R820T tuner -[R82XX] PLL not locked! -Sampling at 1024000 S/s. -Tuned to 868000000 Hz. -Tuner gain set to 40.20 dB. -Reading samples in async mode... -{"Sequence":43096,"Watt": 7256.7,"kWh":21483.315,"battery":100,"FreqErr":0.83} -``` +To configure Home Assistant have a look in the [home-assistant](home_assistant) folder. diff --git a/oldDoc/README.md b/oldDoc/README.md new file mode 100644 index 0000000..3f8aa74 --- /dev/null +++ b/oldDoc/README.md @@ -0,0 +1,330 @@ +A BIG thanks to the https://www.facebook.com/groups/SHgruppen on Facebook and a special superduper thanks to the core coder @strigeus! +https://github.com/strigeus/sparsnas_decoder + +This is a fork is adapted for output in JSON format to a MQTT broker. + +If you want to implement the same thing as below into an ESP8266 please see the following project: https://github.com/bphermansson/EspSparsnasGateway + +This is a decoder for IKEA SPARSNÄS. +=================================== + +It uses RTL-SDR running on a Raspberry Pi to demodulate the FSK signal, and decodes the packet. + +The packet data is encrypted using your sender's ID. The sender ID is the last 6 digits of the serial number located under the battery. + +Prerequisites: +------------- +Tested on a Raspberry Pi 3 with Raspbian Stretch with a NooElec Micro 3 USB SDR dongle. You should be able to use any RTL-SDR compatible dongle. + +An IKEA Sparsnäs unit (duuh) + +An MQTT broker installed, see this video for support/help to get it going: https://www.youtube.com/watch?v=VaWdvVVYU3A + +You can also pipe your MQTT data via node-red to InfluxDB for diplaying in for example Grafana, @naestrom have done a great job creating a node-red flow, please find more info here: https://github.com/Naesstrom/sparsnas_mqtt_nodered_influxdb + +The installation: +------------- +Make sure you have updated your Raspbian installation, then +``` +cd ~ +sudo apt-get install libusb-1.0 rtl-sdr g++ git mosquitto-clients +git clone https://github.com/tubalainen/sparsnas_decoder +``` +The decoder is now cloned into /home/pi/sparsnas_decoder + +Using your favorite text editor, create a new file named /etc/modprobe.d/no-rtl.conf and put the following text in the file. You need to run that text editor as sudo (i.e. 'sudo vi' or 'sudo nano' etc) to write to the modprobe.d directory: +``` +sudo nano /etc/modprobe.d/no-rtl.conf +``` +``` +blacklist dvb_usb_rtl28xxu +blacklist rtl2832 +blacklist rtl2830 +``` +Reboot your pi with the USB dongle installed in one of the USB ports. + +Test your dongle +``` +rtl_test +``` +Outcome should look something like this: +``` +pi@raspberrypi:~ $ rtl_test +Found 1 device(s): + 0: Realtek, RTL2838UHIDIR, SN: 00000001 + +Using device 0: Generic RTL2832U OEM +Found Rafael Micro R820T tuner +Supported gain values (29): 0.0 0.9 1.4 2.7 3.7 7.7 8.7 12.5 14.4 15.7 16.6 19.7 20.7 22.9 25.4 28.0 29.7 32.8 33.8 36.4 37.2 38.6 40.2 42.1 43.4 43.9 44.5 48.0 49.6 +[R82XX] PLL not locked! +Sampling at 2048000 S/s. + +Info: This tool will continuously read from the device, and report if +samples get lost. If you observe no further output, everything is fine. + +Reading samples in async mode... +lost at least 24 bytes +``` + +How to build the decoder: +------------- +Take a photo/make a note of your transmitter ID number underneeth the battery lid of the transmitter (not the display) + +First edit sparsnas_decode.cpp to setup your sender id. +Then: +```g++ -o sparsnas_decode -O3 sparsnas_decode.cpp``` + + +How to calibrate to your sender frequency: +------------------------------------------ +```rtl_sdr -f 868000000 -s 1024000 -g 40 - > sparsnas.raw``` + +Wait about 20 seconds. Press Ctrl-C. + +```./sparsnas_decode sparsnas.raw --find-frequencies``` + +Wait about a minute for it to finish. It prints something like: + +```#define FREQUENCIES {65050.000000, 105050.000000}``` + +Edit your sparsnas_decode.cpp with those values and rebuild it according to the How to build step above. + +Perform a testrun: +----------- +```rtl_sdr -f 868000000 -s 1024000 -g 40 - | ./sparsnas_decode``` + +Create a bash script to send IKEA Sparsnäs output to MQTT broker: +----------- +The script will reside in /home/pi/ + +``` +pi@rpitest:~ $ nano sparsnas.sh +``` +``` +#!/bin/bash +#To make sure that the mqtt broker is started, make the script wait 5 minutes. +sleep 5m +#Start sending information to mosquitto_pub +rtl_sdr -f 868000000 -s 1024000 -g 40 - | sparsnas_decode 2>&1 | /usr/bin/mosquitto_pub -h 192.168.x.x -u username -P password -i sparsnas -l -t "home/sparsnas" & +``` + +Make the bash script executable +```chmod +x sparsnas.sh``` + +Copy the binary file to /usr/sbin +```sudo cp /home/pi/sparsnas_decoder/sparsnas_decode /usr/sbin``` + +Test the script: +```./sparsnas.sh``` + +Make it run on startup +```sudo nano /etc/rc.local``` +``` +#!/bin/sh -e +# +# rc.local +# +# This script is executed at the end of each multiuser runlevel. +# Make sure that the script will "exit 0" on success or any other +# value on error. +# +# In order to enable or disable this script just change the execution +# bits. +# +# By default this script does nothing. + +# Print the IP address +_IP=$(hostname -I) || true +if [ "$_IP" ]; then + printf "My IP address is %s\n" "$_IP" +fi + +/home/pi/sparsnas.sh + +exit 0 +``` + +Check status and traffic on your MQTT broker: +``` +mosquitto_sub -v -h 192.168.x.x -u username -P password -t '#' +``` +Example of outcome: +``` +core-ssh:~# mosquitto_sub -v -h 192.168.x.x -u username -P password -t '#' +home/sparsnas {"Sequence":35851,"Watt": 7372.8,"kWh":18889.219,"battery":100,"FreqErr":0.57} +``` + +Create a sensor and monthly automation for Home Assistant (example code, configuration.yaml): +---------------------------------- +``` +mqtt: + broker: 127.0.0.1 + port: 1883 + client_id: home-assistant-1 + username: username + password: password + discovery: true + discovery_prefix: homeassistant + +sensor: + - platform: mqtt + state_topic: "home/sparsnas" + name: "Sparsnäs energy consumption momentary" + unit_of_measurement: "W" + value_template: '{{ value_json.Watt | round(1) }}' +# Note that the sensor below will be resetted if you remove/reinstall the batteries in the Sparsnäs transmitter + - platform: mqtt + state_topic: "home/sparsnas" + name: "Sparsnäs energy consumption over time" + unit_of_measurement: "kWh" + value_template: '{{ value_json.kWh | round(1) }}' + - platform: mqtt + state_topic: "home/sparsnas" + name: "Sparsnäs Battery remaining" + unit_of_measurement: "%" + value_template: '{{ value_json.battery | round(1) }}' + - platform: mqtt + state_topic: "home/sparsnas" + name: "Sparsnäs Frequency Error" + unit_of_measurement: "%" + value_template: '{{ value_json.FreqErr }}' + - platform: mqtt + name: "Sparsnäs template kwh sensor day" + state_topic: "template/kwh/day" + - platform: mqtt + name: "Sparsnäs template kwh sensor month" + state_topic: "template/kwh/month" + - platform: template + sensors: + kwh_current_month: + friendly_name: "Sparsnäs current month" + unit_of_measurement: "kWh" + value_template: >- + {{ (float(states.sensor.sparsnas_energy_consumption_over_time.state) - float(states.sensor.sparsnas_template_kwh_sensor_month.state)) | round(1) }} + kwh_today: + friendly_name: "Sparsnäs current day" + unit_of_measurement: "kWh" + value_template: >- + {{ (float(states.sensor.sparsnas_energy_consumption_over_time.state) - float(states.sensor.sparsnas_template_kwh_sensor_day.state)) | round(1) }} + +# Thanks to @bhaap and @naestrom for the monthly and daily automation below +automation: +- action: + - data: + payload_template: '{{ states(''sensor.sparsnas_energy_consumption_over_time'') + }}' + retain: 'true' + topic: template/kwh/month + service: mqtt.publish + alias: Sparsnäs monthly consumption + condition: + - condition: template + value_template: '{{ now().month() | string == "1" }}' + - condition: template + value_template: '{{ now().month() | string == "2" }}' + - condition: template + value_template: '{{ now().month() | string == "3" }}' + - condition: template + value_template: '{{ now().month() | string == "4" }}' + - condition: template + value_template: '{{ now().month() | string == "5" }}' + - condition: template + value_template: '{{ now().month() | string == "6" }}' + - condition: template + value_template: '{{ now().month() | string == "7" }}' + - condition: template + value_template: '{{ now().month() | string == "8" }}' + - condition: template + value_template: '{{ now().month() | string == "9" }}' + - condition: template + value_template: '{{ now().month() | string == "10" }}' + - condition: template + value_template: '{{ now().month() | string == "11" }}' + - condition: template + value_template: '{{ now().month() | string == "12" }}' + id: '1516722867362' + trigger: + - at: 00:00:01 + platform: time +- action: + - data: + payload_template: "{{ states('sensor.sparsnas_energy_consumption_over_time')}}" + retain: 'true' + topic: template/kwh/day + service: mqtt.publish + alias: Sparsnäs daily consumption + condition: [] + id: '1516806539856' + trigger: + - at: 00:00:02 + platform: time +``` + +Use two or more Sparsnäs at the same location (Experts only): +------------------ +It is possible to use twoor more Sparsnäs at the same time. +You will need to compile one sparsnas_decode per Sparsnäs Energy meter. The same sdr_rtl instance can be used. + +Edit your sparsnas.sh to look something like the following after you have compiled and copied all your instances to /usr/sbin/. +``` +#!/bin/bash + +rtl_sdr -f 868000000 -s 1024000 -g 40 - | pee 'sparsnas_decode 2>&1' 'sparsnas_decode_bilen 2>&1' | /usr/bin/mosquitto_pub -h 192.168.xx.xx -u username -P password -i sparsnas -l -t "home/sparsnas" & +``` + +If the "#define frequencies" are too close you will run into problems. + +If you do not want to spam your MQTT with "BAD" messages you would need to remove or comment out the "BAD" statements in the .cpp file above the JSON printout "elseif" + + + +Technical details on the decoder: +------------------ +SPARSNÄS uses a CC1101 chip configured for GFSK modulation at 868MHz. The two FSK symbol frequencies once downconverted to baseband are roughly 67kHz and 105kHz. (EDIT: It seems like with a different RTL-SDR the baseband frequencies are 12.5khz and 50khz. Maybe my first RTL-SDR was inaccurate). The packet length is 18 bytes, including the length byte, and a 16-bit CRC is appended to the end. The CC1101 sync word is 0xD201. The packet payload byte 5 to 17 are encrypted using a key derived from the sender device's ID. + +The encryption key is a repeating XOR key: +``` + const uint32_t sensor_id_sub = SENSOR_ID - 0x5D38E8CB; + enc_key[0] = (uint8_t)(sensor_id_sub >> 24); + enc_key[1] = (uint8_t)(sensor_id_sub); + enc_key[2] = (uint8_t)(sensor_id_sub >> 8); + enc_key[3] = 0x47; + enc_key[4] = (uint8_t)(sensor_id_sub >> 16); +```` + +Packet format (big endian): +``` +0: uint8_t length; // Always 0x11 +1: uint8_t sender_id_lo; // Lowest byte of sender ID +2: uint8_t unknown; // Not sure +3: uint8_t major_version; // Always 0x07 - the major version number of the sender. +4: uint8_t minor_version; // Always 0x0E - the minor version number of the sender. +5: uint32_t sender_id; // ID of sender +9: uint16_t time; // Time in units of 15 seconds. +11:uint16_t effect; // Current effect usage +13:uint32_t pulses; // Total number of pulses +17:uint8_t battery; // Battery level, 0-100. +``` +This is how to convert the 'effect' field into Watt: +``` +float watt = (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect); +``` + +Note: Due to how the encryption works, it's possible for two different SPARSNÄS with unique sender_id to confuse each other. When decrypting a packet from sender B with the A key, it's possible that the resulting packet gets a sender_id equal to A, which means A will display incorrectly decrypted data from the B device. + +Example usage -- Also for troubleshooting +------------- +``` +pi@raspberrypi:~ $ rtl_sdr -f 868000000 -s 1024000 -g 40 - | sparsnas_decode +Found 1 device(s): + 0: Realtek, RTL2838UHIDIR, SN: 00000001 + +Using device 0: Generic RTL2832U OEM +Found Rafael Micro R820T tuner +[R82XX] PLL not locked! +Sampling at 1024000 S/s. +Tuned to 868000000 Hz. +Tuner gain set to 40.20 dB. +Reading samples in async mode... +{"Sequence":43096,"Watt": 7256.7,"kWh":21483.315,"battery":100,"FreqErr":0.83} +``` diff --git a/help.png b/oldDoc/help.png similarity index 100% rename from help.png rename to oldDoc/help.png From b06411114790b6d89fad2595d92079f8b69ab1cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Lindstr=C3=B6m?= Date: Thu, 1 Mar 2018 11:21:48 +0100 Subject: [PATCH 49/82] Added functional reconnect for mqtt (#2) * Test move mqtt connect before publish. * Test reconnect and loop * added forever * more info in error message * more info on error * Move int ret * Revert to witout _forever * Added some crc checks, fixed loop_start and removed loop inside the publish if statement * Fixed issues. * debugging for effect. * Also changed watt to double from float --- sparsnas_decode.cpp | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index a447dcc..fff7ecf 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -32,7 +32,7 @@ uint32_t MQTT_PORT = 1883; char MQTT_USERNAME[64]; char MQTT_PASSWORD[64]; char MQTT_TOPIC[64]; - +char MQTT_CRC_TOPIC[64]; FILE *outfile; int testing=0; @@ -178,38 +178,43 @@ class SignalDetector { for (int i = 0; i < 18; i++) m += sprintf(m, "%.2X ", data_[i]); m += sprintf(m, "\""); - } else { + } else if (crc == packet_crc) { bad = false; int seq = (dec[9] << 8 | dec[10]); - int effect = (dec[11] << 8 | dec[12]); + unsigned int effect = (dec[11] << 8 | dec[12]); int pulse = (dec[13] << 24 | dec[14] << 16 | dec[15] << 8 | dec[16]); int battery = dec[17]; float watt = effect * 24; int data4 = data_[4]^0x0f; // Note that data_[4] cycles between 0-3 when you first put in the batterys in t$ if(data4 == 1){ - watt = (float)((3600000 / PULSES_PER_KWH) * 1024) / (effect); + watt = (double)((3600000 / PULSES_PER_KWH) * 1024) / (effect); } - m += sprintf(m, "{\"Sequence\": %5d,\"Watt\": %7.2f,\"kWh\": %d.%.3d,\"battery\": %d,\"FreqErr\": %.2f", seq, watt, pulse/PULSES_PER_KWH, pulse%PULSES_PER_KWH, battery, freq); + m += sprintf(m, "{\"Sequence\": %5d,\"Watt\": %7.2f,\"kWh\": %d.%.3d,\"battery\": %d,\"FreqErr\": %.2f,\"Effect\": %d", seq, watt, pulse/PULSES_PER_KWH, pulse%PULSES_PER_KWH, battery, freq, effect); if (testing && crc == packet_crc) { error_sum += fabs(freq); error_sum_count += 1; } + } else { + m += sprintf(m, "{\"CRC\": \"ERR\""); } - m += sprintf(m, (crc == packet_crc) ? ",\"CRC\":\"ok\"" : ",\"CRC\": \"ERR\""); - m += sprintf(m, ",\"Sensor\":%6d}\n", SENSOR_ID); + char* topic = (crc == packet_crc) ? MQTT_TOPIC : MQTT_CRC_TOPIC; if (!testing) { if (mosq && !bad) { - int ret = mosquitto_publish (mosq, NULL, MQTT_TOPIC, strlen(mesg) - 1, mesg, 0, false); - if (ret) { - fprintf(stderr, "Can't publish to Mosquitto server\n"); - // Tear down the connecton and exit. - mosquitto_disconnect (mosq); - mosquitto_destroy (mosq); - mosquitto_lib_cleanup(); - exit(-1); + int ret = mosquitto_publish (mosq, NULL, topic, strlen(mesg) - 1, mesg, 0, true); + if ( ret != MOSQ_ERR_SUCCESS) { + mosquitto_reconnect(mosq); + ret = mosquitto_publish (mosq, NULL, topic, strlen(mesg) - 1, mesg, 0, true); + if (ret != MOSQ_ERR_SUCCESS) { + fprintf(stderr, "Can't publish to Mosquitto server %d %s\n", ret, mosquitto_strerror(ret) ); + // Tear down the connecton and exit. + mosquitto_disconnect (mosq); + mosquitto_destroy (mosq); + mosquitto_lib_cleanup(); + exit(-1); + } } } else bad ? fprintf(stderr, "%s", mesg) : printf("%s", mesg); @@ -440,6 +445,8 @@ int main(int argc, char **argv) else sprintf(MQTT_TOPIC, "sparsnas/%d", SENSOR_ID); + sprintf(MQTT_CRC_TOPIC, "%s/crc", MQTT_TOPIC); + memset(MQTT_USERNAME, '\0', sizeof(MQTT_USERNAME)); if (const char *env_p = std::getenv("MQTT_USERNAME")) strncpy(MQTT_USERNAME, env_p, sizeof(MQTT_USERNAME)-1); @@ -453,6 +460,7 @@ int main(int argc, char **argv) // Create a new Mosquitto runtime instance with a random client ID, mosq = mosquitto_new (NULL, true, NULL); + mosquitto_loop_start(mosq); if (mosq) { //Set username and password (will be ignored of MQTT_USERNAME=NULL) From 508c1823bc315a9210728872988eab06cee549d8 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Thu, 1 Mar 2018 12:55:42 +0100 Subject: [PATCH 50/82] updated automations for home-assistant --- home-assistant/sparsnas_mqtt.yaml | 83 ++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 29 deletions(-) diff --git a/home-assistant/sparsnas_mqtt.yaml b/home-assistant/sparsnas_mqtt.yaml index cec9830..ff3e7a9 100644 --- a/home-assistant/sparsnas_mqtt.yaml +++ b/home-assistant/sparsnas_mqtt.yaml @@ -54,41 +54,47 @@ sensor: automation: - action: - data: - payload_template: '{{ states(''sensor.sparsnas_energy_consumption_over_time'') - }}' + payload_template: '{{ states(''sensor.sparsnas_energy_consumption_over_time'') }}' retain: 'true' topic: template/kwh/month service: mqtt.publish alias: Sparsnäs monthly consumption - condition: - - condition: template - value_template: '{{ now().month() | string == "1" }}' - - condition: template - value_template: '{{ now().month() | string == "2" }}' - - condition: template - value_template: '{{ now().month() | string == "3" }}' - - condition: template - value_template: '{{ now().month() | string == "4" }}' - - condition: template - value_template: '{{ now().month() | string == "5" }}' - - condition: template - value_template: '{{ now().month() | string == "6" }}' - - condition: template - value_template: '{{ now().month() | string == "7" }}' - - condition: template - value_template: '{{ now().month() | string == "8" }}' - - condition: template - value_template: '{{ now().month() | string == "9" }}' - - condition: template - value_template: '{{ now().month() | string == "10" }}' - - condition: template - value_template: '{{ now().month() | string == "11" }}' - - condition: template - value_template: '{{ now().month() | string == "12" }}' - id: '1516722867362' trigger: - - at: 00:00:01 platform: time + at: '00:00:01' + condition: + condition: and + conditions: + - condition: template + value_template: '{{ now().day | string == "1" }}' + - condition: or + conditions: + - condition: template + value_template: '{{ now().month | string == "1" }}' + - condition: template + value_template: '{{ now().month | string == "2" }}' + - condition: template + value_template: '{{ now().month | string == "3" }}' + - condition: template + value_template: '{{ now().month | string == "4" }}' + - condition: template + value_template: '{{ now().month | string == "5" }}' + - condition: template + value_template: '{{ now().month | string == "6" }}' + - condition: template + value_template: '{{ now().month | string == "7" }}' + - condition: template + value_template: '{{ now().month | string == "8" }}' + - condition: template + value_template: '{{ now().month | string == "9" }}' + - condition: template + value_template: '{{ now().month | string == "10" }}' + - condition: template + value_template: '{{ now().month | string == "11" }}' + - condition: template + value_template: '{{ now().month | string == "12" }}' + id: '1516722867362' + - action: - data: payload_template: "{{ states('sensor.sparsnas_energy_consumption_over_time')}}" @@ -102,6 +108,25 @@ automation: - at: 00:00:02 platform: time +- action: + - data: + entity_id: automation.sparsnas_daily_consumption + service: automation.trigger + - data: + entity_id: automation.sparsnas_monthly_consumption + service: automation.trigger + alias: trigger-sparsnas-if-unknown + condition: + condition: state + entity_id: sensor.sparsnas_template_kwh_sensor_month + state: Unknown + trigger: + - event: state + entity_id: sensor.sparsnas_energy_consumption_over_time + from: 'unknown' + id: '159293779102' + + group: sparsnas: name: Energy Consumption From 2fddf7d6631c0910d29a1c49d551e725c21c0f91 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Thu, 1 Mar 2018 12:56:53 +0100 Subject: [PATCH 51/82] added configuration options for custom-ui --- home-assistant/sparsnas_mqtt.yaml | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/home-assistant/sparsnas_mqtt.yaml b/home-assistant/sparsnas_mqtt.yaml index ff3e7a9..92bc00f 100644 --- a/home-assistant/sparsnas_mqtt.yaml +++ b/home-assistant/sparsnas_mqtt.yaml @@ -1,3 +1,36 @@ +homeassistant: + customize: + sensor.sparsnas_battery_remaining: + hidden: true + icon: mdi:battery-outline + sensor.sparsnas_frequency_error: + hidden: true + icon: mdi:signal + sensor.kwh_today: + group: + group.sparsnas: + friendly_name: Today + icon: mdi:flash-circle + sensor.kwh_current_month: + group: + group.sparsnas: + friendly_name: This Month + icon: mdi:flash-circle + sensor.sparsnas_template_kwh_sensor_day: + hidden: true + sensor.sparsnas_template_kwh_sensor_month: + hidden: true + sensor.sparsnas_energy_consumption_over_time: + hidden: true + icon: mdi:flash-circle + sensor.sparsnas_energy_consumption_momentary: + extra_data_template: '${"Battery: " + entities[''sensor.sparsnas_battery_remaining''].state + + "%, Signal: " + entities[''sensor.sparsnas_frequency_error''].state}' + group: + group.sparsnas: + friendly_name: Power usage + icon: mdi:flash-circle + sensor: - platform: mqtt state_topic: "home/sparsnas" From 404c61158e28a43becfb9b470c9b3aa634af9b5b Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Thu, 1 Mar 2018 12:59:33 +0100 Subject: [PATCH 52/82] changed state_topic to use !secret sparsnas_sensor --- home-assistant/sparsnas_mqtt.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/home-assistant/sparsnas_mqtt.yaml b/home-assistant/sparsnas_mqtt.yaml index 92bc00f..3bb2e04 100644 --- a/home-assistant/sparsnas_mqtt.yaml +++ b/home-assistant/sparsnas_mqtt.yaml @@ -33,28 +33,28 @@ homeassistant: sensor: - platform: mqtt - state_topic: "home/sparsnas" + state_topic: !secret sparsnas_sensor name: "Sparsnäs energy consumption momentary" unit_of_measurement: "W" value_template: '{{ value_json.Watt | round(1) }}' icon: mdi:flash-circle # Note that the sensor below will be resetted if you remove/reinstall the batteries in the Sparsnäs transmitter - platform: mqtt - state_topic: "home/sparsnas" + state_topic: !secret sparsnas_sensor name: "Sparsnäs energy consumption over time" unit_of_measurement: "kWh" value_template: '{{ value_json.kWh | round(1) }}' hidden: true icon: mdi:flash-circle - platform: mqtt - state_topic: "home/sparsnas" + state_topic: !secret sparsnas_sensor name: "Sparsnäs Battery remaining" unit_of_measurement: "%" value_template: '{{ value_json.battery | round(1) }}' hidden: true icon: mdi:battery-outline - platform: mqtt - state_topic: "home/sparsnas" + state_topic: !secret sparsnas_sensor name: "Sparsnäs Frequency Error" unit_of_measurement: "%" value_template: '{{ value_json.FreqErr }}' From b6f9bebe28811af529b287156b4ad3b96c5d2c9e Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Thu, 1 Mar 2018 13:03:46 +0100 Subject: [PATCH 53/82] Update readme.md --- home-assistant/readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/home-assistant/readme.md b/home-assistant/readme.md index 555fd45..fb3f6f0 100644 --- a/home-assistant/readme.md +++ b/home-assistant/readme.md @@ -6,3 +6,6 @@ Once loaded the package will create the following panel in Home Assistant. + +## Configuration +Either replace all occurances of `!secret sparsnas_sensor` with the mqtt topic the decoder is sending on (typically `sparsnas/SENSOR_ID`), or (preferrable) configure Home Assistant to use [secrets](https://home-assistant.io/docs/configuration/secrets/) and set the secret `sparsnas_sensor` to the right mqtt topic (typically `sparsnas/SENSOR_ID`). From 556f34d2a6bc1c95e785707698a407807fbf779f Mon Sep 17 00:00:00 2001 From: tubalainen Date: Thu, 1 Mar 2018 13:14:31 +0100 Subject: [PATCH 54/82] Minor adjustments --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7fdb79f..990c7ae 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ It is highly recommended to use the [docker](https://docker.com) version describ The following instructions are for you that for some reason don't want to use docker, and assumes a debian or ubuntu based environment. ```sh -sudo apt-get update && apt-get install -y git g++ libmosquitto-dev libmosquitto1 rtl-sdr zsh busybox +sudo apt-get update && sudo apt-get install -y git g++ libmosquitto-dev libmosquitto1 rtl-sdr zsh busybox cd ~ git clone https://github.com/fredrike/sparsnas_decoder cd sparsnas_decoder @@ -146,4 +146,4 @@ sparsnas/602064 {"Sequence": 47318,"Watt": 2424.00,"kWh": 4207.933,"battery": 10 sparsnas/722270 {"Sequence": 77,"Watt": 3245.07,"kWh": 0.483,"battery": 100,"FreqErr": 0.40,"CRC":"ok","Sensor":722270} ``` -To configure Home Assistant have a look in the [home-assistant](home_assistant) folder. \ No newline at end of file +To configure Home Assistant have a look in the [home-assistant](home_assistant) folder. From 0ffa81aef773fde34a4b93708ea51521a3c9d339 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Thu, 1 Mar 2018 13:17:39 +0100 Subject: [PATCH 55/82] Minor adjustments --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 990c7ae..00ce356 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ The following instructions are for you that for some reason don't want to use do ```sh sudo apt-get update && sudo apt-get install -y git g++ libmosquitto-dev libmosquitto1 rtl-sdr zsh busybox cd ~ -git clone https://github.com/fredrike/sparsnas_decoder +git clone https://github.com/tubalainen/sparsnas_decoder cd sparsnas_decoder git checkout dockerVersion From d31d091597cef04e0edf8f185185d1ff282c8289 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Thu, 1 Mar 2018 13:37:12 +0100 Subject: [PATCH 56/82] Updates --- docker.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker.md b/docker.md index 1ee9945..c19d17e 100644 --- a/docker.md +++ b/docker.md @@ -7,16 +7,16 @@ ``` docker build -t sparsnas --build-arg SENSORS="1234 1000" \ - --build-arg MQTT_HOST=192.168.x.x --build-arg MQTT_PORT \ - --build-arg=MQTT_USERNAME=username --build-arg=MQTT_PASSWORD \ - https://github.com/fredrike/sparsnas_decoder.git#dockerVersion + --build-arg MQTT_HOST=192.168.x.x --build-arg MQTT_PORT=1183 \ + --build-arg=MQTT_USERNAME=username --build-arg=MQTT_PASSWORD=password \ + https://github.com/tubalainen/sparsnas_decoder.git#dockerVersion ``` 2. Run the container (it is possible to set the arguments above at runtime as `-e MQTT_HOST=192.168.1.2` if you like). ``` -docker run --device=/dev/bus/usb --name=sparsnas --restart=always sparsnas:latest +docker run -it --device=/dev/bus/usb --name=sparsnas --restart=always sparsnas:latest ``` 3. Sit back and enjoy :smile:. From c6b8c552499fa88bd12dcf807b0c2d75f9840bdf Mon Sep 17 00:00:00 2001 From: tubalainen Date: Thu, 1 Mar 2018 16:42:47 +0100 Subject: [PATCH 57/82] Fixed URL for docker --- docker.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker.md b/docker.md index c19d17e..39e3ba0 100644 --- a/docker.md +++ b/docker.md @@ -9,7 +9,7 @@ docker build -t sparsnas --build-arg SENSORS="1234 1000" \ --build-arg MQTT_HOST=192.168.x.x --build-arg MQTT_PORT=1183 \ --build-arg=MQTT_USERNAME=username --build-arg=MQTT_PASSWORD=password \ - https://github.com/tubalainen/sparsnas_decoder.git#dockerVersion + https://github.com/tubalainen/sparsnas_decoder.git ``` 2. Run the container (it is possible to set the arguments above at runtime as From 008819fa0994e568ac75d71e6b9d1be32244ab0a Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Fri, 2 Mar 2018 14:54:46 +0100 Subject: [PATCH 58/82] fix for power calculation when data4 is 0 --- sparsnas_decode.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index fff7ecf..3625de4 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -189,8 +189,11 @@ class SignalDetector { // Note that data_[4] cycles between 0-3 when you first put in the batterys in t$ if(data4 == 1){ watt = (double)((3600000 / PULSES_PER_KWH) * 1024) / (effect); + } else if (data4 == 0 ) { // special mode for low power usage + watt = effect * 0.24 / PULSES_PER_KWH; } - m += sprintf(m, "{\"Sequence\": %5d,\"Watt\": %7.2f,\"kWh\": %d.%.3d,\"battery\": %d,\"FreqErr\": %.2f,\"Effect\": %d", seq, watt, pulse/PULSES_PER_KWH, pulse%PULSES_PER_KWH, battery, freq, effect); + m += sprintf(m, "{\"Sequence\": %5d,\"Watt\": %7.2f,\"kWh\": %d.%.3d,\"battery\": %d,\"FreqErr\": %.2f,\"Effect\": %d,\"Data4\": %d", + seq, watt, pulse/PULSES_PER_KWH, pulse%PULSES_PER_KWH, battery, freq, effect, data4); if (testing && crc == packet_crc) { error_sum += fabs(freq); error_sum_count += 1; From 06e06902531a272f8759513c37b5ea3e3f1ddf16 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Fri, 2 Mar 2018 15:15:37 +0100 Subject: [PATCH 59/82] minor fixes in the automation syntax --- home-assistant/sparsnas_mqtt.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/home-assistant/sparsnas_mqtt.yaml b/home-assistant/sparsnas_mqtt.yaml index 3bb2e04..f99042f 100644 --- a/home-assistant/sparsnas_mqtt.yaml +++ b/home-assistant/sparsnas_mqtt.yaml @@ -150,14 +150,14 @@ automation: service: automation.trigger alias: trigger-sparsnas-if-unknown condition: - condition: state + - condition: state entity_id: sensor.sparsnas_template_kwh_sensor_month state: Unknown + id: '1519996400010' trigger: - - event: state - entity_id: sensor.sparsnas_energy_consumption_over_time - from: 'unknown' - id: '159293779102' + - entity_id: sensor.sparsnas_energy_consumption_over_time + from: unknown + platform: state group: From e2b4ff06d9a30acbc9e24c6c8a59a2396e4f30ed Mon Sep 17 00:00:00 2001 From: tubalainen Date: Fri, 2 Mar 2018 18:42:07 +0100 Subject: [PATCH 60/82] Typo --- docker.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker.md b/docker.md index 39e3ba0..84e835d 100644 --- a/docker.md +++ b/docker.md @@ -7,7 +7,7 @@ ``` docker build -t sparsnas --build-arg SENSORS="1234 1000" \ - --build-arg MQTT_HOST=192.168.x.x --build-arg MQTT_PORT=1183 \ +  --build-arg MQTT_HOST=192.168.x.x --build-arg MQTT_PORT=1883 \ --build-arg=MQTT_USERNAME=username --build-arg=MQTT_PASSWORD=password \ https://github.com/tubalainen/sparsnas_decoder.git ``` From 743a17e24a655a33c556bac84855b8d624363f5b Mon Sep 17 00:00:00 2001 From: tubalainen Date: Mon, 5 Mar 2018 07:42:54 +0100 Subject: [PATCH 61/82] minor updates --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9cc74b5..2f80158 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # docker build --build-arg SENSORS="12 10000" \ -# --build-arg MQTT_HOST=192.168.x.x --build-arg MQTT_PORT \ -# --build-arg=MQTT_USERNAME=username --build-arg=MQTT_PASSWORD +# --build-arg MQTT_HOST=192.168.x.x --build-arg MQTT_PORT=1883 \ +# --build-arg=MQTT_USERNAME=username --build-arg=MQTT_PASSWORD=hemligt FROM alpine:edge as BUILD_ENV From 869d73f4d9ff75ca029cec1fec313bf03992abee Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Tue, 6 Mar 2018 09:55:28 +0100 Subject: [PATCH 62/82] minor fixes in docker version --- Dockerfile | 4 +++- docker.md | 2 +- sparsnas.sh | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2f80158..0671ef9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,8 @@ ENV MQTT_PORT=${MQTT_PORT:-1883} ENV MQTT_USERNAME=$MQTT_USERNAME ENV MQTT_PASSWORD=$MQTT_PASSWORD +RUN : "${SENSORS:?Build argument 'SENSORS' needs to be set and non-empty.}" + COPY --from=BUILD_ENV /build/sparsnas_decode /usr/bin/ COPY sparsnas.sh / @@ -29,6 +31,6 @@ RUN apk add --no-cache --repository http://dl-3.alpinelinux.org/alpine/edge/test mosquitto-libs++ \ zsh -RUN if [ "$SENSORS" ]; then sed -i "s/^SENSORS=.*/SENSORS=($SENSORS)/" /sparsnas.sh; fi +RUN sed -i "s/^SENSORS=.*/SENSORS=(${SENSORS})/" /sparsnas.sh ENTRYPOINT ["/sparsnas.sh"] diff --git a/docker.md b/docker.md index 84e835d..a7115af 100644 --- a/docker.md +++ b/docker.md @@ -8,7 +8,7 @@ ``` docker build -t sparsnas --build-arg SENSORS="1234 1000" \  --build-arg MQTT_HOST=192.168.x.x --build-arg MQTT_PORT=1883 \ - --build-arg=MQTT_USERNAME=username --build-arg=MQTT_PASSWORD=password \ + --build-arg MQTT_USERNAME=username --build-arg MQTT_PASSWORD=password \ https://github.com/tubalainen/sparsnas_decoder.git ``` diff --git a/sparsnas.sh b/sparsnas.sh index 6ea9c5d..d015a03 100755 --- a/sparsnas.sh +++ b/sparsnas.sh @@ -4,7 +4,7 @@ set -o pipefail #Associatve array typeset -A SENSORS -SENSORS=(722270 1000 602064 10000) #(id_1 pulses_1 id_2 pulses_2) +SENSORS=(id_1 pulses_1 id_2 pulses_2) RTL_SDR=(/usr/bin/rtl_sdr -f 868000000 -s 1024000 -g 40 -) #RTL_SDR=(cat /sparsnas.raw) @@ -42,7 +42,7 @@ function decode () { function frequencies () { for sensor in ${(k)SENSORS}; do - grep -e SPARSNAS_FREQ_MAX -e SPARSNAS_FREQ_MIN /tmp/${sensor}.freq >& /dev/null || return 1 + grep -e SPARSNAS_FREQ_MAX -e SPARSNAS_FREQ_MIN /tmp/${sensor}.freq -q || return 1 done return 0 } From 66f377556d555254d4769335f292b25cde5d3576 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Mon, 2 Jul 2018 22:32:22 +0200 Subject: [PATCH 63/82] Mosquitto handles reconnect automatically (when using `mosquitto_loop_start`) --- sparsnas_decode.cpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index 3625de4..c52b16d 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -208,16 +208,7 @@ class SignalDetector { if (mosq && !bad) { int ret = mosquitto_publish (mosq, NULL, topic, strlen(mesg) - 1, mesg, 0, true); if ( ret != MOSQ_ERR_SUCCESS) { - mosquitto_reconnect(mosq); - ret = mosquitto_publish (mosq, NULL, topic, strlen(mesg) - 1, mesg, 0, true); - if (ret != MOSQ_ERR_SUCCESS) { - fprintf(stderr, "Can't publish to Mosquitto server %d %s\n", ret, mosquitto_strerror(ret) ); - // Tear down the connecton and exit. - mosquitto_disconnect (mosq); - mosquitto_destroy (mosq); - mosquitto_lib_cleanup(); - exit(-1); - } + fprintf(stderr, "Can't publish to Mosquitto server. %s\n", ret, mosquitto_strerror(ret) ); } } else bad ? fprintf(stderr, "%s", mesg) : printf("%s", mesg); @@ -468,9 +459,9 @@ int main(int argc, char **argv) if (mosq) { //Set username and password (will be ignored of MQTT_USERNAME=NULL) mosquitto_username_pw_set (mosq, MQTT_USERNAME, MQTT_PASSWORD); - int ret = mosquitto_connect (mosq, MQTT_HOSTNAME, MQTT_PORT, 30); - if (ret) { - fprintf (stderr, "Can't connect to Mosquitto server\n"); + int ret = mosquitto_connect_async (mosq, MQTT_HOSTNAME, MQTT_PORT, 30); + if (ret != MOSQ_ERR_SUCCESS) { + fprintf (stderr, "Mosquitto connect issues, will write to stdout.\n"); mosq = NULL; } } else From 215a4aab3877ef1976ef620255faef159b8ac261 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Thu, 5 Jul 2018 13:50:09 +0200 Subject: [PATCH 64/82] Typo --- sparsnas_decode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index c52b16d..d537691 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -26,7 +26,7 @@ float frequencies[2]; //={67500.0,105000.0}; // MQTT connection struct mosquitto *mosq = NULL; -// MQTT server connection parameters +// MQTT connection parameters char MQTT_HOSTNAME[64]; uint32_t MQTT_PORT = 1883; char MQTT_USERNAME[64]; @@ -208,7 +208,7 @@ class SignalDetector { if (mosq && !bad) { int ret = mosquitto_publish (mosq, NULL, topic, strlen(mesg) - 1, mesg, 0, true); if ( ret != MOSQ_ERR_SUCCESS) { - fprintf(stderr, "Can't publish to Mosquitto server. %s\n", ret, mosquitto_strerror(ret) ); + fprintf(stderr, "Can't publish to Mosquitto server. %s\n", mosquitto_strerror(ret)); } } else bad ? fprintf(stderr, "%s", mesg) : printf("%s", mesg); From 980c12a3ff3fc22b8c6555664a8ce532c9351452 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Fri, 22 Mar 2019 10:57:12 +0100 Subject: [PATCH 65/82] Update Dockerfile url should be http://dl-cdn.alpinelinux.org/alpine/edge/testing/ --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 0671ef9..669141a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,7 +26,7 @@ RUN : "${SENSORS:?Build argument 'SENSORS' needs to be set and non-empty.}" COPY --from=BUILD_ENV /build/sparsnas_decode /usr/bin/ COPY sparsnas.sh / -RUN apk add --no-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ --allow-untrusted \ +RUN apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing/ --allow-untrusted \ rtl-sdr \ mosquitto-libs++ \ zsh From 717d8d37dff18c2fd076c65da78705fece1a4769 Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Fri, 22 Mar 2019 11:39:21 +0100 Subject: [PATCH 66/82] divide as float instead --- sparsnas_decode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index d537691..b0bd874 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -192,8 +192,8 @@ class SignalDetector { } else if (data4 == 0 ) { // special mode for low power usage watt = effect * 0.24 / PULSES_PER_KWH; } - m += sprintf(m, "{\"Sequence\": %5d,\"Watt\": %7.2f,\"kWh\": %d.%.3d,\"battery\": %d,\"FreqErr\": %.2f,\"Effect\": %d,\"Data4\": %d", - seq, watt, pulse/PULSES_PER_KWH, pulse%PULSES_PER_KWH, battery, freq, effect, data4); + m += sprintf(m, "{\"Sequence\": %5d,\"Watt\": %7.2f,\"kWh\": %.3f,\"battery\": %d,\"FreqErr\": %.2f,\"Effect\": %d,\"Data4\": %d", + seq, watt, pulse/(float)PULSES_PER_KWH, battery, freq, effect, data4); if (testing && crc == packet_crc) { error_sum += fabs(freq); error_sum_count += 1; From 99b8bce7f55761b28966183eaa45eb4a60277dee Mon Sep 17 00:00:00 2001 From: Fredrik Erlandsson Date: Tue, 2 Apr 2019 07:50:13 +0200 Subject: [PATCH 67/82] style fix for output string --- sparsnas_decode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sparsnas_decode.cpp b/sparsnas_decode.cpp index b0bd874..2bd1b85 100644 --- a/sparsnas_decode.cpp +++ b/sparsnas_decode.cpp @@ -192,7 +192,7 @@ class SignalDetector { } else if (data4 == 0 ) { // special mode for low power usage watt = effect * 0.24 / PULSES_PER_KWH; } - m += sprintf(m, "{\"Sequence\": %5d,\"Watt\": %7.2f,\"kWh\": %.3f,\"battery\": %d,\"FreqErr\": %.2f,\"Effect\": %d,\"Data4\": %d", + m += sprintf(m, "{\"Sequence\":%6d, \"Watt\":%8.2f, \"kWh\":%9.3f, \"battery\":%4d, \"FreqErr\":%5.2f, \"Effect\":%5d, \"Data4\":%2d", seq, watt, pulse/(float)PULSES_PER_KWH, battery, freq, effect, data4); if (testing && crc == packet_crc) { error_sum += fabs(freq); @@ -202,7 +202,7 @@ class SignalDetector { m += sprintf(m, "{\"CRC\": \"ERR\""); } - m += sprintf(m, ",\"Sensor\":%6d}\n", SENSOR_ID); + m += sprintf(m, ", \"Sensor\": %6d}\n", SENSOR_ID); char* topic = (crc == packet_crc) ? MQTT_TOPIC : MQTT_CRC_TOPIC; if (!testing) { if (mosq && !bad) { From a422428f757516265887cd3737154d18cc8bbed5 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Sat, 20 Apr 2019 07:56:37 +0200 Subject: [PATCH 68/82] Update on SDR dongle models --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 00ce356..4deff1a 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,8 @@ The packet data is encrypted using your sender's ID. The sender ID is the last 6 Prerequisites: ------------- -Tested on a Raspberry Pi 3 with Raspbian Stretch with a NooElec Micro 3 USB SDR dongle. You should be able to use any RTL-SDR compatible dongle. +Tested on a Raspberry Pi 3 with Raspbian Stretch with a NooElec Nano 3 USB SDR dongle. You should be able to use any RTL-SDR compatible dongle. If you live in Sweden/EU the NooElec Micro 3 dongle can be bought from www.amazon.de (free shipping on orders over 25 EUR). +Of get your self the "real deal", an original RTL-SDR v3 from https://www.lohelectronics.se/kommunikation/amatorradio/radio/rtl-sdr-r820t2-rtl2832u-1ppm-tcxo-sma-sdr . Dont forget to make your self an antenna, the IKEA Sparsnäs transmits on 868 MHz so a 1/2 wave lenght wire connected to the center pin in the antenna port should do the trick. Make the wire approximently 16 cm long. An IKEA Sparsnäs unit (duuh) From ef7630d785d4b5f5c74265dbbdaddcd193448c47 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Sat, 20 Apr 2019 08:00:00 +0200 Subject: [PATCH 69/82] hassio updates --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4deff1a..01c09c1 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ If you want to implement the same thing as below into an ESP8266 please see the If you want to get into the bits and bytes of the system please see: https://github.com/kodarn/Sparsnas +Asif just made a HASSIO Addon based on this code, check out his repository here: https://github.com/asifma/hassio-addons + This is a decoder for IKEA SPARSNÄS. =================================== From a3d402383a544ed364a13b0dc5524d4e12b40dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jari=20Ylim=C3=A4inen?= Date: Sat, 20 Jul 2019 09:50:12 +0300 Subject: [PATCH 70/82] Pin Alpine version to 3.7 --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 669141a..70e235d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,14 +2,14 @@ # --build-arg MQTT_HOST=192.168.x.x --build-arg MQTT_PORT=1883 \ # --build-arg=MQTT_USERNAME=username --build-arg=MQTT_PASSWORD=hemligt -FROM alpine:edge as BUILD_ENV +FROM alpine:3.7 as BUILD_ENV COPY ./sparsnas_decode.cpp /build/ RUN apk add --no-cache g++ mosquitto-dev && \ g++ -o /build/sparsnas_decode -O2 -Wall /build/sparsnas_decode.cpp -lmosquitto -FROM alpine:edge +FROM alpine:3.7 ARG SENSORS ARG MQTT_HOST From b3ad5f1b62dbef5311ece834d56dffb13526e7dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jari=20Ylim=C3=A4inen?= Date: Sat, 20 Jul 2019 09:52:02 +0300 Subject: [PATCH 71/82] Reorder commands for better caching --- Dockerfile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 70e235d..7183956 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,14 +23,15 @@ ENV MQTT_PASSWORD=$MQTT_PASSWORD RUN : "${SENSORS:?Build argument 'SENSORS' needs to be set and non-empty.}" -COPY --from=BUILD_ENV /build/sparsnas_decode /usr/bin/ -COPY sparsnas.sh / - RUN apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing/ --allow-untrusted \ rtl-sdr \ mosquitto-libs++ \ zsh +COPY --from=BUILD_ENV /build/sparsnas_decode /usr/bin/ + +COPY sparsnas.sh / + RUN sed -i "s/^SENSORS=.*/SENSORS=(${SENSORS})/" /sparsnas.sh ENTRYPOINT ["/sparsnas.sh"] From 5bfc0499cf21678ce73793d38b44c142decb81d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jari=20Ylim=C3=A4inen?= Date: Wed, 24 Jul 2019 09:26:45 +0300 Subject: [PATCH 72/82] Remove empty line Co-Authored-By: Fredrik Erlandsson --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7183956..9b1c95a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,6 @@ RUN apk add --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/te zsh COPY --from=BUILD_ENV /build/sparsnas_decode /usr/bin/ - COPY sparsnas.sh / RUN sed -i "s/^SENSORS=.*/SENSORS=(${SENSORS})/" /sparsnas.sh From 058d10a707aa36aa086e1870faf01f6ef10c62ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jari=20Ylim=C3=A4inen?= Date: Wed, 24 Jul 2019 09:30:36 +0300 Subject: [PATCH 73/82] Remove deprecated -t parameter --- sparsnas.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sparsnas.sh b/sparsnas.sh index d015a03..88cc682 100755 --- a/sparsnas.sh +++ b/sparsnas.sh @@ -55,7 +55,7 @@ if ( frequencies ); then eval $CMD else echo "Need to find frequencies" - busybox timeout -t 45 $RTL_SDR > /tmp/sparsnas.raw + busybox timeout 45 $RTL_SDR > /tmp/sparsnas.raw #ls -l /tmp/sparsnas.raw F=`findfreq` # echo $F From 0b717342f72de5f0a3a71dd582714b39f5105eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jari=20Ylim=C3=A4inen?= Date: Wed, 24 Jul 2019 09:36:33 +0300 Subject: [PATCH 74/82] Revert "Pin Alpine version to 3.7" This reverts commit a3d402383a544ed364a13b0dc5524d4e12b40dfb. --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9b1c95a..330adb8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,14 +2,14 @@ # --build-arg MQTT_HOST=192.168.x.x --build-arg MQTT_PORT=1883 \ # --build-arg=MQTT_USERNAME=username --build-arg=MQTT_PASSWORD=hemligt -FROM alpine:3.7 as BUILD_ENV +FROM alpine:edge as BUILD_ENV COPY ./sparsnas_decode.cpp /build/ RUN apk add --no-cache g++ mosquitto-dev && \ g++ -o /build/sparsnas_decode -O2 -Wall /build/sparsnas_decode.cpp -lmosquitto -FROM alpine:3.7 +FROM alpine:edge ARG SENSORS ARG MQTT_HOST From 2ec8b74a312d794797ef071a1da34a247adbe4fc Mon Sep 17 00:00:00 2001 From: tubalainen Date: Sun, 4 Aug 2019 21:13:40 +0200 Subject: [PATCH 75/82] Clarification on sender id --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 01c09c1..6735560 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ lost at least 24 bytes How to configure the decoder: ------------- -Take a photo/make a note of your transmitter ID number underneeth the battery lid of the transmitter (not the display) +Take a photo/make a note of your transmitter ID number underneeth the battery lid of the transmitter (not the display. Obmit the 400 and take the remaining eight numbers and make a note of them. Edit sparsnas.sh to setup your sender id. You are allowed to have unlimitied number of senders and the format is `transmitter_id pulses/kWh` followed by a space. From e7dbb8fc8842c0073cae7996c62b63c52ecd0e41 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Sun, 4 Aug 2019 21:14:27 +0200 Subject: [PATCH 76/82] Six not eight .. TTB .. Time To Bed! ;) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6735560..c4fc218 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ lost at least 24 bytes How to configure the decoder: ------------- -Take a photo/make a note of your transmitter ID number underneeth the battery lid of the transmitter (not the display. Obmit the 400 and take the remaining eight numbers and make a note of them. +Take a photo/make a note of your transmitter ID number underneeth the battery lid of the transmitter (not the display. Obmit the 400 and take the remaining six numbers and make a note of them. Edit sparsnas.sh to setup your sender id. You are allowed to have unlimitied number of senders and the format is `transmitter_id pulses/kWh` followed by a space. From e71ab2988901426940860314e1349dead33e1dc3 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Fri, 12 Jun 2020 08:02:07 +0200 Subject: [PATCH 77/82] Updated --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c4fc218..c99d403 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ https://github.com/strigeus/sparsnas_decoder This is a fork is adapted for output in JSON format to a MQTT broker. +[![buy me and @fredrike a coffee](https://www.buymeacoffee.com/assets/img/custom_images/yellow_img.png)](https://www.buymeacoffee.com/tubalainen) + If you want to implement the same thing as below into an ESP8266 please see the following project: https://github.com/bphermansson/EspSparsnasGateway If you want to get into the bits and bytes of the system please see: From 1a9577b7f33e6fd1b46f0140f5c36f2def9cfee4 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Fri, 12 Jun 2020 08:02:54 +0200 Subject: [PATCH 78/82] Update again --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c99d403..a8ba6ff 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,8 @@ https://github.com/strigeus/sparsnas_decoder This is a fork is adapted for output in JSON format to a MQTT broker. -[![buy me and @fredrike a coffee](https://www.buymeacoffee.com/assets/img/custom_images/yellow_img.png)](https://www.buymeacoffee.com/tubalainen) +If you want to buy me and @fredrike a coffee, please feel free. +[![buy me a coffee](https://www.buymeacoffee.com/assets/img/custom_images/yellow_img.png)](https://www.buymeacoffee.com/tubalainen) If you want to implement the same thing as below into an ESP8266 please see the following project: https://github.com/bphermansson/EspSparsnasGateway From 50f6fb8d6889104f03397928f070409fe77db461 Mon Sep 17 00:00:00 2001 From: tubalainen Date: Fri, 12 Jun 2020 08:03:14 +0200 Subject: [PATCH 79/82] Gah --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index a8ba6ff..abf9134 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ https://github.com/strigeus/sparsnas_decoder This is a fork is adapted for output in JSON format to a MQTT broker. -If you want to buy me and @fredrike a coffee, please feel free. [![buy me a coffee](https://www.buymeacoffee.com/assets/img/custom_images/yellow_img.png)](https://www.buymeacoffee.com/tubalainen) If you want to implement the same thing as below into an ESP8266 please see the following project: https://github.com/bphermansson/EspSparsnasGateway From 9271be6ac8a9fdae10cd5c0f7985f509efef5d52 Mon Sep 17 00:00:00 2001 From: parhedberg Date: Wed, 8 Sep 2021 17:35:58 +0200 Subject: [PATCH 80/82] Some config generates errors Sensor mqtt does not seem to have an "hidden" attribute. Also group does not have view? At least it is throwing errors in config validation. Source: https://www.home-assistant.io/integrations/sensor.mqtt/ https://www.home-assistant.io/integrations/group/ --- home-assistant/sparsnas_mqtt.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/home-assistant/sparsnas_mqtt.yaml b/home-assistant/sparsnas_mqtt.yaml index f99042f..2475923 100644 --- a/home-assistant/sparsnas_mqtt.yaml +++ b/home-assistant/sparsnas_mqtt.yaml @@ -44,32 +44,27 @@ sensor: name: "Sparsnäs energy consumption over time" unit_of_measurement: "kWh" value_template: '{{ value_json.kWh | round(1) }}' - hidden: true icon: mdi:flash-circle - platform: mqtt state_topic: !secret sparsnas_sensor name: "Sparsnäs Battery remaining" unit_of_measurement: "%" value_template: '{{ value_json.battery | round(1) }}' - hidden: true icon: mdi:battery-outline - platform: mqtt state_topic: !secret sparsnas_sensor name: "Sparsnäs Frequency Error" unit_of_measurement: "%" value_template: '{{ value_json.FreqErr }}' - hidden: true icon: mdi:signal - platform: mqtt name: "Sparsnäs template kwh sensor day" state_topic: "template/kwh/day" icon: mdi:flash-circle - hidden: true - platform: mqtt name: "Sparsnäs template kwh sensor month" state_topic: "template/kwh/month" icon: mdi:flash-circle - hidden: true - platform: template sensors: kwh_current_month: @@ -163,7 +158,6 @@ automation: group: sparsnas: name: Energy Consumption - view: no icon: mdi:flash-circle entities: - sensor.sparsnas_energy_consumption_momentary From 95bc53d77cc02188b70c106f65fe4f4c5bc47b63 Mon Sep 17 00:00:00 2001 From: parhedberg Date: Thu, 9 Sep 2021 10:22:45 +0200 Subject: [PATCH 81/82] Enable energy dashboard Update to be able to use the entitys in Energy Dashboard --- home-assistant/sparsnas_mqtt.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/home-assistant/sparsnas_mqtt.yaml b/home-assistant/sparsnas_mqtt.yaml index f99042f..fe49c1d 100644 --- a/home-assistant/sparsnas_mqtt.yaml +++ b/home-assistant/sparsnas_mqtt.yaml @@ -46,6 +46,8 @@ sensor: value_template: '{{ value_json.kWh | round(1) }}' hidden: true icon: mdi:flash-circle + device_class: energy + state_class: total_increasing - platform: mqtt state_topic: !secret sparsnas_sensor name: "Sparsnäs Battery remaining" From 19d57be524a2d26b41e21e6c28f0976e21718f1e Mon Sep 17 00:00:00 2001 From: tubalainen Date: Tue, 7 Dec 2021 09:37:13 +0100 Subject: [PATCH 82/82] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index abf9134..99daad6 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +**This repository has been retired. Please use alternative forks.** + +******************************************************************* + + A BIG thanks to the https://www.facebook.com/groups/SHgruppen on Facebook and a special superduper thanks to the core coder @strigeus! https://github.com/strigeus/sparsnas_decoder