Script for analyzing data from a temperature and humidity sensor
About
This script is a monitoring and alerting tool designed for the Papouch TH2E temperature and humidity sensor. It periodically retrieves live measurement data from the device’s XML endpoint (fresh.xml) and evaluates whether the readings remain within predefined safe ranges.
THIS IS NOT SPONSORED!
This script is tested with:
- Device type: TH2E
- Firmware version: 7.2/19 (Created 06.03.2023 09:20:01)
- Core:TH2E; v0436.04.08; f66 97; t1; h1; rtc
If a temperature or humidity value goes outside the allowed limits, the script:
- detects the abnormal reading,
- sends an email alert to the configured recipients,
- logs the event locally,
- continues monitoring until the value returns to normal.
TH2E: Ethernet thermometer and hygrometer:
https://en.papouch.com/th2e-ethernet-thermometer-and-hygrometer-p4825
TH3: Temperature and humidity sensor:
https://en.papouch.com/th3-temperature-and-humidity-sensor-p4605
When the sensor readings stabilize again, the script sends a “back to normal” notification.
The script supports:
- Temperature monitoring
- Humidity monitoring
- Automatic detection of XML namespaces (Papouch TH2E sometimes returns XML with or without namespaces)
- State tracking (normal → alert → verify → normal)
- Email notifications via SMTP
- Local logging of all readings and events
- Automatic startup on system boot (Windows Task Scheduler or Linux systemd)
- Virtual environment isolation to avoid dependency conflicts
It is intended for environments where continuous monitoring of temperature and humidity is required, such as:
- server rooms
- network cabinets
- laboratories
- storage rooms
The script is lightweight, requires minimal system resources, and is suitable for running on Windows or Linux.
General & disclaimer
This script has been tested and verified on Windows 10/11 systems. A Linux version of the installation guide is provided for convenience (not tested), but the script has not been formally tested on Linux, and behavior may differ depending on distribution, environment configuration, and system security policies.
Real‑world deployments may vary. Network configurations, Python versions, system permissions, SMTP settings, and sensor firmware differences can all affect how the script behaves in production environments.
All commands, configuration changes, and system modifications described in this guide are performed at your own risk. Before deploying the script in a production environment, ensure that:
- you understand each step,
- you have appropriate permissions,
- you have tested the script in a safe environment,
- and you have backups or rollback options if needed.
This script is provided “as is,” without guarantees. Use it responsibly and verify that it meets your operational requirements before relying on it for critical monitoring.
Example data from endpoint
<?xml version="1.0" encoding="iso-8859-2"?>
<root xmlns="http://www.papouch.com/xml/th2e/act">
<sns id="1" type="1" status="0" unit="0" val="20.7" w-min="17.0" w-max="26.0" e-min-val=" 15.4" e-max-val=" 26.4" e-min-dte="11/21/2025 00:47:57" e-max-dte="11/25/2025 08:55:03" /><sns id="2" type="2" status="0" unit="3" val="12.7" w-min="0.0" w-max="55.0" e-min-val=" 11.7" e-max-val=" 63.4" e-min-dte="02/02/2026 08:52:08" e-max-dte="11/25/2025 09:39:07" /><sns id="3" type="3" status="0" unit="0" val="-8.9" w-min="" w-max="" e-min-val=" -9.6" e-max-val=" 17.8" e-min-dte="02/02/2026 08:40:12" e-max-dte="11/25/2025 08:54:56" /><status frm="1" location="EXAMPLE-NAME" time="02/02/2026 10:18:45" typesens="3" /></root>
Example manual downloaded data
<root>
<sns id="1" type="1" status="0" unit="0" val="20.7" w-min="17.0" w-max="26.0" e-min-val=" 15.4" e-max-val=" 26.4" e-min-dte="11/21/2025 00:47:57" e-max-dte="11/25/2025 08:55:03"/>
<sns id="2" type="2" status="0" unit="3" val="12.3" w-min="0.0" w-max="55.0" e-min-val=" 11.7" e-max-val=" 63.4" e-min-dte="02/02/2026 08:52:08" e-max-dte="11/25/2025 09:39:07"/>
<sns id="3" type="3" status="0" unit="0" val="-9.3" w-min="" w-max="" e-min-val=" -9.6" e-max-val=" 17.8" e-min-dte="02/02/2026 08:40:12" e-max-dte="11/25/2025 08:54:56"/>
<status frm="1" location="EXAMPLE-NAME" time="02/02/2026 9:00:11" typesens="3"/>
</root>
Script
import time
import datetime
import pytz
import smtplib
import requests
import xml.etree.ElementTree as ET
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# === SMTP CONFIGURATION ===
URL = "http://IP/fresh.xml"
USERNAME = "admin"
PASSWORD = "PASSWORD"
SENDER_EMAIL = "SENDER_EMAIL"
SENDER_PASSWORD = "SENDER_PASSWORD"
RECEIVER_EMAILS = ["[email protected]", "[email protected]"]
SMTP_SERVER = "SMTP_SERVER"
SMTP_PORT = SMTP_PORT
# === SCRIPT CONFIGURATION ===
NORMAL_TIME_INTERVAL = 1200
ERROR_TIME_INTERVAL = 300
CHECK_TIME_INTERVAL = NORMAL_TIME_INTERVAL
MIN_TEMP_VALUE = 13.0
MAX_TEMP_VALUE = 26.0
MIN_HUM_VALUE = 8.0
MAX_HUM_VALUE = 50.0
SUBJECT_ALERT = "SENSOR ALERT {atmospheric_parameter}"
SUBJECT_NORMAL = "SENSOR INFO {atmospheric_parameter}"
BODY_ALERT = "{atmospheric_parameter} has exceeded the allowed range {permissible_value}! Current value is {val}"
BODY_NORMAL = "{atmospheric_parameter} has returned to the allowed range {permissible_value}. Current value is {val}"
LOG_RUN_FILE = "log_launch.txt"
LOG_DATA_FILE = "log_data.txt"
ATMOSPHERIC_PARAMETERS = {
"1": "Temperature",
"2": "Humidity"
}
LIMITS = {
"1": (MIN_TEMP_VALUE, MAX_TEMP_VALUE),
"2": (MIN_HUM_VALUE, MAX_HUM_VALUE)
}
SENSOR_STATES = {} # normal / alert / verify
ALERT_TYPE = {} # min / max
# === FUNCTIONS ===
def str_actual_date_time():
return datetime.datetime.now().strftime("%D %T")
def save_to_log(data):
with open(LOG_DATA_FILE, "a") as f:
f.write(f"[{str_actual_date_time()}]\n{data}\n")
def send_email(subject, body):
try:
timezone = pytz.timezone('Europe/Warsaw')
local_time = datetime.datetime.now(timezone)
date_str = local_time.strftime("%a, %d %b %Y %H:%M:%S %z")
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as smtp:
smtp.starttls()
smtp.login(SENDER_EMAIL, SENDER_PASSWORD)
for receiver in RECEIVER_EMAILS:
msg = MIMEMultipart()
msg["From"] = SENDER_EMAIL
msg["To"] = receiver
msg["Subject"] = subject
msg["Date"] = date_str
msg.attach(MIMEText(body, "plain"))
smtp.send_message(msg)
except Exception as e:
print(f"Error: {e}")
def values_in_range(sns_id, val):
if sns_id not in LIMITS:
return True
low, high = LIMITS[sns_id]
return low < val < high
def fetch_and_process_data():
global CHECK_TIME_INTERVAL
try:
response = requests.get(URL, auth=(USERNAME, PASSWORD), timeout=180)
response.raise_for_status()
root = ET.fromstring(response.text)
# detect namespace (in downloaded XML it may be missing, via browser it may be present)
tag = root.tag
if tag.startswith("{"):
# XML with namespace
ns_uri = tag.split("}")[0].strip("{")
ns = {"n": ns_uri}
sns_list = root.findall(".//n:sns", ns)
else:
# XML with out namespace
sns_list = root.findall(".//sns")
CHECK_TIME_INTERVAL = NORMAL_TIME_INTERVAL
for sns in sns_list:
sns_id = sns.get("id")
if sns_id == '3': # skip dew point
break
sns_val = float(sns.get("val"))
is_ok = values_in_range(sns_id, sns_val)
prev_state = SENSOR_STATES.get(sns_id, "normal")
if sns_id in ATMOSPHERIC_PARAMETERS:
save_to_log(f"{ATMOSPHERIC_PARAMETERS[sns_id]} = {sns_val}")
min_val, max_val = LIMITS[sns_id]
ap = ATMOSPHERIC_PARAMETERS[sns_id]
# === ALERT ===
if not is_ok:
SENSOR_STATES[sns_id] = "alert"
if sns_val < min_val:
ALERT_TYPE[sns_id] = "min"
subject = SUBJECT_ALERT.format(atmospheric_parameter=ap)
body = (
f"{ap} dropped below the allowed minimum {min_val}. "
f"Current value is {sns_val}"
)
elif sns_val > max_val:
ALERT_TYPE[sns_id] = "max"
subject = SUBJECT_ALERT.format(atmospheric_parameter=ap)
body = (
f"{ap} exceeded the allowed maximum {max_val}. "
f"Current value is {sns_val}"
)
send_email(subject, body)
# === FIRST OK READING AFTER ALERT ===
elif is_ok and prev_state == "alert":
SENSOR_STATES[sns_id] = "verify"
# === SECOND OK READING → BACK TO NORMAL ===
elif is_ok and prev_state == "verify":
if ALERT_TYPE.get(sns_id) == "min":
pv = min_val
else:
pv = max_val
send_email(
SUBJECT_NORMAL.format(atmospheric_parameter=ap),
BODY_NORMAL.format(atmospheric_parameter=ap, permissible_value=pv, val=sns_val)
)
SENSOR_STATES[sns_id] = "normal"
# === NORMAL READING ===
elif is_ok and prev_state == "normal":
continue
except Exception as e:
CHECK_TIME_INTERVAL = ERROR_TIME_INTERVAL
send_email("CZUJKA ERROR", f"{str_actual_date_time()} something went wrong\nException:\n{e}")
# === START LOG ===
with open(LOG_RUN_FILE, "a") as f:
send_email("SENSOR INFO Script started", (f"{str_actual_date_time()} script started"))
f.write("Script started: " + str_actual_date_time() + "\n")
# === MAIN LOOP ===
if __name__ == "__main__":
while True:
fetch_and_process_data()
time.sleep(CHECK_TIME_INTERVAL)
Windows instruction
Check if Python is installed
python3 --version
If Microsoft Store opens: Install Python from the Store.
Prepare the directory structure
Create a folder for the script, e.g.: alerts_sensor Enter the folder Extract/copy the script files into this folder
Important: the script must NOT be loose on the Desktop — it must be inside a folder.
Create a virtual environment (venv)
Create the venv:
python3 -m venv venv
Activate the venv:
.\venv\Scripts\activate
If activation fails, run:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Explanation:
- RemoteSigned allows all local scripts and only signed scripts from the internet
- Scope CurrentUser applies only to your user account
Then activate again:
.\venv\Scripts\activate
After activation, your prompt should start with (venv).
Install required Python modules
Inside the activated venv:
pip install requests pytz
Verify installation:
pip show requests
It should show a path inside the venv folder.
Run the script manually
Ensure venv is active:
.\venv\Scripts\activate
Run the script:
python3 alerts.py
or, if needed (see section 6):
python alerts.py
Stop the script: CTRL + C.
Error ModuleNotFoundError
This means you are running the system Python, not the venv Python.
Check which Python is being used:
where python
where python3
You should see:
...\alerts_sensor\venv\Scripts\python.exe
If not, run the script explicitly with venv Python:
venv\Scripts\python.exe alerts.py
or
python alerts.py
Automatically start the script after reboot (Task Scheduler)
Open Task Scheduler and create a new task (not a basic task)
General tab:
- Name: any name
- Run whether user is logged on or not
- Do NOT store password
- Run with highest privileges
- Configure for: Windows 10
Triggers → New…
- Begin the task: At startup
- Enabled: Yes
Actions → New…
- Action: Start a program
- Program/script: C:\Users\USER\PATH\TO\alerts_sensor\venv\Scripts\python.exe
- Add arguments (optional): alerts.py
- Start in (optional): C:\Users\USER\PATH\TO\alerts_sensor
Conditions Leave default settings.
Settings Enable ONLY:
- Allow task to be run on demand
- If the task fails, restart every 5 minutes
- Attempt up to 3 times
- If the task is already running: Do not start a new instance
Linux instruction
Check if Python is installed
python3 --version
If Python is NOT installed
(Ubuntu/Debian)
sudo apt update
sudo apt install python3 python3-venv python3-pip -y
(CentOS/RHEL)
sudo dnf install python3 python3-pip -y
Prepare the directory structure
Create a directory and enter, e.g.: alerts_sensor:
mkdir alerts_sensor && cd alerts_sensor
Extract/copy the script files into this directory:
unzip alerts_sensor.py
Make the script executable:
chmod +x alerts.py
Create a virtual environment (venv)
Inside the script directory:
python3 -m venv venv
Activate the environment:
source venv/bin/activate
After activation you should see something like:
(venv) user@host:~/alerts_sensor$
Install required Python modules
Install dependencies:
pip install requests pytz
Verify installation (should show a path inside venv):
pip show requests
Run the script manually
In the project directory:
source venv/bin/activate
python3 alerts.py
Stop the script CTRL + C
Error ModuleNotFoundError
If you still get “ModuleNotFoundError” after installing modules
The most common cause: You are running the system Python instead of the venv Python.
Check which Python is being used:
which python3
which python
It should point to ~/alerts_sensor/venv/bin/python3
If it does NOT, run the script explicitly with venv Python:
venv/bin/python3 alerts.py
Automatically start the script after reboot (systemd)
Create a systemd service file:
sudo vim /etc/systemd/system/alerts.service
Paste the following:
[Unit]
Description=Scripty for sensor alerts
After=network.target
[Service]
Type=simple
User=YOUR_USERNAME
WorkingDirectory=/home/YOUR_USERNAME/alerts_sensor
ExecStart=/home/YOUR_USERNAME/alerts_sensor/venv/bin/python3 /home/YOUR_USERNAME/alerts_sensor/alerts.py
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Reload systemd:
sudo systemctl daemon-reload
Enable service at boot:
sudo systemctl enable alerts.service
Start the service:
sudo systemctl start alerts.service