bot/commands/__init__.py

131 lines
4.3 KiB
Python

import asyncio
import aiomqtt
from discord_webhook import DiscordEmbed
import commands.bottleclip as bottleclip
_mqtt_host = None
def setup(bot, mqtt_host):
global _mqtt_host
_mqtt_host = mqtt_host
# !state
bot.command(description="Bitlair Space State")(state)
# !co2
bot.command(description="co2 levels")(co2)
# !temp
bot.command(description="Temperature")(temp)
# !humid
bot.command(description="Humidity")(humid)
# !np
bot.command(description="Now Playing")(np)
# !bottleclip
bot.command(
name="bottleclip",
description="Generate a bottle-clip STL file suitable for printing",
)(bottleclip.command)
async def mqtt_get_one(topic, timeout=20):
async with asyncio.timeout(timeout):
async with aiomqtt.Client(_mqtt_host) as mq:
await mq.subscribe(topic)
return await anext(mq.messages)
async def state(ctx):
async with ctx.typing():
try:
msg = await mqtt_get_one("bitlair/state")
space_state = msg.payload.decode("ascii")
if space_state == "open":
await ctx.reply("Bitlair is OPEN! :sunglasses:")
elif space_state == "closed":
await ctx.reply("Bitlair is closed :pensive:")
except Exception as err:
await ctx.reply("Meh, stuk")
raise err
async def co2(ctx, where="hoofdruimte"):
async with ctx.typing():
try:
msg = await mqtt_get_one(f"bitlair/climate/{where}/co2_ppm")
await ctx.reply(f"{where}: {msg.payload.decode('ascii')} ppm\n")
except Exception as err:
await ctx.reply("Meh, stuk")
raise err
async def temp(ctx, where="hoofdruimte"):
async with ctx.typing():
try:
msg = await mqtt_get_one(f"bitlair/climate/{where}/temperature_c")
await ctx.reply(f"{where}: {msg.payload.decode('ascii')} °C\n")
except Exception as err:
await ctx.reply("Meh, stuk")
raise err
async def humid(ctx, where="hoofdruimte"):
async with ctx.typing():
try:
msg = await mqtt_get_one(f"bitlair/climate/{where}/humidity_pct")
await ctx.reply(f"{where}: {msg.payload.decode('ascii')} pct\n")
except Exception as err:
await ctx.reply("Meh, stuk")
raise err
async def np(ctx):
async with ctx.typing():
await ctx.reply("Now playing: Darude - Sandstorm")
async def run_events(mqtt_host):
retained = {
"bitlair/doorduino/doorbell",
"bitlair/doorduino/dooropen", # Unused. Payload is 0|1 like the doorbell.
"bitlair/doorduino/lockstate",
"bitlair/photos",
"bitlair/state",
"bitlair/state/djo",
}
non_retained = {
"bitlair/alarm/events",
}
async with aiomqtt.Client(mqtt_host) as mq:
await asyncio.gather(
*[mq.subscribe(topic) for topic in retained | non_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
payload = msg.payload.decode("ascii")
if msg.topic.matches("bitlair/alarm/events"):
if not payload.startswith("RP"): # Ignore communication tests.
yield f"Alarm event: {payload}"
elif msg.topic.matches("bitlair/state"):
yield f"Bitlair is now {payload.upper()}"
elif msg.topic.matches("bitlair/state/djo"):
yield f"DJO is now {payload.upper()}"
elif msg.topic.matches("bitlair/doorduino/lockstate"):
yield f"Doorduino: lockstate {payload.upper()}"
elif msg.topic.matches("bitlair/doorduino/doorbell") and payload == "1":
yield "DEURBEL! Open de deur beneden!"
elif msg.topic.matches("bitlair/photos"):
embed = DiscordEmbed(title="WIP Cam", color="fc5d1d")
embed.set_url(f"https://bitlair.nl/fotos/view/{payload}")
embed.set_image(f"https://bitlair.nl/fotos/photos/{payload}")
yield embed
else:
continue