Skip to content

Instantly share code, notes, and snippets.

@seanghay
Last active February 10, 2024 13:36
Show Gist options
  • Save seanghay/bd68e90048892cc05984aaf11af05c92 to your computer and use it in GitHub Desktop.
Save seanghay/bd68e90048892cc05984aaf11af05c92 to your computer and use it in GitHub Desktop.
Xiaomi Light Bar Remote via RPi + nRF24
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Xiaomi LightBar Controller</title>
<style>
* {
box-sizing: border-box;
font-family: inherit;
}
h1,
h2,
h3,
h4,
h5,
h6,
p,
a,
button {
padding: 0;
margin: 0;
}
:root {
font-size: 14px;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
text-rendering: optimizeLegibility;
color-scheme: dark;
background-color: #333333;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cg fill='%23ffffff' fill-opacity='0.025'%3E%3Cpolygon fill-rule='evenodd' points='8 4 12 6 8 8 6 12 4 8 0 6 4 4 6 0 8 4'/%3E%3C/g%3E%3C/svg%3E");
}
.container {
padding: 1rem;
max-width: 512px;
margin: 0 auto;
}
.flex {
display: flex;
}
.flex-col {
display: flex;
flex-direction: column;
}
.gap {
gap: 1.2rem;
}
.text-center {
text-align: center;
}
.flex-1 {
flex: 1;
}
button {
font-size: 1.1rem;
font-weight: 600;
padding: 1rem 1.8rem;
border-radius: .6rem;
border: 2px solid rgba(255, 255, 255, .1);
background-color: rgba(255, 255, 255, .1);
transition: .2s ease;
user-select: none;
color: rgba(255, 255, 255, .8);
}
button:hover {
color: rgba(255, 255, 255, 1);
border: 2px solid rgba(255, 255, 255, .2);
background-color: rgba(255, 255, 255, .2);
}
button:active {
color: rgba(255, 255, 255, 1);
border: 2px solid rgba(255, 255, 255, .15);
background-color: rgba(255, 255, 255, .15);
}
.self-center {
align-self: center;
}
</style>
<script>
async function invoke(id) {
try {
await fetch("/i/" + id)
} catch (e) {
console.error(e)
}
}
</script>
</head>
<body>
<div class="container flex-col gap">
<svg class="self-center" xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 24 24">
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="1.5">
<path
d="M14.5 19.5h-5m5 0c0-.713 0-1.07.038-1.307c.123-.763.144-.812.631-1.412c.151-.186.711-.688 1.832-1.692A7.481 7.481 0 0 0 19.5 9.5a7.47 7.47 0 0 0-.427-2.5M14.5 19.5c0 .935 0 1.402-.201 1.75a1.5 1.5 0 0 1-.549.549C13.402 22 12.935 22 12 22s-1.402 0-1.75-.201a1.5 1.5 0 0 1-.549-.549c-.201-.348-.201-.815-.201-1.75m0 0c0-.713 0-1.07-.038-1.307c-.123-.763-.144-.812-.631-1.412c-.151-.186-.712-.688-1.832-1.692A7.5 7.5 0 0 1 15.744 3" />
<path stroke-linejoin="round" d="m12.786 8.5l-2.143 3h3l-2.143 3" />
</g>
</svg>
<h3 class="text-center">Light Bar Controller</h3>
<button onclick="invoke('on_off')">ON / OFF</button>
<p>Brightness</p>
<div class="flex gap">
<button onclick="invoke('lower')" class="flex-1">-</button>
<button onclick="invoke('higher')" class="flex-1">+</button>
</div>
<p>Temperature</p>
<div class="flex gap">
<button onclick="invoke('cooler')" class="flex-1">-</button>
<button onclick="invoke('warmer')" class="flex-1">+</button>
</div>
<p>Settings</p>
<button onclick="invoke('reset')">Reset</button>
</div>
</body>
</html>
from fastapi import FastAPI
from fastapi.responses import Response
from fastapi.staticfiles import StaticFiles
from xiaomi_lightbar import Lightbar
bar = Lightbar(25, 0, 0x522842)
app = FastAPI()
@app.get("/i/{id}")
def invoke(id: str):
if id == "on_off":
bar.on_off()
if id == "lower":
bar.lower()
if id == "higher":
bar.higher()
if id == "cooler":
bar.cooler()
if id == "warmer":
bar.warmer()
if id == "reset":
bar.reset()
return Response(None, 200)
app.mount("/", StaticFiles(directory="public", html=True), name="public")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment