Initial commit
commit
52f9b6bd6a
@ -0,0 +1,16 @@
|
|||||||
|
# Use any Home Assistant lights with Hyperion
|
||||||
|
|
||||||
|
## Setup:
|
||||||
|
|
||||||
|
1. Clone this respository;
|
||||||
|
|
||||||
|
2. Edit `config.js` to your liking. There are two light types: `rgb` and
|
||||||
|
`dim`. The names of the lights must represent light entity id that are
|
||||||
|
present in Home Assistant;
|
||||||
|
|
||||||
|
3. Generate a Home Assistant Token using the /profile view (scroll to the bottom
|
||||||
|
of the view);
|
||||||
|
4. Setup a new LED instance in Hyperion using the `udpraw` controller. Set the
|
||||||
|
number of lights to the amount of lights you've specified in the `config.js`
|
||||||
|
file;
|
||||||
|
5. Run `HA_TOKEN=your_token12341234123432 node .`
|
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = {
|
||||||
|
lights: [
|
||||||
|
{ id: "lampa_na_parapecie_rgb_light", type: "rgb" },
|
||||||
|
{
|
||||||
|
id: "ikea_of_sweden_tradfribulbe27wsglobeopal1055lm_light" /* lampa stojąca */,
|
||||||
|
type: "dim",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
@ -0,0 +1,5 @@
|
|||||||
|
const TOKEN = process.env.HA_TOKEN;
|
||||||
|
const HA_URL = process.env.HA_URL || "http://127.0.0.1:8123";
|
||||||
|
const PORT = parseInt(process.env.HA_BRIDGE_PORT || "41234");
|
||||||
|
|
||||||
|
module.exports = { TOKEN, HA_URL, PORT };
|
@ -0,0 +1,42 @@
|
|||||||
|
const dgram = require("node:dgram");
|
||||||
|
const server = dgram.createSocket("udp4");
|
||||||
|
|
||||||
|
const { lights } = require("./config.js");
|
||||||
|
const light_loop = require("./light-loop.js");
|
||||||
|
const latest_color = require("./latest_color.js");
|
||||||
|
const { TOKEN, PORT } = require("./env.js");
|
||||||
|
|
||||||
|
server.on("error", (err) => {
|
||||||
|
console.error(`server error:\n${err.stack}`);
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
const debug = false;
|
||||||
|
const max_brightness = 0.8;
|
||||||
|
|
||||||
|
if (!TOKEN) {
|
||||||
|
throw new Error(
|
||||||
|
"Provide the Home Assistant Long Lived Token as a HA_TOKEN environment variable. Go to /profile in Home Assistant and scroll down.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Remember to set the Hyperion output controller type to UDPRAW and set it to output ${lights.length} lights`,
|
||||||
|
);
|
||||||
|
|
||||||
|
server.on("message", (msg, rinfo) => {
|
||||||
|
latest_color.set(Array.from(msg).map((e) => parseInt(e)));
|
||||||
|
if (debug) {
|
||||||
|
console.log("Received colors:", latest_color.get());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on("listening", async () => {
|
||||||
|
const address = server.address();
|
||||||
|
console.log(`server listening ${address.address}:${address.port}`);
|
||||||
|
for (let i in lights) {
|
||||||
|
light_loop(i, max_brightness, debug);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.bind(PORT);
|
@ -0,0 +1,20 @@
|
|||||||
|
const { lights } = require("./config.js");
|
||||||
|
|
||||||
|
let latest_color = [];
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
for (let i in lights) {
|
||||||
|
for (let j = 1; j <= 3; j++) {
|
||||||
|
latest_color.push(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
get: () => {
|
||||||
|
return latest_color;
|
||||||
|
},
|
||||||
|
|
||||||
|
set: (new_color) => {
|
||||||
|
latest_color = new_color;
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,59 @@
|
|||||||
|
const { HA_URL, TOKEN } = require("./env.js");
|
||||||
|
|
||||||
|
const { sleep } = require("./util.js");
|
||||||
|
const latest_color = require("./latest_color.js");
|
||||||
|
const { lights } = require("./config.js");
|
||||||
|
|
||||||
|
async function send_color(light_data, color, max_brightness, debug) {
|
||||||
|
const brightness = (color[0] + color[1] + color[2]) / 3 / 255;
|
||||||
|
let body = { entity_id: `light.${light_data.id}`, transition: 0.18 };
|
||||||
|
|
||||||
|
if (light_data.type == "rgb") {
|
||||||
|
body.rgb_color = color;
|
||||||
|
body.brightness = Math.floor(
|
||||||
|
Math.max(...latest_color.get()) * max_brightness,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// body.brightness = Math.max(1, Math.round(255 * brightness)); // 0 seems to turn it off and make it slower to react
|
||||||
|
body.brightness = Math.floor(
|
||||||
|
Math.max(...latest_color.get()) * max_brightness,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(`${HA_URL}/api/services/light/turn_on`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${TOKEN}`,
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
}).then(async (response) => {
|
||||||
|
if (debug) {
|
||||||
|
console.log(await response.text());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = async function light_loop(light_index, max_brightness, debug) {
|
||||||
|
let last_time = 0;
|
||||||
|
let last_color = [0, 0, 0];
|
||||||
|
while (true) {
|
||||||
|
last_time = Date.now();
|
||||||
|
const from = 3 * light_index;
|
||||||
|
const to = 3 * (light_index + 1);
|
||||||
|
const current_color = latest_color.get().slice(from, to);
|
||||||
|
let is_changed = false;
|
||||||
|
for (i in last_color) {
|
||||||
|
if (last_color[i] != current_color[i]) {
|
||||||
|
is_changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_changed) {
|
||||||
|
await send_color(lights[light_index], current_color);
|
||||||
|
last_color = current_color;
|
||||||
|
} else {
|
||||||
|
await sleep(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "hyperion-ha",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
Loading…
Reference in New Issue