131 lines
4.3 KiB
Python
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
|