eq3-bluetooth-thermostat
Version:
Use this library to control your "equiva Bluetooth Smart" or "eQ-3 Bluetooth Smart" radiator thermostats with javascript. Works both in the Browser (with Web Bluetooth) and with Node.js (using Bluez, linux only).
94 lines (76 loc) • 2.59 kB
JavaScript
;
export const serviceUuid = "3e135142-654f-9090-134a-a6ff5bb77046";
const writeUuid = "3fa4585a-ce4a-3bad-db4b-b8df8179ea09";
const notificationUuid = "d0e8434d-cd29-0996-af41-6c90f4e0eb2a";
function addListener(target, name, listener) {
if ("addListener" in target) target.addListener(name, listener);
else if ("addEventListener" in target) target.addEventListener(name, listener);
else throw new Error("can't add event listener");
}
function removeListener(target, name, listener) {
if ("removeListener" in target) target.removeListener(name, listener);
else if ("removeEventListener" in target) target.removeEventListener(name, listener);
else throw new Error("can't remove event listener");
}
function isBitSet(value, bit) {
return (value & bit) == bit;
}
function parseStatus(data) {
return {
status: {
manual: isBitSet(data.getUint8(2), 1),
holiday: isBitSet(data.getUint8(2), 2),
boost: isBitSet(data.getUint8(2), 4),
dst: isBitSet(data.getUint8(2), 8),
openWindow: isBitSet(data.getUint8(2), 16),
lowBattery: isBitSet(data.getUint8(2), 128),
},
valvePosition: data.getUint8(3),
targetTemperature: data.getUint8(5) / 2,
};
}
export async function request(device, payload) {
let server = await device.gatt.connect();
let service = await server.getPrimaryService(serviceUuid);
let writes = await service.getCharacteristic(writeUuid);
let notifications = await service.getCharacteristic(notificationUuid);
await notifications.startNotifications();
let response = await new Promise(resolve => {
let listener = e => {
removeListener(notifications, "characteristicvaluechanged", listener);
resolve(parseStatus(e.target.value));
};
addListener(notifications, "characteristicvaluechanged", listener);
writes.writeValue(payload).catch(e => {
removeListener(notifications, "characteristicvaluechanged", listener);
reject(e);
});
});
await notifications.stopNotifications();
await server.disconnect();
return response;
}
export async function getStatus(device) {
let now = new Date();
return await request(device, new Uint8Array([
0x03,
now.getFullYear() % 100,
now.getMonth() + 1,
now.getDate(),
now.getHours(),
now.getMinutes(),
now.getSeconds(),
]).buffer);
}
export async function setTargetTemperature(device, temperature) {
return await request(device, new Uint8Array([
0x41,
Math.round(temperature*2),
]).buffer);
}
export async function setBoost(device, enabled) {
return await request(device, new Uint8Array([
0x45,
enabled ? 0x01 : 0x00,
]).buffer);
}