Sign inSign up

eddgrant/lan2rf-gateway-stats

By eddgrant

•Updated 11 days ago

A simple utility which obtains data from an Intergas LAN2RF device, and sends it to an InfluxDB endp

Image
Monitoring & observability
0

4.8K

eddgrant/lan2rf-gateway-stats repository overview

⁠lan2rf-gateway-stats

A simple utility which obtains data from an Intergas LAN2RF device, and sends it to InfluxDB.

⁠How to use it

The application is published on Docker Hub as the [https://hub.docker.com/r/eddgrant/lan2rf-gateway-stats⁠) image.

⁠Run InfluxDB locally

An InfluxDB endpoint is required to collect temperature data. You might have an InfluxDB setup already, but if not one can be easily created by running the following commands:

docker network create -d bridge lan2rf-gateway-stats
docker run --rm \
  --net=lan2rf-gateway-stats \
  -p 8086:8086 \
  -e DOCKER_INFLUXDB_INIT_MODE="setup" \
  -e DOCKER_INFLUXDB_INIT_USERNAME="my-very-secure-influxdb-username" \
  -e DOCKER_INFLUXDB_INIT_PASSWORD="my-very-secure-influxdb-password" \
  -e DOCKER_INFLUXDB_INIT_ORG="my-influxdb-org" \
  -e DOCKER_INFLUXDB_INIT_BUCKET="intergas" \
  -e DOCKER_INFLUXDB_INIT_RETENTION="1w" \
  -e DOCKER_INFLUXDB_INIT_ADMIN_TOKEN="my-very-secure-influxdb-token" \
  --name influxdb \
  influxdb:2.0

⁠Run lan2rf-gateway-stats

To run the lan2rf-gateway-stats Docker image run the following command:

docker run --rm \
  --net=lan2rf-gateway-stats \
  --env LAN2RF_CHECK_INTERVAL=30s \
  --env LAN2RF_URL="http://192.168.2.58" \
  --env INFLUXDB_ORG="my-influxdb-org" \
  --env INFLUXDB_BUCKET="intergas" \
  --env INFLUXDB_TOKEN="my-very-secure-influxdb-token" \
  --env INFLUXDB_URL="http://influxdb:8086?connectTimeout=5S&readTimeout=5S&writeTimeout=5S" \
    eddgrant/lan2rf-gateway-stats:local

Ensure that the InfluxDB variables match the ones used when setting up InfluxDB.

Ensure that you set a valid hostname / IP address for the LAN2RF_URL environment variable.

⁠Check the logs

lan2rf-gateway-stats should start and begin to log its output to the console:

 __  __ _                                  _   
|  \/  (_) ___ _ __ ___  _ __   __ _ _   _| |_ 
| |\/| | |/ __| '__/ _ \| '_ \ / _` | | | | __|
| |  | | | (__| | | (_) | | | | (_| | |_| | |_ 
|_|  |_|_|\___|_|  \___/|_| |_|\__,_|\__,_|\__|
18:54:27.573 [main] INFO  c.e.l.intergas.LAN2RFConfiguration - LAN2RF Configuration: source='lan2rf', room1Name='room1', room2Name='room2', checkInterval='PT30S', measurements.boiler='true', measurements.room1='true', measurements.room2='true'
18:54:27.578 [main] INFO  i.m.l.PropertiesLoggingLevelsConfigurer - Setting log level 'ERROR' for logger: 'com'
18:54:27.579 [main] INFO  i.m.l.PropertiesLoggingLevelsConfigurer - Setting log level 'INFO' for logger: 'com.eddgrant'
18:54:27.580 [main] INFO  i.m.l.PropertiesLoggingLevelsConfigurer - Setting log level 'ERROR' for logger: 'io'
18:54:27.463 [main] INFO  c.e.l.LAN2RFGatewayStatsApp - Starting subscription to LAN2RF data.
18:54:27.567 [main] INFO  c.e.l.LAN2RFGatewayStatsApp - Subscription to LAN2RF data created.
18:54:27.623 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 644ms. Server Running: http://9cbacfcd290f:8080

By default lan2rf-gateway-stats will check the status data every minute.

This can be altered by setting the LAN2RF_CHECK_INTERVAL environment variable e.g.

CHECKS_SCHEDULE_EXPRESSION=30s`

LAN2RF_CHECK_INTERVAL uses a duration format, so the following are all valid values:

LAN2RF_CHECK_INTERVAL=30s # Check every 30 seconds
LAN2RF_CHECK_INTERVAL=2m # Check every 2 minutes
LAN2RF_CHECK_INTERVAL=4h # Check every 4 hours

Each time the status data is sent to InfluxDB an INFO level log entry is written e.g.

19:00:25.478 [DefaultDispatcher-worker-1] INFO  c.e.l.p.influxdb.StatusDataPublisher - Status Data measurements sent at: 2025-11-04T19:00:25.443115704Z

⁠Measurement Configuration

By default, all measurements are published to InfluxDB. You can disable certain groups of measurements by setting the following environment variables to false:

  • LAN2RF_MEASUREMENTS_BOILER: Disable all boiler-related measurements.
  • LAN2RF_MEASUREMENTS_ROOM1: Disable all Room 1 measurements.
  • LAN2RF_MEASUREMENTS_ROOM2: Disable all Room 2 measurements.

For example, to disable Room 2 measurements, you would run the following command:

docker run --rm \
  --net=lan2rf-gateway-stats \
  --env LAN2RF_MEASUREMENTS_ROOM2=false \
  --env LAN2RF_CHECK_INTERVAL=30s \
  --env LAN2RF_URL="http://192.168.2.58" \
  --env INFLUXDB_ORG="my-influxdb-org" \
  --env INFLUXDB_BUCKET="intergas" \
  --env INFLUXDB_TOKEN="my-very-secure-influxdb-token" \
  --env INFLUXDB_URL="http://influxdb:8086?connectTimeout=5S&readTimeout=5S&writeTimeout=5S" \
    eddgrant/lan2rf-gateway-stats:local

⁠Authentication

By default, lan2rf-gateway-stats talks to the LAN2RF device anonymously. This matches the LAN2RF's default, where HTTP Basic Auth is disabled.

If you have enabled Basic Auth on your LAN2RF device, set both of the following environment variables to provide credentials:

  • LAN2RF_BASIC_AUTH_USERNAME
  • LAN2RF_BASIC_AUTH_PASSWORD

If only one is set, Basic Auth remains disabled. Example:

docker run --rm \
  --net=lan2rf-gateway-stats \
  --env LAN2RF_CHECK_INTERVAL=30s \
  --env LAN2RF_URL="http://192.168.2.58" \
  --env LAN2RF_BASIC_AUTH_USERNAME="my-lan2rf-user" \
  --env LAN2RF_BASIC_AUTH_PASSWORD="my-lan2rf-password" \
  --env INFLUXDB_ORG="my-influxdb-org" \
  --env INFLUXDB_BUCKET="intergas" \
  --env INFLUXDB_TOKEN="my-very-secure-influxdb-token" \
  --env INFLUXDB_URL="http://influxdb:8086?connectTimeout=5S&readTimeout=5S&writeTimeout=5S" \
    eddgrant/lan2rf-gateway-stats:local

You can verify the configuration by looking for the basicAuthEnabled field in the startup log line, e.g.

INFO  c.e.l.intergas.LAN2RFConfiguration - LAN2RF Configuration: source='lan2rf', ..., basicAuthEnabled='true'

If authentication fails (HTTP 401 or 403), the application logs an error message naming the two environment variables and will automatically retry on the next polling interval. The application does not crash on auth failure.

⁠:warning: Security warning — Basic Auth over plaintext HTTP

The LAN2RF does not support TLS, so all traffic between this application and the device — including HTTP Basic Auth credentials — travels over the network in plaintext. HTTP Basic Auth sends the Authorization: Basic <base64(username:password)> header on every request; base64 is encoding, not encryption. Anyone able to observe traffic on your LAN can trivially recover the credentials.

If you enable Basic Auth on your LAN2RF:

  • Only use it on a trusted, well-segmented network.
  • Treat the credentials as low-value and rotate them frequently.
  • Never reuse the password for any other system or account.

⁠The Data

The application publishes several types of measurements to InfluxDB, each representing a different aspect of the heating system's status.

⁠Measurements

All measurements have a source value which defaults to "lan2rf". This can be overridden by setting the value of the LAN2RF_SOURCE environment variable.

Below is a description of each measurement, its associated tags, and the data it holds.

Temperature

Tag (source)Tag (location)Tag (type)Field (value)Description
${LAN2RF_SOURCE:-"lan2rf"}central_heating, tap, room1, room2RECORDED, SETPOINT, SETPOINT_OVERRIDEThe temperature in degrees Celsius.Stores temperature readings, target setpoints, and temporary overrides for different parts of the system.

The location tags room1 and room2 use default names of room1 and room2. These can be overridden by setting the LAN2RF_ROOM1_NAME and LAN2RF_ROOM2_NAME environment variables.

Pressure

Tag (source)Tag (location)Field (value)Description
${LAN2RF_SOURCE:-"lan2rf"}central_heatingThe pressure in bar.Stores the water pressure of the central heating circuit.

OperationalStatus

Tag (source)Tag (name)Field (value)Description
${LAN2RF_SOURCE:-"lan2rf"}LOCKED_OUT, PUMP_ACTIVE, TAP_FUNCTION_ACTIVE, BURNER_ACTIVEtrue or falseStores the boolean on/off status for key operational components.

TextStatus

Tag (source)Tag (subject)Field (value)Description
${LAN2RF_SOURCE:-"lan2rf"}STATUS_DISPLAY_CODEA string representing the status (e.g., "Standby").Stores the human-readable status message displayed by the boiler.
⁠Example Flux Queries

You can use the InfluxDB UI or API to query this data. Here are some example Flux queries to get you started.

1. Get the last recorded central heating temperature:

from(bucket: "intergas")
  |> range(start: -1h)
  |> filter(fn: (r) => r["source"] == "lan2rf")
  |> filter(fn: (r) => r["_measurement"] == "Temperature")
  |> filter(fn: (r) => r["location"] == "central_heating")
  |> filter(fn: (r) => r["type"] == "RECORDED")
  |> last()

2. See when the burner has been active in the last hour:

from(bucket: "intergas")
  |> range(start: -1h)
  |> filter(fn: (r) => r["source"] == "lan2rf")
  |> filter(fn: (r) => r["_measurement"] == "OperationalStatus")
  |> filter(fn: (r) => r["subject"] == "BURNER_ACTIVE")
  |> filter(fn: (r) => r["_value"] == true)

TODO: I don't know how the LAN2RF reports this data. Does it require the burner to be live exactly when the data request is made, or does it remember if the burner has been active within a given timeframe, instead reporting that back? Need to understand this as this and the frequency at which data is collected could significantly influence reporting accuracy.

3. Retrieve the boiler's text status over the last 24 hours:

from(bucket: "intergas")
  |> range(start: -24h)
  |> filter(fn: (r) => r["source"] == "lan2rf")
  |> filter(fn: (r) => r["_measurement"] == "TextStatus")
  |> filter(fn: (r) => r["subject"] == "STATUS_DISPLAY_CODE")
  |> distinct(column: "_value")

4. Graph the recorded room temperature vs. its setpoint and/or setpoint override for "room1":

from(bucket: "intergas")
  |> range(start: -12h)
  |> filter(fn: (r) => r["source"] == "lan2rf")
  |> filter(fn: (r) => r["_measurement"] == "Temperature")
  |> filter(fn: (r) => r["location"] == "room1")
  |> filter(fn: (r) => r["type"] == "RECORDED" or r["type"] == "SETPOINT" or r["type"] == "SETPOINT_OVERRIDE")
  |> pivot(rowKey:["_time"], columnKey: ["type"], valueColumn: "_value")

5. View all data points for each collection interval:

This query groups all measurements by their timestamp, creating a wide table that shows the complete state of the system for each time the application published data.

from(bucket: "intergas")
  |> range(start: -10m)
  |> filter(fn: (r) => r["source"] == "lan2rf")
  |> pivot(
      rowKey:["_time"],
      columnKey: ["_measurement"],
      valueColumn: "_value"
  )
  |> sort(columns: ["_time"], desc: true)

⁠Debugging

If you want to see what data is being sent to InfluxDB, without having to query InfluxDB itself. You can configure DEBUG level logging on the StatusDataPublisher logger. This can be done by adding the following environment variable to the Docker run command:

--env LOGGER_LEVELS_COM_EDDGRANT_LAN2RFGATEWAYSTATS_PERSISTENCE_INFLUXDB_StatusDataPublisher=DEBUG

This will emit a log entry containing the measurement data, each time it is sent to InfluxDB e.g.

19:29:40.034 [DefaultDispatcher-worker-1] DEBUG c.e.l.p.influxdb.StatusDataPublisher - Measurements: [
Temperature(source=lan2rf, location=central_heating, value=52.25, type=RECORDED, time=2025-11-14T19:29:39.979889657Z),
Temperature(source=lan2rf, location=room1, value=20.0, type=RECORDED, time=2025-11-14T19:29:39.979889657Z),
Temperature(source=lan2rf, location=room2, value=327.67, type=RECORDED, time=2025-11-14T19:29:39.979889657Z),
Temperature(source=lan2rf, location=tap, value=36.37, type=RECORDED, time=2025-11-14T19:29:39.979889657Z),
Temperature(source=lan2rf, location=room1, value=20.0, type=SETPOINT, time=2025-11-14T19:29:39.979889657Z),
Temperature(source=lan2rf, location=room1, value=0.0, type=SETPOINT_OVERRIDE, time=2025-11-14T19:29:39.979889657Z),
Temperature(source=lan2rf, location=room2, value=327.67, type=SETPOINT, time=2025-11-14T19:29:39.979889657Z),
Temperature(source=lan2rf, location=room2, value=0.0, type=SETPOINT_OVERRIDE, time=2025-11-14T19:29:39.979889657Z),
Pressure(source=lan2rf, subject=central_heating, value=1.25, time=2025-11-14T19:29:39.979889657Z),
TextStatus(source=lan2rf, subject=STATUS_DISPLAY_CODE, value=Standby, time=2025-11-14T19:29:39.979889657Z),
OperationalStatus(source=lan2rf, subject=LOCKED_OUT, active=false, time=2025-11-14T19:29:39.979889657Z),
OperationalStatus(source=lan2rf, subject=PUMP_ACTIVE, active=false, time=2025-11-14T19:29:39.979889657Z),
OperationalStatus(source=lan2rf, subject=TAP_FUNCTION_ACTIVE, active=false, time=2025-11-14T19:29:39.979889657Z),
OperationalStatus(source=lan2rf, subject=BURNER_ACTIVE, active=false, time=2025-11-14T19:29:39.979889657Z)]

Note: The above log entry excerpt has had carriage returns added to aid readability. The log entry is written on a single line in the app.

Tag summary

Content type

Image

Digest

sha256:7967371f1…

Size

39.2 MB

Last updated

16 days ago

docker pull eddgrant/lan2rf-gateway-stats