from pymodbus.client import ModbusSerialClient as ModbusClient
import subprocess
import time
import threading
# ==========================================
# MQTT-asetukset
# ==========================================
MQTT_BROKER = '192.168.0.160'
MQTT_TOPIC_BASE = 'rotenso'
MQTT_USER = 'root'
MQTT_PASSWORD = 'salasana'
# ==========================================
# Modbus
# ==========================================
client = ModbusClient(
port='/dev/ttyUSB1',
baudrate=9600,
bytesize=8,
parity='N',
stopbits=1,
timeout=1
)
SLAVE = 11
INTERVAL = 30
# Estetään yhtäaikaiset Modbus-käskyt
modbus_lock = threading.Lock()
# ==========================================
# MQTT julkaisu
# ==========================================
def publish_mqtt(topic_suffix, value):
topic = f"{MQTT_TOPIC_BASE}/{topic_suffix}"
try:
subprocess.run([
'mosquitto_pub',
'-h', MQTT_BROKER,
'-u', MQTT_USER,
'-P', MQTT_PASSWORD,
'-t', topic,
'-m', str(value),
'-r'
], check=True)
print(f"MQTT {topic}: {value}")
except subprocess.CalledProcessError as e:
print(f"MQTT ERROR {topic}: {e}")
# ==========================================
# Modbus rekisterin luku
# ==========================================
def read_register(register_address):
try:
with modbus_lock:
result = client.read_holding_registers(
register_address,
1,
slave=SLAVE
)
if result.isError():
return None
return result.registers[0]
except Exception as e:
print(f"Modbus READ ERROR 0x{register_address:04X}: {e}")
return None
# ==========================================
# Mittaus + MQTT
# ==========================================
def read_and_publish(register_address, topic_suffix, value_type):
try:
value = read_register(register_address)
if value is None:
print(f"Modbus ERROR: {topic_suffix}")
return
# Lämpötila /10
if value_type == 'temperature':
if value >= 32768:
value -= 65536
value = value / 10.0
# Kompressorin taajuus /10
elif value_type == 'frequency':
value = value / 10.0
# Water flow
elif value_type == 'flow':
value = value
publish_mqtt(topic_suffix, value)
except Exception as e:
print(f"ERROR {topic_suffix}: {e}")
# ==========================================
# Luetaan kaikki mittaukset
# ==========================================
def read_all():
print("\n--- Rotenso ---")
read_and_publish(
0x00CE,
"varaaja",
'temperature'
)
read_and_publish(
0x0001,
"ulkolampotila",
'temperature'
)
read_and_publish(
0x0002,
"sisailman_lampotila",
'temperature'
)
read_and_publish(
0x0003,
"tuloveden_lampotila",
'temperature'
)
read_and_publish(
0x0004,
"lahtoveden_lampotila",
'temperature'
)
read_and_publish(
0x0005,
"kylmaaine_t2b",
'temperature'
)
read_and_publish(
0x000A,
"purkauslampotila",
'temperature'
)
read_and_publish(
0x000B,
"ilmanvaihtimen_lampotila_t3",
'temperature'
)
read_and_publish(
0x0017,
"kompressorin_taajuus",
'frequency'
)
read_and_publish(
0x0055,
"pumpun_nopeus",
'normal'
)
read_and_publish(
0x102A,
"water_flow",
'flow'
)
read_and_publish(
0x1005,
"fan_speed",
'normal'
)
# ======================================
# Setting mode 0x002C
# ======================================
setting_mode = read_register(0x002C)
if setting_mode is not None:
if setting_mode == 0:
publish_mqtt("power", "OFF")
elif setting_mode == 4:
publish_mqtt("power", "ON")
else:
publish_mqtt("power", "ON")
print(f"Setting mode 0x002C: {setting_mode}")
# ======================================
# Running mode 0x002D
# ======================================
running_mode = read_register(0x002D)
if running_mode is not None:
publish_mqtt(
"running_mode",
running_mode
)
print(f"Running mode 0x002D: {running_mode}")
print("--- Seuraava mittaus 30 s kuluttua ---")
# ==========================================
# Rotenson käynnistys / sammutus
# ==========================================
def set_power(command):
command = command.strip().upper()
if command == "ON":
value = 4
print(">>> Rotenso KÄYNNISTYS")
elif command == "OFF":
value = 0
print(">>> Rotenso SAMMUTUS")
else:
print(f"Virheellinen komento: {command}")
return
try:
with modbus_lock:
result = client.write_register(
0x002C,
value,
slave=SLAVE
)
if result.isError():
print(
f"Modbus WRITE ERROR 0x002C = {value}"
)
return
print(
f"Modbus OK: 0x002C = {value}"
)
# Julkaistaan HA:lle uusi tila
publish_mqtt(
"power",
command
)
except Exception as e:
print(f"Modbus WRITE ERROR: {e}")
# ==========================================
# MQTT-komentojen kuuntelu
# ==========================================
def mqtt_listener():
print("MQTT-komentojen kuuntelu käynnistetty")
while True:
try:
process = subprocess.Popen(
[
'mosquitto_sub',
'-h', MQTT_BROKER,
'-u', MQTT_USER,
'-P', MQTT_PASSWORD,
'-t', 'rotenso/set_power'
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
for line in process.stdout:
command = line.strip()
if command:
print(
f"MQTT komento: {command}"
)
set_power(command)
except Exception as e:
print(
f"MQTT listener ERROR: {e}"
)
print(
"MQTT-yhteys katkesi, "
"yritetään uudelleen 5 s kuluttua..."
)
time.sleep(5)
# ==========================================
# KÄYNNISTYS
# ==========================================
if not client.connect():
print("Modbus-yhteys epäonnistui")
exit(1)
print("Modbus-yhteys OK")
print("Mittausväli: 30 sekuntia")
# Käynnistetään MQTT-kuuntelu omassa säikeessä
listener_thread = threading.Thread(
target=mqtt_listener,
daemon=True
)
listener_thread.start()
# ==========================================
# Pääohjelma
# ==========================================
try:
while True:
read_all()
time.sleep(INTERVAL)
except KeyboardInterrupt:
print("\nOhjelma lopetetaan")
finally:
client.close()
print(
"Modbus-yhteys suljettu"
)