split code up into modules and add pubsub and timers

This commit is contained in:
Travis Shears 2023-08-10 08:11:39 +02:00
parent 05c470197f
commit 001f99db7c
Signed by: travisshears
GPG key ID: D4C2E4DFAB8BABF8
7 changed files with 228 additions and 137 deletions

15
CIRCUITPY/.vscode/settings.json vendored Executable file
View file

@ -0,0 +1,15 @@
{
"python.languageServer": "None",
"python.linting.pylintEnabled": false,
"python.analysis.diagnosticSeverityOverrides": {
"reportMissingModuleSource": "none"
},
"python.analysis.extraPaths": [
"/Users/travis.shears/.vscode/extensions/joedevivo.vscode-circuitpython-0.1.20-darwin-arm64/boards/0x239A/0x80F4",
"/Users/travis.shears/.vscode/extensions/joedevivo.vscode-circuitpython-0.1.20-darwin-arm64/stubs",
"/Users/travis.shears/Library/Application Support/Code/User/globalStorage/joedevivo.vscode-circuitpython/bundle/20230808/adafruit-circuitpython-bundle-py-20230808/lib"
],
"circuitpython.board.version": "8.2.0",
"circuitpython.board.vid": "0x239A",
"circuitpython.board.pid": "0x80F4"
}

View file

@ -1,144 +1,37 @@
import os
import board
import time
import busio
from adafruit_bme280 import basic as adafruit_bme280
import supervisor
from weather_station.pubsub import PubSub
from weather_station.timer import Timer
from weather_station.bme280 import BME280
from weather_station.airlift import AirLift
pubsub = PubSub()
airlift = AirLift(pubsub)
bme280 = BME280(pubsub)
timers = [
Timer(pubsub, 120),
Timer(pubsub, 86400),
]
i2c = busio.I2C(scl=board.GP15, sda=board.GP14)
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c, 0x76)
# location's pressure (hPa) QNH at sea level
bme280.sea_level_pressure = 1016 # value from https://metar-taf.com/EDDM
board_reload_calls_count = 0
def board_reload(_):
global board_reload_calls_count
if board_reload_calls_count > 0:
print("resetting board in 10 sec")
time.sleep(10)
supervisor.reload()
board_reload_calls_count += 1
pubsub.subscribe('tick 86400', board_reload)
from digitalio import DigitalInOut
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
from adafruit_esp32spi import adafruit_esp32spi
import adafruit_minimqtt.adafruit_minimqtt as MQTT
esp32_cs = DigitalInOut(board.GP21)
esp32_ready = DigitalInOut(board.GP22)
esp32_reset = DigitalInOut(board.GP17)
spi = busio.SPI(clock=board.GP18, MOSI=board.GP19, MISO=board.GP20)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
if esp.status == adafruit_esp32spi.WL_IDLE_STATUS:
print("ESP32 found and in idle mode")
print("Firmware vers.", esp.firmware_version)
print("MAC addr:", [hex(i) for i in esp.MAC_address])
for ap in esp.scan_networks():
print("\t%s\t\tRSSI: %d" % (str(ap['ssid'], 'utf-8'), ap['rssi']))
print("Connecting to AP...")
while not esp.is_connected:
try:
esp.connect_AP(
os.getenv("WIFI_SSID"), os.getenv("WIFI_PASSWORD")
)
except RuntimeError as e:
print("could not connect to AP, retrying: ", e)
continue
print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi)
# MQTT inspiration from https://docs.circuitpython.org/projects/minimqtt/en/latest/examples.html
mqtt_state_topic = "homeassistant/sensor/balcony_weather_station/bme_280_001/state"
mqtt_config_topic_temperature = "homeassistant/sensor/balcony_weather_station/bme_280_001_temp/config"
mqtt_config_topic_humidity = "homeassistant/sensor/balcony_weather_station/bme_280_001_humi/config"
mqtt_config_topic_pressure = "homeassistant/sensor/balcony_weather_station/bme_280_001_pres/config"
def connect(mqtt_client, userdata, flags, rc):
print("Connected to MQTT Broker!")
print("Flags: {0}\n RC: {1}".format(flags, rc))
def disconnect(mqtt_client, userdata, rc):
print("Disconnected from MQTT Broker!")
def subscribe(mqtt_client, userdata, topic, granted_qos):
print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos))
def unsubscribe(mqtt_client, userdata, topic, pid):
print("Unsubscribed from {0} with PID {1}".format(topic, pid))
def publish(mqtt_client, userdata, topic, pid):
print("Published to {0} with PID {1}".format(topic, pid))
def message(client, topic, message):
print("New message on topic {0}: {1}".format(topic, message))
socket.set_interface(esp)
MQTT.set_socket(socket, esp)
mqtt_client = MQTT.MQTT(
broker=os.getenv("MQTT_HOST"),
username=os.getenv("MQTT_USER"),
password=os.getenv("MQTT_PASSWORD"),
)
mqtt_client.on_connect = connect
mqtt_client.on_disconnect = disconnect
mqtt_client.on_subscribe = subscribe
mqtt_client.on_unsubscribe = unsubscribe
mqtt_client.on_publish = publish
mqtt_client.on_message = message
print("Attempting to connect to %s" % mqtt_client.broker)
mqtt_client.connect()
print("Publishing to %s" % mqtt_config_topic_temperature)
config_msg_temperature = """{
"name": "BEM 280 Temperature",
"device_class": "temperature",
"unit_of_measurement": "°C",
"value_template": "{{ value_json.temperature}}",
"state_topic": "homeassistant/sensor/balcony_weather_station/bme_280_001/state",
"unique_id": "balcony_weather_station_bme_280_001_temperature",
"device": {
"identifiers": "balcony_weather_station_001",
"name": "Balcony Weather Station"
}
}"""
mqtt_client.publish(mqtt_config_topic_temperature, config_msg_temperature)
print("Publishing to %s" % mqtt_config_topic_humidity)
config_msg_humidity = """{
"name": "BEM 280 Humidity",
"device_class": "humidity",
"unit_of_measurement": "%",
"value_template": "{{ value_json.humidity}}",
"state_topic": "homeassistant/sensor/balcony_weather_station/bme_280_001/state",
"unique_id": "balcony_weather_station_bme_280_001_humidity",
"device": {
"identifiers": "balcony_weather_station_001",
"name": "Balcony Weather Station"
}
}"""
mqtt_client.publish(mqtt_config_topic_humidity, config_msg_humidity)
print("Publishing to %s" % mqtt_config_topic_pressure)
config_msg_pressure = """{
"name": "BEM 280 Pressure",
"device_class": "pressure",
"unit_of_measurement": "hPa",
"value_template": "{{ value_json.pressure}}",
"state_topic": "homeassistant/sensor/balcony_weather_station/bme_280_001/state",
"unique_id": "balcony_weather_station_bme_280_001_pressure",
"device": {
"identifiers": "balcony_weather_station_001",
"name": "Balcony Weather Station"
}
}"""
mqtt_client.publish(mqtt_config_topic_pressure, config_msg_pressure)
while True:
msg = f"""{{
"temperature": {bme280.temperature:.1f},
"humidity": {bme280.relative_humidity:.1f},
"pressure": {bme280.pressure:.1f}
}}"""
mqtt_client.publish(mqtt_state_topic, msg)
time.sleep(30)
try:
for timer in timers:
t = time.time()
timer.tick(t)
except Exception as e:
# raise # for debugging
print("encountered problem", e)
time.sleep(60)
supervisor.reload()
print("Disconnecting from %s" % mqtt_client.broker)
mqtt_client.disconnect()
print("Done!")

View file

View file

@ -0,0 +1,84 @@
import os
import board
import busio
import time
from digitalio import DigitalInOut
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
from adafruit_esp32spi import adafruit_esp32spi
import adafruit_minimqtt.adafruit_minimqtt as MQTT
class AirLift():
def __init__(self, pubsub):
self.ssid = os.getenv("WIFI_SSID")
self.wifi_pass = os.getenv("WIFI_PASSWORD")
self._setup()
pubsub.subscribe("mqtt_pub", self._publish)
def _publish(self, body):
self.mqtt_client.publish(body['topic'], body['msg'])
# print(body['msg'])
def reset(self):
print("Resetting AIRLIFT")
self.esp.reset()
time.sleep(5)
self._connect()
def _connect(self):
while not self.esp.is_connected:
try:
print(f"Trying to connect to {self.ssid}")
self.esp.connect_AP(self.ssid, self.wifi_pass)
except ConnectionError as e:
print("Could not connect to AP, sleeping for 10 sec then trying again\n", e)
time.sleep(10)
continue
print("Connected to", str(self.esp.ssid, "utf-8"), "\tRSSI:", self.esp.rssi)
def _setup(self):
esp32_cs = DigitalInOut(board.GP21)
esp32_ready = DigitalInOut(board.GP22)
esp32_reset = DigitalInOut(board.GP17)
spi = busio.SPI(clock=board.GP18, MOSI=board.GP19, MISO=board.GP20)
self.esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
if self.esp.status == adafruit_esp32spi.WL_IDLE_STATUS:
print("ESP32 found and in idle mode")
print("Firmware vers.", self.esp.firmware_version)
print("MAC addr:", [hex(i) for i in self.esp.MAC_address])
self._connect()
# MQTT inspiration from https://docs.circuitpython.org/projects/minimqtt/en/latest/examples.html
def connect(mqtt_client, userdata, flags, rc):
print("Connected to MQTT Broker!")
print("Flags: {0}\n RC: {1}".format(flags, rc))
def disconnect(mqtt_client, userdata, rc):
print("Disconnected from MQTT Broker!")
def subscribe(mqtt_client, userdata, topic, granted_qos):
print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos))
def unsubscribe(mqtt_client, userdata, topic, pid):
print("Unsubscribed from {0} with PID {1}".format(topic, pid))
def publish(mqtt_client, userdata, topic, pid):
print("Published to {0} with PID {1}".format(topic, pid))
def message(client, topic, message):
print("New message on topic {0}: {1}".format(topic, message))
socket.set_interface(self.esp)
MQTT.set_socket(socket, self.esp)
self.mqtt_client = MQTT.MQTT(
broker=os.getenv("MQTT_HOST"),
username=os.getenv("MQTT_USER"),
password=os.getenv("MQTT_PASSWORD"),
)
self.mqtt_client.on_connect = connect
self.mqtt_client.on_disconnect = disconnect
self.mqtt_client.on_subscribe = subscribe
self.mqtt_client.on_unsubscribe = unsubscribe
self.mqtt_client.on_publish = publish
self.mqtt_client.on_message = message
print("Attempting to connect to %s" % self.mqtt_client.broker)
self.mqtt_client.connect()

View file

@ -0,0 +1,73 @@
import board
import busio
from adafruit_bme280 import basic as adafruit_bme280
class BME280():
mqtt_state_topic = "homeassistant/sensor/balcony_weather_station/bme_280_001/state"
mqtt_config_topic_temperature = "homeassistant/sensor/balcony_weather_station/bme_280_001_temp/config"
mqtt_config_topic_humidity = "homeassistant/sensor/balcony_weather_station/bme_280_001_humi/config"
mqtt_config_topic_pressure = "homeassistant/sensor/balcony_weather_station/bme_280_001_pres/config"
config_msg_temperature = """{
"name": "BEM 280 Temperature",
"device_class": "temperature",
"unit_of_measurement": "°C",
"value_template": "{{ value_json.temperature}}",
"state_topic": "homeassistant/sensor/balcony_weather_station/bme_280_001/state",
"unique_id": "balcony_weather_station_bme_280_001_temperature",
"device": {
"identifiers": "balcony_weather_station_001",
"name": "Balcony Weather Station"
}
}"""
config_msg_pressure = """{
"name": "BEM 280 Pressure",
"device_class": "pressure",
"unit_of_measurement": "hPa",
"value_template": "{{ value_json.pressure}}",
"state_topic": "homeassistant/sensor/balcony_weather_station/bme_280_001/state",
"unique_id": "balcony_weather_station_bme_280_001_pressure",
"device": {
"identifiers": "balcony_weather_station_001",
"name": "Balcony Weather Station"
}
}"""
config_msg_humidity = """{
"name": "BEM 280 Humidity",
"device_class": "humidity",
"unit_of_measurement": "%",
"value_template": "{{ value_json.humidity}}",
"state_topic": "homeassistant/sensor/balcony_weather_station/bme_280_001/state",
"unique_id": "balcony_weather_station_bme_280_001_humidity",
"device": {
"identifiers": "balcony_weather_station_001",
"name": "Balcony Weather Station"
}
}"""
def __init__(self, pubsub):
self.pubsub = pubsub
i2c = busio.I2C(scl=board.GP15, sda=board.GP14)
self.bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c, 0x76)
self.bme280.sea_level_pressure = 1016 # value from https://metar-taf.com/EDDM
self.pubsub.publish('mqtt_pub', {
'topic': self.mqtt_config_topic_temperature,
'msg': self.config_msg_temperature
})
self.pubsub.publish('mqtt_pub', {
'topic': self.mqtt_config_topic_humidity,
'msg': self.config_msg_humidity
})
self.pubsub.publish('mqtt_pub', {
'topic': self.mqtt_config_topic_pressure,
'msg': self.config_msg_pressure
})
self.pubsub.subscribe('tick 120', self._send)
def _send(self, _):
msg = f"""{{
"temperature": {self.bme280.temperature:.1f},
"humidity": {self.bme280.relative_humidity:.1f},
"pressure": {self.bme280.pressure:.1f}
}}"""
self.pubsub.publish('mqtt_pub', {'topic': self.mqtt_state_topic, 'msg': msg})

View file

@ -0,0 +1,16 @@
class PubSub:
def __init__(self):
self.cbs = {}
def _check_msg_type(self, msg_type):
if msg_type not in self.cbs:
self.cbs[msg_type] = []
def subscribe(self, msg_type, cb):
self._check_msg_type(msg_type)
self.cbs[msg_type].append(cb)
def publish(self, msg_type, body):
self._check_msg_type(msg_type)
print(f"publishing msg {msg_type}")
for cb in self.cbs[msg_type]:
cb(body)

View file

@ -0,0 +1,10 @@
class Timer:
def __init__(self, pubsub, timer_length):
self.timer_length = timer_length
self.last_tick = 0
self.pubsub = pubsub
self.topic = f"tick {self.timer_length}"
def tick(self, now):
if (now - self.last_tick) > self.timer_length:
self.last_tick = now
self.pubsub.publish(self.topic, {})