import { Appliance } from '../appliance.js'; import { DaikinException } from '../exceptions.js'; import type { TranslationMap } from '../types.js'; export class DaikinBRP084 extends Appliance { static TRANSLATIONS: TranslationMap = { mode: { '0300': 'auto', '0200': 'cool', '0100': 'heat', '0000': 'fan', '0500': 'dry', '00': 'off', '01': 'on', }, f_rate: { '0A00': 'auto', '0B00': 'quiet', '0300': '1', '0400': '2', '0500': '3', '0600': '4', '0700': '5', }, f_dir: { 'off': 'off', 'vertical': 'vertical', 'horizontal': 'horizontal', 'both': '3d', }, en_hol: { '0': 'off', '1': 'on', }, }; private static MODE_MAP: Record = { '0300': 'auto', '0200': 'cool', '0100': 'heat', '0000': 'fan', '0500': 'dry', }; private static FAN_MODE_MAP: Record = { '0A00': 'auto', '0B00': 'quiet', '0300': '1', '0400': '2', '0500': '3', '0600': '4', '0700': '5', }; private static REVERSE_MODE_MAP: Record = Object.fromEntries( Object.entries(DaikinBRP084.MODE_MAP).map(([k, v]) => [v, k]), ); private static REVERSE_FAN_MODE_MAP: Record = Object.fromEntries( Object.entries(DaikinBRP084.FAN_MODE_MAP).map(([k, v]) => [v, k]), ); private static TURN_OFF_SWING_AXIS = '000000'; private static TURN_ON_SWING_AXIS = '0F0000'; private url: string; constructor(deviceId: string) { super(deviceId); this.url = `${this.baseUrl}/dsiot/multireq`; } async init(): Promise { if (this.values.size === 0) { await this.updateStatus(); } } async updateStatus(_resources?: string[]): Promise { const payload = { requests: [ { op: 2, to: '/dsiot/edge/adr_0100.dgc_status?filter=pv,pt,md' }, { op: 2, to: '/dsiot/edge/adr_0200.dgc_status?filter=pv,pt,md' }, { op: 2, to: '/dsiot/edge/adr_0100.i_power.week_power?filter=pv,pt,md' }, { op: 2, to: '/dsiot/edge.adp_i' }, ], }; try { const response = await this.httpClient.post>(this.url, payload); if (!response || !response.responses) { throw new DaikinException('Invalid response from device'); } this.extractValues(response as unknown as Record); } catch (error) { throw new DaikinException( `Failed to communicate with device: ${error instanceof Error ? error.message : String(error)}`, ); } } private extractValues(response: Record): void { try { const responses = (response.responses as Array<{ fr: string; pc: Record }>) .filter((r) => r.fr); const findValue = (keys: string[]): string | null => { let current: unknown = { responses }; for (const key of keys) { if (Array.isArray(current)) { const found = current.find((item) => (item as Record).pn === key); current = found ? (found as Record).pv : null; } else if (current && typeof current === 'object') { current = (current as Record)[key]; } else { return null; } } return current as string | null; }; const macPath = ['/dsiot/edge.adp_i', 'adp_i', 'mac']; const mac = this.findValueByPn(response as unknown as { responses: Array<{ fr: string; pc: Record }> }, ...macPath); if (mac) this.values.set('mac', mac); const powerPath = ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_A002', 'p_01']; const isOff = this.findValueByPn(response as unknown as { responses: Array<{ fr: string; pc: Record }> }, ...powerPath) === '00'; const modePath = ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_01']; const modeValue = this.findValueByPn(response as unknown as { responses: Array<{ fr: string; pc: Record }> }, ...modePath); this.values.set('pow', isOff ? '0' : '1'); this.values.set('mode', isOff ? 'off' : (DaikinBRP084.MODE_MAP[modeValue || ''] || 'off')); const otempPath = ['/dsiot/edge/adr_0200.dgc_status', 'dgc_status', 'e_1003', 'e_A00D', 'p_01']; const otempValue = this.findValueByPn(response as unknown as { responses: Array<{ fr: string; pc: Record }> }, ...otempPath); if (otempValue) this.values.set('otemp', this.hexToTemp(otempValue).toString()); const htempPath = ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_A00B', 'p_01']; const htempValue = this.findValueByPn(response as unknown as { responses: Array<{ fr: string; pc: Record }> }, ...htempPath); if (htempValue) this.values.set('htemp', this.hexToTemp(htempValue, 1).toString()); const mode = this.values.get('mode'); if (mode && mode !== 'off') { const tempSettings = this.getTempSettingsPath(mode); if (tempSettings) { const stempValue = this.findValueByPn(response as unknown as { responses: Array<{ fr: string; pc: Record }> }, ...tempSettings); if (stempValue) this.values.set('stemp', this.hexToTemp(stempValue).toString()); } const fanSettings = this.getFanSettingsPath(mode); if (fanSettings) { const fanValue = this.findValueByPn(response as unknown as { responses: Array<{ fr: string; pc: Record }> }, ...fanSettings); if (fanValue) this.values.set('f_rate', DaikinBRP084.FAN_MODE_MAP[fanValue] || 'auto'); } } } catch (error) { console.error('Error extracting values:', error); } } private findValueByPn(data: { responses: Array<{ fr: string; pc: Record }> }, ...keys: string[]): string | null { const fr = keys[0]; const dataItems = data.responses.filter((x) => x.fr === fr); let current: unknown = dataItems; const remainingKeys = keys.slice(1); for (const key of remainingKeys) { let found = false; if (Array.isArray(current)) { for (const pcs of current) { if ((pcs as Record).pn === key) { if (remainingKeys.indexOf(key) === remainingKeys.length - 1) { return (pcs as Record).pv as string; } current = (pcs as Record).pch; found = true; break; } } } if (!found) return null; } return null; } private getTempSettingsPath(mode: string): string[] | null { const pathMap: Record = { cool: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_02'], heat: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_03'], auto: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_1D'], }; return pathMap[mode] || null; } private getFanSettingsPath(mode: string): string[] | null { const pathMap: Record = { auto: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_26'], cool: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_09'], heat: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_0A'], fan: ['/dsiot/edge/adr_0100.dgc_status', 'dgc_status', 'e_1002', 'e_3001', 'p_28'], }; return pathMap[mode] || null; } private hexToTemp(value: string, divisor = 2): number { return parseInt(value.slice(0, 2), 16) / divisor; } private tempToHex(temperature: number, divisor = 2): string { return Math.round(temperature * divisor).toString(16).padStart(2, '0'); } async set(settings: Record): Promise { if (settings.mode === 'off') { this.values.set('pow', '0'); } else if (settings.mode) { this.values.set('pow', '1'); this.values.set('mode', settings.mode); } if (settings.stemp) { const mode = this.values.get('mode'); const tempPath = this.getTempSettingsPath(mode || 'cool'); if (tempPath) { const tempHex = this.tempToHex(parseFloat(settings.stemp)); this.values.set('stemp', settings.stemp); } } await this.updateStatus(); } get supportAwayMode(): boolean { return false; } get supportAdvancedModes(): boolean { return false; } get supportZoneCount(): boolean { return false; } }