186 lines
5.6 KiB
Python
186 lines
5.6 KiB
Python
import time
|
|
from machine import Pin, SoftI2C
|
|
import network
|
|
from simple_mqtt import SimpleMQTT
|
|
|
|
# -------------------------------------------------------------
|
|
# Configuration Loader
|
|
# -------------------------------------------------------------
|
|
try:
|
|
import config as Config
|
|
except ImportError:
|
|
print("ERROR: config.py not found.")
|
|
print("Create config.py on the device with your Wi-Fi and MQTT credentials.")
|
|
while True:
|
|
time.sleep(2)
|
|
|
|
NODE_NAME = getattr(Config, "NODE_NAME", Config.NODE_ID)
|
|
SENSOR_TYPE = getattr(Config, "SENSOR_TYPE", "bme280").lower()
|
|
STATE_TOPIC = f"homeassistant/sensor/{Config.NODE_ID}/state"
|
|
|
|
# Dynamic sensor driver import
|
|
if SENSOR_TYPE == "sht41":
|
|
from sht41 import SHT41
|
|
elif SENSOR_TYPE == "bme280":
|
|
from bme280 import BME280
|
|
else:
|
|
raise ValueError(f"Unsupported SENSOR_TYPE '{SENSOR_TYPE}'. Use 'bme280' or 'sht41'.")
|
|
|
|
|
|
# -------------------------------------------------------------
|
|
# Home Assistant MQTT Discovery
|
|
# -------------------------------------------------------------
|
|
def register_ha_discovery(mqtt, sensor_type):
|
|
device_info = (
|
|
f'"device":{{'
|
|
f'"identifiers":["{Config.NODE_ID}"],'
|
|
f'"name":"{NODE_NAME}",'
|
|
f'"model":"Pico 2 W ({sensor_type.upper()})",'
|
|
f'"manufacturer":"Raspberry Pi"'
|
|
f"}}"
|
|
)
|
|
|
|
configs = [
|
|
{
|
|
"id": "temperature",
|
|
"name": "Temperature",
|
|
"unit": "°C",
|
|
"cls": "temperature",
|
|
"val_tpl": "{{ value_json.temperature }}",
|
|
},
|
|
{
|
|
"id": "humidity",
|
|
"name": "Humidity",
|
|
"unit": "%",
|
|
"cls": "humidity",
|
|
"val_tpl": "{{ value_json.humidity }}",
|
|
},
|
|
]
|
|
|
|
if sensor_type == "bme280":
|
|
configs.append(
|
|
{
|
|
"id": "pressure",
|
|
"name": "Pressure",
|
|
"unit": "hPa",
|
|
"cls": "atmospheric_pressure",
|
|
"val_tpl": "{{ value_json.pressure }}",
|
|
}
|
|
)
|
|
|
|
for c in configs:
|
|
topic = f"homeassistant/sensor/{Config.NODE_ID}/{c['id']}/config"
|
|
unique_id = f"{Config.NODE_ID}_{c['id']}"
|
|
payload = (
|
|
f'{{"name":"{c["name"]}",'
|
|
f'"has_entity_name":true,'
|
|
f'"unique_id":"{unique_id}",'
|
|
f'"device_class":"{c["cls"]}",'
|
|
f'"state_class":"measurement",'
|
|
f'"unit_of_measurement":"{c["unit"]}",'
|
|
f'"state_topic":"{STATE_TOPIC}",'
|
|
f'"value_template":"{c["val_tpl"]}",'
|
|
f"{device_info}}}"
|
|
)
|
|
mqtt.publish(topic, payload, retain=True)
|
|
print(f"Registered HA discovery: {c['id']}")
|
|
|
|
|
|
# -------------------------------------------------------------
|
|
# Wi-Fi Connection
|
|
# -------------------------------------------------------------
|
|
def connect_wifi():
|
|
wlan = network.WLAN(network.STA_IF)
|
|
wlan.active(True)
|
|
if not wlan.isconnected():
|
|
print(f"Connecting to Wi-Fi '{Config.WIFI_SSID}'...")
|
|
wlan.connect(Config.WIFI_SSID, Config.WIFI_PASS)
|
|
timeout = 15
|
|
while not wlan.isconnected() and timeout > 0:
|
|
time.sleep(1)
|
|
timeout -= 1
|
|
|
|
if not wlan.isconnected():
|
|
raise RuntimeError("Failed to connect to Wi-Fi")
|
|
print(f"Wi-Fi connected. IP: {wlan.ifconfig()[0]}")
|
|
|
|
|
|
# -------------------------------------------------------------
|
|
# Panic Handler
|
|
# -------------------------------------------------------------
|
|
def panic(err=None):
|
|
if err:
|
|
print(f"Fatal error: {err}")
|
|
led = Pin("LED", Pin.OUT)
|
|
while True:
|
|
led.toggle()
|
|
time.sleep(0.1)
|
|
|
|
|
|
# -------------------------------------------------------------
|
|
# Main Execution Loop
|
|
# -------------------------------------------------------------
|
|
def main():
|
|
i2c = SoftI2C(sda=Pin(4), scl=Pin(5), freq=100000)
|
|
|
|
if SENSOR_TYPE == "sht41":
|
|
print("Using SHT41 sensor (0x44)")
|
|
sensor = SHT41(i2c)
|
|
else:
|
|
print("Using BME280 sensor (0x77)")
|
|
sensor = BME280(i2c)
|
|
|
|
connect_wifi()
|
|
|
|
mqtt_user = getattr(Config, "MQTT_USER", None)
|
|
mqtt_pass = getattr(Config, "MQTT_PASS", None)
|
|
mqtt_port = getattr(Config, "MQTT_PORT", 1883)
|
|
|
|
mqtt = SimpleMQTT(Config.MQTT_HOST, mqtt_port)
|
|
print(f"Connecting to MQTT broker at {Config.MQTT_HOST}...")
|
|
mqtt.connect(Config.CLIENT_ID, mqtt_user, mqtt_pass)
|
|
print("MQTT connected.")
|
|
|
|
register_ha_discovery(mqtt, SENSOR_TYPE)
|
|
|
|
interval = getattr(Config, "UPDATE_INTERVAL", 30)
|
|
while True:
|
|
try:
|
|
data = sensor.read_values()
|
|
|
|
if data.get("pressure") is not None:
|
|
state_json = (
|
|
f'{{"temperature":{data["temperature"]},'
|
|
f'"humidity":{data["humidity"]},'
|
|
f'"pressure":{data["pressure"]}}}'
|
|
)
|
|
else:
|
|
state_json = (
|
|
f'{{"temperature":{data["temperature"]},'
|
|
f'"humidity":{data["humidity"]}}}'
|
|
)
|
|
|
|
mqtt.publish(STATE_TOPIC, state_json)
|
|
print(f"[{NODE_NAME}] Published: {state_json}")
|
|
except Exception as e:
|
|
print(f"Error publishing state: {e}")
|
|
try:
|
|
mqtt.disconnect()
|
|
time.sleep(2)
|
|
connect_wifi()
|
|
mqtt.connect(Config.CLIENT_ID, mqtt_user, mqtt_pass)
|
|
except Exception as reconnect_err:
|
|
print(f"Reconnect failed: {reconnect_err}")
|
|
|
|
time.sleep(interval)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
time.sleep(2)
|
|
Pin("LED", Pin.OUT).on()
|
|
|
|
try:
|
|
main()
|
|
except Exception as e:
|
|
panic(e)
|