@homebridge-plugins/homebridge-smarthq
Version:
The SmartHQ plugin allows you to interact with SmartHQ Devices in HomeKit and with Siri.
159 lines • 7.85 kB
JavaScript
import { Buffer } from 'node:buffer';
import axios from 'axios';
import { interval, skipWhile } from 'rxjs';
import { ERD_TYPES } from '../settings.js';
import { deviceBase } from './device.js';
export class SmartHQIceMaker extends deviceBase {
platform;
device;
opalProductionLimit = Infinity;
oimPowerSvcName = 'Opal Power';
oimProgressSvcName = 'Opal Progress';
oimNightlightSvcName = 'Opal Nightlight';
constructor(platform, accessory, device) {
super(platform, accessory, device);
this.platform = platform;
this.device = device;
if (this.platform.config.options?.OPL) {
this.opalProductionLimit = this.platform.config.options.OPL;
}
this.debugLog(`Opal IceMaker Features: ${JSON.stringify(accessory.context.device.features)}`);
const opalIceMakerPowerService = this.accessory.getService(this.oimPowerSvcName) ?? this.accessory.addService(this.platform.Service.Switch, this.oimPowerSvcName, 'opal-power');
opalIceMakerPowerService
.getCharacteristic(this.platform.Characteristic.On)
.onGet(() => this.readErd(ERD_TYPES.OIM_POWER).then(r => Number.parseInt(r) !== 0))
.onSet(value => this.writeErd(ERD_TYPES.OIM_POWER, value));
const lightbulb = this.accessory.getService(this.oimNightlightSvcName)
|| this.accessory.addService(this.platform.Service.Lightbulb, this.oimNightlightSvcName);
lightbulb.getCharacteristic(this.platform.Characteristic.On)
.onGet(async () => {
const currentLevel = await this.readErd(ERD_TYPES.OIM_LIGHT_LEVEL);
return currentLevel !== '00'; // If the level is not OFF (00), return true (ON)
})
.onSet(async (value) => {
let newState;
const currentLevel = await this.readErd(ERD_TYPES.OIM_LIGHT_LEVEL);
if (value) {
// If turning the light ON, cycle through states
if (currentLevel === '00') {
newState = '02'; // LOW
}
else if (currentLevel === '02') {
newState = '01'; // HIGH
}
}
else {
newState = '00'; // OFF
}
await this.writeErd(ERD_TYPES.OIM_LIGHT_LEVEL, newState);
this.debugLog(`Light state changed to: ${newState}`);
});
lightbulb.getCharacteristic(this.platform.Characteristic.Brightness)
.setProps({
minValue: 0, // Minimum value for brightness (OFF)
maxValue: 100, // Maximum value for brightness (HIGH)
minStep: 50, // Step value (only allow OFF, LOW, and HIGH)
})
.onGet(async () => {
const currentLevel = await this.readErd(ERD_TYPES.OIM_LIGHT_LEVEL);
// Map the light level to brightness values
if (currentLevel === '02') { // HIGH
return 50; // Dim Brightness
}
else if (currentLevel === '01') { // LOW
return 100; // Full Brightness
}
else {
return 0; // OFF
}
})
.onSet(async (value) => {
let newState;
if (value === 50) {
newState = '02'; // LOW (for dim brightness)
}
else if (value === 100) {
newState = '01'; // HIGH (for full brightness)
}
else {
newState = '00'; // OFF
}
await this.writeErd(ERD_TYPES.OIM_LIGHT_LEVEL, newState);
this.debugLog(`Light brightness changed to: ${newState}`);
});
// Opal Progress service
const opalProgressSvc = this.accessory.getService(this.oimProgressSvcName)
|| this.accessory.addService(this.platform.Service.Fanv2, this.oimProgressSvcName);
opalProgressSvc.getCharacteristic(this.platform.Characteristic.Active)
.setProps({
// Omit PAIRED_WRITE to prevent user from setting Active to False, is read only.
perms: ["ev" /* Perms.EVENTS */, "pr" /* Perms.PAIRED_READ */],
});
opalProgressSvc.getCharacteristic(this.platform.Characteristic.RotationSpeed)
.setProps({
format: "int" /* Formats.INT */,
unit: "percentage" /* Units.PERCENTAGE */,
minStep: 1,
minValue: 0,
maxValue: 100,
perms: ["ev" /* Perms.EVENTS */, "pr" /* Perms.PAIRED_READ */],
})
.removeOnGet()
.removeOnSet();
// Get default accessory information service
const opalIceMakerMetadataService = this.accessory.getService('');
// Metadata for Device Form, Opal Production Limit
opalIceMakerMetadataService
?.getCharacteristic(this.platform.Characteristic.ProductData)
.onGet(() => 'l=Production_Limit&i=OPL&t=number&d=0&p=1-100,_0_to_unset');
// Start an update interval
interval(this.deviceRefreshRate * 1000)
.pipe(skipWhile(() => !this.accessory.services.find(svc => svc.displayName === this.oimProgressSvcName)))
.subscribe(async () => {
const currentProductionValue = await this.getProductionValue();
const oimPowerSvc = this.accessory.services.find(accSvc => accSvc.displayName === this.oimPowerSvcName);
const oimProgressSvc = this.accessory.services.find(accSvc => accSvc.displayName === this.oimProgressSvcName);
// Math.min: If opalProductionLimit is Infinity (default/unset value), set to 100. Otherwise use the opalProductionLimit that is less than 100
const progressBarProductionValue = Math.floor((100 / Math.min(this.opalProductionLimit, 100)) * currentProductionValue);
oimProgressSvc?.updateCharacteristic(this.platform.Characteristic.RotationSpeed, Math.min(progressBarProductionValue, 100));
oimProgressSvc?.updateCharacteristic(this.platform.Characteristic.Active, currentProductionValue > 0 ? 1 : 0);
// If opalProductLimit is Infinity (default/unset value), the machine will turn off by itself
if (currentProductionValue > this.opalProductionLimit) {
oimPowerSvc?.setCharacteristic(this.platform.Characteristic.On, false);
}
});
}
async getProductionValue() {
try {
const erdVal = await this.readErd(ERD_TYPES.OIM_PRODUCTION);
const hexToIntVal = Buffer.from(erdVal, 'hex').readUInt8(0);
const productionValue = Math.min(hexToIntVal, 100);
const completionistMsg = hexToIntVal > 100 ? `, Completion: ${hexToIntVal}` : '';
this.debugSuccessLog(`Progress Svc: Value: ${productionValue}, Limit: ${this.opalProductionLimit}${completionistMsg}`);
return productionValue;
}
catch (error) {
const typedErr = error;
this.errorLog(`Failed to read production value: ${typedErr.message}`);
// Default to 0 if there's an error
return 0;
}
}
async readErd(erd) {
const d = await axios
.get(`/appliance/${this.accessory.context.device.applianceId}/erd/${erd}`);
return String(d.data.value);
}
async writeErd(erd, value) {
await axios
.post(`/appliance/${this.accessory.context.device.applianceId}/erd/${erd}`, {
kind: 'appliance#erdListEntry',
userId: this.accessory.context.userId,
applianceId: this.accessory.context.device.applianceId,
erd,
value: typeof value === 'boolean' ? (value ? '01' : '00') : value,
});
return undefined;
}
}
//# sourceMappingURL=icemaker.js.map