async publishFromState(deviceId, model, stateModel, stateList, options) { let isGroup = false; const has_elevated_debug = this.stController.checkDebugDevice(deviceId) this.log.debug(`publishFromState : ${deviceId} ${model} ${safeJsonStringify(stateList)}`); if (model === 'group') { isGroup = true; deviceId = parseInt(deviceId); } try { const entity = await this.zbController.resolveEntity(deviceId); this.log.debug(`entity: ${deviceId} ${model} ${safeJsonStringify(entity)}`); const mappedModel = entity.mapped; if (!mappedModel) { this.log.debug(`No mapped model for ${model}`); if (has_elevated_debug) this.log.warn(`ELEVATED: No mapped model for ${model}`) return; } if (!mappedModel.toZigbee) { this.log.error(`No toZigbee in mapped model for ${model}`); return; } stateList.forEach(async changedState => { const stateDesc = changedState.stateDesc; const value = changedState.value; if (stateDesc.id === 'send_payload') { try { const json_value = JSON.parse(value); const payload = {device: deviceId.replace('0x', ''), payload: json_value}; const result = await this.sendPayload(payload); if (result.hasOwnProperty('success') && result.success) { this.acknowledgeState(deviceId, model, stateDesc, value); } } catch (error) { this.log.warn(`send_payload: ${value} does not parse as JSON Object : ${error.message}`); return; } return; } if (stateDesc.isOption) { // acknowledge state with given value this.acknowledgeState(deviceId, model, stateDesc, value); // process sync state list //this.processSyncStatesList(deviceId, modelId, syncStateList); // if this is the device query state => trigger the device query // on activation of the 'device_query' state trigger hardware query where possible if (stateDesc.id === 'device_query') { if (this.query_device_block.indexOf(deviceId) > -1) { this.log.warn(`Device query for '${entity.device.ieeeAddr}' blocked`); return; } if (mappedModel) { this.query_device_block.push(deviceId); this.log.debug(`Device query for '${entity.device.ieeeAddr}' started`); for (const converter of mappedModel.toZigbee) { if (converter.hasOwnProperty('convertGet')) { for (const ckey of converter.key) { try { await converter.convertGet(entity.device.endpoints[0], ckey, {}); } catch (error) { this.log.warn(`Failed to read state '${JSON.stringify(ckey)}'of '${entity.device.ieeeAddr}' after query with '${JSON.stringify(error)}'`); } } } } this.log.debug(`Device query for '${entity.device.ieeeAddr}' done`); const idToRemove = deviceId; setTimeout(() => { const idx = this.query_device_block.indexOf(idToRemove); if (idx > -1) { this.query_device_block.splice(idx); } }, 10000); } return; } return; } let converter = undefined; for (const c of mappedModel.toZigbee) { if (!c.hasOwnProperty('convertSet')) continue; this.log.debug(`Type of toZigbee is '${typeof c}', Contains key ${(c.hasOwnProperty('key')?JSON.stringify(c.key):'false ')}`) if (!c.hasOwnProperty('key') && c.hasOwnProperty('convertSet') && converter === undefined) { converter = c; if (has_elevated_debug) this.log.warn(`ELEVATED: setting converter to keyless converter for ${deviceID} of type ${model}`) this.log.debug('setting converter to keyless converter') continue; } if (c.key.includes(stateDesc.prop) || c.key.includes(stateDesc.setattr) || c.key.includes(stateDesc.id)) { this.log.debug(`${(converter===undefined?'Setting':'Overriding')}' converter to converter with key(s)'${JSON.stringify(c.key)}}`) if (has_elevated_debug) this.log.warn(`ELEVATED: ${(converter===undefined?'Setting':'Overriding')}' converter to converter with key(s)'${JSON.stringify(c.key)}}`) converter = c; } } /* if (!mappedModel.toZigbee[0].hasOwnProperty('key') && mappedModel.toZigbee[0].hasOwnProperty('convertSet')) converter = mappedModel.toZigbee[0]; converter = mappedModel.toZigbee.find(c => c && c.hasOwnProperty('key') && (c.key.includes(stateDesc.prop) || c.key.includes(stateDesc.setattr) || c.key.includes(stateDesc.id))); */ if (converter === undefined) { this.log.error(`No converter available for '${model}' with key '${stateDesc.id}' `); this.sendError(`No converter available for '${model}' with key '${stateDesc.id}' `); return; } const preparedValue = (stateDesc.setter) ? stateDesc.setter(value, options) : value; const preparedOptions = (stateDesc.setterOpt) ? stateDesc.setterOpt(value, options) : {}; let syncStateList = []; if (stateModel && stateModel.syncStates) { stateModel.syncStates.forEach(syncFunct => { const res = syncFunct(stateDesc, value, options); if (res) { syncStateList = syncStateList.concat(res); } }); } const epName = stateDesc.epname !== undefined ? stateDesc.epname : (stateDesc.prop || stateDesc.id); const key = stateDesc.prop || stateDesc.id || stateDesc.setattr; this.log.debug(`convert ${key}, ${safeJsonStringify(preparedValue)}, ${safeJsonStringify(preparedOptions)}`); if (has_elevated_debug) this.log.warn(`ELEVATED: convert ${key}, ${safeJsonStringify(preparedValue)}, ${safeJsonStringify(preparedOptions)} for device ${deviceId}`); let target; if (model === 'group') { target = entity.mapped; } else { target = await this.zbController.resolveEntity(deviceId, epName); target = target.endpoint; } this.log.debug(`target: ${safeJsonStringify(target)}`); const meta = { endpoint_name: epName, options: preparedOptions, device: entity.device, mapped: model === 'group' ? [] : mappedModel, message: {[key]: preparedValue}, logger: this.log, state: {}, }; // new toZigbee if (preparedValue !== undefined && Object.keys(meta.message).filter(p => p.startsWith('state')).length > 0) { if (typeof preparedValue === 'number') { meta.message.state = preparedValue > 0 ? 'ON' : 'OFF'; } else { meta.message.state = preparedValue; } } if (preparedOptions !== undefined) { if (preparedOptions.hasOwnProperty('state')) { meta.state = preparedOptions.state; } } try { const result = await converter.convertSet(target, key, preparedValue, meta); this.log.debug(`convert result ${safeJsonStringify(result)}`); if (has_elevated_debug) this.log.warn(`ELEVATED: convert result ${safeJsonStringify(result)} for device ${deviceId}`); if (result !== undefined) { if (stateModel && !isGroup) { this.acknowledgeState(deviceId, model, stateDesc, value); } // process sync state list this.processSyncStatesList(deviceId, model, syncStateList); if (isGroup) { await this.callPluginMethod('queryGroupMemberState', [deviceId, stateDesc]); this.acknowledgeState(deviceId, model, stateDesc, value); } } else if (has_elevated_debug) this.log.warn(`Error convert result for ${key} with ${safeJsonStringify(preparedValue)} is undefined on device ${deviceId}.`); } catch (error) { if (has_elevated_debug) this.log.warn(`caught error ${safeJsonStringify(error)} is undefined on device ${deviceId}.`); this.filterError(`Error ${error.code} on send command to ${deviceId}.` + ` Error: ${error.stack}`, `Send command to ${deviceId} failed with`, error); } }); } catch (err) { this.log.error(`No entity for ${deviceId}`); } }