homebridge-ikettle
Version:
Simple HomeKit control for your iKettle. Easy access without faffing with Siri Shortcuts/IFTTT.
159 lines • 6.3 kB
JavaScript
import { initializeApp } from 'firebase/app';
import { getAuth, signInWithEmailAndPassword } from 'firebase/auth';
import { getDatabase, ref, get, child, push, onValue } from 'firebase/database';
import { Observable } from 'rxjs';
import { SUPPORTED_DEVICES } from './models/constants.js';
export class iKettleService {
log;
app;
static instance;
userCredential;
constructor(log) {
this.log = log;
const firebaseConfig = {
apiKey: 'AIzaSyD0IOPaFj2zkMs5_rStrSRQ-m02bLwj88c',
authDomain: 'smarter-live.firebaseapp.com',
databaseURL: 'https://smarter-live.firebaseio.com',
projectId: 'smarter-live',
storageBucket: 'smarter-live.appspot.com',
messagingSenderId: '41919779740'
};
this.app = initializeApp(firebaseConfig);
}
// TODO: Unsure if this is the best way to got about things
static getInstance(log) {
if (!iKettleService.instance) {
iKettleService.instance = new iKettleService(log);
}
return iKettleService.instance;
}
connect(config) {
return new Observable((subscriber) => {
(async () => {
try {
const auth = getAuth(this.app);
this.userCredential = await signInWithEmailAndPassword(auth, config.email, config.password);
const userId = this.userCredential.user.uid;
const user = await this.getUser(userId);
for (const networkId in user.networks_index) {
const network = await this.getNetwork(networkId);
for (const [deviceId, deviceName] of Object.entries(network.associated_devices)) {
const deviceModel = await this.getDevice(deviceId);
if (SUPPORTED_DEVICES.includes(deviceModel.status.device_model)) {
deviceModel.id = deviceId;
deviceModel.userId = userId;
deviceModel.name = deviceName;
subscriber.next(deviceModel);
}
}
}
subscriber.complete();
}
catch (error) {
subscriber.error(error);
}
})();
});
}
async getUser(userId) {
const dbRef = ref(getDatabase());
try {
const userSnapshot = await get(child(dbRef, `users/${userId}`));
if (userSnapshot.exists()) {
return userSnapshot.val();
}
}
catch (error) {
this.log.error(JSON.stringify(error));
}
throw new Error("Couldn't find user details");
}
async getNetwork(networkId) {
const dbRef = ref(getDatabase());
try {
const networkSnapshot = await get(child(dbRef, `networks/${networkId}`));
if (networkSnapshot.exists()) {
return networkSnapshot.val();
}
}
catch (error) {
this.log.error(JSON.stringify(error));
}
throw new Error("Couldn't find network details");
}
async getDevice(deviceId) {
const dbRef = ref(getDatabase());
try {
const networkSnapshot = await get(child(dbRef, `devices/${deviceId}`));
if (networkSnapshot.exists()) {
return networkSnapshot.val();
}
}
catch (error) {
this.log.error(JSON.stringify(error));
}
throw new Error("Couldn't find device details");
}
watchDevice(deviceId) {
const dbRef = ref(getDatabase(), `devices/${deviceId}`);
return new Observable((observer) => {
const unsubscribe = onValue(dbRef, (snapshot) => {
const data = snapshot.val();
observer.next(data);
}, (error) => {
observer.error(error);
});
return () => unsubscribe();
});
}
async startBoil(userId, deviceId) {
const data = { user_id: userId, value: true };
await this.sendCommand(deviceId, 'start_boil', data);
}
async stopBoil(userId, deviceId) {
const data = { user_id: userId, value: true };
await this.sendCommand(deviceId, 'stop_boil', data);
}
async setBoilTemp(userId, deviceId, temp) {
const data = { user_id: userId, value: temp };
await this.sendCommand(deviceId, 'set_boil_temperature', data);
}
async setManualBoilTemp(userId, deviceId, temp) {
const data = { user_id: userId, value: temp };
await this.sendCommand(deviceId, 'set_manual_boil_temperature', data);
}
async setFormulaMode(userId, deviceId, value) {
const data = { user_id: userId, value: value };
await this.sendCommand(deviceId, 'set_formula_mode_enable', data);
}
async setFormulaModeTemperature(userId, deviceId, temp) {
const data = { user_id: userId, value: temp };
await this.sendCommand(deviceId, 'set_formula_mode_temperature', data);
}
async setKeepWarmTime(userId, deviceId, minutes) {
const data = { user_id: userId, value: minutes };
await this.sendCommand(deviceId, 'set_keep_warm_time', data);
}
async sendCommand(deviceId, command, commandData) {
this.authenticate();
const db = getDatabase(this.app);
this.log.debug(`Sending command: ${JSON.stringify(commandData)}`);
try {
const commandRef = ref(db, `devices/${deviceId}/commands/${command}`);
await push(commandRef, commandData);
}
catch (error) {
this.log.error('Error sending command:', error);
}
}
async authenticate() {
// I think this will handle it for me?
await this.userCredential?.user.getIdToken();
return;
const tokenExpiry = new Date(this.userCredential.user.stsTokenManager.expirationTime);
if (tokenExpiry <= new Date()) {
await this.userCredential?.user.getIdToken(true);
}
}
}
//# sourceMappingURL=iKettleService.js.map