node-red-contrib-deconz
Advanced tools
Comparing version 2.0.0-beta.16 to 2.0.0-beta.17
const NODE_PATH = '/node-red-contrib-deconz/'; | ||
const path = require('path'); | ||
const ConfigMigration = require("./src/migration/ConfigMigration"); | ||
const DeconzAPI = require("./src/runtime/DeconzAPI"); | ||
@@ -134,15 +135,2 @@ module.exports = function (RED) { | ||
}); | ||
// RED.httpAdmin.get(NODE_PATH + 'gwscanner', function (req, res) { | ||
// // let ip = require("ip"); | ||
// // console.log ( ip.address() ); | ||
// | ||
// let portscanner = require('portscanner'); | ||
// | ||
// // 127.0.0.1 is the default hostname; not required to provide | ||
// portscanner.findAPortNotInUse([80], '127.0.0.1').then(port => { | ||
// console.log(`Port ${port} is available!`); | ||
// | ||
// // Now start your service on this port... | ||
// }); | ||
// }); | ||
@@ -158,2 +146,9 @@ RED.httpAdmin.get(NODE_PATH + 'configurationMigration', function (req, res) { | ||
RED.httpAdmin.get(NODE_PATH + 'serverAutoconfig', async function (req, res) { | ||
let data = req.query; | ||
let config = JSON.parse(data.config); | ||
let api = new DeconzAPI(config); | ||
let result = await api.discoverSettings(config.discoverParam || {}); | ||
res.json(result); | ||
}); | ||
}; |
@@ -46,23 +46,26 @@ const got = require('got'); | ||
node.on('close', () => this.onClose()); | ||
(async () => { | ||
//TODO make the delay configurable | ||
await Utils.sleep(1500); | ||
try { | ||
//TODO make the delay configurable | ||
await Utils.sleep(1500); | ||
await node.discoverDevices({ | ||
forceRefresh: true | ||
}); | ||
this.refreshDiscoverTimer = setInterval(() => { | ||
node.discoverDevices({ | ||
await node.discoverDevices({ | ||
forceRefresh: true | ||
}); | ||
}, node.refreshDiscoverInterval); | ||
this.refreshDiscoverTimer = setInterval(() => { | ||
node.discoverDevices({ | ||
forceRefresh: true | ||
}); | ||
}, node.refreshDiscoverInterval); | ||
node.ready = true; | ||
node.ready = true; | ||
this.setupDeconzSocket(node); | ||
} catch (e) { | ||
node.ready = false; | ||
node.error("Deconz Server node error " + e.toString()); | ||
} | ||
node.emit('onStart'); | ||
this.setupDeconzSocket(node); | ||
})(); | ||
@@ -522,2 +525,11 @@ } | ||
updateNodeStatus(node, msgToSend) { | ||
if (node.server.ready === false) { | ||
node.status({ | ||
fill: "red", | ||
shape: "dot", | ||
text: "node-red-contrib-deconz/server:status.server_node_error" | ||
}); | ||
return; | ||
} | ||
if (node.config.search_type === "device" && node.config.device_list.length === 0) { | ||
@@ -573,3 +585,3 @@ node.status({ | ||
if (firstOutputRule === undefined) return; | ||
if (firstOutputRule.payload.length === 1 && | ||
if (Array.isArray(firstOutputRule.payload) && firstOutputRule.payload.length === 1 && | ||
!['__complete__', '__each__'].includes(firstOutputRule.payload[0]) | ||
@@ -576,0 +588,0 @@ ) { |
@@ -51,3 +51,3 @@ { | ||
}, | ||
"version": "2.0.0-beta.16", | ||
"version": "2.0.0-beta.17", | ||
"devDependencies": { | ||
@@ -54,0 +54,0 @@ "grunt": "^1.3.0", |
@@ -1,2 +0,2 @@ | ||
class DeconzEditor{constructor(node,options={}){this.node=node,this.options=options}get elements(){return{}}get NRCD(){return"node-red-contrib-deconz"}findElements(){this.$elements={},Object.keys(this.elements).forEach(k=>{this.$elements[k]=this.findElement(this.elements[k])})}findElement(identifier){return"#"!==identifier.charAt(0)&&"."!==identifier.charAt(0)&&(identifier="#"+identifier),$(identifier)}async init(){this.findElements()}async connect(){}sendError(msg,timeout=1e4){let myNotification=RED.notify(msg,{timeout:timeout,type:"error",buttons:[{text:"okay",class:"primary",click:()=>myNotification.close()}]})}getIcon(icon,includeClass=!1){return"deconz"===icon?"icons/node-red-contrib-deconz/icon-color.png":"homekit"===icon?"icons/node-red-contrib-deconz/homekit-logo.png":RED.nodes.fontAwesome.getIconList().includes(`fa-${icon}`)?`${includeClass?"fa ":""}fa-${icon}`:icon}createIconElement(icon,container,isLarge=!1){if("fa-"===icon.substr(0,3)){if(RED.nodes.fontAwesome.getIconUnicode(icon)){let faIconElement=$("<i/>").appendTo(container);return void faIconElement.addClass("fa "+icon+(isLarge?" fa-lg":""))}icon=RED.settings.apiRootUrl+"icons/node-red/arrow-in.svg"}let imageIconElement=$("<div/>").appendTo(container);imageIconElement.css("backgroundImage","url("+icon+")")}getI18n(prefix,suffix,value={}){let _path=prefix;suffix&&(_path+=`.${suffix}`),value.defaultValue="__undefined__";value=RED._(_path,value);if("__undefined__"!==value)return value}async generateSimpleListField(container,options){let input=$("<select/>",{id:options.id});if(options.choices)for(var[key,value]of options.choices)input.append($("<option/>").attr("value",key).html(RED._(value)));var row=await this.generateInputWithLabel(input,options);return container.append(row),void 0!==options.currentValue&&input.val(options.currentValue),input}async generateTypedInput(container,inputType){let input=$("<input/>",{id:inputType.id,placeholder:RED._(inputType.placeholder)});inputType=$("<input/>",{id:`${inputType.id}_type`,type:"hidden"});return input.append(inputType),input}async initTypedInput(input,options){options=$.extend({addDefaultTypes:!0,displayOnlyIcon:!1,value:{},width:"200px"},options);let typedInputOptions=$.extend({types:["msg","flow","global"]},options.typedInput);if(typedInputOptions.typeField=options.typeId,options.addDefaultTypes&&(typedInputOptions.types.push("msg"),typedInputOptions.types.push("flow"),typedInputOptions.types.push("global"),typedInputOptions.types.push("jsonata")),options.displayOnlyIcon){let that=this;function valueLabel(a,b){let typeDefinition;for(const type of this.typeList)"object"==typeof type&&type.value===this.propertyType&&(typeDefinition=type);void 0!==typeDefinition&&void 0!==typeDefinition.icon&&(this.oldValue=this.input.val(),this.input.val(""),this.valueLabelContainer.hide(),that.createIconElement(typeDefinition.icon,this.selectLabel),this.selectTrigger.addClass("red-ui-typedInput-full-width"),this.selectLabel.show())}var type;for(type of typedInputOptions.types)"string"!=typeof type&&(type.hasValue=!0,type.valueLabel=valueLabel)}input.typedInput(typedInputOptions),void 0!==options.width&&input.typedInput("width",options.width),options.value&&(void 0!==options.value.type&&input.typedInput("type",options.value.type),void 0!==options.value.value&&input.typedInput("value",options.value.value))}async generateTypedInputField(container,options){var input=await this.generateTypedInput(container,{id:options.id,placeholder:this.getI18n(options.i18n,"placeholder")}),row=await this.generateInputWithLabel(input,options);return container.append(row),await this.initTypedInput(input,options),input}async generateDoubleTypedInputField(container,optionsFirst,optionsSecond){var inputFirst=await this.generateTypedInput(container,optionsFirst);let row=await this.generateInputWithLabel(inputFirst,optionsFirst);var inputSecond=await this.generateTypedInput(container,optionsSecond);row.append(inputSecond),container.append(row),optionsFirst.displayOnlyIcon=!0,optionsFirst.width="50px",optionsSecond.width="150px",await this.initTypedInput(inputFirst,optionsFirst),await this.initTypedInput(inputSecond,optionsSecond)}generateTypedInputType(i18n,name,data={}){if(data.value=name,void 0===data.label&&(data.label=this.getI18n(i18n,`options.${name}.label`,{})||name),!1!==data.icon&&void 0===data.icon&&(data.icon=this.getIcon(this.getI18n(i18n,`options.${name}.icon`))),data.icon&&"fa-"===data.icon.substr(0,3)&&(data.icon="fa "+data.icon),Array.isArray(data.subOptions)){Array.isArray(data.options)||(data.options=[]);for(const opt of data.subOptions)data.options.push(this.generateTypedInputType(`${i18n}.options.${name}`,"string"==typeof opt?opt:opt.name,{icon:!1}))}return data}async generateCheckboxField(container,options){var input=$("<input/>",{id:options.id,type:"checkbox",style:"display: table-cell; width: 14px;vertical-align: top;margin-right: 5px",checked:options.currentValue});let row=await this.generateInputWithLabel(input,options);row.append($("<span/>").html(RED._(options.descText)).css("display","table-cell")),container.append(row)}async generateInputWithLabel(input,options={}){let row=$("<div/>",{class:"form-row",style:"padding:5px;margin:0;display:table;min-width:420px;"});var inputID=input.attr("id");if(inputID){let labelElement=$("<label/>");labelElement.attr("for",inputID),labelElement.attr("class","l-width"),labelElement.attr("style","display:table-cell;"),void 0===options.title&&(options.title=this.getI18n(options.i18n,"title")),options.title&&labelElement.attr("title",this.getI18n(options.i18n,"title")),void 0===options.icon&&(options.icon=this.getI18n(options.i18n,"icon")),options.icon&&(this.createIconElement(this.getIcon(options.icon),labelElement),labelElement.append(" ")),void 0===options.label&&(options.label=this.getI18n(options.i18n,"label")),options.label&&labelElement.append(`<span>${options.label}</span>`),row.append(labelElement)}return input.css("display","table-cell"),row.append(input),row}async generateHR(container,topBottom="5px",leftRight="50px"){container.append(`<hr style="margin: ${topBottom} ${leftRight};">`)}async generateSeparator(container,label){container.append(`<div class="separator">${RED._(label)}</div>`)}}class DeconzMainEditor extends DeconzEditor{constructor(node,options={}){super(node,$.extend(!0,{have:{statustext:!0,query:!0,device:!0,output_rules:!1,commands:!1,specific:!1},device:{batteryFilter:!1},output_rules:{format:{single:!0,array:!1,sum:!1,average:!1,min:!1,max:!1},type:{attribute:!0,state:!0,config:!0,homekit:!1,scene_call:!1}},commands:{type:{deconz_state:!0,homekit:!0,custom:!0,pause:!0}},specific:{output:{}}},options)),this.subEditor={},this.initDone=!1,this.options.have.statustext&&(this.subEditor.statustext=new DeconzStatusTextEditor(this.node,this.options.statustext)),this.options.have.device&&(this.subEditor.device=new DeconzDeviceEditor(this.node,this.options.device)),this.options.have.query&&(this.subEditor.query=new DeconzQueryEditor(this.node,this.options.query)),this.options.have.specific&&"deconz-output"===this.node.type&&(this.subEditor.specific=new DeconzSpecificOutputEditor(this.node,this.options.specific.output)),this.options.have.output_rules&&(this.subEditor.output_rules=new DeconzOutputRuleListEditor(this.node,this.options.output_rules)),this.options.have.commands&&(this.subEditor.commands=new DeconzCommandListEditor(this.node,this.options.commands))}get elements(){return{server:"node-input-server"}}async configurationMigration(){if(!((this.node.config_version||0)>=this.node._def.defaults.config_version.value)){let config={};for(const key of Object.keys(this.node._def.defaults))config[key]=this.node[key];var data={type:this.node.type,config:JSON.stringify(config)};let errorMsg="Error while migrating the configuration of the node from version "+(this.node.config_version||0)+" to version "+this.node._def.defaults.config_version.value+".",result=await $.getJSON(`${this.NRCD}/configurationMigration`,data).catch((t,u)=>{this.sendError(errorMsg)});if(result&&!result.notNeeded){if(result.new)for(var[key,value]of Object.entries(result.new))this.node[key]=value;if(result.delete&&Array.isArray(result.delete))for(const key of result.delete)delete this.node[key];result.errors&&Array.isArray(result.errors)&&0<result.errors.length&&this.sendError(errorMsg+"<br><li>"+result.errors.join("</li><li>")+"</li>")}}}async init(){await new Promise(resolve=>setTimeout(resolve,100)),await this.configurationMigration(),await super.init(),this.serverNode=RED.nodes.node(this.$elements.server.val()),this.initPromises=[];for(const editor of Object.values(this.subEditor))this.initPromises.push(editor.init(this));await Promise.all(this.initPromises),this.initDone=!0,delete this.initPromises;let connectPromises=[];for(const editor of Object.values(this.subEditor))connectPromises.push(editor.connect());await Promise.all(connectPromises)}async isInitialized(){this.initDone||await Promise.all(this.initPromises)}async updateQueryDeviceDisplay(options){var type=this.subEditor.query.$elements.select.typedInput("type");switch(type){case"device":await this.subEditor.device.updateList(options);break;case"json":case"jsonata":this.subEditor.query.$elements.select.typedInput("validate")&&await this.subEditor.query.updateList(options)}await this.subEditor.device.display("device"===type),await this.subEditor.query.display("device"!==type)}oneditsave(){var newRules;this.options.have.output_rules&&(newRules=this.subEditor.output_rules.value,this.node.outputs=newRules.length,this.node.output_rules=newRules),this.options.have.commands&&(this.node.commands=this.subEditor.commands.value),this.options.have.specific&&(this.node.specific=this.subEditor.specific.value)}}class DeconzStatusTextEditor extends DeconzEditor{constructor(node,options={}){super(node,$.extend({allowedTypes:["msg","jsonata"]},options))}get elements(){return{statustext:"node-input-statustext"}}async init(mainEditor){await super.init(),this.mainEditor=mainEditor,this.initTypedInput()}initTypedInput(){let options=[];this.mainEditor.options.have.statustext&&options.push({value:"auto",label:RED._(`${this.NRCD}/server:editor.inputs.statustext.options.auto`),icon:`icons/${this.NRCD}/icon-color.png`,hasValue:!1}),this.$elements.statustext.typedInput({type:"auto",types:options.concat(this.options.allowedTypes),typeField:`#${this.elements.statustext}_type`})}}class DeconzDeviceListEditor extends DeconzEditor{constructor(node,options={}){super(node,options)}get xhrURL(){return`${this.NRCD}/itemlist`}get xhrParams(){return{controllerID:this.mainEditor.serverNode.id,forceRefresh:this.options.refresh}}async display(display=!0){if(this.$elements.showHide)return display?this.$elements.showHide.show():this.$elements.showHide.hide(),this.$elements.showHide.promise()}async getItems(options,result){result.forceRefresh=options.refresh;result=await $.getJSON(this.xhrURL,result).catch((t,u)=>{this.sendError(400===t.status&&t.responseText?t.responseText:u.toString())});if(!result||!result.error_message)return this.formatItemList(result.items,options.keepOnlyMatched);console.warn(result.error_message)}async updateList(devices){devices=$.extend({refresh:!0},devices);let list=this.$elements.list,params=this.xhrParams;!0===this.options.batteryFilter&&(devices.keepOnlyMatched=!0,params.query=JSON.stringify({type:"match",match:{"config.battery":{type:"complex",operator:"!==",value:void 0}}}));devices=await this.getItems(devices,params);list.children().remove(),devices&&this.generateHtmlItemList(devices,this.$elements.list),list.multipleSelect("refresh"),devices&&list.multipleSelect("enable")}formatItemList(items,keepOnlyMatched=!1){let itemList={};var injectItems=(part,matched)=>{part.forEach(item=>{var device_type=item.type;void 0===itemList[device_type]&&(itemList[device_type]=[]),item.query_match=matched,itemList[device_type].push(item)})};return injectItems(items.matched,!0),!1===keepOnlyMatched&&injectItems(items.rejected,!1),itemList}generateHtmlItemList(items,htmlContainer){var group_key,item_list,queryMode=this.constructor===DeconzQueryEditor;for([group_key,item_list]of Object.entries(items).sort((x,y)=>{x=x[0].toLowerCase(),y=y[0].toLowerCase();return x<y?-1:y<x?1:0})){let groupHtml=$("<optgroup/>").attr("label",group_key);for(const item of item_list.sort((x,y)=>{x=x.name.toLowerCase(),y=y.name.toLowerCase();return x<y?-1:y<x?1:0})){let label=item.name;"groups"===item.device_type&&(label+=" (lights: "+item.lights.length,item.scenes.length&&(label+=", scenes: "+item.scenes.length),label+=")");let opt=$("<option>"+label+"</option>").attr("value",item.device_path);queryMode&&item.query_match&&opt.attr("selected",""),opt.appendTo(groupHtml)}groupHtml.appendTo(htmlContainer)}}}class DeconzQueryEditor extends DeconzDeviceListEditor{constructor(node,options={}){super(node,$.extend({allowedTypes:["json","jsonata"]},options))}get elements(){return{select:"node-input-query",list:"node-input-query_result",showHide:".deconz-query-selector",refreshButton:"#force-refresh-query-result"}}get type(){return this.$elements.select.typedInput("type")}set type(val){this.$elements.list.typedInput("type",val)}get value(){return this.$elements.select.typedInput("value")}set value(val){this.$elements.list.typedInput("value",val)}get xhrParams(){let params=super.xhrParams;return params.query=this.value,params.queryType=this.type,params.nodeID=this.node.id,params}async init(mainEditor){await super.init(),this.mainEditor=mainEditor,this.initTypedInput(),this.$elements.list.multipleSelect({maxHeight:300,dropWidth:320,width:320,single:!1,selectAll:!1,filter:!0,filterPlaceholder:RED._(`${this.NRCD}/server:editor.inputs.device.device.filter`),placeholder:RED._(`${this.NRCD}/server:editor.multiselect.none_selected`),numberDisplayed:1,disableIfEmpty:!0,showClear:!1,hideOptgroupCheckboxes:!0,filterGroup:!0,onClick:view=>{this.$elements.list.multipleSelect(view.selected?"uncheck":"check",view.value)}}),await this.mainEditor.updateQueryDeviceDisplay({useSavedData:!0})}initTypedInput(){let options=[];this.mainEditor.options.have.device&&options.push({value:"device",label:RED._(`${this.NRCD}/server:editor.inputs.device.query.options.device`),icon:`icons/${this.NRCD}/icon-color.png`,hasValue:!1}),this.$elements.select.typedInput({type:"text",types:options.concat(this.options.allowedTypes),typeField:"#node-input-search_type"})}async connect(){await super.connect(),this.$elements.select.on("change",()=>{this.mainEditor.updateQueryDeviceDisplay({useSavedData:!0}),this.mainEditor.subEditor.output_rules.refresh()}),this.$elements.refreshButton.on("click",()=>{this.updateList(),this.mainEditor.subEditor.output_rules.refresh()})}}class DeconzDeviceEditor extends DeconzDeviceListEditor{constructor(node,options={}){super(node,$.extend({batteryFilter:!1},options))}get elements(){return{list:"node-input-device_list",showHide:".deconz-device-selector",refreshButton:"#force-refresh"}}get value(){return this.$elements.list.multipleSelect("getSelects")}set value(val){this.$elements.list.multipleSelect("setSelects",val)}async init(mainEditor){await super.init(),this.mainEditor=mainEditor,this.$elements.list.multipleSelect({maxHeight:300,dropWidth:320,width:320,single:"multiple"!==this.$elements.list.attr("multiple"),filter:!0,filterPlaceholder:RED._(`${this.NRCD}/server:editor.inputs.device.device.filter`),placeholder:RED._(`${this.NRCD}/server:editor.multiselect.none_selected`),showClear:!0})}async connect(){await super.connect(),this.$elements.refreshButton.on("click",()=>{this.updateList($.extend(this.options,{useSelectedData:!0})),this.mainEditor.options.have.output_rules&&this.mainEditor.subEditor.output_rules.refresh()}),this.mainEditor.options.have.output_rules&&this.$elements.list.on("change",()=>{this.mainEditor.subEditor.output_rules.refresh()})}async updateList(options){let itemsSelected;(options=$.extend({useSavedData:!1,useSelectedData:!1},options)).useSelectedData&&(itemsSelected=this.$elements.list.multipleSelect("getSelects")),await super.updateList(options),options.useSavedData&&Array.isArray(this.node.device_list)?this.$elements.list.multipleSelect("setSelects",this.node.device_list):options.useSelectedData&&Array.isArray(itemsSelected)&&this.$elements.list.multipleSelect("setSelects",itemsSelected)}}class DeconzSpecificOutputEditor extends DeconzEditor{constructor(node,options={}){super(node,$.extend({},options))}get elements(){return{container:"specific-container",delay:"node-input-delay",result:"node-input-result"}}get default(){return{delay:{type:"num",value:50},result:{type:"at_end"}}}async init(){this.node.specific=$.extend(!0,this.default,this.node.specific);var container=this.findElement(this.elements.container);await this.generateSeparator(container,`${this.NRCD}/server:editor.inputs.separator.specific`),await this.generateDelayField(container,this.node.specific.delay),await this.generateResultField(container,this.node.specific.result),await super.init()}async generateDelayField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.specific.output.delay`;await this.generateTypedInputField(container,{id:this.elements.delay,i18n:i18n,value:value,width:"250px",typedInput:{types:["num"]}})}async generateResultField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.specific.output.result`;await this.generateTypedInputField(container,{id:this.elements.result,i18n:i18n,value:value,width:"250px",typedInput:{types:[this.generateTypedInputType(i18n,"never",{hasValue:!1}),this.generateTypedInputType(i18n,"after_command",{hasValue:!1}),this.generateTypedInputType(i18n,"at_end",{hasValue:!1})]}})}async connect(){await super.connect()}get value(){return{delay:{type:this.$elements.delay.typedInput("type"),value:this.$elements.delay.typedInput("value")},result:{type:this.$elements.result.typedInput("type"),value:this.$elements.result.typedInput("value")}}}}class DeconzListItemEditor extends DeconzEditor{constructor(node,listEditor,container,options={}){super(node,options),this.listEditor=listEditor,container.uniqueId(),this.uniqueId=container.attr("id"),this.container=container}set index(value){void 0!==value&&this.$elements&&this.$elements.outputButton&&this.$elements.outputButton.find(".node-input-rule-index").html(value+1),this._index=value}get index(){return this._index}async init(){await this.generateOutputButton(this.container.children().first()),await super.init()}async generateOutputButton(container){$("<a/>",{id:this.elements.outputButton,class:"red-ui-button top-right-badge"}).append(` → <span class="node-input-rule-index">${this.index+1}</span> `).appendTo(container)}}class DeconzListItemListEditor extends DeconzEditor{constructor(node,options={}){super(node,options),this.items={}}get listType(){return"item"}get buttons(){return[]}async init(mainEditor){await super.init(),this.mainEditor=mainEditor}async initList(itemEditorClass,items=[]){var buttons=this.buttons;this.$elements.list.editableList({sortable:!0,removable:!0,height:"auto",addButton:0===buttons.length,buttons:buttons,addItem:(row,index,item)=>{let itemEditor=new itemEditorClass(this.node,this,row,this.options);item.uniqueId=itemEditor.uniqueId,this.items[item.uniqueId]=itemEditor,itemEditor.init(item,index)},removeItem:item=>{if(!item.uniqueId||!this.items[item.uniqueId])throw new Error(`Error while removing the ${this.listType}, the ${this.listType} ${item.uniqueId} does not exist.`);var deletedIndex=this.items[item.uniqueId].index;delete this.items[item.uniqueId];for(const item of Object.values(this.items))item.index>deletedIndex&&item.index--},sortItems:items=>{items.each((index,item)=>{if(!this.items[item.attr("id")])throw new Error(`Error while moving the ${this.listType}, the ${this.listType} ${index+1} does not exist.`);this.items[item.attr("id")].index=index})}}),0<items.length&&this.$elements.list.editableList("addItems",items)}get value(){let result=[];for(const rule of Object.values(this.items).sort((a,b)=>a.index-b.index))result.push(rule.value);return result}refresh(){}}class DeconzOutputRuleEditor extends DeconzListItemEditor{constructor(node,listEditor,container,options={}){super(node,listEditor,container,options=$.extend({enableEachState:!0},options))}get elements(){let elements={};for(const key of["format","type","payload","output","onstart","onerror","outputButton"])elements[key]=`node-input-output-rule-${this.uniqueId}-${key}`;return elements}get value(){let value={};switch(value.type=this.$elements.type.val(),value.format=this.$elements.format.val(),value.type){case"attribute":case"state":case"config":"deconz-input"===this.node.type&&(value.output=this.$elements.output.val()),["deconz-input","deconz-battery"].includes(this.node.type)&&(value.onstart=this.$elements.onstart.is(":checked")),["deconz-input","deconz-get"].includes(this.node.type)&&(value.payload=this.$elements.payload.multipleSelect("getSelects"));break;case"homekit":["deconz-input","deconz-battery"].includes(this.node.type)&&(value.onstart=this.$elements.onstart.is(":checked")),"deconz-input"===this.node.type&&(value.onerror=this.$elements.onerror.is(":checked"))}return value}get defaultRule(){let rule={type:"state",payload:["__complete__"],format:"single"};return"deconz-input"===this.node.type&&(rule.output="always",rule.onstart=!0,rule.onerror=!0),"deconz-battery"===this.node.type&&(rule.onstart=!0),rule}async init(rule,index){this._index=index,rule=$.extend(!0,this.defaultRule,rule),await this.generatePayloadTypeField(this.container,rule.type),["deconz-input","deconz-get"].includes(this.node.type)&&await this.generatePayloadField(this.container),await this.generatePayloadFormatField(this.container,rule.format),"deconz-input"===this.node.type&&await this.generateOutputField(this.container,(void 0!==rule.output?rule:this.defaultRule).output),["deconz-input","deconz-battery"].includes(this.node.type)&&await this.generateOnStartField(this.container,(void 0!==rule.onstart?rule:this.defaultRule).onstart),"deconz-input"===this.node.type&&await this.generateOnErrorField(this.container,(void 0!==rule.onerror?rule:this.defaultRule).onerror),await super.init(),await this.listEditor.mainEditor.isInitialized(),await this.initPayloadList(rule.payload),await this.updateShowHide(rule.type),await this.connect()}async connect(){await super.connect(),this.$elements.type.on("change",()=>{var type=this.$elements.type.val();["attribute","state","config"].includes(type)&&this.updatePayloadList(),this.updateShowHide(type)}),this.$elements.outputButton.on("click",()=>{try{let nodes=RED.nodes.filterLinks({source:this.node,sourcePort:this.index}).map(l=>{var result=l.target.type;return""!==l.target.name?result+":"+l.target.name:void 0!==l.target._def.label?result+":"+l.target._def.label():result}),myNotification=RED.notify(`The output ${this.index+1} is sending message to ${nodes.length} nodes :<br>${nodes.join("<br>")}`,{modal:!0,timeout:5e3,buttons:[{text:"okay",class:"primary",click:()=>myNotification.close()}]})}catch(e){this.sendError(`This is using not documented API so can be broken at anytime.<br>Error while getting connected nodes: ${e.toString()}`)}})}async updateShowHide(type){switch(type){case"attribute":case"state":case"config":this.$elements.payload.closest(".form-row").show(),this.$elements.output.closest(".form-row").show(),this.$elements.onstart.closest(".form-row").show(),this.$elements.onerror.closest(".form-row").hide();break;case"homekit":this.$elements.payload.closest(".form-row").hide(),this.$elements.output.closest(".form-row").hide(),this.$elements.onstart.closest(".form-row").show(),this.$elements.onerror.closest(".form-row").show();break;case"scene_call":this.$elements.payload.closest(".form-row").hide(),this.$elements.output.closest(".form-row").hide(),this.$elements.onstart.closest(".form-row").hide(),this.$elements.onerror.closest(".form-row").hide()}}async updatePayloadList(){this.$elements.payload.multipleSelect("disable"),this.$elements.payload.children().remove();var queryType=this.listEditor.mainEditor.subEditor.query.type,devices=this.listEditor.mainEditor.subEditor.device.value,type=this.$elements.type.val();if(["attribute","state","config"].includes(type)){var i18n=`${this.NRCD}/server:editor.inputs.outputs.payload`;let html='<option value="__complete__">'+RED._(`${i18n}.options.complete`)+"</option>";if(!0===this.options.enableEachState&&(html+='<option value="__each__">'+RED._(`${i18n}.options.each`)+"</option>"),this.$elements.payload.html(html),"device"===queryType){var data=await $.getJSON(`${this.NRCD}/${type}list`,{controllerID:this.listEditor.mainEditor.serverNode.id,devices:JSON.stringify(this.listEditor.mainEditor.subEditor.device.value)});for(const _type of"attribute"===type?["attribute","state","config"]:[type]){let groupHtml=$("<optgroup/>",{label:RED._(`${i18n}.group_label.${_type}`)});for(const item of Object.keys(data.count[_type]).sort()){let sample=data.sample[_type][item];sample="string"==typeof sample?`"${sample}"`:Array.isArray(sample)?`[${sample.toString()}]`:null===sample||void 0===sample?"NULL":sample.toString();let label;var count=data.count[_type][item];label=count===devices.length?RED._(`${i18n}.item_list`,{name:item,sample:sample}):RED._(`${i18n}.item_list_mix`,{name:item,sample:sample,item_count:count,device_count:devices.length}),$("<option>"+label+"</option>").attr("value","attribute"===type&&"attribute"!==_type?`${_type}.${item}`:item).appendTo(groupHtml)}$.isEmptyObject(data.count[_type])||groupHtml.appendTo(this.$elements.payload)}}this.$elements.payload.multipleSelect("refresh").multipleSelect("enable")}}async initPayloadList(value){let list=this.$elements.payload;list.addClass("multiple-select"),list.multipleSelect({maxHeight:300,dropWidth:300,width:200,numberDisplayed:1,single:!1,selectAll:!1,container:".node-input-output-container-row",filter:!0,filterPlaceholder:RED._(`${this.NRCD}/server:editor.inputs.device.device.filter`),placeholder:RED._(`${this.NRCD}/server:editor.multiselect.none_selected`),onClick:view=>{if(view.selected)switch(view.value){case"__complete__":case"__each__":list.multipleSelect("setSelects",[view.value]);break;default:list.multipleSelect("uncheck","__complete__"),list.multipleSelect("uncheck","__each__")}},onUncheckAll:()=>{list.multipleSelect("setSelects","__complete__")},onOptgroupClick:view=>{view.selected&&(list.multipleSelect("uncheck","__complete__"),list.multipleSelect("uncheck","__each__"))}}),await this.updatePayloadList(),value&&list.multipleSelect("setSelects",value)}async generatePayloadTypeField(container,value){var type,enabled,i18n=`${this.NRCD}/server:editor.inputs.outputs.type`;let choices=[];for([type,enabled]of Object.entries(this.listEditor.options.type))enabled&&choices.push([type,`${i18n}.options.${type}`]);await this.generateSimpleListField(container,{id:this.elements.type,i18n:i18n,choices:choices,currentValue:value})}async generatePayloadField(container){var i18n=`${this.NRCD}/server:editor.inputs.outputs.payload`;await this.generateSimpleListField(container,{id:this.elements.payload,i18n:i18n})}async generatePayloadFormatField(container,value){var format,enabled,i18n=`${this.NRCD}/server:editor.inputs.outputs.format`;let choices=[];for([format,enabled]of Object.entries(this.listEditor.options.format))enabled&&choices.push([format,`${i18n}.options.${format}`]);await this.generateSimpleListField(container,{id:this.elements.format,i18n:i18n,choices:choices,currentValue:value})}async generateOutputField(container,value){var i18n=`${this.NRCD}/server:editor.inputs.outputs.output`;await this.generateSimpleListField(container,{id:this.elements.output,i18n:i18n,choices:[["always",`${i18n}.options.always`],["onchange",`${i18n}.options.onchange`],["onupdate",`${i18n}.options.onupdate`]],currentValue:value})}async generateOnStartField(container,value){var i18n=`${this.NRCD}/server:editor.inputs.outputs.on_start`;await this.generateCheckboxField(container,{id:this.elements.onstart,i18n:i18n,currentValue:value})}async generateOnErrorField(container,value){var i18n=`${this.NRCD}/server:editor.inputs.outputs.on_error`;await this.generateCheckboxField(container,{id:this.elements.onerror,i18n:i18n,currentValue:value})}}class DeconzOutputRuleListEditor extends DeconzListItemListEditor{get elements(){return{list:"node-input-output-container"}}get listType(){return"rule"}get buttons(){let buttons=[];var type_name,i18n=`${this.NRCD}/server:editor.inputs.outputs.type`;for(const[type,enabled]of Object.entries(this.options.type))enabled&&(type_name=this.getI18n(`${i18n}.options.${type}`),buttons.push({label:this.getI18n(`${i18n}.add_button`,"label",{type:type_name}),icon:this.getIcon(this.getI18n(`${i18n}.add_button`,"icon"),!0),title:this.getI18n(`${i18n}.add_button`,"title",{type:type_name}),click:()=>this.$elements.list.editableList("addItem",{type:type})}));return buttons}async init(mainEditor){await super.init(mainEditor),await this.initList(DeconzOutputRuleEditor,this.node.output_rules)}refresh(){for(const rule of Object.values(this.items))rule.updatePayloadList()}}class DeconzCommandEditor extends DeconzListItemEditor{constructor(node,listEditor,container,options={}){super(node,listEditor,container,options=$.extend({},options)),this.containers={}}get lightKeys(){return["bri","sat","hue","ct","xy"]}get argKeys(){return["on","alert","effect","colorloopspeed","open","stop","lift","tilt","group","scene","target","command","payload","delay","transitiontime","retryonerror","aftererror"]}get elements(){let keys=this.argKeys;keys.push("typedomain"),keys.push("outputButton"),keys.push("scene_picker"),keys.push("scene_picker_refresh");for(const lightKey of this.lightKeys)keys.push(lightKey),keys.push(lightKey+"_direction");let elements={};for(const key of keys)elements[key]=`node-input-output-rule-${this.uniqueId}-${key}`;return elements}set value(command){}get value(){let value={arg:{}};value.type=this.$elements.typedomain.typedInput("type"),value.domain=this.$elements.typedomain.typedInput("value");for(const key of this.argKeys)value.arg[key]={type:this.$elements[key].typedInput("type"),value:this.$elements[key].typedInput("value")};for(const key of this.lightKeys)value.arg[key]={direction:this.$elements[key+"_direction"].typedInput("type"),type:this.$elements[key].typedInput("type"),value:this.$elements[key].typedInput("value")};return value}get defaultCommand(){return{type:"deconz_state",domain:"lights",target:"state",arg:{on:{type:"keep"},bri:{direction:"set",type:"num"},sat:{direction:"set",type:"num"},hue:{direction:"set",type:"num"},ct:{direction:"set",type:"num"},xy:{direction:"set",type:"num"},alert:{type:"str"},effect:{type:"str"},colorloopspeed:{type:"num"},transitiontime:{type:"num"},command:{type:"str",value:"on"},payload:{type:"msg",value:"payload"},delay:{type:"num",value:2e3},target:{type:"state"},group:{type:"num"},scene_call:{type:"num"},retryonerror:{type:"num",value:0},aftererror:{type:"continue"}}}}async init(command,index){this._index=index,command=$.extend(!0,this.defaultCommand,command),await this.generateTypeDomainField(this.container,{type:command.type,value:command.domain}),this.containers.light=$("<div>").appendTo(this.container),await this.generateLightOnField(this.containers.light,command.arg.on);for(const lightType of["bri","sat","hue","ct","xy"])await this.generateLightColorField(this.containers.light,lightType,command.arg[lightType]),"bri"===lightType&&await this.generateHR(this.containers.light);await this.generateHR(this.containers.light),await this.generateLightAlertField(this.containers.light,command.arg.alert),await this.generateLightEffectField(this.containers.light,command.arg.effect),await this.generateLightColorLoopSpeedField(this.containers.light,command.arg.colorloopspeed),this.containers.windows_cover=$("<div>").appendTo(this.container),await this.generateCoverOpenField(this.containers.windows_cover,command.arg.open),await this.generateCoverStopField(this.containers.windows_cover,command.arg.stop),await this.generateCoverLiftField(this.containers.windows_cover,command.arg.lift),await this.generateCoverTiltField(this.containers.windows_cover,command.arg.tilt),this.containers.scene_call=$("<div>").appendTo(this.container),await this.generateScenePickerField(this.containers.scene_call,`${command.arg.group}.${command.arg.scene}`),await this.generateSceneGroupField(this.containers.scene_call,command.arg.group),await this.generateSceneSceneField(this.containers.scene_call,command.arg.scene),this.containers.command=$("<div>").appendTo(this.container),await this.generateTargetField(this.containers.command,command.arg.target),await this.generateCommandField(this.containers.command,command.arg.command),this.containers.payload=$("<div>").appendTo(this.container),await this.generatePayloadField(this.containers.payload,command.arg.payload),this.containers.pause=$("<div>").appendTo(this.container),await this.generatePauseDelayField(this.containers.pause,command.arg.delay),this.containers.transition=$("<div>").appendTo(this.container),await this.generateHR(this.containers.transition),await this.generateCommonTransitionTimeField(this.containers.transition,command.arg.transitiontime),this.containers.common=$("<div>").appendTo(this.container),await this.generateHR(this.containers.common),await this.generateCommonOnErrorRetryField(this.containers.common,command.arg.retryonerror),await this.generateCommonOnErrorAfterField(this.containers.common,command.arg.aftererror),await super.init(),await this.listEditor.mainEditor.isInitialized(),await this.updateShowHide(command.type,command.domain),await this.connect()}async connect(){await super.connect(),this.$elements.typedomain.on("change",(event,type,value)=>{this.updateShowHide(type,value)});const updateSceneGroupSelection=()=>{let value=this.$elements.scene_picker.multipleSelect("getSelects");var parts;1===value.length&&(this.$elements.group.off("change",updateScenePickerSelection),this.$elements.scene.off("change",updateScenePickerSelection),parts=value[0].split("."),this.$elements.group.typedInput("type","num"),this.$elements.group.typedInput("value",parts[0]),this.$elements.scene.typedInput("type","num"),this.$elements.scene.typedInput("value",parts[1]),this.$elements.group.on("change",updateScenePickerSelection),this.$elements.scene.on("change",updateScenePickerSelection))},updateScenePickerSelection=()=>{this.$elements.scene_picker.off("change",updateSceneGroupSelection),this.$elements.scene_picker.multipleSelect("setSelects","num"!==this.$elements.group.typedInput("type")||"num"!==this.$elements.group.typedInput("type")?[]:[`${this.$elements.group.typedInput("value")}.${this.$elements.scene.typedInput("value")}`]),this.$elements.scene_picker.on("change",updateSceneGroupSelection)};this.$elements.scene_picker.on("change",updateSceneGroupSelection),this.$elements.group.on("change",updateScenePickerSelection),this.$elements.scene.on("change",updateScenePickerSelection),this.$elements.scene_picker_refresh.on("click",()=>this.updateSceneList())}async updateShowHide(type,domain){let containers=[];switch(type){case"deconz_state":switch(domain){case"lights":case"groups":containers.push("light"),containers.push("transition");break;case"covers":containers.push("windows_cover");break;case"scene_call":containers.push("scene_call"),await this.updateSceneList()}containers.push("common");break;case"homekit":this.$elements.payload.typedInput("types",["msg","flow","global","json","jsonata"]),containers.push("payload"),containers.push("transition"),containers.push("common");break;case"custom":containers.push("command"),this.$elements.payload.typedInput("types",["msg","flow","global","str","num","bool","json","jsonata","date"]),containers.push("payload"),containers.push("transition"),containers.push("common");break;case"pause":containers.push("pause")}for(var[key,value]of Object.entries(this.containers))value.toggle(containers.includes(key))}async updateSceneList(){this.$elements.scene_picker.multipleSelect("disable"),this.$elements.scene_picker.children().remove();let queryEditor=this.listEditor.mainEditor.subEditor.query;if(void 0!==queryEditor){let params=queryEditor.xhrParams;params.queryType="json",params.query=JSON.stringify({match:{device_type:"groups"}});var groups=await queryEditor.getItems({refresh:!0,keepOnlyMatched:!0},params);if(void 0!==groups.LightGroup){for(const group of groups.LightGroup){let groupHtml=$("<optgroup/>",{label:`${group.id} - ${group.name}`});if(group.scenes&&0<group.scenes.length){for(const scene of group.scenes)$(`<option>${scene.id} - ${scene.name}</option>`).attr("value",`${group.id}.${scene.id}`).appendTo(groupHtml);groupHtml.appendTo(this.$elements.scene_picker)}}this.$elements.scene_picker.multipleSelect("refresh").multipleSelect("enable"),this.$elements.scene_picker.multipleSelect("setSelects","num"!==this.$elements.group.typedInput("type")||"num"!==this.$elements.group.typedInput("type")?[]:[`${this.$elements.group.typedInput("value")}.${this.$elements.scene.typedInput("value")}`])}}}async generateTypeDomainField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type`;await this.generateTypedInputField(container,{id:this.elements.typedomain,i18n:i18n,value:value,addDefaultTypes:!1,typedInput:{default:"deconz_state",types:[this.generateTypedInputType(i18n,"deconz_state",{subOptions:["lights","covers","groups","scene_call"]}),this.generateTypedInputType(i18n,"homekit",{hasValue:!1}),this.generateTypedInputType(i18n,"custom",{hasValue:!1}),this.generateTypedInputType(i18n,"pause",{hasValue:!1})]}})}async generateLightOnField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.lights.fields.on`;await this.generateTypedInputField(container,{id:this.elements.on,i18n:i18n,value:value,typedInput:{default:"keep",types:[this.generateTypedInputType(i18n,"keep",{hasValue:!1}),this.generateTypedInputType(i18n,"set",{subOptions:["true","false"]}),this.generateTypedInputType(i18n,"toggle",{hasValue:!1})]}})}async generateLightColorField(container,fieldName,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.lights.fields`;let fieldFormat=["num"],directionsFormat=[this.generateTypedInputType(`${i18n}.lightFields`,"set",{hasValue:!1})];switch(fieldName){case"bri":fieldFormat.push("str"),directionsFormat.push(this.generateTypedInputType(`${i18n}.lightFields`,"inc",{hasValue:!1})),directionsFormat.push(this.generateTypedInputType(`${i18n}.lightFields`,"dec",{hasValue:!1})),directionsFormat.push(this.generateTypedInputType(`${i18n}.lightFields`,"detect_from_value",{hasValue:!1}));break;case"ct":fieldFormat.push("str"),fieldFormat.push(this.generateTypedInputType(`${i18n}.ct`,"deconz",{subOptions:["cold","white","warm"]})),directionsFormat.push(this.generateTypedInputType(`${i18n}.lightFields`,"inc",{hasValue:!1})),directionsFormat.push(this.generateTypedInputType(`${i18n}.lightFields`,"dec",{hasValue:!1})),directionsFormat.push(this.generateTypedInputType(`${i18n}.lightFields`,"detect_from_value",{hasValue:!1}));break;case"xy":fieldFormat=["json"]}await this.generateDoubleTypedInputField(container,{id:this.elements[`${fieldName}_direction`],i18n:`${i18n}.${fieldName}`,addDefaultTypes:!1,value:{type:value.direction},typedInput:{types:directionsFormat}},{id:this.elements[fieldName],value:{type:value.type,value:["xy"===fieldName&&void 0===value.value?"[]":value.value]},typedInput:{types:fieldFormat}})}async generateLightAlertField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.lights.fields.alert`;await this.generateTypedInputField(container,{id:this.elements.alert,i18n:i18n,value:value,typedInput:{types:["str",this.generateTypedInputType(i18n,"deconz",{subOptions:["none","select","lselect"]})]}})}async generateLightEffectField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.lights.fields.effect`;await this.generateTypedInputField(container,{id:this.elements.effect,i18n:i18n,value:value,typedInput:{types:["str",this.generateTypedInputType(i18n,"deconz",{subOptions:["none","colorloop"]})]}})}async generateLightColorLoopSpeedField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.lights.fields.colorloopspeed`;await this.generateTypedInputField(container,{id:this.elements.colorloopspeed,i18n:i18n,value:value,typedInput:{types:["num"]}})}async generateCoverOpenField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.covers.fields.open`;await this.generateTypedInputField(container,{id:this.elements.open,i18n:i18n,value:value,typedInput:{types:[this.generateTypedInputType(i18n,"keep",{hasValue:!1}),this.generateTypedInputType(i18n,"set",{subOptions:["true","false"]}),this.generateTypedInputType(i18n,"toggle",{hasValue:!1})]}})}async generateCoverStopField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.covers.fields.stop`;await this.generateTypedInputField(container,{id:this.elements.stop,i18n:i18n,value:value,typedInput:{types:[this.generateTypedInputType(i18n,"keep",{hasValue:!1}),this.generateTypedInputType(i18n,"set",{subOptions:["true","false"]})]}})}async generateCoverLiftField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.covers.fields.lift`;await this.generateTypedInputField(container,{id:this.elements.lift,i18n:i18n,value:value,typedInput:{types:["num","str",this.generateTypedInputType(i18n,"stop",{hasValue:!1})]}})}async generateCoverTiltField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.covers.fields.tilt`;await this.generateTypedInputField(container,{id:this.elements.tilt,i18n:i18n,value:value,typedInput:{types:["num"]}})}async generateScenePickerField(container,value=0){var buttonElement=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.scene_call.fields.picker`;let list=await this.generateSimpleListField(container,{id:this.elements.scene_picker,i18n:buttonElement});list.addClass("multiple-select"),list.multipleSelect({maxHeight:300,dropWidth:300,width:200,numberDisplayed:1,single:!0,singleRadio:!0,hideOptgroupCheckboxes:!0,showClear:!0,selectAll:!1,filter:!0,filterPlaceholder:this.getI18n(buttonElement,"filter_place_holder"),placeholder:RED._(`${this.NRCD}/server:editor.multiselect.none_selected`),container:".node-input-output-container-row"});buttonElement=$("<a/>",{id:this.elements.scene_picker_refresh,class:"red-ui-button",style:"margin-left:10px;"});this.createIconElement(this.getIcon("refresh"),buttonElement),list.closest(".form-row").append(buttonElement)}async generateSceneGroupField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.scene_call.fields.group`;await this.generateTypedInputField(container,{id:this.elements.group,i18n:i18n,value:value,typedInput:{types:["num"]}})}async generateSceneSceneField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.scene_call.fields.scene`;await this.generateTypedInputField(container,{id:this.elements.scene,i18n:i18n,value:value,typedInput:{types:["num","str",this.generateTypedInputType(i18n,"deconz",{subOptions:["next","prev"]})]}})}async generateTargetField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.common.fields.target`;await this.generateTypedInputField(container,{id:this.elements.target,i18n:i18n,value:value,typedInput:{types:[this.generateTypedInputType(i18n,"attribute",{hasValue:!1}),this.generateTypedInputType(i18n,"state",{hasValue:!1}),this.generateTypedInputType(i18n,"config",{hasValue:!1})]}})}async generateCommandField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.common.fields.command`;await this.generateTypedInputField(container,{id:this.elements.command,i18n:i18n,value:value,typedInput:{types:["str",this.generateTypedInputType(i18n,"object",{hasValue:!1})]}})}async generatePayloadField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.common.fields.payload`;await this.generateTypedInputField(container,{id:this.elements.payload,i18n:i18n,value:value,addDefaultTypes:!1,typedInput:{types:["msg","flow","global","str","num","bool","json","jsonata","date"]}})}async generatePauseDelayField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.pause.fields.delay`;await this.generateTypedInputField(container,{id:this.elements.delay,i18n:i18n,value:value,typedInput:{types:["num"]}})}async generateCommonTransitionTimeField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.common.fields.transitiontime`;await this.generateTypedInputField(container,{id:this.elements.transitiontime,i18n:i18n,value:value,typedInput:{types:["num"]}})}async generateCommonOnErrorRetryField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.common.fields.retryonerror`;await this.generateTypedInputField(container,{id:this.elements.retryonerror,i18n:i18n,value:value,typedInput:{types:["num"]}})}async generateCommonOnErrorAfterField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.common.fields.aftererror`;await this.generateTypedInputField(container,{id:this.elements.aftererror,i18n:i18n,value:value,typedInput:{types:[this.generateTypedInputType(i18n,"continue",{hasValue:!1}),this.generateTypedInputType(i18n,"stop",{hasValue:!1})]}})}}class DeconzCommandListEditor extends DeconzListItemListEditor{get elements(){return{list:"node-input-output-container"}}get listType(){return"command"}get buttons(){let buttons=[];var type_name,i18n=`${this.NRCD}/server:editor.inputs.commands.type`;for(const[type,enabled]of Object.entries(this.options.type))enabled&&(type_name=this.getI18n(`${i18n}.options.${type}`,"label"),buttons.push({label:this.getI18n(`${i18n}.add_button`,"label",{type:type_name}),icon:this.getIcon(this.getI18n(`${i18n}.add_button`,"icon"),!0),title:this.getI18n(`${i18n}.add_button`,"title",{type:type_name}),click:()=>this.$elements.list.editableList("addItem",{type:type})}));return buttons}async init(mainEditor){await super.init(mainEditor),await this.initList(DeconzCommandEditor,this.node.commands)}} | ||
class DeconzEditor{constructor(node,options={}){this.node=node,this.options=options}get elements(){return{}}get NRCD(){return"node-red-contrib-deconz"}findElements(){this.$elements={},Object.keys(this.elements).forEach(k=>{this.$elements[k]=this.findElement(this.elements[k])})}findElement(identifier){return"#"!==identifier.charAt(0)&&"."!==identifier.charAt(0)&&(identifier="#"+identifier),$(identifier)}async init(){this.findElements()}async connect(){}sendError(msg,timeout=1e4){let myNotification=RED.notify(msg,{timeout:timeout,type:"error",buttons:[{text:"okay",class:"primary",click:()=>myNotification.close()}]})}getIcon(icon,includeClass=!1){return"deconz"===icon?"icons/node-red-contrib-deconz/icon-color.png":"homekit"===icon?"icons/node-red-contrib-deconz/homekit-logo.png":RED.nodes.fontAwesome.getIconList().includes(`fa-${icon}`)?`${includeClass?"fa ":""}fa-${icon}`:icon}createIconElement(icon,container,isLarge=!1){if("fa-"===icon.substr(0,3)){if(RED.nodes.fontAwesome.getIconUnicode(icon)){let faIconElement=$("<i/>").appendTo(container);return void faIconElement.addClass("fa "+icon+(isLarge?" fa-lg":""))}icon=RED.settings.apiRootUrl+"icons/node-red/arrow-in.svg"}let imageIconElement=$("<div/>").appendTo(container);imageIconElement.css("backgroundImage","url("+icon+")")}getI18n(prefix,suffix,value={}){let _path=prefix;suffix&&(_path+=`.${suffix}`),value.defaultValue="__undefined__";value=RED._(_path,value);if("__undefined__"!==value)return value}async generateSimpleListField(container,options){let input=$("<select/>",{id:options.id});if(options.choices)for(var[key,value]of options.choices)input.append($("<option/>").attr("value",key).html(RED._(value)));var row=await this.generateInputWithLabel(input,options);return container.append(row),void 0!==options.currentValue&&input.val(options.currentValue),input}async generateTypedInput(container,inputType){let input=$("<input/>",{id:inputType.id,placeholder:RED._(inputType.placeholder)});inputType=$("<input/>",{id:`${inputType.id}_type`,type:"hidden"});return input.append(inputType),input}async initTypedInput(input,options){options=$.extend({addDefaultTypes:!0,displayOnlyIcon:!1,value:{},width:"200px"},options);let typedInputOptions=$.extend({types:["msg","flow","global"]},options.typedInput);if(typedInputOptions.typeField=options.typeId,options.addDefaultTypes&&(typedInputOptions.types.push("msg"),typedInputOptions.types.push("flow"),typedInputOptions.types.push("global"),typedInputOptions.types.push("jsonata")),options.displayOnlyIcon){let that=this;function valueLabel(a,b){let typeDefinition;for(const type of this.typeList)"object"==typeof type&&type.value===this.propertyType&&(typeDefinition=type);void 0!==typeDefinition&&void 0!==typeDefinition.icon&&(this.oldValue=this.input.val(),this.input.val(""),this.valueLabelContainer.hide(),that.createIconElement(typeDefinition.icon,this.selectLabel),this.selectTrigger.addClass("red-ui-typedInput-full-width"),this.selectLabel.show())}var type;for(type of typedInputOptions.types)"string"!=typeof type&&(type.hasValue=!0,type.valueLabel=valueLabel)}input.typedInput(typedInputOptions),void 0!==options.width&&input.typedInput("width",options.width),options.value&&(void 0!==options.value.type&&input.typedInput("type",options.value.type),void 0!==options.value.value&&input.typedInput("value",options.value.value))}async generateTypedInputField(container,options){var input=await this.generateTypedInput(container,{id:options.id,placeholder:this.getI18n(options.i18n,"placeholder")}),row=await this.generateInputWithLabel(input,options);return container.append(row),await this.initTypedInput(input,options),input}async generateDoubleTypedInputField(container,optionsFirst,optionsSecond){var inputFirst=await this.generateTypedInput(container,optionsFirst);let row=await this.generateInputWithLabel(inputFirst,optionsFirst);var inputSecond=await this.generateTypedInput(container,optionsSecond);row.append(inputSecond),container.append(row),optionsFirst.displayOnlyIcon=!0,optionsFirst.width="50px",optionsSecond.width="150px",await this.initTypedInput(inputFirst,optionsFirst),await this.initTypedInput(inputSecond,optionsSecond)}generateTypedInputType(i18n,name,data={}){if(data.value=name,void 0===data.label&&(data.label=this.getI18n(i18n,`options.${name}.label`,{})||name),!1!==data.icon&&void 0===data.icon&&(data.icon=this.getIcon(this.getI18n(i18n,`options.${name}.icon`))),data.icon&&"fa-"===data.icon.substr(0,3)&&(data.icon="fa "+data.icon),Array.isArray(data.subOptions)){Array.isArray(data.options)||(data.options=[]);for(const opt of data.subOptions)data.options.push(this.generateTypedInputType(`${i18n}.options.${name}`,"string"==typeof opt?opt:opt.name,{icon:!1}))}return data}async generateCheckboxField(container,options){var input=$("<input/>",{id:options.id,type:"checkbox",style:"display: table-cell; width: 14px;vertical-align: top;margin-right: 5px",checked:options.currentValue});let row=await this.generateInputWithLabel(input,options);row.append($("<span/>").html(RED._(options.descText)).css("display","table-cell")),container.append(row)}async generateInputWithLabel(input,options={}){let row=$("<div/>",{class:"form-row",style:"padding:5px;margin:0;display:table;min-width:420px;"});var inputID=input.attr("id");if(inputID){let labelElement=$("<label/>");labelElement.attr("for",inputID),labelElement.attr("class","l-width"),labelElement.attr("style","display:table-cell;"),void 0===options.title&&(options.title=this.getI18n(options.i18n,"title")),options.title&&labelElement.attr("title",this.getI18n(options.i18n,"title")),void 0===options.icon&&(options.icon=this.getI18n(options.i18n,"icon")),options.icon&&(this.createIconElement(this.getIcon(options.icon),labelElement),labelElement.append(" ")),void 0===options.label&&(options.label=this.getI18n(options.i18n,"label")),options.label&&labelElement.append(`<span>${options.label}</span>`),row.append(labelElement)}return input.css("display","table-cell"),row.append(input),row}async generateHR(container,topBottom="5px",leftRight="50px"){container.append(`<hr style="margin: ${topBottom} ${leftRight};">`)}async generateSeparator(container,label){container.append(`<div class="separator">${RED._(label)}</div>`)}}class DeconzMainEditor extends DeconzEditor{constructor(node,options={}){if(super(node,$.extend(!0,{have:{statustext:!0,query:!0,device:!0,output_rules:!1,commands:!1,specific:!1},device:{batteryFilter:!1},output_rules:{format:{single:!0,array:!1,sum:!1,average:!1,min:!1,max:!1},type:{attribute:!0,state:!0,config:!0,homekit:!1,scene_call:!1}},commands:{type:{deconz_state:!0,homekit:!0,custom:!0,pause:!0}},specific:{output:{}}},options)),this.subEditor={},this.initDone=!1,this.options.have.statustext&&(this.subEditor.statustext=new DeconzStatusTextEditor(this.node,this.options.statustext)),this.options.have.device&&(this.subEditor.device=new DeconzDeviceEditor(this.node,this.options.device)),this.options.have.query&&(this.subEditor.query=new DeconzQueryEditor(this.node,this.options.query)),this.options.have.specific)switch(this.node.type){case"deconz-output":this.subEditor.specific=new DeconzSpecificOutputEditor(this.node,this.options.specific.output);break;case"deconz-server":this.subEditor.specific=new DeconzSpecificServerEditor(this.node,this.options.specific.server)}this.options.have.output_rules&&(this.subEditor.output_rules=new DeconzOutputRuleListEditor(this.node,this.options.output_rules)),this.options.have.commands&&(this.subEditor.commands=new DeconzCommandListEditor(this.node,this.options.commands))}get elements(){return{server:"node-input-server"}}async configurationMigration(){if(!((this.node.config_version||0)>=this.node._def.defaults.config_version.value)){let config={};for(const key of Object.keys(this.node._def.defaults))config[key]=this.node[key];var data={type:this.node.type,config:JSON.stringify(config)};let errorMsg="Error while migrating the configuration of the node from version "+(this.node.config_version||0)+" to version "+this.node._def.defaults.config_version.value+".",result=await $.getJSON(`${this.NRCD}/configurationMigration`,data).catch((t,u)=>{this.sendError(errorMsg)});if(result&&!result.notNeeded){if(result.new)for(var[key,value]of Object.entries(result.new))this.node[key]=value;if(result.delete&&Array.isArray(result.delete))for(const key of result.delete)delete this.node[key];result.errors&&Array.isArray(result.errors)&&0<result.errors.length&&this.sendError(errorMsg+"<br><li>"+result.errors.join("</li><li>")+"</li>")}}}async init(){await new Promise(resolve=>setTimeout(resolve,100)),await this.configurationMigration(),await super.init(),this.serverNode=RED.nodes.node(this.$elements.server.val()),this.initPromises=[];for(const editor of Object.values(this.subEditor))this.initPromises.push(editor.init(this));await Promise.all(this.initPromises),this.initDone=!0,delete this.initPromises;let connectPromises=[];for(const editor of Object.values(this.subEditor))connectPromises.push(editor.connect());await Promise.all(connectPromises)}async isInitialized(){this.initDone||await Promise.all(this.initPromises)}async updateQueryDeviceDisplay(options){var type=this.subEditor.query.$elements.select.typedInput("type");switch(type){case"device":await this.subEditor.device.updateList(options);break;case"json":case"jsonata":this.subEditor.query.$elements.select.typedInput("validate")&&await this.subEditor.query.updateList(options)}await this.subEditor.device.display("device"===type),await this.subEditor.query.display("device"!==type)}oneditsave(){var newRules;this.options.have.output_rules&&(newRules=this.subEditor.output_rules.value,this.node.outputs=newRules.length,this.node.output_rules=newRules),this.options.have.commands&&(this.node.commands=this.subEditor.commands.value),this.options.have.specific&&(this.node.specific=this.subEditor.specific.value)}}class DeconzStatusTextEditor extends DeconzEditor{constructor(node,options={}){super(node,$.extend({allowedTypes:["msg","jsonata"]},options))}get elements(){return{statustext:"node-input-statustext"}}async init(mainEditor){await super.init(),this.mainEditor=mainEditor,this.initTypedInput()}initTypedInput(){let options=[];this.mainEditor.options.have.statustext&&options.push({value:"auto",label:RED._(`${this.NRCD}/server:editor.inputs.statustext.options.auto`),icon:`icons/${this.NRCD}/icon-color.png`,hasValue:!1}),this.$elements.statustext.typedInput({type:"auto",types:options.concat(this.options.allowedTypes),typeField:`#${this.elements.statustext}_type`})}}class DeconzDeviceListEditor extends DeconzEditor{constructor(node,options={}){super(node,options)}get xhrURL(){return`${this.NRCD}/itemlist`}get xhrParams(){return{controllerID:this.mainEditor.serverNode.id,forceRefresh:this.options.refresh}}async display(display=!0){if(this.$elements.showHide)return display?this.$elements.showHide.show():this.$elements.showHide.hide(),this.$elements.showHide.promise()}async getItems(options,result){result.forceRefresh=options.refresh;result=await $.getJSON(this.xhrURL,result).catch((t,u)=>{this.sendError(400===t.status&&t.responseText?t.responseText:u.toString())});if(!result||!result.error_message)return this.formatItemList(result.items,options.keepOnlyMatched);console.warn(result.error_message)}async updateList(devices){devices=$.extend({refresh:!0},devices);let list=this.$elements.list,params=this.xhrParams;!0===this.options.batteryFilter&&(devices.keepOnlyMatched=!0,params.query=JSON.stringify({type:"match",match:{"config.battery":{type:"complex",operator:"!==",value:void 0}}}));devices=await this.getItems(devices,params);list.children().remove(),devices&&this.generateHtmlItemList(devices,this.$elements.list),list.multipleSelect("refresh"),devices&&list.multipleSelect("enable")}formatItemList(items,keepOnlyMatched=!1){let itemList={};var injectItems=(part,matched)=>{part.forEach(item=>{var device_type=item.type;void 0===itemList[device_type]&&(itemList[device_type]=[]),item.query_match=matched,itemList[device_type].push(item)})};return injectItems(items.matched,!0),!1===keepOnlyMatched&&injectItems(items.rejected,!1),itemList}generateHtmlItemList(items,htmlContainer){var group_key,item_list,queryMode=this.constructor===DeconzQueryEditor;for([group_key,item_list]of Object.entries(items).sort((x,y)=>{x=x[0].toLowerCase(),y=y[0].toLowerCase();return x<y?-1:y<x?1:0})){let groupHtml=$("<optgroup/>").attr("label",group_key);for(const item of item_list.sort((x,y)=>{x=x.name.toLowerCase(),y=y.name.toLowerCase();return x<y?-1:y<x?1:0})){let label=item.name;"groups"===item.device_type&&(label+=" (lights: "+item.lights.length,item.scenes.length&&(label+=", scenes: "+item.scenes.length),label+=")");let opt=$("<option>"+label+"</option>").attr("value",item.device_path);queryMode&&item.query_match&&opt.attr("selected",""),opt.appendTo(groupHtml)}groupHtml.appendTo(htmlContainer)}}}class DeconzQueryEditor extends DeconzDeviceListEditor{constructor(node,options={}){super(node,$.extend({allowedTypes:["json","jsonata"]},options))}get elements(){return{select:"node-input-query",list:"node-input-query_result",showHide:".deconz-query-selector",refreshButton:"#force-refresh-query-result"}}get type(){return this.$elements.select.typedInput("type")}set type(val){this.$elements.list.typedInput("type",val)}get value(){return this.$elements.select.typedInput("value")}set value(val){this.$elements.list.typedInput("value",val)}get xhrParams(){let params=super.xhrParams;return params.query=this.value,params.queryType=this.type,params.nodeID=this.node.id,params}async init(mainEditor){await super.init(),this.mainEditor=mainEditor,this.initTypedInput(),this.$elements.list.multipleSelect({maxHeight:300,dropWidth:320,width:320,single:!1,selectAll:!1,filter:!0,filterPlaceholder:RED._(`${this.NRCD}/server:editor.inputs.device.device.filter`),placeholder:RED._(`${this.NRCD}/server:editor.multiselect.none_selected`),numberDisplayed:1,disableIfEmpty:!0,showClear:!1,hideOptgroupCheckboxes:!0,filterGroup:!0,onClick:view=>{this.$elements.list.multipleSelect(view.selected?"uncheck":"check",view.value)}}),await this.mainEditor.updateQueryDeviceDisplay({useSavedData:!0})}initTypedInput(){let options=[];this.mainEditor.options.have.device&&options.push({value:"device",label:RED._(`${this.NRCD}/server:editor.inputs.device.query.options.device`),icon:`icons/${this.NRCD}/icon-color.png`,hasValue:!1}),this.$elements.select.typedInput({type:"text",types:options.concat(this.options.allowedTypes),typeField:"#node-input-search_type"})}async connect(){await super.connect(),this.$elements.select.on("change",()=>{this.mainEditor.updateQueryDeviceDisplay({useSavedData:!0}),this.mainEditor.subEditor.output_rules.refresh()}),this.$elements.refreshButton.on("click",()=>{this.updateList(),this.mainEditor.subEditor.output_rules.refresh()})}}class DeconzDeviceEditor extends DeconzDeviceListEditor{constructor(node,options={}){super(node,$.extend({batteryFilter:!1},options))}get elements(){return{list:"node-input-device_list",showHide:".deconz-device-selector",refreshButton:"#force-refresh"}}get value(){return this.$elements.list.multipleSelect("getSelects")}set value(val){this.$elements.list.multipleSelect("setSelects",val)}async init(mainEditor){await super.init(),this.mainEditor=mainEditor,this.$elements.list.multipleSelect({maxHeight:300,dropWidth:320,width:320,single:"multiple"!==this.$elements.list.attr("multiple"),filter:!0,filterPlaceholder:RED._(`${this.NRCD}/server:editor.inputs.device.device.filter`),placeholder:RED._(`${this.NRCD}/server:editor.multiselect.none_selected`),showClear:!0})}async connect(){await super.connect(),this.$elements.refreshButton.on("click",()=>{this.updateList($.extend(this.options,{useSelectedData:!0})),this.mainEditor.options.have.output_rules&&this.mainEditor.subEditor.output_rules.refresh()}),this.mainEditor.options.have.output_rules&&this.$elements.list.on("change",()=>{this.mainEditor.subEditor.output_rules.refresh()})}async updateList(options){let itemsSelected;(options=$.extend({useSavedData:!1,useSelectedData:!1},options)).useSelectedData&&(itemsSelected=this.$elements.list.multipleSelect("getSelects")),await super.updateList(options),options.useSavedData&&Array.isArray(this.node.device_list)?this.$elements.list.multipleSelect("setSelects",this.node.device_list):options.useSelectedData&&Array.isArray(itemsSelected)&&this.$elements.list.multipleSelect("setSelects",itemsSelected)}}class DeconzSpecificOutputEditor extends DeconzEditor{constructor(node,options={}){super(node,$.extend({},options))}get elements(){return{container:"specific-container",delay:"node-input-delay",result:"node-input-result"}}get default(){return{delay:{type:"num",value:50},result:{type:"at_end"}}}async init(){this.node.specific=$.extend(!0,this.default,this.node.specific);var container=this.findElement(this.elements.container);await this.generateSeparator(container,`${this.NRCD}/server:editor.inputs.separator.specific`),await this.generateDelayField(container,this.node.specific.delay),await this.generateResultField(container,this.node.specific.result),await super.init()}async generateDelayField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.specific.output.delay`;await this.generateTypedInputField(container,{id:this.elements.delay,i18n:i18n,value:value,width:"250px",typedInput:{types:["num"]}})}async generateResultField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.specific.output.result`;await this.generateTypedInputField(container,{id:this.elements.result,i18n:i18n,value:value,width:"250px",typedInput:{types:[this.generateTypedInputType(i18n,"never",{hasValue:!1}),this.generateTypedInputType(i18n,"after_command",{hasValue:!1}),this.generateTypedInputType(i18n,"at_end",{hasValue:!1})]}})}async connect(){await super.connect()}get value(){return{delay:{type:this.$elements.delay.typedInput("type"),value:this.$elements.delay.typedInput("value")},result:{type:this.$elements.result.typedInput("type"),value:this.$elements.result.typedInput("value")}}}}class DeconzSpecificServerEditor extends DeconzEditor{constructor(node,options={}){super(node,$.extend({},options))}get elements(){return{name:"node-config-input-name",ip:"node-config-input-ip",port:"node-config-input-port",apikey:"node-config-input-secured_apikey",ws_port:"node-config-input-ws_port",secure:"node-config-input-secure",polling:"node-config-input-polling",getSettingsButton:"node-contrib-deconz-get-settings"}}get default(){return{name:"",ip:"",port:"",apikey:"",ws_port:"",secure:!1,polling:15}}get xhrURL(){return`${this.NRCD}/serverAutoconfig`}async init(){this.node.specific=$.extend(!0,this.default,this.node.specific),await super.init()}async connect(){await super.connect(),this.$elements.getSettingsButton.on("click",()=>this.discoverParams())}async discoverParams(overrideSettings){void 0===overrideSettings&&(overrideSettings={});let myNotification,stop=!1,closeNotification=()=>{myNotification&&"function"==typeof myNotification.close&&myNotification.close(),stop=!0};myNotification=RED.notify("<p>Trying to find the server settings, please wait...<br>This can take up to 15 seconds.</p>",{modal:!0,fixed:!0,type:"info",buttons:[{text:"Cancel",class:"primary",click:closeNotification}]});try{let params=Object.assign({},this.value,overrideSettings);void 0===params.discoverParam&&(params.discoverParam={}),params.discoverParam.devicetype="Node-Red Deconz Plugin"+(this.node?` id:${this.node.id}`:"");let request=await $.getJSON(this.xhrURL,{config:JSON.stringify(params)}).catch((t,u)=>{this.sendError(400===t.status&&t.responseText?t.responseText:u.toString())});if(stop)return;if(request.error){let html=`<p>Error ${request.error.code}: ${request.error.description}</p>`,buttons=[{text:"Cancel",click:closeNotification}];switch(request.error.code){case"GATEWAY_CHOICE":html+="<p>There is multiple Deconz device in you network, please select the one you want to configure.</p>";let idPrefix="node-red-contrib-deconz-gateway-id-",node=this;function clickMethod(e){closeNotification();let id=Number(e.target.id.substr(idPrefix.length));isNaN(id)&&(id=void 0),request.currentSettings.discoverParam.discoverResultID=id,node.discoverParams(request.currentSettings)}var index,gateway;for([index,gateway]of request.error.gateway_list.entries())buttons.push({text:`#${index+1}: ${gateway.name}`,id:idPrefix+index,class:"primary",click:clickMethod});buttons.push(buttons.shift());break;case"DECONZ_ERROR":101===request.error.type&&(buttons.unshift({text:"I pressed the link button",class:"primary",click:()=>{closeNotification(),this.discoverParams(request.currentSettings)}}),html+="<p>The reason why the request failed is that the gateway was not unlocked. This mechanism is needed to prevent anybody from access to the gateway without being permitted to do so.</p>",html+="<ul><li>In a new browser tab open the <a href='http://phoscon.de/pwa/' target='_blank'>Phoscon App</a></li><li>Click on Menu -> Settings -> Gateway</li><li>Click on \"Advanced\" button</li><li>Click on the \"Authenticate app\" button</li></ul>",html+=`<p>Within 60 seconds after unlocking the gateway, click on the button "${buttons[0].text}".</p>`);break;default:buttons[buttons.length-1].text="Cancel"}html+=`<p>Logs:</p><pre>${request.log.join("\n")}</pre>`,closeNotification(),myNotification=RED.notify(html,{modal:!0,fixed:!0,type:"error",buttons:buttons})}else request.success?(closeNotification(),myNotification=RED.notify("<p>Settings fetched successfully !</p>",{modal:!1,fixed:!1,type:"success"}),this.value=request.currentSettings):(closeNotification(),myNotification=RED.notify(`<p>Unknown error : ${JSON.stringify(request)}</p>`,{modal:!0,fixed:!0,type:"error",buttons:[{text:"Ok",class:"primary",click:closeNotification}]}))}catch(error){closeNotification(),myNotification=RED.notify(`<p>Error while processing request: ${error.toString()}</p>`,{type:"error"})}}get value(){return{name:this.$elements.name.val(),ip:this.$elements.ip.val(),port:this.$elements.port.val(),apikey:this.$elements.apikey.val(),ws_port:this.$elements.ws_port.val(),secure:this.$elements.secure.prop("checked"),polling:this.$elements.polling.val()}}set value(newValues){this.$elements.name.val(newValues.name),this.$elements.ip.val(newValues.ip),this.$elements.port.val(newValues.port),this.$elements.apikey.val(newValues.apikey),this.$elements.ws_port.val(newValues.ws_port),this.$elements.secure.prop("checked",newValues.secure),this.$elements.polling.val(newValues.polling);for(const element of Object.values(this.$elements))element.change()}}class DeconzListItemEditor extends DeconzEditor{constructor(node,listEditor,container,options={}){super(node,options),this.listEditor=listEditor,container.uniqueId(),this.uniqueId=container.attr("id"),this.container=container}set index(value){void 0!==value&&this.$elements&&this.$elements.outputButton&&this.$elements.outputButton.find(".node-input-rule-index").html(value+1),this._index=value}get index(){return this._index}async init(){await this.generateOutputButton(this.container.children().first()),await super.init()}async generateOutputButton(container){$("<a/>",{id:this.elements.outputButton,class:"red-ui-button top-right-badge"}).append(` → <span class="node-input-rule-index">${this.index+1}</span> `).appendTo(container)}}class DeconzListItemListEditor extends DeconzEditor{constructor(node,options={}){super(node,options),this.items={}}get listType(){return"item"}get buttons(){return[]}async init(mainEditor){await super.init(),this.mainEditor=mainEditor}async initList(itemEditorClass,items=[]){var buttons=this.buttons;this.$elements.list.editableList({sortable:!0,removable:!0,height:"auto",addButton:0===buttons.length,buttons:buttons,addItem:(row,index,item)=>{let itemEditor=new itemEditorClass(this.node,this,row,this.options);item.uniqueId=itemEditor.uniqueId,this.items[item.uniqueId]=itemEditor,itemEditor.init(item,index)},removeItem:item=>{if(!item.uniqueId||!this.items[item.uniqueId])throw new Error(`Error while removing the ${this.listType}, the ${this.listType} ${item.uniqueId} does not exist.`);var deletedIndex=this.items[item.uniqueId].index;delete this.items[item.uniqueId];for(const item of Object.values(this.items))item.index>deletedIndex&&item.index--},sortItems:items=>{items.each((index,item)=>{if(!this.items[item.attr("id")])throw new Error(`Error while moving the ${this.listType}, the ${this.listType} ${index+1} does not exist.`);this.items[item.attr("id")].index=index})}}),0<items.length&&this.$elements.list.editableList("addItems",items)}get value(){let result=[];for(const rule of Object.values(this.items).sort((a,b)=>a.index-b.index))result.push(rule.value);return result}refresh(){}}class DeconzOutputRuleEditor extends DeconzListItemEditor{constructor(node,listEditor,container,options={}){super(node,listEditor,container,options=$.extend({enableEachState:!0},options))}get elements(){let elements={};for(const key of["format","type","payload","output","onstart","onerror","outputButton"])elements[key]=`node-input-output-rule-${this.uniqueId}-${key}`;return elements}get value(){let value={};switch(value.type=this.$elements.type.val(),value.format=this.$elements.format.val(),value.type){case"attribute":case"state":case"config":"deconz-input"===this.node.type&&(value.output=this.$elements.output.val()),["deconz-input","deconz-battery"].includes(this.node.type)&&(value.onstart=this.$elements.onstart.is(":checked")),["deconz-input","deconz-get"].includes(this.node.type)&&(value.payload=this.$elements.payload.multipleSelect("getSelects"));break;case"homekit":["deconz-input","deconz-battery"].includes(this.node.type)&&(value.onstart=this.$elements.onstart.is(":checked")),"deconz-input"===this.node.type&&(value.onerror=this.$elements.onerror.is(":checked"))}return value}get defaultRule(){let rule={type:"state",payload:["__complete__"],format:"single"};return"deconz-input"===this.node.type&&(rule.output="always",rule.onstart=!0,rule.onerror=!0),"deconz-battery"===this.node.type&&(rule.onstart=!0),rule}async init(rule,index){this._index=index,rule=$.extend(!0,this.defaultRule,rule),await this.generatePayloadTypeField(this.container,rule.type),["deconz-input","deconz-get"].includes(this.node.type)&&await this.generatePayloadField(this.container),await this.generatePayloadFormatField(this.container,rule.format),"deconz-input"===this.node.type&&await this.generateOutputField(this.container,(void 0!==rule.output?rule:this.defaultRule).output),["deconz-input","deconz-battery"].includes(this.node.type)&&await this.generateOnStartField(this.container,(void 0!==rule.onstart?rule:this.defaultRule).onstart),"deconz-input"===this.node.type&&await this.generateOnErrorField(this.container,(void 0!==rule.onerror?rule:this.defaultRule).onerror),await super.init(),await this.listEditor.mainEditor.isInitialized(),await this.initPayloadList(rule.payload),await this.updateShowHide(rule.type),await this.connect()}async connect(){await super.connect(),this.$elements.type.on("change",()=>{var type=this.$elements.type.val();["attribute","state","config"].includes(type)&&this.updatePayloadList(),this.updateShowHide(type)}),this.$elements.outputButton.on("click",()=>{try{let nodes=RED.nodes.filterLinks({source:this.node,sourcePort:this.index}).map(l=>{var result=l.target.type;return""!==l.target.name?result+":"+l.target.name:void 0!==l.target._def.label?result+":"+l.target._def.label():result}),myNotification=RED.notify(`The output ${this.index+1} is sending message to ${nodes.length} nodes :<br>${nodes.join("<br>")}`,{modal:!0,timeout:5e3,buttons:[{text:"okay",class:"primary",click:()=>myNotification.close()}]})}catch(e){this.sendError(`This is using not documented API so can be broken at anytime.<br>Error while getting connected nodes: ${e.toString()}`)}})}async updateShowHide(type){switch(type){case"attribute":case"state":case"config":this.$elements.payload.closest(".form-row").show(),this.$elements.output.closest(".form-row").show(),this.$elements.onstart.closest(".form-row").show(),this.$elements.onerror.closest(".form-row").hide();break;case"homekit":this.$elements.payload.closest(".form-row").hide(),this.$elements.output.closest(".form-row").hide(),this.$elements.onstart.closest(".form-row").show(),this.$elements.onerror.closest(".form-row").show();break;case"scene_call":this.$elements.payload.closest(".form-row").hide(),this.$elements.output.closest(".form-row").hide(),this.$elements.onstart.closest(".form-row").hide(),this.$elements.onerror.closest(".form-row").hide()}}async updatePayloadList(){this.$elements.payload.multipleSelect("disable"),this.$elements.payload.children().remove();var queryType=this.listEditor.mainEditor.subEditor.query.type,devices=this.listEditor.mainEditor.subEditor.device.value,type=this.$elements.type.val();if(["attribute","state","config"].includes(type)){var i18n=`${this.NRCD}/server:editor.inputs.outputs.payload`;let html='<option value="__complete__">'+RED._(`${i18n}.options.complete`)+"</option>";if(!0===this.options.enableEachState&&(html+='<option value="__each__">'+RED._(`${i18n}.options.each`)+"</option>"),this.$elements.payload.html(html),"device"===queryType){var data=await $.getJSON(`${this.NRCD}/${type}list`,{controllerID:this.listEditor.mainEditor.serverNode.id,devices:JSON.stringify(this.listEditor.mainEditor.subEditor.device.value)});for(const _type of"attribute"===type?["attribute","state","config"]:[type]){let groupHtml=$("<optgroup/>",{label:RED._(`${i18n}.group_label.${_type}`)});for(const item of Object.keys(data.count[_type]).sort()){let sample=data.sample[_type][item];sample="string"==typeof sample?`"${sample}"`:Array.isArray(sample)?`[${sample.toString()}]`:null===sample||void 0===sample?"NULL":sample.toString();let label;var count=data.count[_type][item];label=count===devices.length?RED._(`${i18n}.item_list`,{name:item,sample:sample}):RED._(`${i18n}.item_list_mix`,{name:item,sample:sample,item_count:count,device_count:devices.length}),$("<option>"+label+"</option>").attr("value","attribute"===type&&"attribute"!==_type?`${_type}.${item}`:item).appendTo(groupHtml)}$.isEmptyObject(data.count[_type])||groupHtml.appendTo(this.$elements.payload)}}this.$elements.payload.multipleSelect("refresh").multipleSelect("enable")}}async initPayloadList(value){let list=this.$elements.payload;list.addClass("multiple-select"),list.multipleSelect({maxHeight:300,dropWidth:300,width:200,numberDisplayed:1,single:!1,selectAll:!1,container:".node-input-output-container-row",filter:!0,filterPlaceholder:RED._(`${this.NRCD}/server:editor.inputs.device.device.filter`),placeholder:RED._(`${this.NRCD}/server:editor.multiselect.none_selected`),onClick:view=>{if(view.selected)switch(view.value){case"__complete__":case"__each__":list.multipleSelect("setSelects",[view.value]);break;default:list.multipleSelect("uncheck","__complete__"),list.multipleSelect("uncheck","__each__")}},onUncheckAll:()=>{list.multipleSelect("setSelects","__complete__")},onOptgroupClick:view=>{view.selected&&(list.multipleSelect("uncheck","__complete__"),list.multipleSelect("uncheck","__each__"))}}),await this.updatePayloadList(),value&&list.multipleSelect("setSelects",value)}async generatePayloadTypeField(container,value){var type,enabled,i18n=`${this.NRCD}/server:editor.inputs.outputs.type`;let choices=[];for([type,enabled]of Object.entries(this.listEditor.options.type))enabled&&choices.push([type,`${i18n}.options.${type}`]);await this.generateSimpleListField(container,{id:this.elements.type,i18n:i18n,choices:choices,currentValue:value})}async generatePayloadField(container){var i18n=`${this.NRCD}/server:editor.inputs.outputs.payload`;await this.generateSimpleListField(container,{id:this.elements.payload,i18n:i18n})}async generatePayloadFormatField(container,value){var format,enabled,i18n=`${this.NRCD}/server:editor.inputs.outputs.format`;let choices=[];for([format,enabled]of Object.entries(this.listEditor.options.format))enabled&&choices.push([format,`${i18n}.options.${format}`]);await this.generateSimpleListField(container,{id:this.elements.format,i18n:i18n,choices:choices,currentValue:value})}async generateOutputField(container,value){var i18n=`${this.NRCD}/server:editor.inputs.outputs.output`;await this.generateSimpleListField(container,{id:this.elements.output,i18n:i18n,choices:[["always",`${i18n}.options.always`],["onchange",`${i18n}.options.onchange`],["onupdate",`${i18n}.options.onupdate`]],currentValue:value})}async generateOnStartField(container,value){var i18n=`${this.NRCD}/server:editor.inputs.outputs.on_start`;await this.generateCheckboxField(container,{id:this.elements.onstart,i18n:i18n,currentValue:value})}async generateOnErrorField(container,value){var i18n=`${this.NRCD}/server:editor.inputs.outputs.on_error`;await this.generateCheckboxField(container,{id:this.elements.onerror,i18n:i18n,currentValue:value})}}class DeconzOutputRuleListEditor extends DeconzListItemListEditor{get elements(){return{list:"node-input-output-container"}}get listType(){return"rule"}get buttons(){let buttons=[];var type_name,i18n=`${this.NRCD}/server:editor.inputs.outputs.type`;for(const[type,enabled]of Object.entries(this.options.type))enabled&&(type_name=this.getI18n(`${i18n}.options.${type}`),buttons.push({label:this.getI18n(`${i18n}.add_button`,"label",{type:type_name}),icon:this.getIcon(this.getI18n(`${i18n}.add_button`,"icon"),!0),title:this.getI18n(`${i18n}.add_button`,"title",{type:type_name}),click:()=>this.$elements.list.editableList("addItem",{type:type})}));return buttons}async init(mainEditor){await super.init(mainEditor),await this.initList(DeconzOutputRuleEditor,this.node.output_rules)}refresh(){for(const rule of Object.values(this.items))rule.updatePayloadList()}}class DeconzCommandEditor extends DeconzListItemEditor{constructor(node,listEditor,container,options={}){super(node,listEditor,container,options=$.extend({},options)),this.containers={}}get lightKeys(){return["bri","sat","hue","ct","xy"]}get argKeys(){return["on","alert","effect","colorloopspeed","open","stop","lift","tilt","group","scene","target","command","payload","delay","transitiontime","retryonerror","aftererror"]}get elements(){let keys=this.argKeys;keys.push("typedomain"),keys.push("outputButton"),keys.push("scene_picker"),keys.push("scene_picker_refresh");for(const lightKey of this.lightKeys)keys.push(lightKey),keys.push(lightKey+"_direction");let elements={};for(const key of keys)elements[key]=`node-input-output-rule-${this.uniqueId}-${key}`;return elements}set value(command){}get value(){let value={arg:{}};value.type=this.$elements.typedomain.typedInput("type"),value.domain=this.$elements.typedomain.typedInput("value");for(const key of this.argKeys)value.arg[key]={type:this.$elements[key].typedInput("type"),value:this.$elements[key].typedInput("value")};for(const key of this.lightKeys)value.arg[key]={direction:this.$elements[key+"_direction"].typedInput("type"),type:this.$elements[key].typedInput("type"),value:this.$elements[key].typedInput("value")};return value}get defaultCommand(){return{type:"deconz_state",domain:"lights",target:"state",arg:{on:{type:"keep"},bri:{direction:"set",type:"num"},sat:{direction:"set",type:"num"},hue:{direction:"set",type:"num"},ct:{direction:"set",type:"num"},xy:{direction:"set",type:"num"},alert:{type:"str"},effect:{type:"str"},colorloopspeed:{type:"num"},transitiontime:{type:"num"},command:{type:"str",value:"on"},payload:{type:"msg",value:"payload"},delay:{type:"num",value:2e3},target:{type:"state"},group:{type:"num"},scene_call:{type:"num"},retryonerror:{type:"num",value:0},aftererror:{type:"continue"}}}}async init(command,index){this._index=index,command=$.extend(!0,this.defaultCommand,command),await this.generateTypeDomainField(this.container,{type:command.type,value:command.domain}),this.containers.light=$("<div>").appendTo(this.container),await this.generateLightOnField(this.containers.light,command.arg.on);for(const lightType of["bri","sat","hue","ct","xy"])await this.generateLightColorField(this.containers.light,lightType,command.arg[lightType]),"bri"===lightType&&await this.generateHR(this.containers.light);await this.generateHR(this.containers.light),await this.generateLightAlertField(this.containers.light,command.arg.alert),await this.generateLightEffectField(this.containers.light,command.arg.effect),await this.generateLightColorLoopSpeedField(this.containers.light,command.arg.colorloopspeed),this.containers.windows_cover=$("<div>").appendTo(this.container),await this.generateCoverOpenField(this.containers.windows_cover,command.arg.open),await this.generateCoverStopField(this.containers.windows_cover,command.arg.stop),await this.generateCoverLiftField(this.containers.windows_cover,command.arg.lift),await this.generateCoverTiltField(this.containers.windows_cover,command.arg.tilt),this.containers.scene_call=$("<div>").appendTo(this.container),await this.generateScenePickerField(this.containers.scene_call,`${command.arg.group}.${command.arg.scene}`),await this.generateSceneGroupField(this.containers.scene_call,command.arg.group),await this.generateSceneSceneField(this.containers.scene_call,command.arg.scene),this.containers.command=$("<div>").appendTo(this.container),await this.generateTargetField(this.containers.command,command.arg.target),await this.generateCommandField(this.containers.command,command.arg.command),this.containers.payload=$("<div>").appendTo(this.container),await this.generatePayloadField(this.containers.payload,command.arg.payload),this.containers.pause=$("<div>").appendTo(this.container),await this.generatePauseDelayField(this.containers.pause,command.arg.delay),this.containers.transition=$("<div>").appendTo(this.container),await this.generateHR(this.containers.transition),await this.generateCommonTransitionTimeField(this.containers.transition,command.arg.transitiontime),this.containers.common=$("<div>").appendTo(this.container),await this.generateHR(this.containers.common),await this.generateCommonOnErrorRetryField(this.containers.common,command.arg.retryonerror),await this.generateCommonOnErrorAfterField(this.containers.common,command.arg.aftererror),await super.init(),await this.listEditor.mainEditor.isInitialized(),await this.updateShowHide(command.type,command.domain),await this.connect()}async connect(){await super.connect(),this.$elements.typedomain.on("change",(event,type,value)=>{this.updateShowHide(type,value)});const updateSceneGroupSelection=()=>{let value=this.$elements.scene_picker.multipleSelect("getSelects");var parts;1===value.length&&(this.$elements.group.off("change",updateScenePickerSelection),this.$elements.scene.off("change",updateScenePickerSelection),parts=value[0].split("."),this.$elements.group.typedInput("type","num"),this.$elements.group.typedInput("value",parts[0]),this.$elements.scene.typedInput("type","num"),this.$elements.scene.typedInput("value",parts[1]),this.$elements.group.on("change",updateScenePickerSelection),this.$elements.scene.on("change",updateScenePickerSelection))},updateScenePickerSelection=()=>{this.$elements.scene_picker.off("change",updateSceneGroupSelection),this.$elements.scene_picker.multipleSelect("setSelects","num"!==this.$elements.group.typedInput("type")||"num"!==this.$elements.group.typedInput("type")?[]:[`${this.$elements.group.typedInput("value")}.${this.$elements.scene.typedInput("value")}`]),this.$elements.scene_picker.on("change",updateSceneGroupSelection)};this.$elements.scene_picker.on("change",updateSceneGroupSelection),this.$elements.group.on("change",updateScenePickerSelection),this.$elements.scene.on("change",updateScenePickerSelection),this.$elements.scene_picker_refresh.on("click",()=>this.updateSceneList())}async updateShowHide(type,domain){let containers=[];switch(type){case"deconz_state":switch(domain){case"lights":case"groups":containers.push("light"),containers.push("transition");break;case"covers":containers.push("windows_cover");break;case"scene_call":containers.push("scene_call"),await this.updateSceneList()}containers.push("common");break;case"homekit":this.$elements.payload.typedInput("types",["msg","flow","global","json","jsonata"]),containers.push("payload"),containers.push("transition"),containers.push("common");break;case"custom":containers.push("command"),this.$elements.payload.typedInput("types",["msg","flow","global","str","num","bool","json","jsonata","date"]),containers.push("payload"),containers.push("transition"),containers.push("common");break;case"pause":containers.push("pause")}for(var[key,value]of Object.entries(this.containers))value.toggle(containers.includes(key))}async updateSceneList(){this.$elements.scene_picker.multipleSelect("disable"),this.$elements.scene_picker.children().remove();let queryEditor=this.listEditor.mainEditor.subEditor.query;if(void 0!==queryEditor){let params=queryEditor.xhrParams;params.queryType="json",params.query=JSON.stringify({match:{device_type:"groups"}});var groups=await queryEditor.getItems({refresh:!0,keepOnlyMatched:!0},params);if(void 0!==groups.LightGroup){for(const group of groups.LightGroup){let groupHtml=$("<optgroup/>",{label:`${group.id} - ${group.name}`});if(group.scenes&&0<group.scenes.length){for(const scene of group.scenes)$(`<option>${scene.id} - ${scene.name}</option>`).attr("value",`${group.id}.${scene.id}`).appendTo(groupHtml);groupHtml.appendTo(this.$elements.scene_picker)}}this.$elements.scene_picker.multipleSelect("refresh").multipleSelect("enable"),this.$elements.scene_picker.multipleSelect("setSelects","num"!==this.$elements.group.typedInput("type")||"num"!==this.$elements.group.typedInput("type")?[]:[`${this.$elements.group.typedInput("value")}.${this.$elements.scene.typedInput("value")}`])}}}async generateTypeDomainField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type`;await this.generateTypedInputField(container,{id:this.elements.typedomain,i18n:i18n,value:value,addDefaultTypes:!1,typedInput:{default:"deconz_state",types:[this.generateTypedInputType(i18n,"deconz_state",{subOptions:["lights","covers","groups","scene_call"]}),this.generateTypedInputType(i18n,"homekit",{hasValue:!1}),this.generateTypedInputType(i18n,"custom",{hasValue:!1}),this.generateTypedInputType(i18n,"pause",{hasValue:!1})]}})}async generateLightOnField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.lights.fields.on`;await this.generateTypedInputField(container,{id:this.elements.on,i18n:i18n,value:value,typedInput:{default:"keep",types:[this.generateTypedInputType(i18n,"keep",{hasValue:!1}),this.generateTypedInputType(i18n,"set",{subOptions:["true","false"]}),this.generateTypedInputType(i18n,"toggle",{hasValue:!1})]}})}async generateLightColorField(container,fieldName,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.lights.fields`;let fieldFormat=["num"],directionsFormat=[this.generateTypedInputType(`${i18n}.lightFields`,"set",{hasValue:!1})];switch(fieldName){case"bri":fieldFormat.push("str"),directionsFormat.push(this.generateTypedInputType(`${i18n}.lightFields`,"inc",{hasValue:!1})),directionsFormat.push(this.generateTypedInputType(`${i18n}.lightFields`,"dec",{hasValue:!1})),directionsFormat.push(this.generateTypedInputType(`${i18n}.lightFields`,"detect_from_value",{hasValue:!1}));break;case"ct":fieldFormat.push("str"),fieldFormat.push(this.generateTypedInputType(`${i18n}.ct`,"deconz",{subOptions:["cold","white","warm"]})),directionsFormat.push(this.generateTypedInputType(`${i18n}.lightFields`,"inc",{hasValue:!1})),directionsFormat.push(this.generateTypedInputType(`${i18n}.lightFields`,"dec",{hasValue:!1})),directionsFormat.push(this.generateTypedInputType(`${i18n}.lightFields`,"detect_from_value",{hasValue:!1}));break;case"xy":fieldFormat=["json"]}await this.generateDoubleTypedInputField(container,{id:this.elements[`${fieldName}_direction`],i18n:`${i18n}.${fieldName}`,addDefaultTypes:!1,value:{type:value.direction},typedInput:{types:directionsFormat}},{id:this.elements[fieldName],value:{type:value.type,value:["xy"===fieldName&&void 0===value.value?"[]":value.value]},typedInput:{types:fieldFormat}})}async generateLightAlertField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.lights.fields.alert`;await this.generateTypedInputField(container,{id:this.elements.alert,i18n:i18n,value:value,typedInput:{types:["str",this.generateTypedInputType(i18n,"deconz",{subOptions:["none","select","lselect"]})]}})}async generateLightEffectField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.lights.fields.effect`;await this.generateTypedInputField(container,{id:this.elements.effect,i18n:i18n,value:value,typedInput:{types:["str",this.generateTypedInputType(i18n,"deconz",{subOptions:["none","colorloop"]})]}})}async generateLightColorLoopSpeedField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.lights.fields.colorloopspeed`;await this.generateTypedInputField(container,{id:this.elements.colorloopspeed,i18n:i18n,value:value,typedInput:{types:["num"]}})}async generateCoverOpenField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.covers.fields.open`;await this.generateTypedInputField(container,{id:this.elements.open,i18n:i18n,value:value,typedInput:{types:[this.generateTypedInputType(i18n,"keep",{hasValue:!1}),this.generateTypedInputType(i18n,"set",{subOptions:["true","false"]}),this.generateTypedInputType(i18n,"toggle",{hasValue:!1})]}})}async generateCoverStopField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.covers.fields.stop`;await this.generateTypedInputField(container,{id:this.elements.stop,i18n:i18n,value:value,typedInput:{types:[this.generateTypedInputType(i18n,"keep",{hasValue:!1}),this.generateTypedInputType(i18n,"set",{subOptions:["true","false"]})]}})}async generateCoverLiftField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.covers.fields.lift`;await this.generateTypedInputField(container,{id:this.elements.lift,i18n:i18n,value:value,typedInput:{types:["num","str",this.generateTypedInputType(i18n,"stop",{hasValue:!1})]}})}async generateCoverTiltField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.covers.fields.tilt`;await this.generateTypedInputField(container,{id:this.elements.tilt,i18n:i18n,value:value,typedInput:{types:["num"]}})}async generateScenePickerField(container,value=0){var buttonElement=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.scene_call.fields.picker`;let list=await this.generateSimpleListField(container,{id:this.elements.scene_picker,i18n:buttonElement});list.addClass("multiple-select"),list.multipleSelect({maxHeight:300,dropWidth:300,width:200,numberDisplayed:1,single:!0,singleRadio:!0,hideOptgroupCheckboxes:!0,showClear:!0,selectAll:!1,filter:!0,filterPlaceholder:this.getI18n(buttonElement,"filter_place_holder"),placeholder:RED._(`${this.NRCD}/server:editor.multiselect.none_selected`),container:".node-input-output-container-row"});buttonElement=$("<a/>",{id:this.elements.scene_picker_refresh,class:"red-ui-button",style:"margin-left:10px;"});this.createIconElement(this.getIcon("refresh"),buttonElement),list.closest(".form-row").append(buttonElement)}async generateSceneGroupField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.scene_call.fields.group`;await this.generateTypedInputField(container,{id:this.elements.group,i18n:i18n,value:value,typedInput:{types:["num"]}})}async generateSceneSceneField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.scene_call.fields.scene`;await this.generateTypedInputField(container,{id:this.elements.scene,i18n:i18n,value:value,typedInput:{types:["num","str",this.generateTypedInputType(i18n,"deconz",{subOptions:["next","prev"]})]}})}async generateTargetField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.common.fields.target`;await this.generateTypedInputField(container,{id:this.elements.target,i18n:i18n,value:value,typedInput:{types:[this.generateTypedInputType(i18n,"attribute",{hasValue:!1}),this.generateTypedInputType(i18n,"state",{hasValue:!1}),this.generateTypedInputType(i18n,"config",{hasValue:!1})]}})}async generateCommandField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.common.fields.command`;await this.generateTypedInputField(container,{id:this.elements.command,i18n:i18n,value:value,typedInput:{types:["str",this.generateTypedInputType(i18n,"object",{hasValue:!1})]}})}async generatePayloadField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.common.fields.payload`;await this.generateTypedInputField(container,{id:this.elements.payload,i18n:i18n,value:value,addDefaultTypes:!1,typedInput:{types:["msg","flow","global","str","num","bool","json","jsonata","date"]}})}async generatePauseDelayField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.pause.fields.delay`;await this.generateTypedInputField(container,{id:this.elements.delay,i18n:i18n,value:value,typedInput:{types:["num"]}})}async generateCommonTransitionTimeField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.common.fields.transitiontime`;await this.generateTypedInputField(container,{id:this.elements.transitiontime,i18n:i18n,value:value,typedInput:{types:["num"]}})}async generateCommonOnErrorRetryField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.common.fields.retryonerror`;await this.generateTypedInputField(container,{id:this.elements.retryonerror,i18n:i18n,value:value,typedInput:{types:["num"]}})}async generateCommonOnErrorAfterField(container,value={}){var i18n=`${this.NRCD}/server:editor.inputs.commands.type.options.deconz_state.options.common.fields.aftererror`;await this.generateTypedInputField(container,{id:this.elements.aftererror,i18n:i18n,value:value,typedInput:{types:[this.generateTypedInputType(i18n,"continue",{hasValue:!1}),this.generateTypedInputType(i18n,"stop",{hasValue:!1})]}})}}class DeconzCommandListEditor extends DeconzListItemListEditor{get elements(){return{list:"node-input-output-container"}}get listType(){return"command"}get buttons(){let buttons=[];var type_name,i18n=`${this.NRCD}/server:editor.inputs.commands.type`;for(const[type,enabled]of Object.entries(this.options.type))enabled&&(type_name=this.getI18n(`${i18n}.options.${type}`,"label"),buttons.push({label:this.getI18n(`${i18n}.add_button`,"label",{type:type_name}),icon:this.getIcon(this.getI18n(`${i18n}.add_button`,"icon"),!0),title:this.getI18n(`${i18n}.add_button`,"title",{type:type_name}),click:()=>this.$elements.list.editableList("addItem",{type:type})}));return buttons}async init(mainEditor){await super.init(mainEditor),await this.initList(DeconzCommandEditor,this.node.commands)}} | ||
//# sourceMappingURL=deconz-editor.js.map |
const got = require('got'); | ||
const dns = require('dns'); | ||
const dnsPromises = dns.promises; | ||
class DeconzAPI { | ||
constructor(options) { | ||
Object.assign({}, this.defaultOptions, options); | ||
options = Object.assign({}, this.defaultOptions, options); | ||
this.name = options.name; | ||
this.ip = options.ip; | ||
this.port = options.port; | ||
this.ws_port = options.ws_port; | ||
this.key = options.key !== undefined ? options.key : '<nouser>'; | ||
this.secured = options.secured; | ||
this.version = options.version; | ||
this.polling = options.polling; | ||
this.versions = [ | ||
@@ -17,3 +23,3 @@ '1', '1.1', '2' | ||
this.url = { | ||
discover: 'https://phoscon.de/discover', | ||
discover: () => 'https://phoscon.de/discover', | ||
api: () => `http${this.secured ? 's' : ''}://${this.ip}:${this.port}/api`, | ||
@@ -107,2 +113,212 @@ challenge: () => `${this.url.api()}/challenge`, // Undocumented | ||
async discoverSettings(opt) { | ||
let options = Object.assign({}, { | ||
discoverResultID: undefined, | ||
devicetype: 'Unknown' | ||
}, opt); | ||
let response = {log: []}; | ||
response.log.push(`Fetching data from '${this.url.discover()}'.`); | ||
let discoverResult = await this.getDiscoveryData(options.discoverResultID); | ||
if (Array.isArray(discoverResult) && discoverResult.length === 1) options.discoverResultID = 0; | ||
let discoverData; | ||
if (discoverResult === undefined) { | ||
response.log.push(`No data fetched from '${this.url.discover()}'.`); | ||
} else { | ||
if (Array.isArray(discoverResult) && | ||
discoverResult.length > 1 && | ||
discoverResult[options.discoverResultID] === undefined | ||
) { | ||
response.log.push("Got mutiple result and no choice has already been made."); | ||
response.log.push(JSON.stringify(discoverResult)); | ||
response.error = { | ||
code: 'GATEWAY_CHOICE', | ||
description: 'Multiple gateways founds.', | ||
gateway_list: discoverResult | ||
}; | ||
response.currentSettings = this.settings; | ||
response.currentSettings.discoverParam = options; | ||
return response; | ||
} else { | ||
discoverData = discoverResult[options.discoverResultID]; | ||
response.log.push(`Using result #${options.discoverResultID + 1}: ` + JSON.stringify(discoverData)); | ||
} | ||
} | ||
if (discoverData) { | ||
if (this.name === undefined || String(this.name).length === 0) this.name = discoverData.name; | ||
if (this.ip === undefined || String(this.ip).length === 0) { | ||
this.ip = discoverData.internalipaddress; | ||
try { | ||
response.log.push(`Trying to get a dns name for fetched IP "${this.ip}".`); | ||
let dnsNames = await dnsPromises.reverse(this.ip); | ||
if (dnsNames.length === 0) { | ||
response.log.push("No domain name found."); | ||
} else if (dnsNames.length === 1) { | ||
this.ip = dnsNames[0]; | ||
response.log.push(`Found domain name "${this.ip}".`); | ||
} else { | ||
this.ip = dnsNames[0]; | ||
response.log.push(`Found multiple domain name "${dnsNames.toString()}".`); | ||
response.log.push(`Using domain name "${this.ip}".`); | ||
} | ||
} catch (e) { | ||
response.log.push("No domain name found."); | ||
} | ||
} | ||
if (this.port === undefined || String(this.port).length === 0) this.port = discoverData.internalport; | ||
} | ||
if ((this.key === undefined || String(this.key).length === 0) || this.key === '<nouser>') { | ||
response.log.push("No valid API key provided, trying acquiring one."); | ||
this.key = '<nouser>'; | ||
let apiQuery; | ||
let guesses = [ | ||
{secured: this.secured, ip: this.ip}, | ||
{secured: false, ip: 'core-deconz.local.hass.io'}, | ||
{secured: false, ip: 'homeassistant.local'}, | ||
]; | ||
if (apiQuery === undefined) { | ||
for (const guess of guesses) { | ||
this.secured = guess.secured; | ||
this.ip = guess.ip; | ||
let bridgeID = await this.getConfig('bridgeid'); | ||
if (bridgeID === undefined) { | ||
response.log.push(`Requesting api key at ${this.url.main()}... Failed.`); | ||
continue; | ||
} | ||
response.log.push(`Found gateway ID "${bridgeID}" at "${this.url.main()}".`); | ||
if (discoverData && discoverData.id !== bridgeID) { | ||
response.log.push(`Bridge id mismatch, got "${bridgeID}" and expect "${discoverData.id}". Skipped.`); | ||
continue; | ||
} | ||
apiQuery = await this.getAPIKey(options.devicetype); | ||
if (apiQuery !== undefined) break; | ||
} | ||
} | ||
if (apiQuery === undefined) { | ||
response.log.push("No response from the server."); | ||
response.error = { | ||
code: 'SERVER_TIMEOUT', | ||
description: 'No response from the server, please try to set an IP-Address.' | ||
}; | ||
return response; | ||
} | ||
if (apiQuery.error) { | ||
response.log.push("Error while requesting api key."); | ||
response.log.push(apiQuery.error.description); | ||
response.error = { | ||
code: 'DECONZ_ERROR', | ||
type: apiQuery.error.type, | ||
description: apiQuery.error.description | ||
}; | ||
response.currentSettings = this.settings; | ||
response.currentSettings.discoverParam = options; | ||
return response; | ||
} | ||
if (apiQuery.success) { | ||
response.log.push("Successfully got a key."); | ||
this.key = apiQuery.success.username; | ||
} | ||
} | ||
if ((this.ws_port === undefined || String(this.ws_port).length === 0)) { | ||
this.ws_port = await this.getConfig('websocketport'); | ||
} | ||
response.success = true; | ||
response.currentSettings = this.settings; | ||
return response; | ||
} | ||
async getDiscoveryData(resultID) { | ||
try { | ||
const discover = await got( | ||
this.url.discover(), | ||
{ | ||
method: 'GET', | ||
retry: 1, | ||
responseType: 'json', | ||
timeout: 2000 | ||
} | ||
); | ||
if (resultID !== undefined) return [discover.body[resultID]]; | ||
return discover.body; | ||
} catch (e) { | ||
console.warn(e.toString()); | ||
} | ||
} | ||
async getAPIKey(devicetype) { | ||
try { | ||
const discover = await got( | ||
this.url.api(), | ||
{ | ||
method: 'POST', | ||
retry: 1, | ||
json: {devicetype: devicetype}, | ||
responseType: 'json', | ||
timeout: 2000 | ||
} | ||
); | ||
return discover.body[0]; | ||
} catch (e) { | ||
if (e instanceof got.RequestError && | ||
e.response !== undefined && | ||
e.response.statusCode === 403 | ||
) { | ||
if (Array.isArray(e.response.body)) { | ||
return e.response.body[0]; | ||
} | ||
} else { | ||
console.warn(e.toString()); | ||
} | ||
} | ||
} | ||
async getConfig(keyName) { | ||
try { | ||
const discover = await got( | ||
this.url.main() + this.url.config.main(), | ||
{ | ||
method: 'GET', | ||
retry: 1, | ||
responseType: 'json', | ||
timeout: 2000 | ||
} | ||
); | ||
return keyName === undefined ? discover.body : discover.body[keyName]; | ||
} catch (e) { | ||
console.warn(e.toString()); | ||
} | ||
} | ||
get settings() { | ||
console.log({ | ||
name: this.name, | ||
ip: this.ip, | ||
port: this.port, | ||
apikey: this.key, | ||
ws_port: this.ws_port, | ||
secure: this.secured, | ||
polling: this.polling | ||
}); | ||
return { | ||
name: this.name, | ||
ip: this.ip, | ||
port: this.port, | ||
apikey: this.key, | ||
ws_port: this.ws_port, | ||
secure: this.secured, | ||
polling: this.polling | ||
}; | ||
} | ||
} | ||
@@ -109,0 +325,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
468143
6566
1