Uploading server data (meteo) to Lemmy instance
A Python application that reads a physical weather station over Modbus TCP and publishes its measurements to a single, regularly updated Lemmy post.
It collects temperature, relative humidity and atmospheric pressure, stores the history in SQLite, generates three monthly charts, and uploads the results through Lemmy's API. A standard Linux CRON job runs the complete cycle every hour.
Latest release
v1.1.1: Pillow security update upgrades Pillow to 13.0.0 and retains the recovery and heartbeat features of v1.1.0.
Features
- Configurable Modbus host, TCP port, device ID and register addresses.
- Holding registers (FC03) or input registers (FC04), with signed or unsigned 16-bit values.
- A separate multiplier and offset for each measurement.
- Validation, retries and a short station status: working correctly or station error.
- SQLite history, including failed readings without replacing them with zeroes.
- Three monthly PNG charts: temperature, humidity and pressure.
- Updates to one existing post, preserving the static text outside the managed section.
- Optional community pinning when the publishing account has moderator permissions.
- User-level CRON scheduling, isolated Python dependencies and file locks against overlapping cycles.
How it works
Modbus TCP device
|
v
Read registers -> apply scaling -> validate -> SQLite + latest status
|
v
Three monthly charts
|
v
Upload images and update one Lemmy post
Each value is calculated on the collection server:
measurement = decoded_register * multiplier + offset
For example, a register value of 234 with a multiplier of 0.1 becomes 23.4 °C. Lemmy receives formatted Markdown text and uploaded PNG images, not raw Modbus registers. The current presentation uses Polish labels and decimal commas.
The charts cover the current calendar month in the configured time zone. Missing readings break the plotted lines. Old measurements remain in the database when a new month begins.
Requirements
- Linux with Python 3.11 or later; validated on Debian 12 with Python 3.11.2.
- Network access to the Modbus device and HTTPS access to the Lemmy instance.
- An active Lemmy account allowed to post in the selected community and upload images.
- Lemmy API v3, based on the working Lemmy 0.19.11 deployment. API v4 is not implemented.
- Moderator or administrator permissions only if community pinning is enabled.
The application supports one 16-bit register per measurement. Multi-register floats and custom word order are not implemented. The three registers are read separately, so they do not need to be adjacent; the readings are not a single atomic hardware sample. The program performs no Modbus writes.
No Docker container or incoming web server is required on the collection machine. Installation does not require rebooting the server.
Installation
1. Download the application
Download and extract the package from Releases, or clone this repository:
git clone https://github.com/adamzend/Uploading-server-data-meteo-to-Lemmy-instance.git meteo
cd meteo
2. Install the system dependencies and Python environment
sudo apt-get update
sudo apt-get install python3 python3-venv cron ca-certificates mc
sh install.sh
Run install.sh as the ordinary Linux user who will run the station. It creates .venv, installs the pinned dependencies and copies config.example.json to config.json if needed. It does not enable CRON or publish anything.
3. Configure and test the measurements
mcedit config.json
chmod 600 config.json
.venv/bin/python settings.py
.venv/bin/python -m unittest discover -v
.venv/bin/python meteo.py
.venv/bin/python charts.py
Fill in the Modbus host, port, device ID, function code and all three register addresses. Choose the correct signedness and multiplier for each sensor. Leave lemmy.enabled set to false while testing.
settings.py validates configuration without connecting to external services. The test suite uses simulated data and API responses. meteo.py performs a real reading and adds it to the database. Check data/status.json and the three images in wykresy/.
4. Configure Lemmy
In config.json, enter the instance's canonical HTTPS origin, the publishing account's username and password, and the numeric community ID. Set the post title and heading.
- Leave
post_idasnullto find or create the weather post. - Set
post_idto update an existing post owned by this account in this community. - Enable
pin_in_communityonly when the account has the required permissions. - The program authenticates through the API and caches a JWT locally. It does not create accounts or grant permissions. TOTP login is not implemented.
For a new post, you can prepare a static description of your sensors:
cp static.example.txt static.txt
mcedit static.txt
The content of static.txt is appended only when a new post is created. Edit the static section directly in Lemmy afterward.
For an existing post, place these markers on separate lines with blank lines around the managed content:
[meteo-start]: #
Measurements and charts will be inserted here.
[meteo-end]: #
Your permanent sensor description goes here.
The program replaces only the section between the markers. Missing, duplicated or reversed markers cause publication to stop instead of replacing the entire post.
Set lemmy.enabled to true, then run the first complete cycle:
.venv/bin/python settings.py
.venv/bin/python run_cycle.py
Check the resulting post. Its URL is recorded in logs/meteo.log, and its ID is saved in data/lemmy-state.json.
5. Enable hourly updates
.venv/bin/python install_cron.py
crontab -l
systemctl is-active cron
If CRON is disabled:
sudo systemctl enable --now cron
The installer schedules a cycle at the beginning of every hour, preserves unrelated jobs and saves a backup of the previous crontab. It manages one # meteo-collector job per Linux user. The schedule follows the server's CRON time zone; chart dates use timezone from the application configuration.
To edit the schedule or remove this job:
EDITOR=mcedit crontab -e
.venv/bin/python install_cron.py --remove
When changing the interval, also adjust stale_after_minutes and lemmy.update_note. Running the installer again restores the hourly schedule.
Configuration reference
The complete template is config.example.json.
| Setting | Purpose |
|---|---|
timezone |
IANA time zone for dates and month boundaries; default UTC. |
stale_after_minutes |
Maximum acceptable reading age at publication, also the chart gap threshold; default 90. |
modbus.host, port, unit_id |
Device network address, TCP port and Modbus unit ID. |
modbus.function_code |
3 for holding registers or 4 for input registers. |
modbus.timeout_seconds, attempts, retry_delay_seconds |
Timeout and retry controls. |
sensors.<name>.address |
Protocol register address, from 0 to 65535. All three must be filled in. |
sensors.<name>.signed |
true for int16, false for uint16. |
sensors.<name>.multiplier, offset |
Per-sensor conversion applied after decoding the sign. |
sensors.<name>.min, max |
Valid range of the converted measurement. |
lemmy.enabled |
Enables publishing; disabled in the template. |
lemmy.instance, username, password |
Instance origin and publishing account credentials. |
lemmy.community_id, post_id |
Local community ID and optional existing post ID. |
lemmy.title, heading, update_note |
New post title, in-post heading and update description. |
lemmy.pin_in_community |
Requests community pinning; does not grant moderation rights. |
Sensor names are temperature, humidity and pressure. Converted values must be in °C, %, and hPa, respectively. Addresses are sent as configured: the application does not automatically subtract 1 or 40001 from a manufacturer's register numbering.
Detailed Polish documentation is also included: installation and operation and configuration.
Operation and data
| File or directory | Contents |
|---|---|
run_cycle.py |
Measurement, chart generation and publication in order. |
meteo.py |
Modbus reads, conversion, validation and storage. |
charts.py |
Monthly charts and their manifest. |
publish_lemmy.py |
Authentication, image upload and post updates. |
data/ |
SQLite history, latest status, JWT, post state and upload history. |
wykresy/ |
Three PNG charts and their manifest. |
logs/ |
Application and CRON logs. |
.venv/, .cache/ |
Local Python environment and plotting cache. |
A failed reading is stored as an error. The post can show the last successful values with their timestamp and an error status. If the entire server, network connection or Lemmy API becomes unavailable, the post cannot update itself: check the last measurement time. This status is not an independent external uptime monitor.
Old uploaded chart images are not automatically deleted from pict-rs. Application logs rotate, but logs/cron.log does not. Plan storage and backups accordingly.
To migrate, disable the old CRON job and let its current cycle finish, then copy the application, your private configuration and the complete data/ directory. Recreate the environment with sh install.sh, test one cycle and enable CRON on the new server. Do not run two publishers against the same post. For a live SQLite backup, use the SQLite backup API instead of copying only the database file while WAL writes are active.
Credentials and verification
config.json contains your password in plain text and must remain private. It and the generated data, logs and tokens are excluded by .gitignore. Do not publish them. The public template has no device address, account credentials or deployment-specific register map.
The first release passed 18 automated tests on Debian 12 / Python 3.11.2, covering Modbus parsing and retries, scaling, month boundaries, missing measurements, PNG generation and preservation of static post content. Tests use simulated network responses; compatibility with every Lemmy deployment is not implied.
A real working weather station
This project is not just a theoretical idea. It is based on a physical, operating weather station that sends real measurements to an active Lemmy instance. The configurable application in this repository was prepared from that working deployment.
Live weather post: Stacja meteo — Szymbark
The screenshot below was captured from the actual public post on 13 October 2026. It shows the measured values, station status and three monthly charts; it is not a mockup.
Lemmy instance homepage: szymbark.net
License
This project is licensed under the GNU General Public License v3.0 (GPL-3.0-only). See LICENSE for the full license text.
v1.1.0: recovery and heartbeat verification
See release notes and upgrade instructions for the reasons for these changes, configuration examples and monitoring limitations. Heartbeat checking is optional and disabled by default; enable it to detect frozen source data behind a responding HMI.
