├── pics
├── 00.png
├── 01.png
├── 02.png
├── 03.png
├── 04a.png
├── 05.png
├── 06.png
├── 07.png
├── 08.png
├── 09.png
├── 10.png
└── 11.png
├── Dockerfile
├── loxone2influxdb.service
├── test
└── test_Loxone2InfluxDB.py
├── README.md
├── 2.Virtual output.md
├── Loxone2InfluxDB.py
└── 1.UDP logger.md
/pics/00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/budulinek/easy-loxone-influx/HEAD/pics/00.png
--------------------------------------------------------------------------------
/pics/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/budulinek/easy-loxone-influx/HEAD/pics/01.png
--------------------------------------------------------------------------------
/pics/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/budulinek/easy-loxone-influx/HEAD/pics/02.png
--------------------------------------------------------------------------------
/pics/03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/budulinek/easy-loxone-influx/HEAD/pics/03.png
--------------------------------------------------------------------------------
/pics/04a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/budulinek/easy-loxone-influx/HEAD/pics/04a.png
--------------------------------------------------------------------------------
/pics/05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/budulinek/easy-loxone-influx/HEAD/pics/05.png
--------------------------------------------------------------------------------
/pics/06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/budulinek/easy-loxone-influx/HEAD/pics/06.png
--------------------------------------------------------------------------------
/pics/07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/budulinek/easy-loxone-influx/HEAD/pics/07.png
--------------------------------------------------------------------------------
/pics/08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/budulinek/easy-loxone-influx/HEAD/pics/08.png
--------------------------------------------------------------------------------
/pics/09.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/budulinek/easy-loxone-influx/HEAD/pics/09.png
--------------------------------------------------------------------------------
/pics/10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/budulinek/easy-loxone-influx/HEAD/pics/10.png
--------------------------------------------------------------------------------
/pics/11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/budulinek/easy-loxone-influx/HEAD/pics/11.png
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3-alpine
2 |
3 | WORKDIR /usr/src/app
4 |
5 | RUN pip install --no-cache-dir influxdb
6 |
7 | COPY . .
8 |
9 | CMD [ "python", "./Loxone2InfluxDB.py" ]
10 |
--------------------------------------------------------------------------------
/loxone2influxdb.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Loxone to InfluxDB Service
3 | After=network.target
4 |
5 | [Service]
6 | SyslogIdentifier=loxone2influxdb
7 | Type=simple
8 | Environment=PYTHONUNBUFFERED=1
9 | Restart=always
10 |
11 | # EDIT PATH TO THE PYTHON SCRIPT!
12 | ExecStart=/usr/bin/python /etc/loxone2influxdb/Loxone2InfluxDB.py
13 |
14 | [Install]
15 | WantedBy=multi-user.target
16 |
--------------------------------------------------------------------------------
/test/test_Loxone2InfluxDB.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from dateutil import tz
3 | from Loxone2InfluxDB import parse_log_data
4 |
5 |
6 | class ParsingTestCase(unittest.TestCase):
7 |
8 | def test_parsing_simple(self):
9 | result = parse_log_data('2020-09-10 19:46:20;Bedroom temperature;23.0'.encode(), tz.tzutc(), tz.tzutc())
10 | self.assertEqual({
11 | 'time': '2020-09-10T19:46:20Z',
12 | 'measurement': 'Bedroom temperature',
13 | 'fields': {'value': 23.0},
14 | 'tags': {'Source': 'Loxone', 'Tag_1': '', 'Tag_2': '', 'Tag_3': ''},
15 | }, result[0])
16 |
17 | def test_parsing_values__notags_vs_tags(self):
18 | result = parse_log_data('2020-09-10 19:46:20;TEMP;-3.8'.encode(), tz.tzutc(), tz.tzutc())
19 | self.assertEqual(-3.8, result[0]['fields']['value'])
20 | result = parse_log_data('2020-09-10 19:46:20;TEMP;-3.35;MyTag'.encode(), tz.tzutc(), tz.tzutc())
21 | self.assertEqual(-3.35, result[0]['fields']['value'])
22 |
23 | def test_parsing_different_values(self):
24 | result = parse_log_data('2020-09-10 19:46:20;Bedroom temperature;22'.encode(), tz.tzutc(), tz.tzutc())
25 | self.assertEqual(22, result[0]['fields']['value'])
26 |
27 |
28 | if __name__ == '__main__':
29 | unittest.main()
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # easy-loxone-influx
2 |
3 |
4 | ## What is it good for?
5 |
6 | This tutorial shows you how to easily send Loxone statistics to InfluxDB 1.8 timescale database through UDP messages. Once you have your data stored InfluxDB, you can visualize them with Grafana.
7 |
8 |
9 |
10 | ## How to connect Loxone to InfluxDB?
11 |
12 | In contrast to most other solutions and tutorials, this one does not use websockets for sending statistics from Loxone, but simple UDP (or HTTP) messages. It is very easy to setup and use. The configuration of individual measurements and statistics is done through Loxone Config.
13 |
14 | There are essentially two solutions. Both of them allow you to set measurement names and tags for your statistics within Loxone Config. Choose for yourself and go to more detailed tutorial below:
15 |
16 | ### 1. UDP logger
17 |
18 | **Pros:**
19 |
20 | * Easily attach UDP Logger to most objects in Loxone Config (no need to draw program in Loxone Config).
21 |
22 | **Cons:**
23 |
24 | * Needs external Python script.
25 | * Messages sent by Loxone utilize custom syntax.
26 | * Loxone logger can only send statistics via UDP, not HTTP(S).
27 |
28 | Here is a quick look at how it works:
29 |
30 |
31 |
32 | For detailed instructions, go to a [UDP LOGGER -> INFLUXDB TUTORIAL](https://github.com/budulinek/easy-loxone-influx/blob/master/1.UDP%20logger.md) .
33 |
34 | ### 2. Virtual output
35 |
36 | **Pros:**
37 |
38 | * Direct connection between Loxone and InfluxDB (no external script or third-party software).
39 | * Messages sent by Loxone comply to [InfluxDB line protocol](https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_tutorial/).
40 | * Virtual output can send statistics either via UDP or HTTP(S) messages.
41 |
42 | **Cons:**
43 |
44 | * Requires you to draw a program in Loxone Config.
45 |
46 | Here is a quick look at how it works:
47 |
48 |
49 |
50 | For detailed instructions, go to a [VIRTUAL OPUTPUT -> INFLUXDB TUTORIAL](https://github.com/budulinek/easy-loxone-influx/blob/master/2.Virtual%20output.md) .
51 |
--------------------------------------------------------------------------------
/2.Virtual output.md:
--------------------------------------------------------------------------------
1 | This tutorial allows you to send Loxone statistics directly to InfluxDB using Virtual Outputs.
2 |
3 | ### 1. Configure UDP / HTTP / HTTPS listeners in InfluxDB
4 |
5 | Be aware that this tutorial is intended for InfluxDB 1.8. The new version InfluxDB 2.0 does not use influxdb.conf config file (but WebGUI interface), it also does not have built-in UDP and HTTPS listeners (but requires Telegraph for parsing feeding data to the database).
6 |
7 | #### Option 1: UDP
8 |
9 | UDP listener is disabled by default. Edit `/etc/influxdb/influxdb.conf` and enable the UDP listener, set the port for listener and the name of the database that you want to write to:
10 |
11 | ```
12 | [[udp]]
13 | enabled = true
14 | bind-address = ":8089"
15 | database = "loxone"
16 | ```
17 |
18 | See InfluxDB [config manual](https://docs.influxdata.com/influxdb/v1.8/administration/config/#udp-settings) for more details.
19 |
20 | #### Option 2: HTTP
21 |
22 | HTTP endpoint should work out of the box.
23 |
24 | #### Option 3: HTTPS
25 |
26 | If you have Loxone Miniserver 2. generation and you want (or need) to encrypt traffic between Loxone and the InfluxDB server, just follow their [official documentation](https://docs.influxdata.com/influxdb/v1.8/administration/https_setup/) (not tested by me).
27 |
28 | ### 2. Configure Virtual Output in Loxone
29 |
30 | Loxone Config > Miniserver > Virtual Outputs > Create new Virtual Output
31 |
32 | #### UDP:
33 |
34 | `Address` `/dev/udp/192.168.1.22/8089`
35 |
36 |
37 |
38 | #### HTTP(S):
39 |
40 | For HTTP(S) address Loxone uses slightly different syntax:
41 |
42 | `Address` ` http://192.168.1.22:8086 `
43 |
44 | ### 3. Configure Virtual Output Command
45 |
46 | Loxone Config > Miniserver > Virtual Outputs > Create new Virtual Output Command
47 |
48 | #### UDP:
49 |
50 | Specify `Command for ON` and `Command for OFF`, which should correspond to [InfluxDB line protocol](https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_tutorial/):
51 |
52 | `,= =`
53 |
54 | Do not forget space between measurement (tag) and field. At the same time all spaces in tag keys, tag values, and field keys must be escaped with `\`. So the `Command for ON` can look like this:
55 |
56 | `Temperature,room=Living\ room value=`
57 |
58 | But this way you would need unique Virtual Output Command for each measurement and tag. So it is better to specify only measurement name in the Virtual Output Command and fill the rest (tag and field) in Loxone schema:
59 |
60 | `Temperature,room=`
61 |
62 |
63 |
64 | #### HTTP(S):
65 |
66 | This is slightly different. The InfluxDB line protocol should go into `HTTP Post command for ON` and `HTTP Post command for OFF`.
67 |
68 | The `Command for ON` and `Command for OFF`, contain URL endpoint. Here we need to specify database name. For UDP listener, the database is specified in `/etc/influxdb/influxdb.conf` , but for HTTP we have to supply the database name, user name and password through URL (see the [documentation](https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_tutorial/#getting-data-in-the-database) for more details):
69 |
70 | `/write?db=loxone&precision=s&u=your_user&p=your_password`
71 |
72 |
73 |
74 | ### 4. Loxone Config program
75 |
76 | Now it is time to draw in Loxone Config. We will build the rest of the UDP message (tag name and value) dynamically, through the `Status` block. We can thus use one Virtual Output Command as a "connector" to write statistics data from multiple sensors (differentiated by tag value).
77 |
78 |
79 |
80 | All `Status` blocks are identical (except for the name), so you can easily copy-paste them in your program. And edit only their name. `Status` blocks contain a single rule with the following `Status-text`:
81 |
82 | ` value=`
83 |
84 |
85 |
86 | As you can see, the tag is specified through block name. But remember, you have to follow the rules of the to [InfluxDB line protocol](https://docs.influxdata.com/influxdb/v1.8/write_protocols/line_protocol_tutorial/). Do not forget to escape any spaces in the block name with backslash `\`.
87 |
88 | That is it! You have successfully connected all your temperature sensors with InfluxDB. Whenever one of the temperature sensors changes its value, new datapoint is pushed to InfluxDB.
89 |
90 | You can now go ahead and create another Virtual Output Command for another measurement (for example humidity) and again, you can connect all your humidity sensors to one Virtual Output Command. This approach with "shared" Virtual Output Command works as long as sensor values do not change at the same time. If you have analog or digital data which change at EXACTLY same moment, you would need different approach (it is doable, but beyond the scope of this tutorial).
91 |
92 |
--------------------------------------------------------------------------------
/Loxone2InfluxDB.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 |
3 | # Simple script to import Loxone UDP logs into InfluxDB
4 | import os
5 | import socket
6 | import json
7 | import argparse
8 | import re
9 | import logging
10 | from influxdb import InfluxDBClient
11 | from datetime import datetime
12 | from dateutil import tz
13 | # suppress warnings for unverified https request
14 | import urllib3
15 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
16 |
17 | # =============== Configuration =====================
18 | # hostname and port of InfluxDB http API
19 | host = os.getenv('INFLUXDB_HOST', '127.0.0.1')
20 | port = int(os.getenv('INFLUXDB_PORT', 8086))
21 | # use https for connection to InfluxDB
22 | ssl = bool(os.getenv('INFLUXDB_HTTPS', False))
23 | # verify https connection
24 | verify = bool(os.getenv('INFLUXDB_VERIFY_HTTPS', False))
25 | # InfluxDB database name
26 | dbname = os.getenv('INFLUXDB_DATABASE', 'loxone')
27 | # InfluxDB login credentials (optional, specify if you have enabled authentication)
28 | dbuser = os.getenv('INFLUXDB_USER')
29 | dbuser_code = os.getenv('INFLUXDB_PASSWORD')
30 | # local IP and port where the script is listening for UDP packets from Loxone
31 | localIP = os.getenv('BIND_ADDRESS', '0.0.0.0')
32 | localPort = int(os.getenv('BIND_PORT', 2222))
33 |
34 |
35 | def parse_log_data(data, from_zone, to_zone, debug=False):
36 | """
37 | Parse received message
38 | Syntax: ;;:;;;
39 | Example: "2020-09-10 19:46:20;Bedroom temperature;23.0"
40 | ** TO DO ** Need to add checks in case something goes wrong
41 | """
42 | logging.debug('Received: %s', data)
43 |
44 | end_timestamp = data.find(b';')
45 | end_name = data.find(b';', end_timestamp+1)
46 | end_alias = data.find(b':', end_name+1)
47 | if end_alias < 0: # -1 means not found
48 | end_alias = end_name
49 | end_value = data.find(b';', end_alias+1)
50 |
51 | if end_value < 0: # ; char not found after value
52 | end_tag1 = 0
53 | end_value = len(data)
54 | else:
55 | end_tag1 = data.find(b';', end_value + 1)
56 |
57 | if end_tag1 > 0:
58 | end_tag2 = data.find(b';', end_tag1+1)
59 | else:
60 | end_tag2 = 0
61 | if end_tag2 > 0:
62 | end_tag3 = data.find(b';', end_tag2+1)
63 | else:
64 | end_tag3 = 0
65 | numeric_const_pattern = r'[-+]? (?: (?: \d* \. \d+ ) | (?: \d+ \.? ) )(?: [Ee] [+-]? \d+ ) ?'
66 | rx = re.compile(numeric_const_pattern, re.VERBOSE)
67 |
68 | # Timestamp Extraction
69 | parsed_data = {'TimeStamp': data[0:end_timestamp]}
70 | parsed_data['TimeStamp'] = parsed_data['TimeStamp'].replace(b' ', b'T')+b'Z'
71 | # Timezone conversion to UTC
72 | local = datetime.strptime(parsed_data['TimeStamp'].decode('utf-8'), b'%Y-%m-%dT%H:%M:%SZ'.decode('utf-8'))
73 | local = local.replace(tzinfo=from_zone)
74 | utc = local.astimezone(to_zone)
75 | parsed_data['TimeStamp'] = utc.strftime('%Y-%m-%dT%H:%M:%SZ')
76 |
77 | # Name Extraction
78 | parsed_data['Name'] = data[end_timestamp+1:end_name]
79 |
80 | # Alias Extraction
81 | if end_alias != end_name:
82 | parsed_data['Name'] = data[end_name+1:end_alias]
83 |
84 | # Value Extraction
85 | parsed_data['Value'] = rx.findall(data[end_alias+1:end_value].decode('utf-8'))[0]
86 |
87 | # Tag_1 Extraction
88 | parsed_data['Tag_1'] = data[end_value+1:end_tag1].rstrip()
89 |
90 | # Tag_2 Extraction
91 | parsed_data['Tag_2'] = data[end_tag1+1:end_tag2].rstrip()
92 |
93 | # Tag_3 Extraction
94 | parsed_data['Tag_3'] = data[end_tag2+1:end_tag3].rstrip()
95 |
96 | # Create Json body for Influx
97 | json_body = [
98 | {
99 | "measurement": parsed_data['Name'].decode('utf-8'),
100 | "tags": {
101 | "Tag_1": parsed_data['Tag_1'].decode('utf-8'),
102 | "Tag_2": parsed_data['Tag_2'].decode('utf-8'),
103 | "Tag_3": parsed_data['Tag_3'].decode('utf-8'),
104 | "Source": "Loxone",
105 | },
106 | "time": parsed_data['TimeStamp'], # "2009-11-10T23:00:00Z",
107 | "fields": {
108 | "value": float(parsed_data['Value'])
109 | }
110 | }
111 | ]
112 |
113 | if debug:
114 | logging.debug(json.dumps(json_body, indent=2))
115 |
116 | return json_body
117 |
118 |
119 | def main(host, port, ssl, verify, debug=False):
120 | """Instantiate a connection to the InfluxDB and stard listening on UDP port for incoming messages"""
121 |
122 | logging.basicConfig(level=logging.DEBUG if debug else logging.INFO,
123 | format='%(asctime)s %(levelname)s - %(message)s')
124 | logging.info('Creating InfluxDB client - connection: %s:%s, db: %s', host, port, dbname)
125 |
126 | client = InfluxDBClient(host, port, dbuser, dbuser_code, dbname, ssl, verify)
127 |
128 | # get TZ info
129 | to_zone = tz.tzutc()
130 | from_zone = tz.tzlocal()
131 |
132 | # A UDP server
133 | # Set up a UDP server
134 | logging.info('Listening for incoming Loxone UDP packets on %s:%s', localIP, localPort)
135 | udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
136 |
137 | # Listen on local port
138 | # (to all IP addresses on this system)
139 | listen_addr = (localIP, localPort)
140 | udp_sock.bind(listen_addr)
141 | logging.debug('Socket attached')
142 |
143 | # Report on all data packets received and
144 | # where they came from in each case (as this is
145 | # UDP, each may be from a different source and it's
146 | # up to the server to sort this out!)
147 | while True:
148 | data, addr = udp_sock.recvfrom(1024)
149 | json_body_log = parse_log_data(data, from_zone, to_zone, debug)
150 | # Write to influx DB
151 | client.write_points(json_body_log)
152 |
153 |
154 | def parse_args():
155 | """Parse the args."""
156 | parser = argparse.ArgumentParser(
157 | add_help=False, description='Simple Loxone to InfluxDB script')
158 | parser.add_argument('-h', '--host', type=str, required=False,
159 | default=host,
160 | help='hostname of InfluxDB http API')
161 | parser.add_argument('-p', '--port', type=int, required=False, default=port,
162 | help='port of InfluxDB http API')
163 | parser.add_argument('-s', '--ssl', default=ssl, action="store_true",
164 | help='use https to connect to InfluxDB')
165 | parser.add_argument('-v', '--verify', default=verify, action="store_true",
166 | help='verify https connection to InfluxDB')
167 | parser.add_argument('-d', '--debug', action="store_true", default=bool(os.getenv('DEBUG', False)),
168 | help='debug code')
169 | parser.add_argument('-?', '--help', action='help',
170 | help='show this help message and exit')
171 | return parser.parse_args()
172 |
173 |
174 | if __name__ == '__main__':
175 | args = parse_args()
176 | main(host=args.host, port=args.port, ssl=args.ssl, verify=args.verify, debug=args.debug)
177 |
--------------------------------------------------------------------------------
/1.UDP logger.md:
--------------------------------------------------------------------------------
1 | This tutorial allows you to send Loxone statistics to InfluxDB using UDP Logger. You need a Python script running on your server.
2 |
3 | ### 1. Run Python script
4 |
5 | This is just a polished version of a script from Jan De Bock (https://groups.google.com/forum/#!topic/loxone-english/ijOUU8FHMKA ). Big thanks!
6 |
7 | The script runs on your server, parses Loxone UDP Logger messages (which use custom syntax) and pushes the data into InfluxDB database.
8 |
9 | There are two options how to deploy this Python script: as a systemd service or as a Docker container.
10 |
11 | #### Option 1. Run as systemd service
12 |
13 | * **Make sure you have Python and other required packages**
14 |
15 | ```
16 | apt update
17 | apt install python python-influxdb libfile-slurp-perl libfile-touch-perl libjson-perl libhttp-tiny-perl
18 | ```
19 |
20 | * **Download and configure the script**
21 | Configuration at the beginning of the script:
22 |
23 | ```
24 | # hostname and port of InfluxDB http API
25 | host = '127.0.0.1'
26 | port = 8086
27 | # use https for connection to InfluxDB
28 | ssl = False
29 | # verify https connection
30 | verify = False
31 | # InfluxDB database name
32 | dbname = 'loxone'
33 | # InfluxDB login credentials (optional, specify if you have enabled authentication)
34 | dbuser = 'some_user'
35 | dbuser_code = 'some_password'
36 | # local IP and port where the script is listening for UDP packets from Loxone
37 | localIP = '192.168.1.222'
38 | localPort = 2222
39 | ```
40 |
41 | Alternatively, you can specify some of these variables via optional command line arguments.
42 |
43 | * **Test the script manually**
44 |
45 | ```
46 | usage: Loxone2InfluxDB.py [-h HOST] [-p PORT] [-s] [-v] [-d] [-?]
47 |
48 | Simple Loxone to InfluxDB script
49 |
50 | optional arguments:
51 | -h HOST, --host HOST hostname of InfluxDB http API
52 | -p PORT, --port PORT port of InfluxDB http API
53 | -s, --ssl use https to connect to InfluxDB
54 | -v, --verify verify https connection to InfluxDB
55 | -d, --debug debug code
56 | -?, --help show this help message and exit
57 | ```
58 |
59 | Use debug argument for testing.
60 |
61 | * **Create systemd service**
62 |
63 | If everything works as expected, . You can use ```loxone2influxdb.service``` provided here as a template. Copy (or symlink) the file to ```/etc/systemd/system``` and run ```systemctl enable loxone2influxdb```
64 |
65 | Remember - the script is not failsafe. It will simply crash if it receives data in wrong format. So you really need systemd service with both autostart and restart after failure.
66 |
67 |
68 | #### Option 2. Run as Docker container
69 |
70 | 1. Build Docker image
71 |
72 | ```
73 | docker build -t easy-loxone-influx .
74 | ```
75 |
76 | 1. Run container using env variables for configuration. Example:
77 |
78 | ```
79 | docker run --rm -ti -e INFLUXDB_HOST=192.168.1.10 -e TZ=Europe/Prague -p 2222:2222/udp easy-loxone-influx
80 | ```
81 |
82 | _Note:_ Beware of different timezones. Loxone sends timestamps in local time omiting timezone information.
83 | Use `TZ` env variable to set the same timezone as Loxone is running inside the alpine container.
84 |
85 | * `TZ` - set container timezone
86 | * `INFLUXDB_HOST` - hostname of InfluxDB http API (default: 127.0.0.1)
87 | * `INFLUXDB_PORT` - port of InfluxDB http API (default: 8086)
88 | * `INFLUXDB_HTTPS` - use HTTP with SSL/TLS for InflixDB API communication (true/false) (default: false)
89 | * `INFLUXDB_VERIFY_HTTPS` - verify SSL/TLS certificate (true/false) (default: false)
90 | * `INFLUXDB_DATABASE` - InfluxDB database name (default: loxone)
91 | * `INFLUXDB_USER` - InfluxDB http API user
92 | * `INFLUXDB_PASSWORD` - InfluxDB http API password
93 | * `BIND_ADDRESS` - the address to listen on for UDP packets (default: 0.0.0.0)
94 | * `BIND_PORT` - the port to listen on for UDP packets (default: 2222)
95 |
96 |
97 | ### 2. Configure UDP Logger in Loxone
98 |
99 | Loxone Config > Miniserver > Messages > Create new Logger
100 | `Address of logger` `/dev/udp/192.168.1.222/2222`
101 | (insert IP and port where the script is listening for UDP packets)
102 |
103 |
104 |
105 | ### 3. Assign logger to a periphery or block in Loxone
106 |
107 | All peripheries (inputs and outputs) and most block allow you to assign a logger to them. Just look for `Logging/Mail/Call/Track` in the properties tab and assign your newly created logger here. Also set the value in `Message when ON/analogue changes` and `Message when OFF`.
108 |
109 |
110 |
111 | That's it! There is no need to mess with UUIDs, users and permissions. Within InfluxDB / Grafana, you will find your measurements under the `Name` (or `Description`) specified in Loxone Config.
112 |
113 | Now go ahead and add few more measurements! There are actually 2 ways how to do that:
114 |
115 | 1. Edit the `Logging/Mail/Call/Track` section in the properties tab of your periphery (input/output) or a block. This is a prefered solution which we use here in step 5.
116 | 2. Drag and drop the logger into your program (see step 8).
117 |
118 | If you have a lot of measurements assigned to a logger, it is a good idea to double check the names, aliases and tags of your measurements. Find your InfluxDB logger under the periphery tree, highlight all items attached to the logger and then hit `Edit shared properties...` in the properties tab.
119 |
120 |
121 |
122 | Here you will get a nice overview of all names (see columns `Name` and `Description`), aliases and tags (see columns `Message when ON/analogue changes` and `Message when Off`) of all measurements sent to InfluxDB. You can edit them. Make sure that each measurement is uniquely identified. Usually this is done through unique `Name` or `Description`, but if you "Drag and drop" the logger into your program, you must set custom alias.
123 |
124 | ### 4. Custom alias (optional)
125 |
126 | If you want to give your measurement in InfluxDB/Grafana some custom name (different from `Name` or `Description` you use in Loxone), just edit `Message when ON/analogue changes` and `Message when OFF`. Add your alias (followed by colon) before value, for example
127 |
128 |
129 |
130 | ### 5. Custom tags (optional)
131 |
132 | You can also add up to three custom tags to your measurements which will be shown in InfluxDB/Grafana as Tag_1, Tag_2 and Tag_3. Since Loxone UDP logger does not record Room or Category, you can use custom tags to add them manually. Again, edit `Message when ON/analogue changes` and `Message when OFF`. Add your tags after value, separated by semicolon. You can for example set Tag_1 and Tag_2:
133 |
134 | `;Lights;Kitchen`
135 |
136 | or just Tag_3:
137 |
138 | `;;;My house`
139 |
140 | ### 6. Periodic logging
141 |
142 | UDP logs are sent whenever the perifery (input or output) or block change their value. This is fine for analog values which change frequently. However, for some critical digital values (or analog values with infrequent changes) we need periodic checks in order to make our readings more reliable. Loxone does not offer periodic logging, but we can use a workaround with `Analogue Memory` and `Pulse Generator`. Grab your InfluxDB logger and build the following schema. Set the period in pulse generator and the attached logger will send the UDP log periodically, even if the digital or analog value attached to the memory does not change.
143 | However, there is one problem with this solution: Loxone will use the name of the logger (in our case "InfluxDB") as a measurement name in the UDP log message. Therefore, you MUST use alias in order to assign a unique name to your measurement (see step 6):
144 |
145 | 
146 |
147 | If your pulse generator is really fast, you will need to decrease `Minimum time interval`.
148 |
149 | Since we are configuring everything inside Loxone Config, you can even change the logging period dynamically. In the example above, we can for example set the loging period down to 10s and disable the pulse generator when the front door is closed. Or we can attach a more sofisticated program to the `TL` input (parameter) of the pulse generator: set the pulse period to 1 hour when the front door is closed and 10s when the door is open. With this setup, you will of course receive not only the scheduled UDP message, but also the UDP message on value change (door closed or open).
150 |
151 | ### 7. ... and some more advanced stuff for the brave ones ...
152 |
153 | The syntax of the UDP log message parsed by my python script looks like this:
154 |
155 | `;;:;;;`
156 |
157 | Timestamp is always set by Loxone. Measurement name is determined by Loxone, based on `Name` or `Description` in the properties of your periphery. Then we can manually set an alias, overriding the original measurement name (see step 6). Value is set dynamically, using the built-in syntax of the Loxone Config (for example `` - see step 5). Then we can manually set tags (see step 7).
158 |
159 | But Loxone Config actually allows you to build the whole UDP log message dynamically (with the sole exception of the timestamp), through the `Status` block. Each `Status` block has 4 inputs (AI1 - AI4) which accept digital, analog but also text data. Have a look at this configuration of the `Status` block (be careful about colons and semicolons):
160 |
161 |
162 |
163 | The config means that AI1 will be used as "original" measurement name, AI2 is an alias (overriding original name), AI3 is parsed as value and anything sent via AI4 (digital/analog/textual data) is parsed as Tag_1. Wondering how you set Tag_2 and Tag_3? No problem, `Status` block has a text output (`TQ`), so you can chain them. Link as many `Status` block as you want. Then assign an InfluxDB logger to the last `Status` block in the chain.
164 |
165 | In addition, you can set different `Status-text` for different combinations of conditions. So the composition of the UDP message will depend in the value of the inputs A1-A4 (you can use this feature to compose your own error messages).
--------------------------------------------------------------------------------