iobroker.roborock
Version:
117 lines (101 loc) • 3.68 kB
text/typescript
import type { FeatureDependencies } from "../../baseDeviceFeatures";
import { DeviceStateWriter } from "../../deviceStateWriter";
import { VACUUM_CONSTANTS } from "../vacuumConstants";
export class B01ConsumableService {
private readonly stateWriter: DeviceStateWriter;
constructor(
private deps: FeatureDependencies,
private duid: string
) {
this.stateWriter = new DeviceStateWriter(deps, duid);
}
private readonly PROP_MAP: Record<string, string> = {
main_brush: "main_brush_work_time",
side_brush: "side_brush_work_time",
hypa: "filter_work_time",
main_sensor: "sensor_dirty_time",
filter_element: "filter_element_work_time"
};
public async updateConsumables(data?: unknown): Promise<void> {
let resultObj: Record<string, any> | undefined;
if (data) {
resultObj = data as Record<string, any>;
} else {
const props = VACUUM_CONSTANTS.b01SettingsProps;
const result = await this.deps.adapter.requestsHandler.sendRequest(this.duid, "prop.get", { property: props });
if (Array.isArray(result) && result.length === props.length) {
resultObj = {};
for (let i = 0; i < props.length; i++) {
const key = props[i];
const mappedKey = this.PROP_MAP[key] || key;
resultObj[mappedKey] = result[i];
}
} else if (typeof result === "object" && result !== null) {
resultObj = result as Record<string, any>;
}
}
if (resultObj) {
await this.processConsumables(resultObj);
}
}
protected async processConsumables(resultObj: Record<string, unknown>): Promise<void> {
await this.stateWriter.ensureFolder("consumables");
await this.stateWriter.ensureFolder("resetConsumables");
for (const key in resultObj) {
if (!key.endsWith("_work_time") && !key.endsWith("_work_times") && !key.endsWith("_dirty_time")) {
continue;
}
let val = resultObj[key] as number;
// Consumable name normalization
const deviceName = key.replace(/(_work_times|_work_time|_dirty_time)$/, "");
const translationKey = VACUUM_CONSTANTS.consumableTranslationKeys[deviceName as keyof typeof VACUUM_CONSTANTS.consumableTranslationKeys];
const localizedName = translationKey ? this.deps.adapter.translationManager.get(translationKey, deviceName) : deviceName;
let unit = "";
let suffix = "";
if (key.endsWith("_work_times")) {
unit = "cycles";
suffix = " cycles";
} else if (key.endsWith("_work_time") || key.endsWith("_dirty_time")) {
const totalSeconds = this.getConsumableLifeSpan(deviceName) * 3600;
if (totalSeconds > 0) {
// Convert seconds used to remaining hours
val = Math.max(0, Math.round((totalSeconds - val) / 3600));
unit = "h";
suffix = " remaining time";
} else {
unit = "s";
suffix = " work time";
}
}
// Create/Update the raw state exactly as it comes from the robot (but converted to h if applicable)
await this.stateWriter.ensureAndSetValueState(`consumables.${key}`, {
name: `${localizedName}${suffix}`,
type: "number",
role: "value",
unit: unit,
write: false
}, val);
// Reset Button
const resetKey = `reset_${deviceName}`;
await this.stateWriter.ensureState(`resetConsumables.${resetKey}`, {
name: `Reset ${localizedName}`,
type: "boolean",
role: "button",
write: true,
def: false
}, { resetParam: key });
}
}
private getConsumableLifeSpan(deviceName: string): number {
switch (deviceName) {
case "main_brush": return 300;
case "side_brush": return 200;
case "filter":
case "filter_element": return 150;
case "sensor": return 30;
case "strainer": return 150;
case "cleaning_brush": return 300;
default: return 0;
}
}
}