Replace paho-mqtt with aiomqtt
This commit is contained in:
parent
37e349b8d1
commit
07cab2d2d1
3 changed files with 94 additions and 87 deletions
129
main.py
129
main.py
|
@ -1,17 +1,15 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# Bitlair HobbyBot
|
import asyncio
|
||||||
|
|
||||||
from time import sleep
|
|
||||||
from discord import Intents
|
|
||||||
from discord.ext import commands
|
|
||||||
from discord_webhook import DiscordWebhook, DiscordEmbed
|
|
||||||
import pytz
|
|
||||||
import paho.mqtt.client as mqtt
|
|
||||||
import paho.mqtt.subscribe as subscribe
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
import aiomqtt
|
||||||
|
import pytz
|
||||||
|
from discord import Intents
|
||||||
|
from discord.ext import commands
|
||||||
|
from discord_webhook import DiscordEmbed, DiscordWebhook
|
||||||
|
|
||||||
mqtt_host = os.getenv("MQTT_HOST")
|
mqtt_host = os.getenv("MQTT_HOST")
|
||||||
if not mqtt_host:
|
if not mqtt_host:
|
||||||
|
@ -37,13 +35,11 @@ intents.members = True
|
||||||
HobbyBot = commands.Bot(command_prefix="!", description="Bitlair Bot", intents=intents)
|
HobbyBot = commands.Bot(command_prefix="!", description="Bitlair Bot", intents=intents)
|
||||||
|
|
||||||
|
|
||||||
def mqtt_get_one(topic):
|
async def mqtt_get_one(topic, timeout=20):
|
||||||
try:
|
async with asyncio.timeout(timeout):
|
||||||
msg = subscribe.simple(topic, hostname=mqtt_host, keepalive=10)
|
async with aiomqtt.Client(mqtt_host) as mq:
|
||||||
return msg.payload.decode()
|
await mq.subscribe(topic)
|
||||||
except Exception as err:
|
return await anext(mq.messages)
|
||||||
print(err)
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
# Define bot commands
|
# Define bot commands
|
||||||
|
@ -56,35 +52,52 @@ async def on_ready():
|
||||||
@HobbyBot.command(description="Bitlair Space State")
|
@HobbyBot.command(description="Bitlair Space State")
|
||||||
async def state(ctx):
|
async def state(ctx):
|
||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
spaceState = mqtt_get_one("bitlair/state")
|
try:
|
||||||
if spaceState == "open":
|
msg = await mqtt_get_one("bitlair/state")
|
||||||
|
space_state = msg.payload.decode("ascii")
|
||||||
|
if space_state == "open":
|
||||||
await ctx.send("Bitlair is OPEN! :sunglasses:")
|
await ctx.send("Bitlair is OPEN! :sunglasses:")
|
||||||
elif spaceState == "closed":
|
elif space_state == "closed":
|
||||||
await ctx.send("Bitlair is closed :pensive:")
|
await ctx.send("Bitlair is closed :pensive:")
|
||||||
|
except Exception as err:
|
||||||
|
await ctx.send("Meh, stuk")
|
||||||
|
raise err
|
||||||
|
|
||||||
|
|
||||||
# !co2
|
# !co2
|
||||||
@HobbyBot.command(description="co2 levels")
|
@HobbyBot.command(description="co2 levels")
|
||||||
async def co2(ctx):
|
async def co2(ctx):
|
||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
hoofdruimte = mqtt_get_one("bitlair/climate/hoofdruimte_ingang/co2_ppm")
|
try:
|
||||||
await ctx.send("Hoofdruimte: %s ppm\n" % hoofdruimte)
|
msg = await mqtt_get_one("bitlair/climate/hoofdruimte/co2_ppm")
|
||||||
|
await ctx.send(f"Hoofdruimte: {msg.payload.decode('ascii')} ppm\n")
|
||||||
|
except Exception as err:
|
||||||
|
await ctx.send("Meh, stuk")
|
||||||
|
raise err
|
||||||
|
|
||||||
|
|
||||||
# !temp
|
# !temp
|
||||||
@HobbyBot.command(description="Temperature")
|
@HobbyBot.command(description="Temperature")
|
||||||
async def temp(ctx):
|
async def temp(ctx):
|
||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
hoofdruimte = mqtt_get_one("bitlair/climate/hoofdruimte_ingang/temperature_c")
|
try:
|
||||||
await ctx.send("Hoofdruimte: %s °C\n" % hoofdruimte)
|
msg = await mqtt_get_one("bitlair/climate/hoofdruimte/temperature_c")
|
||||||
|
await ctx.send(f"Hoofdruimte: {msg.payload.decode('ascii')} °C\n")
|
||||||
|
except Exception as err:
|
||||||
|
await ctx.send("Meh, stuk")
|
||||||
|
raise err
|
||||||
|
|
||||||
|
|
||||||
# !humid
|
# !humid
|
||||||
@HobbyBot.command(description="Humidity")
|
@HobbyBot.command(description="Humidity")
|
||||||
async def humid(ctx):
|
async def humid(ctx):
|
||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
hoofdruimte = mqtt_get_one("bitlair/climate/hoofdruimte_ingang/humidity_pct")
|
try:
|
||||||
await ctx.send("Hoofdruimte: %s pct\n" % hoofdruimte)
|
msg = await mqtt_get_one("bitlair/climate/hoofdruimte/humidity_pct")
|
||||||
|
await ctx.send(f"Hoofdruimte: {msg.payload.decode('ascii')} pct\n")
|
||||||
|
except Exception as err:
|
||||||
|
await ctx.send("Meh, stuk")
|
||||||
|
raise err
|
||||||
|
|
||||||
|
|
||||||
# !np
|
# !np
|
||||||
|
@ -94,21 +107,12 @@ async def np(ctx):
|
||||||
await ctx.send("Now playing: Darude - Sandstorm")
|
await ctx.send("Now playing: Darude - Sandstorm")
|
||||||
|
|
||||||
|
|
||||||
# define mqtt client stuff
|
|
||||||
#
|
|
||||||
# subscribe to topics
|
|
||||||
def on_connect(client, userdata, flags, rc):
|
|
||||||
client.subscribe("bitlair/alarm")
|
|
||||||
client.subscribe("bitlair/state")
|
|
||||||
client.subscribe("bitlair/state/djo")
|
|
||||||
client.subscribe("bitlair/photos")
|
|
||||||
|
|
||||||
|
|
||||||
def webhook_message(msg):
|
def webhook_message(msg):
|
||||||
webhook = DiscordWebhook(url=webhook_url, rate_limit_retry=True, content=msg)
|
webhook = DiscordWebhook(url=webhook_url, rate_limit_retry=True, content=msg)
|
||||||
webhook.execute()
|
webhook.execute()
|
||||||
|
|
||||||
|
|
||||||
|
async def event_task():
|
||||||
retained = {
|
retained = {
|
||||||
"bitlair/alarm",
|
"bitlair/alarm",
|
||||||
"bitlair/photos",
|
"bitlair/photos",
|
||||||
|
@ -116,47 +120,38 @@ retained = {
|
||||||
"bitlair/state/djo",
|
"bitlair/state/djo",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async with aiomqtt.Client(mqtt_host) as mq:
|
||||||
|
await asyncio.gather(*[mq.subscribe(topic) for topic in retained])
|
||||||
|
async for msg in mq.messages:
|
||||||
|
# Retained messages trigger an initial message on connecting. Prevent relaying them to Discord on startup.
|
||||||
|
if str(msg.topic) in retained:
|
||||||
|
retained.remove(str(msg.topic))
|
||||||
|
continue
|
||||||
|
|
||||||
# post to mqtt discord channel when state changes
|
payload = msg.payload.decode("ascii")
|
||||||
def on_message(client, userdata, msg):
|
|
||||||
try:
|
|
||||||
topic = msg.topic
|
|
||||||
msg = msg.payload.decode()
|
|
||||||
|
|
||||||
# Retained messages trigger an initial message on connecting. Prevent relaying them to
|
if msg.topic.matches("bitlair/alarm"):
|
||||||
# Discord on startup.
|
webhook_message(f"Alarm: {payload}")
|
||||||
if topic in retained:
|
elif msg.topic.matches("bitlair/state"):
|
||||||
retained.remove(topic)
|
webhook_message(f"Bitlair is now {payload.upper()}")
|
||||||
return
|
elif msg.topic.matches("bitlair/state/djo"):
|
||||||
|
webhook_message(f"DJO is now {payload.upper()}")
|
||||||
if topic == "bitlair/alarm":
|
elif msg.topic.matches("bitlair/photos"):
|
||||||
webhook_message("Alarm: %s" % msg)
|
|
||||||
elif topic == "bitlair/state":
|
|
||||||
webhook_message("Bitlair is now %s" % msg.upper())
|
|
||||||
elif topic == "bitlair/state/djo":
|
|
||||||
webhook_message("DJO is now %s" % msg.upper())
|
|
||||||
elif topic == "bitlair/photos":
|
|
||||||
webhook = DiscordWebhook(url=webhook_url, rate_limit_retry=True)
|
webhook = DiscordWebhook(url=webhook_url, rate_limit_retry=True)
|
||||||
embed = DiscordEmbed(title="WIP Cam", color="fc5d1d")
|
embed = DiscordEmbed(title="WIP Cam", color="fc5d1d")
|
||||||
embed.set_url("https://bitlair.nl/fotos/view/" + msg)
|
embed.set_url(f"https://bitlair.nl/fotos/view/{payload}")
|
||||||
embed.set_image("https://bitlair.nl/fotos/photos/" + msg)
|
embed.set_image(f"https://bitlair.nl/fotos/photos/{payload}")
|
||||||
webhook.add_embed(embed)
|
webhook.add_embed(embed)
|
||||||
webhook.execute()
|
webhook.execute()
|
||||||
else:
|
else:
|
||||||
return
|
continue
|
||||||
sleep(1) # Prevent triggering rate limits.
|
sleep(1) # Prevent triggering rate limits.
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
|
|
||||||
|
|
||||||
client = mqtt.Client()
|
async def main():
|
||||||
client.on_connect = on_connect
|
t1 = asyncio.create_task(HobbyBot.start(token))
|
||||||
client.on_message = on_message
|
t2 = asyncio.create_task(event_task())
|
||||||
client.connect(mqtt_host, 1883, 60)
|
await asyncio.gather(t1, t2)
|
||||||
|
|
||||||
# Start mqtt loop and discord bot
|
|
||||||
client.loop_start()
|
|
||||||
HobbyBot.run(token)
|
|
||||||
|
|
||||||
# Exit when bot crashes
|
asyncio.run(main())
|
||||||
client.loop_stop(force=True)
|
|
||||||
|
|
|
@ -5,8 +5,8 @@ description = "Bitlair Discord Bot"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aiomqtt>=2.4.0",
|
||||||
"discord-py>=2.5.2",
|
"discord-py>=2.5.2",
|
||||||
"discord-webhook>=1.4.1",
|
"discord-webhook>=1.4.1",
|
||||||
"paho-mqtt>=2.1.0",
|
|
||||||
"pytz>=2025.2",
|
"pytz>=2025.2",
|
||||||
]
|
]
|
||||||
|
|
16
uv.lock
generated
16
uv.lock
generated
|
@ -64,6 +64,18 @@ wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/1e/3c/143831b32cd23b5263a995b2a1794e10aa42f8a895aae5074c20fda36c07/aiohttp-3.11.18-cp313-cp313-win_amd64.whl", hash = "sha256:bdd619c27e44382cf642223f11cfd4d795161362a5a1fc1fa3940397bc89db01", size = 437658, upload-time = "2025-04-21T09:42:29.209Z" },
|
{ url = "https://files.pythonhosted.org/packages/1e/3c/143831b32cd23b5263a995b2a1794e10aa42f8a895aae5074c20fda36c07/aiohttp-3.11.18-cp313-cp313-win_amd64.whl", hash = "sha256:bdd619c27e44382cf642223f11cfd4d795161362a5a1fc1fa3940397bc89db01", size = 437658, upload-time = "2025-04-21T09:42:29.209Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aiomqtt"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "paho-mqtt" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/45/9a/863bc34c64bc4acb9720a9950bfc77d6f324640cdf1f420bb5d9ee624975/aiomqtt-2.4.0.tar.gz", hash = "sha256:ab0f18fc5b7ffaa57451c407417d674db837b00a9c7d953cccd02be64f046c17", size = 82718, upload-time = "2025-05-03T20:21:27.748Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/0c/2720665998d97d3a9521c03b138a22247e035ba54c4738e934da33c68699/aiomqtt-2.4.0-py3-none-any.whl", hash = "sha256:721296e2b79df5f6c7c4dfc91700ae0166953a4127735c92637859619dbd84e4", size = 15908, upload-time = "2025-05-03T20:21:26.337Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiosignal"
|
name = "aiosignal"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
@ -174,17 +186,17 @@ name = "discord-bot"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
{ name = "aiomqtt" },
|
||||||
{ name = "discord-py" },
|
{ name = "discord-py" },
|
||||||
{ name = "discord-webhook" },
|
{ name = "discord-webhook" },
|
||||||
{ name = "paho-mqtt" },
|
|
||||||
{ name = "pytz" },
|
{ name = "pytz" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
|
{ name = "aiomqtt", specifier = ">=2.4.0" },
|
||||||
{ name = "discord-py", specifier = ">=2.5.2" },
|
{ name = "discord-py", specifier = ">=2.5.2" },
|
||||||
{ name = "discord-webhook", specifier = ">=1.4.1" },
|
{ name = "discord-webhook", specifier = ">=1.4.1" },
|
||||||
{ name = "paho-mqtt", specifier = ">=2.1.0" },
|
|
||||||
{ name = "pytz", specifier = ">=2025.2" },
|
{ name = "pytz", specifier = ">=2025.2" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue