iobroker.roborock
Version:
247 lines (221 loc) • 8.04 kB
text/typescript
// Define types for the dataset structure
interface Dataset {
meta: {
languages: string[];
generated: string;
};
fault_codes: {
[code: string]: {
internal?: string;
[lang: string]: {
title: string;
summary?: string;
} | string | undefined;
};
};
translations: {
[lang: string]: {
[key: string]: string;
};
};
general_translations?: {
[lang: string]: {
[key: string]: string;
};
};
attribute_mappings?: Record<string, string>;
status_map?: Record<string, string>;
}
export class RoborockLocales {
private dataset: Dataset;
private attributeMap: Record<string, string> = {};
constructor(dataset: Dataset) {
this.dataset = dataset;
// Merge attribute mappings from dataset if available
if (this.dataset.attribute_mappings) {
this.attributeMap = {
...this.attributeMap,
...this.dataset.attribute_mappings
};
}
}
private isDatasetValid(): boolean {
return !!this.dataset;
}
/**
* Get translated text for a generic key.
* @param key The translation key (e.g., "setting_consumable_mop")
* @param language The language code (e.g., "en", "de")
*/
public getText(key: string, language: string = "en"): string {
if (!this.isDatasetValid()) return key;
// Normalize ioBroker language codes to dataset keys
let normalizedLang = language.toLowerCase();
if (normalizedLang === "zh-cn") normalizedLang = "zh";
if (normalizedLang === "zh-tw") normalizedLang = "zh-hant";
// Try translations first
// Look up using a case-insensitive check to handle zh-Hant vs zh-hant
const datasetLangKey = Object.keys(this.dataset.translations || {}).find(
(k) => k.toLowerCase() === normalizedLang.toLowerCase()
);
const langData = datasetLangKey ? this.dataset.translations?.[datasetLangKey] : null;
if (langData && langData[key]) {
return langData[key];
}
// Try general_translations
const genData = this.dataset.general_translations?.[language];
if (genData && genData[key]) {
return genData[key];
}
// Fallback to English translations
const enData = this.dataset.translations?.["en"];
if (enData && enData[key]) {
return enData[key];
}
// Fallback to English general_translations
const enGenData = this.dataset.general_translations?.["en"];
if (enGenData && enGenData[key]) {
return enGenData[key];
}
return key; // Return key if not found
}
/**
* Get all available translations for a key.
*/
public getTranslations(key: string): Record<string, string> {
const translations: Record<string, string> = {};
if (!this.isDatasetValid()) return translations;
// Collect from regular translations
if (this.dataset.translations) {
for (const lang in this.dataset.translations) {
const langDict = this.dataset.translations[lang];
if (langDict && langDict[key]) {
translations[lang] = langDict[key];
}
}
}
// Collect from general translations if missing in specific language
if (this.dataset.general_translations) {
for (const lang in this.dataset.general_translations) {
const langDict = this.dataset.general_translations[lang];
if (langDict && langDict[key] && !translations[lang]) {
translations[lang] = langDict[key];
}
}
}
return translations;
}
/**
* Get translated error text.
* @param errorCode The error code
* @param language The language code
*/
public getErrorText(errorCode: string | number, language: string = "en"): string {
let normalizedLang = language.toLowerCase();
if (normalizedLang === "zh-cn") normalizedLang = "zh";
if (normalizedLang === "zh-tw") normalizedLang = "zh-hant";
const codeStr = errorCode.toString();
const fault = this.dataset.fault_codes?.[codeStr];
if (fault) {
const getTitle = (entry: any) =>
(typeof entry === "object" && entry !== null) ? entry.title : entry;
const langTitle = getTitle(fault[normalizedLang]);
if (langTitle && typeof langTitle === "string") return langTitle;
const enTitle = getTitle(fault["en"]);
if (enTitle && typeof enTitle === "string") return enTitle;
}
return `Error ${errorCode}`;
}
/**
* Get the translation key for a given device attribute.
* Returns undefined if no mapping is found.
*/
public getAttributeKey(attribute: string): string | undefined {
if (!this.isDatasetValid()) return undefined;
if (this.dataset.attribute_mappings) {
return this.dataset.attribute_mappings[attribute];
}
return undefined;
}
/**
* Get the translation key for a status code.
*/
public getStatusKey(statusCode: string | number): string | undefined {
if (this.dataset.status_map) {
return this.dataset.status_map[statusCode.toString()];
}
return undefined;
}
public getErrorCodes(): number[] {
if (!this.dataset.fault_codes) return [];
return Object.keys(this.dataset.fault_codes).map(k => parseInt(k)).filter(k => !isNaN(k) && k !== 0);
}
/**
* Get the mapped value for Cloth State (Mop Mount).
* Mirrors logic: if not 0, show warning.
* @param value The cloth_state value
* @param language The language code
*/
public getClothStateText(value: number, language: string = "en"): string {
if (value === 0) {
return this.getText("setting_consumable_mop", language) + ": " + this.getText("common_on", language);
} else if (value === 1) {
// Removed / Not Installed
return this.getText("setting_consumable_change_tips7", language);
} else {
// 2 or other = Dirty / abnormal? Default to generic warning
return this.getText("setting_consumable_change_tips7", language) + ` (${value})`;
}
}
public getWaterBoxModeText(value: number, language: string = "en"): string {
switch (value) {
case 200: return this.getText("home_clean_water_close", language);
case 201: return this.getText("home_clean_water_low", language);
case 202: return this.getText("home_clean_water_medium", language);
case 203: return this.getText("home_clean_water_high", language);
default: return this.getText("unknown", language) || `Water ${value}`;
}
}
public getFanPowerText(value: number, language: string = "en"): string {
switch (value) {
case 101: return this.getText("home_clean_wind_silence", language);
case 102: return this.getText("home_clean_wind_standard", language);
case 103: return this.getText("home_clean_wind_strong", language);
case 104: return this.getText("home_clean_wind_super_strong", language);
case 105: return this.getText("home_clean_wind_max", language);
default: return this.getText("unknown", language) || `Fan ${value}`;
}
}
public getMopModeText(value: number, language: string = "en"): string {
switch (value) {
case 300: return this.getText("home_clean_route_standard", language);
case 301: return this.getText("home_clean_route_carefully", language);
case 302: return this.getText("home_clean_route_deep_plus", language);
case 303: return this.getText("home_clean_route_custom", language);
default: return this.getText("unknown", language) || `MopMode ${value}`;
}
}
public getName(attribute: string, language: string = "en"): string | undefined {
const key = this.attributeMap[attribute];
if (key) {
return this.getText(key, language);
}
return undefined;
}
/**
* Get all translations for an attribute name (for common.name object)
*/
public getNameAll(attribute: string): ioBroker.StringOrTranslated {
const key = this.attributeMap[attribute];
if (key) {
const trans = this.getTranslations(key);
if (Object.keys(trans).length > 0) {
trans["en"] = trans["en"] || key;
return trans as ioBroker.StringOrTranslated;
}
}
// If translation missing, return key (if mapped) or raw attribute name as fallback string.
// This ensures we can use keys like 'water_tank' that are defined in words.js but not in the dataset.
return key || attribute;
}
}