mirror of
				https://github.com/hyperion-project/hyperion.ng.git
				synced 2025-03-01 10:33:28 +00:00 
			
		
		
		
	Fix Kodi Color Calibration, Refactor Wizards (#1718)
* Fix #1674 and refactor wizards * Have own code file per LED-Device Wizard * Include SonarLint feedback * Cleanups * Apply module pattern * Address CodeQL findings * Address CodeQL findings
This commit is contained in:
		| @@ -1,52 +0,0 @@ | |||||||
| $(document).ready( function() { |  | ||||||
|  |  | ||||||
| 	$("#create_user").on("click", function() { |  | ||||||
|     var connectionRetries = 15; |  | ||||||
| 		var data = {"devicetype":"hyperion#"+Date.now()}; |  | ||||||
| 		var UserInterval = setInterval(function(){ |  | ||||||
| 		$.ajax({ |  | ||||||
| 			type: "POST", |  | ||||||
| 			url: 'http://'+$("#ip").val()+'/api', |  | ||||||
| 			processData: false, |  | ||||||
| 			timeout: 1000, |  | ||||||
| 			contentType: 'application/json', |  | ||||||
| 			data: JSON.stringify(data), |  | ||||||
| 			success: function(r) { |  | ||||||
| 					connectionRetries--; |  | ||||||
| 					$("#connectionTime").html(connectionRetries); |  | ||||||
| 					if(connectionRetries == 0) { |  | ||||||
| 						abortConnection(UserInterval); |  | ||||||
|           } |  | ||||||
| 					else |  | ||||||
| 					{ |  | ||||||
| 						$("#abortConnection").hide(); |  | ||||||
| 						$('#pairmodal').modal('show'); |  | ||||||
| 						$("#ip_alert").hide(); |  | ||||||
| 						if (typeof r[0].error != 'undefined') { |  | ||||||
| 							console.log("link not pressed"); |  | ||||||
| 						} |  | ||||||
| 					  if (typeof r[0].success != 'undefined') { |  | ||||||
| 							$('#pairmodal').modal('hide'); |  | ||||||
| 							$('#user').val(r[0].success.username); |  | ||||||
|  |  | ||||||
| 							$( "#hue_lights" ).empty(); |  | ||||||
| 							get_hue_lights(); |  | ||||||
| 							clearInterval(UserInterval); |  | ||||||
| 					  } |  | ||||||
| 					} |  | ||||||
| 			}, |  | ||||||
| 			error: function(XMLHttpRequest, textStatus, errorThrown) { |  | ||||||
| 				$("#ip_alert").show(); |  | ||||||
| 				clearInterval(UserInterval); |  | ||||||
| 			 } |  | ||||||
| 		}); |  | ||||||
|   },1000); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| function abortConnection(UserInterval){ |  | ||||||
| 	clearInterval(UserInterval); |  | ||||||
| 	$("#abortConnection").show(); |  | ||||||
| 	$('#pairmodal').modal('hide'); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| }); |  | ||||||
| @@ -1086,40 +1086,7 @@ $(document).ready(function () { | |||||||
|     conf_editor.validate().length || window.readOnlyMode ? $('#btn_submit_controller').prop('disabled', true) : $('#btn_submit_controller').prop('disabled', false); |     conf_editor.validate().length || window.readOnlyMode ? $('#btn_submit_controller').prop('disabled', true) : $('#btn_submit_controller').prop('disabled', false); | ||||||
|  |  | ||||||
|     // LED controller specific wizards |     // LED controller specific wizards | ||||||
|     $('#btn_wiz_holder').html(""); |     createLedDeviceWizards(ledType); | ||||||
|     $('#btn_led_device_wiz').off(); |  | ||||||
|  |  | ||||||
|     if (ledType == "philipshue") { |  | ||||||
|       var ledWizardType = ledType; |  | ||||||
|       var data = { type: ledWizardType }; |  | ||||||
|       var hue_title = 'wiz_hue_title'; |  | ||||||
|       changeWizard(data, hue_title, startWizardPhilipsHue); |  | ||||||
|     } |  | ||||||
|     else if (ledType == "nanoleaf") { |  | ||||||
|       var ledWizardType = ledType; |  | ||||||
|       var data = { type: ledWizardType }; |  | ||||||
|       var nanoleaf_user_auth_title = 'wiz_nanoleaf_user_auth_title'; |  | ||||||
|       changeWizard(data, nanoleaf_user_auth_title, startWizardNanoleafUserAuth); |  | ||||||
|       $('#btn_wiz_holder').hide(); |  | ||||||
|     } |  | ||||||
|     else if (ledType == "atmoorb") { |  | ||||||
|       var ledWizardType = (this.checked) ? "atmoorb" : ledType; |  | ||||||
|       var data = { type: ledWizardType }; |  | ||||||
|       var atmoorb_title = 'wiz_atmoorb_title'; |  | ||||||
|       changeWizard(data, atmoorb_title, startWizardAtmoOrb); |  | ||||||
|     } |  | ||||||
|     else if (ledType == "yeelight") { |  | ||||||
|       var ledWizardType = (this.checked) ? "yeelight" : ledType; |  | ||||||
|       var data = { type: ledWizardType }; |  | ||||||
|       var yeelight_title = 'wiz_yeelight_title'; |  | ||||||
|       changeWizard(data, yeelight_title, startWizardYeelight); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     function changeWizard(data, hint, fn) { |  | ||||||
|       $('#btn_wiz_holder').html("") |  | ||||||
|       createHint("wizard", $.i18n(hint), "btn_wiz_holder", "btn_led_device_wiz"); |  | ||||||
|       $('#btn_led_device_wiz').off().on('click', data, fn); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     conf_editor.on('ready', function () { |     conf_editor.on('ready', function () { | ||||||
|       var hwLedCountDefault = 1; |       var hwLedCountDefault = 1; | ||||||
|   | |||||||
| @@ -1393,3 +1393,32 @@ function isValidHostnameOrIP(value) { | |||||||
|   return (isValidHostnameOrIP4(value) || isValidIPv6(value) || isValidServicename(value)); |   return (isValidHostnameOrIP4(value) || isValidIPv6(value) || isValidServicename(value)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | const loadedScripts = []; | ||||||
|  |  | ||||||
|  | function isScriptLoaded(src) { | ||||||
|  |   return loadedScripts.indexOf(src) > -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function loadScript(src, callback, ...params) { | ||||||
|  |   if (isScriptLoaded(src)) { | ||||||
|  |     debugMessage('Script ' + src + ' already loaded'); | ||||||
|  |     if (callback && typeof callback === 'function') { | ||||||
|  |       callback( ...params); | ||||||
|  |     } | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   const script = document.createElement('script'); | ||||||
|  |   script.src = src; | ||||||
|  |  | ||||||
|  |   script.onload = function () { | ||||||
|  |     debugMessage('Script ' + src + ' loaded successfully'); | ||||||
|  |     loadedScripts.push(src); | ||||||
|  |  | ||||||
|  |     if (callback && typeof callback === 'function') { | ||||||
|  |       callback(...params); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   document.head.appendChild(script); | ||||||
|  | } | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										283
									
								
								assets/webconfig/js/wizards/LedDevice_atmoorb.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								assets/webconfig/js/wizards/LedDevice_atmoorb.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,283 @@ | |||||||
|  | //**************************** | ||||||
|  | // Wizard AtmoOrb | ||||||
|  | //**************************** | ||||||
|  |  | ||||||
|  | import { ledDeviceWizardUtils as utils } from './LedDevice_utils.js'; | ||||||
|  |  | ||||||
|  | const atmoorbWizard = (() => { | ||||||
|  |  | ||||||
|  |   const lights = []; | ||||||
|  |   let configuredLights = []; | ||||||
|  |  | ||||||
|  |   function getIdInLights(id) { | ||||||
|  |     return lights.filter( | ||||||
|  |       function (lights) { | ||||||
|  |         return lights.id === id | ||||||
|  |       } | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function begin() { | ||||||
|  |  | ||||||
|  |     const configruedOrbIds = conf_editor.getEditor("root.specificOptions.orbIds").getValue().trim(); | ||||||
|  |     if (configruedOrbIds.length !== 0) { | ||||||
|  |       configuredLights = configruedOrbIds.split(",").map(Number); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const multiCastGroup = conf_editor.getEditor("root.specificOptions.host").getValue(); | ||||||
|  |     const multiCastPort = parseInt(conf_editor.getEditor("root.specificOptions.port").getValue()); | ||||||
|  |  | ||||||
|  |     discover(multiCastGroup, multiCastPort); | ||||||
|  |  | ||||||
|  |     $('#btn_wiz_save').off().on("click", function () { | ||||||
|  |       let ledConfig = []; | ||||||
|  |       let finalLights = []; | ||||||
|  |  | ||||||
|  |       //create atmoorb led config | ||||||
|  |       for (let key in lights) { | ||||||
|  |         if ($('#orb_' + key).val() !== "disabled") { | ||||||
|  |           // Set Name to layout-position, if empty | ||||||
|  |           if (lights[key].name === "") { | ||||||
|  |             lights[key].name = $.i18n('conf_leds_layout_cl_' + $('#orb_' + key).val()); | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           finalLights.push(lights[key].id); | ||||||
|  |  | ||||||
|  |           let name = lights[key].id; | ||||||
|  |           if (lights[key].host !== "") | ||||||
|  |             name += ':' + lights[key].host; | ||||||
|  |  | ||||||
|  |           const idx_content = utils.assignLightPos($('#orb_' + key).val(), name); | ||||||
|  |           ledConfig.push(JSON.parse(JSON.stringify(idx_content))); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       //LED layout | ||||||
|  |       window.serverConfig.leds = ledConfig; | ||||||
|  |  | ||||||
|  |       //LED device config | ||||||
|  |       //Start with a clean configuration | ||||||
|  |       let d = {}; | ||||||
|  |  | ||||||
|  |       d.type = 'atmoorb'; | ||||||
|  |       d.hardwareLedCount = finalLights.length; | ||||||
|  |       d.colorOrder = conf_editor.getEditor("root.generalOptions.colorOrder").getValue(); | ||||||
|  |  | ||||||
|  |       d.orbIds = finalLights.toString(); | ||||||
|  |       d.useOrbSmoothing = utils.eV("useOrbSmoothing"); | ||||||
|  |  | ||||||
|  |       d.host = conf_editor.getEditor("root.specificOptions.host").getValue(); | ||||||
|  |       d.port = parseInt(conf_editor.getEditor("root.specificOptions.port").getValue()); | ||||||
|  |       d.latchTime = parseInt(conf_editor.getEditor("root.specificOptions.latchTime").getValue());; | ||||||
|  |  | ||||||
|  |       window.serverConfig.device = d; | ||||||
|  |  | ||||||
|  |       requestWriteConfig(window.serverConfig, true); | ||||||
|  |       resetWizard(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $('#btn_wiz_abort').off().on('click', resetWizard); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async function discover(multiCastGroup, multiCastPort) { | ||||||
|  |     let params = {}; | ||||||
|  |     if (multiCastGroup !== "") { | ||||||
|  |       params.multiCastGroup = multiCastGroup; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (multiCastPort !== 0) { | ||||||
|  |       params.multiCastPort = multiCastPort; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Get discovered lights | ||||||
|  |     const res = await requestLedDeviceDiscovery('atmoorb', params); | ||||||
|  |     if (res && !res.error) { | ||||||
|  |       const r = res.info; | ||||||
|  |  | ||||||
|  |       // Process devices returned by discovery | ||||||
|  |       processDiscoveredDevices(r.devices); | ||||||
|  |  | ||||||
|  |       // Add additional items from configuration | ||||||
|  |       for (const configuredLight of configuredLights) { | ||||||
|  |         processConfiguredLight(configuredLight); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       sortLightsById(); | ||||||
|  |       assign_lights(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function processDiscoveredDevices(devices) { | ||||||
|  |     for (const device of devices) { | ||||||
|  |       if (device.id !== "" && getIdInLights(device.id).length === 0) { | ||||||
|  |         const light = { | ||||||
|  |           id: device.id, | ||||||
|  |           ip: device.ip, | ||||||
|  |           host: device.hostname | ||||||
|  |         }; | ||||||
|  |         lights.push(light); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function processConfiguredLight(configuredLight) { | ||||||
|  |     if (configuredLight !== "" && !isNaN(configuredLight)) { | ||||||
|  |       if (getIdInLights(configuredLight).length === 0) { | ||||||
|  |         const light = { | ||||||
|  |           id: configuredLight, | ||||||
|  |           ip: "", | ||||||
|  |           host: "" | ||||||
|  |         }; | ||||||
|  |         lights.push(light); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function attachIdentifyButtonEvent() { | ||||||
|  |     // Use event delegation to handle clicks on buttons with class "btn-identify" | ||||||
|  |     $('#wizp2_body').on('click', '.btn-identify', function () { | ||||||
|  |       const orbId = $(this).data('orb-id'); | ||||||
|  |       identify(orbId); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function sortLightsById() { | ||||||
|  |     lights.sort((a, b) => (a.id > b.id) ? 1 : -1); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function assign_lights() { | ||||||
|  |     // If records are left for configuration | ||||||
|  |     if (Object.keys(lights).length > 0) { | ||||||
|  |       $('#wh_topcontainer').toggle(false); | ||||||
|  |       $('#orb_ids_t, #btn_wiz_save').toggle(true); | ||||||
|  |  | ||||||
|  |       const lightOptions = [ | ||||||
|  |         "top", "topleft", "topright", | ||||||
|  |         "bottom", "bottomleft", "bottomright", | ||||||
|  |         "left", "lefttop", "leftmiddle", "leftbottom", | ||||||
|  |         "right", "righttop", "rightmiddle", "rightbottom", | ||||||
|  |         "entire", | ||||||
|  |         "lightPosTopLeft112", "lightPosTopLeftNewMid", "lightPosTopLeft121", | ||||||
|  |         "lightPosBottomLeft14", "lightPosBottomLeft12", "lightPosBottomLeft34", "lightPosBottomLeft11", | ||||||
|  |         "lightPosBottomLeft112", "lightPosBottomLeftNewMid", "lightPosBottomLeft121" | ||||||
|  |       ]; | ||||||
|  |  | ||||||
|  |       lightOptions.unshift("disabled"); | ||||||
|  |  | ||||||
|  |       $('.lidsb').html(""); | ||||||
|  |       let pos = ""; | ||||||
|  |  | ||||||
|  |       for (const lightid in lights) { | ||||||
|  |         const orbId = lights[lightid].id; | ||||||
|  |         const orbIp = lights[lightid].ip; | ||||||
|  |         let orbHostname = lights[lightid].host; | ||||||
|  |  | ||||||
|  |         if (orbHostname === "") | ||||||
|  |           orbHostname = $.i18n('edt_dev_spec_lights_itemtitle'); | ||||||
|  |  | ||||||
|  |         let options = ""; | ||||||
|  |         for (const opt in lightOptions) { | ||||||
|  |           const val = lightOptions[opt]; | ||||||
|  |           const txt = (val !== 'entire' && val !== 'disabled') ? 'conf_leds_layout_cl_' : 'wiz_ids_'; | ||||||
|  |           options += '<option value="' + val + '"'; | ||||||
|  |           if (pos === val) options += ' selected="selected"'; | ||||||
|  |           options += '>' + $.i18n(txt + val) + '</option>'; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let enabled = 'enabled'; | ||||||
|  |         if (orbId < 1 || orbId > 255) { | ||||||
|  |           enabled = 'disabled'; | ||||||
|  |           options = '<option value=disabled>' + $.i18n('wiz_atmoorb_unsupported') + '</option>'; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let lightAnnotation = ""; | ||||||
|  |         if (orbIp !== "") { | ||||||
|  |           lightAnnotation = ': ' + orbIp + '<br>(' + orbHostname + ')'; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $('.lidsb').append(createTableRow([orbId + lightAnnotation, '<select id="orb_' + lightid + '" ' + enabled + ' class="orb_sel_watch form-control">' | ||||||
|  |           + options | ||||||
|  |           + '</select>', '<button class="btn btn-sm btn-primary btn-identify" ' + enabled + ' data-orb-id="' + orbId + '")>' | ||||||
|  |           + $.i18n('wiz_identify_light', orbId) + '</button>'])); | ||||||
|  |       } | ||||||
|  |       attachIdentifyButtonEvent(); | ||||||
|  |  | ||||||
|  |       $('.orb_sel_watch').on("change", function () { | ||||||
|  |         let cC = 0; | ||||||
|  |         for (const key in lights) { | ||||||
|  |           if ($('#orb_' + key).val() !== "disabled") { | ||||||
|  |             cC++; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         if (cC === 0 || window.readOnlyMode) | ||||||
|  |           $('#btn_wiz_save').prop("disabled", true); | ||||||
|  |         else | ||||||
|  |           $('#btn_wiz_save').prop("disabled", false); | ||||||
|  |       }); | ||||||
|  |       $('.orb_sel_watch').trigger('change'); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       const noLightsTxt = '<p style="font-weight:bold;color:red;">' + $.i18n('wiz_noLights', 'AtmoOrbs') + '</p>'; | ||||||
|  |       $('#wizp2_body').append(noLightsTxt); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async function identify(orbId) { | ||||||
|  |     const disabled = $('#btn_wiz_save').is(':disabled'); | ||||||
|  |  | ||||||
|  |     // Take care that new record cannot be save during background process | ||||||
|  |     $('#btn_wiz_save').prop('disabled', true); | ||||||
|  |  | ||||||
|  |     const params = { id: orbId }; | ||||||
|  |     await requestLedDeviceIdentification("atmoorb", params); | ||||||
|  |  | ||||||
|  |     if (!window.readOnlyMode) { | ||||||
|  |       $('#btn_wiz_save').prop('disabled', disabled); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return { | ||||||
|  |     start: function (e) { | ||||||
|  |  | ||||||
|  |       //create html | ||||||
|  |       const atmoorb_title = 'wiz_atmoorb_title'; | ||||||
|  |       const atmoorb_intro1 = 'wiz_atmoorb_intro1'; | ||||||
|  |  | ||||||
|  |       $('#wiz_header').html('<i class="fa fa-magic fa-fw"></i>' + $.i18n(atmoorb_title)); | ||||||
|  |       $('#wizp1_body').html('<h4 style="font-weight:bold;text-transform:uppercase;">' + $.i18n(atmoorb_title) + '</h4><p>' + $.i18n(atmoorb_intro1) + '</p>'); | ||||||
|  |  | ||||||
|  |       $('#wizp1_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_cont"><i class="fa fa-fw fa-check"></i>' | ||||||
|  |         + $.i18n('general_btn_continue') + '</button><button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>' | ||||||
|  |         + $.i18n('general_btn_cancel') + '</button>'); | ||||||
|  |  | ||||||
|  |       $('#wizp2_body').html('<div id="wh_topcontainer"></div>'); | ||||||
|  |  | ||||||
|  |       $('#wh_topcontainer').append('<div class="form-group" id="usrcont" style="display:none"></div>'); | ||||||
|  |  | ||||||
|  |       $('#wizp2_body').append('<div id="orb_ids_t" style="display:none"><p style="font-weight:bold" id="orb_id_headline">' + $.i18n('wiz_atmoorb_desc2') + '</p></div>'); | ||||||
|  |  | ||||||
|  |       createTable("lidsh", "lidsb", "orb_ids_t"); | ||||||
|  |       $('.lidsh').append(createTableRow([$.i18n('edt_dev_spec_lights_title'), $.i18n('wiz_pos'), $.i18n('wiz_identify')], true)); | ||||||
|  |       $('#wizp2_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_save" style="display:none"><i class="fa fa-fw fa-save"></i>' | ||||||
|  |         + $.i18n('general_btn_save') + '</button><buttowindow.serverConfig.device = d;n type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>' | ||||||
|  |         + $.i18n('general_btn_cancel') + '</button>'); | ||||||
|  |  | ||||||
|  |       if (getStorage("darkMode") == "on") | ||||||
|  |         $('#wizard_logo').attr("src", 'img/hyperion/logo_negativ.png'); | ||||||
|  |  | ||||||
|  |       //open modal | ||||||
|  |       $("#wizard_modal").modal({ backdrop: "static", keyboard: false, show: true }); | ||||||
|  |  | ||||||
|  |       //listen for continue | ||||||
|  |       $('#btn_wiz_cont').off().on('click', function () { | ||||||
|  |         begin(); | ||||||
|  |         $('#wizp1').toggle(false); | ||||||
|  |         $('#wizp2').toggle(true); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | })(); | ||||||
|  |  | ||||||
|  | export { atmoorbWizard }; | ||||||
							
								
								
									
										94
									
								
								assets/webconfig/js/wizards/LedDevice_nanoleaf.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								assets/webconfig/js/wizards/LedDevice_nanoleaf.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | |||||||
|  | //**************************** | ||||||
|  | // Wizard Nanoleaf | ||||||
|  | //**************************** | ||||||
|  |  | ||||||
|  | const nanoleafWizard = (() => { | ||||||
|  |  | ||||||
|  |   const retryInterval = 2; | ||||||
|  |  | ||||||
|  |   async function createNanoleafUserAuthorization() { | ||||||
|  |     const host = conf_editor.getEditor("root.specificOptions.host").getValue(); | ||||||
|  |     const params = { host }; | ||||||
|  |     let retryTime = 30; | ||||||
|  |  | ||||||
|  |     const UserInterval = setInterval(async function () { | ||||||
|  |       retryTime -= retryInterval; | ||||||
|  |       $("#connectionTime").html(retryTime); | ||||||
|  |  | ||||||
|  |       if (retryTime <= 0) { | ||||||
|  |         handleTimeout(); | ||||||
|  |       } else { | ||||||
|  |         const res = await requestLedDeviceAddAuthorization('nanoleaf', params); | ||||||
|  |         handleResponse(res); | ||||||
|  |       } | ||||||
|  |     }, retryInterval * 1000); | ||||||
|  |  | ||||||
|  |     function handleTimeout() { | ||||||
|  |       clearInterval(UserInterval); | ||||||
|  |       showNotification( | ||||||
|  |         'warning', | ||||||
|  |         $.i18n('wiz_nanoleaf_failure_auth_token'), | ||||||
|  |         $.i18n('wiz_nanoleaf_failure_auth_token_t') | ||||||
|  |       ); | ||||||
|  |       resetWizard(true); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     function handleResponse(res) { | ||||||
|  |       if (res && !res.error) { | ||||||
|  |         const response = res.info; | ||||||
|  |         if (jQuery.isEmptyObject(response)) { | ||||||
|  |           debugMessage(`${retryTime}: Power On/Off button not pressed or device not reachable`); | ||||||
|  |         } else { | ||||||
|  |           const token = response.auth_token; | ||||||
|  |           if (token !== 'undefined') { | ||||||
|  |             conf_editor.getEditor("root.specificOptions.token").setValue(token); | ||||||
|  |           } | ||||||
|  |           clearInterval(UserInterval); | ||||||
|  |           resetWizard(true); | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         clearInterval(UserInterval); | ||||||
|  |         resetWizard(true); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return { | ||||||
|  |     start: function () { | ||||||
|  |       const nanoleaf_user_auth_title = 'wiz_nanoleaf_user_auth_title'; | ||||||
|  |       const nanoleaf_user_auth_intro = 'wiz_nanoleaf_user_auth_intro'; | ||||||
|  |  | ||||||
|  |       $('#wiz_header').html( | ||||||
|  |         `<i class="fa fa-magic fa-fw"></i>${$.i18n(nanoleaf_user_auth_title)}` | ||||||
|  |       ); | ||||||
|  |       $('#wizp1_body').html( | ||||||
|  |         `<h4 style="font-weight:bold;text-transform:uppercase;">${$.i18n(nanoleaf_user_auth_title)}</h4><p>${$.i18n(nanoleaf_user_auth_intro)}</p>` | ||||||
|  |       ); | ||||||
|  |       $('#wizp1_footer').html( | ||||||
|  |         `<button type="button" class="btn btn-primary" id="btn_wiz_cont"><i class="fa fa-fw fa-check"></i>${$.i18n('general_btn_continue')}</button><button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>${$.i18n('general_btn_cancel')}</button>` | ||||||
|  |       ); | ||||||
|  |       $('#wizp3_body').html( | ||||||
|  |         `<span>${$.i18n('wiz_nanoleaf_press_onoff_button')}</span> <br /><br /><center><span id="connectionTime"></span><br /><i class="fa fa-cog fa-spin" style="font-size:100px"></i></center>` | ||||||
|  |       ); | ||||||
|  |  | ||||||
|  |       if (getStorage("darkMode") == "on") { | ||||||
|  |         $('#wizard_logo').attr("src", 'img/hyperion/logo_negativ.png'); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       $("#wizard_modal").modal({ | ||||||
|  |         backdrop: "static", | ||||||
|  |         keyboard: false, | ||||||
|  |         show: true | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       $('#btn_wiz_cont').off().on('click', function () { | ||||||
|  |         createNanoleafUserAuthorization(); | ||||||
|  |         $('#wizp1').toggle(false); | ||||||
|  |         $('#wizp3').toggle(true); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | })(); | ||||||
|  |  | ||||||
|  | export { nanoleafWizard }; | ||||||
|  |  | ||||||
							
								
								
									
										988
									
								
								assets/webconfig/js/wizards/LedDevice_philipshue.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										988
									
								
								assets/webconfig/js/wizards/LedDevice_philipshue.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,988 @@ | |||||||
|  | //**************************** | ||||||
|  | // Wizard Philips Hue | ||||||
|  | //**************************** | ||||||
|  |  | ||||||
|  | import { ledDeviceWizardUtils as utils } from './LedDevice_utils.js'; | ||||||
|  |  | ||||||
|  | const philipshueWizard = (() => { | ||||||
|  |  | ||||||
|  |   // External properties, 2-dimensional arry of [ledType][key] | ||||||
|  |   let devicesProperties = {}; | ||||||
|  |  | ||||||
|  |   let hueIPs = []; | ||||||
|  |   let hueIPsinc = 0; | ||||||
|  |   let hueLights = []; | ||||||
|  |   let hueEntertainmentConfigs = []; | ||||||
|  |   let hueEntertainmentServices = []; | ||||||
|  |   let groupLights = []; | ||||||
|  |   let groupChannels = []; | ||||||
|  |   let groupLightsLocations = []; | ||||||
|  |   let isAPIv2Ready = true; | ||||||
|  |   let isEntertainmentReady = true; | ||||||
|  |  | ||||||
|  |   function checkHueBridge(cb, hueUser) { | ||||||
|  |     const usr = (typeof hueUser != "undefined") ? hueUser : 'config'; | ||||||
|  |     if (usr === 'config') { | ||||||
|  |       $('#wiz_hue_discovered').html(""); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (hueIPs[hueIPsinc]) { | ||||||
|  |       const host = hueIPs[hueIPsinc].host; | ||||||
|  |       const port = hueIPs[hueIPsinc].port; | ||||||
|  |  | ||||||
|  |       if (usr != '') { | ||||||
|  |         getProperties(cb, decodeURIComponent(host), port, usr); | ||||||
|  |       } | ||||||
|  |       else { | ||||||
|  |         cb(false, usr); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (isAPIv2Ready) { | ||||||
|  |         $('#port').val(443); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function checkBridgeResult(reply, usr) { | ||||||
|  |     if (reply) { | ||||||
|  |       //abort checking, first reachable result is used | ||||||
|  |       $('#wiz_hue_ipstate').html(""); | ||||||
|  |       $('#host').val(hueIPs[hueIPsinc].host) | ||||||
|  |       $('#port').val(hueIPs[hueIPsinc].port) | ||||||
|  |  | ||||||
|  |       $('#usrcont').toggle(true); | ||||||
|  |  | ||||||
|  |       checkHueBridge(checkUserResult, $('#user').val()); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       $('#usrcont').toggle(false); | ||||||
|  |       $('#wiz_hue_ipstate').html($.i18n('wiz_hue_failure_ip')); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   function checkUserResult(reply, username) { | ||||||
|  |     $('#usrcont').toggle(true); | ||||||
|  |  | ||||||
|  |     let hue_create_user = 'wiz_hue_e_create_user'; | ||||||
|  |     if (!isEntertainmentReady) { | ||||||
|  |       hue_create_user = 'wiz_hue_create_user'; | ||||||
|  |       $('#hue_client_key_r').toggle(false); | ||||||
|  |     } else { | ||||||
|  |       $('#hue_client_key_r').toggle(true); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $('#wiz_hue_create_user').text($.i18n(hue_create_user)); | ||||||
|  |     $('#wiz_hue_create_user').toggle(true); | ||||||
|  |  | ||||||
|  |     if (reply) { | ||||||
|  |       $('#user').val(username); | ||||||
|  |  | ||||||
|  |       if (isEntertainmentReady && $('#clientkey').val() == "") { | ||||||
|  |         $('#wiz_hue_usrstate').html($.i18n('wiz_hue_e_clientkey_needed')); | ||||||
|  |         $('#wiz_hue_create_user').toggle(true); | ||||||
|  |       } else { | ||||||
|  |         $('#wiz_hue_usrstate').html(""); | ||||||
|  |         $('#wiz_hue_create_user').toggle(false); | ||||||
|  |  | ||||||
|  |         if (isEntertainmentReady) { | ||||||
|  |           $('#hue_id_headline').text($.i18n('wiz_hue_e_desc3')); | ||||||
|  |           $('#hue_grp_ids_t').toggle(true); | ||||||
|  |  | ||||||
|  |           get_hue_groups(username); | ||||||
|  |  | ||||||
|  |         } else { | ||||||
|  |           $('#hue_id_headline').text($.i18n('wiz_hue_desc2')); | ||||||
|  |           $('#hue_grp_ids_t').toggle(false); | ||||||
|  |  | ||||||
|  |           get_hue_lights(username); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       //abort checking, first reachable result is used | ||||||
|  |       $('#wiz_hue_usrstate').html($.i18n('wiz_hue_failure_user')); | ||||||
|  |       $('#wiz_hue_create_user').toggle(true); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   function useGroupId(id, username) { | ||||||
|  |     $('#groupId').val(hueEntertainmentConfigs[id].id); | ||||||
|  |     if (isAPIv2Ready) { | ||||||
|  |       const group = hueEntertainmentConfigs[id]; | ||||||
|  |  | ||||||
|  |       groupLights = []; | ||||||
|  |       for (const light of group.light_services) { | ||||||
|  |         groupLights.push(light.rid); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       groupChannels = []; | ||||||
|  |       for (const channel of group.channels) { | ||||||
|  |         groupChannels.push(channel); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       groupLightsLocations = []; | ||||||
|  |       for (const location of group.locations.service_locations) { | ||||||
|  |         groupLightsLocations.push(location); | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       //Ensure ligthIDs are strings | ||||||
|  |       groupLights = hueEntertainmentConfigs[id].lights.map(num => { | ||||||
|  |         return String(num); | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       const lightLocations = hueEntertainmentConfigs[id].locations; | ||||||
|  |       for (const locationID in lightLocations) { | ||||||
|  |         let lightLocation = {}; | ||||||
|  |  | ||||||
|  |         let position = { | ||||||
|  |           x: lightLocations[locationID][0], | ||||||
|  |           y: lightLocations[locationID][1], | ||||||
|  |           z: lightLocations[locationID][2] | ||||||
|  |         }; | ||||||
|  |         lightLocation.position = position; | ||||||
|  |  | ||||||
|  |         groupLightsLocations.push(lightLocation); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     get_hue_lights(username); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function assignLightEntertainmentPos(isFocusCenter, position, name, id) { | ||||||
|  |  | ||||||
|  |     let x = position.x; | ||||||
|  |     let z = position.z; | ||||||
|  |  | ||||||
|  |     if (isFocusCenter) { | ||||||
|  |       // Map lights as in centered range -0.5 to 0.5 | ||||||
|  |       if (x < -0.5) { | ||||||
|  |         x = -0.5; | ||||||
|  |       } else if (x > 0.5) { | ||||||
|  |         x = 0.5; | ||||||
|  |       } | ||||||
|  |       if (z < -0.5) { | ||||||
|  |         z = -0.5; | ||||||
|  |       } else if (z > 0.5) { | ||||||
|  |         z = 0.5; | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       // Map lights as in full range -1 to 1 | ||||||
|  |       x /= 2; | ||||||
|  |       z /= 2; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const h = x + 0.5; | ||||||
|  |     const v = -z + 0.5; | ||||||
|  |  | ||||||
|  |     const hmin = h - 0.05; | ||||||
|  |     const hmax = h + 0.05; | ||||||
|  |     const vmin = v - 0.05; | ||||||
|  |     const vmax = v + 0.05; | ||||||
|  |  | ||||||
|  |     let layoutObject = { | ||||||
|  |       hmin: hmin < 0 ? 0 : hmin, | ||||||
|  |       hmax: hmax > 1 ? 1 : hmax, | ||||||
|  |       vmin: vmin < 0 ? 0 : vmin, | ||||||
|  |       vmax: vmax > 1 ? 1 : vmax, | ||||||
|  |       name: name | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     if (id !== undefined && id !== null) { | ||||||
|  |       layoutObject.name += "_" + id; | ||||||
|  |     } | ||||||
|  |     return layoutObject; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function assignSegmentedLightPos(segment, position, name) { | ||||||
|  |     let layoutObjects = []; | ||||||
|  |  | ||||||
|  |     let segTotalLength = 0; | ||||||
|  |     for (const key in segment) { | ||||||
|  |  | ||||||
|  |       segTotalLength += segment[key].length; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     let min; | ||||||
|  |     let max; | ||||||
|  |     let horizontal = true; | ||||||
|  |  | ||||||
|  |     let layoutObject = utils.assignLightPos(position, name); | ||||||
|  |     if (position === "left" || position === "right") { | ||||||
|  |       // vertical distribution | ||||||
|  |       min = layoutObject.vmin; | ||||||
|  |       max = layoutObject.vmax; | ||||||
|  |       horizontal = false; | ||||||
|  |  | ||||||
|  |     } else { | ||||||
|  |       // horizontal distribution | ||||||
|  |       min = layoutObject.hmin; | ||||||
|  |       max = layoutObject.hmax; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const step = (max - min) / segTotalLength; | ||||||
|  |     let start = min; | ||||||
|  |  | ||||||
|  |     for (const key in segment) { | ||||||
|  |       min = start; | ||||||
|  |       max = round(start + segment[key].length * step); | ||||||
|  |  | ||||||
|  |       if (horizontal) { | ||||||
|  |         layoutObject.hmin = min; | ||||||
|  |         layoutObject.hmax = max; | ||||||
|  |       } else { | ||||||
|  |         layoutObject.vmin = min; | ||||||
|  |         layoutObject.vmax = max; | ||||||
|  |       } | ||||||
|  |       layoutObject.name = name + "_" + key; | ||||||
|  |       layoutObjects.push(JSON.parse(JSON.stringify(layoutObject))); | ||||||
|  |  | ||||||
|  |       start = max; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return layoutObjects; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function updateBridgeDetails(properties) { | ||||||
|  |     const ledDeviceProperties = properties.config; | ||||||
|  |  | ||||||
|  |     if (!jQuery.isEmptyObject(ledDeviceProperties)) { | ||||||
|  |       isEntertainmentReady = properties.isEntertainmentReady; | ||||||
|  |       isAPIv2Ready = properties.isAPIv2Ready; | ||||||
|  |  | ||||||
|  |       if (ledDeviceProperties.name && ledDeviceProperties.bridgeid && ledDeviceProperties.modelid) { | ||||||
|  |         $('#wiz_hue_discovered').html( | ||||||
|  |           "Bridge: " + ledDeviceProperties.name + | ||||||
|  |           ", Modelid: " + ledDeviceProperties.modelid + | ||||||
|  |           ", Firmware: " + ledDeviceProperties.swversion + "<br/>" + | ||||||
|  |           "API-Version: " + ledDeviceProperties.apiversion + | ||||||
|  |           ", Entertainment: " + (isEntertainmentReady ? "✓" : "-") + | ||||||
|  |           ", APIv2: " + (isAPIv2Ready ? "✓" : "-") | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async function discover() { | ||||||
|  |     $('#wiz_hue_ipstate').html($.i18n('edt_dev_spec_devices_discovery_inprogress')); | ||||||
|  |  | ||||||
|  |     //  $('#wiz_hue_discovered').html("") | ||||||
|  |     const res = await requestLedDeviceDiscovery('philipshue'); | ||||||
|  |     if (res && !res.error) { | ||||||
|  |       const r = res.info; | ||||||
|  |  | ||||||
|  |       // Process devices returned by discovery | ||||||
|  |       if (r.devices.length == 0) { | ||||||
|  |         $('#wiz_hue_ipstate').html($.i18n('wiz_hue_failure_ip')); | ||||||
|  |         $('#wiz_hue_discovered').html("") | ||||||
|  |       } | ||||||
|  |       else { | ||||||
|  |         hueIPs = []; | ||||||
|  |         hueIPsinc = 0; | ||||||
|  |  | ||||||
|  |         let discoveryMethod = "ssdp"; | ||||||
|  |         if (res.info.discoveryMethod) { | ||||||
|  |           discoveryMethod = res.info.discoveryMethod; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (const device of r.devices) { | ||||||
|  |           if (device) { | ||||||
|  |             let host; | ||||||
|  |             let port; | ||||||
|  |             if (discoveryMethod === "ssdp") { | ||||||
|  |               if (device.hostname && device.domain) { | ||||||
|  |                 host = device.hostname + "." + device.domain; | ||||||
|  |                 port = device.port; | ||||||
|  |               } else { | ||||||
|  |                 host = device.ip; | ||||||
|  |                 port = device.port; | ||||||
|  |               } | ||||||
|  |             } else { | ||||||
|  |               host = device.service; | ||||||
|  |               port = device.port; | ||||||
|  |             } | ||||||
|  |             if (host) { | ||||||
|  |  | ||||||
|  |               if (!hueIPs.some(item => item.host === host)) { | ||||||
|  |                 hueIPs.push({ host: host, port: port }); | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $('#wiz_hue_ipstate').html(""); | ||||||
|  |         $('#host').val(hueIPs[hueIPsinc].host) | ||||||
|  |         $('#port').val(hueIPs[hueIPsinc].port) | ||||||
|  |  | ||||||
|  |         $('#hue_bridge_select').html(""); | ||||||
|  |  | ||||||
|  |         for (const key in hueIPs) { | ||||||
|  |           $('#hue_bridge_select').append(createSelOpt(key, hueIPs[key].host)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $('.hue_bridge_sel_watch').on("click", function () { | ||||||
|  |           hueIPsinc = $(this).val(); | ||||||
|  |  | ||||||
|  |           const name = $("#hue_bridge_select option:selected").text(); | ||||||
|  |           $('#host').val(name); | ||||||
|  |           $('#port').val(hueIPs[hueIPsinc].port) | ||||||
|  |  | ||||||
|  |           const usr = $('#user').val(); | ||||||
|  |           if (usr != "") { | ||||||
|  |             checkHueBridge(checkUserResult, usr); | ||||||
|  |           } else { | ||||||
|  |             checkHueBridge(checkBridgeResult); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         $('.hue_bridge_sel_watch').click(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async function getProperties(cb, hostAddress, port, username, resourceFilter) { | ||||||
|  |     let params = { host: hostAddress, username: username, filter: resourceFilter }; | ||||||
|  |     if (port !== 'undefined') { | ||||||
|  |       params.port = parseInt(port); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const ledType = 'philipshue'; | ||||||
|  |     const key = hostAddress; | ||||||
|  |  | ||||||
|  |     //Create ledType cache entry | ||||||
|  |     if (!devicesProperties[ledType]) { | ||||||
|  |       devicesProperties[ledType] = {}; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Use device's properties, if properties in chache | ||||||
|  |     if (devicesProperties[ledType][key] && devicesProperties[ledType][key][username]) { | ||||||
|  |       updateBridgeDetails(devicesProperties[ledType][key]); | ||||||
|  |       cb(true, username); | ||||||
|  |     } else { | ||||||
|  |       const res = await requestLedDeviceProperties(ledType, params); | ||||||
|  |       if (res && !res.error) { | ||||||
|  |         const ledDeviceProperties = res.info.properties; | ||||||
|  |         if (!jQuery.isEmptyObject(ledDeviceProperties)) { | ||||||
|  |  | ||||||
|  |           devicesProperties[ledType][key] = {}; | ||||||
|  |           devicesProperties[ledType][key][username] = ledDeviceProperties; | ||||||
|  |  | ||||||
|  |           isAPIv2Ready = res.info.isAPIv2Ready; | ||||||
|  |           devicesProperties[ledType][key].isAPIv2Ready = isAPIv2Ready; | ||||||
|  |           isEntertainmentReady = res.info.isEntertainmentReady; | ||||||
|  |           devicesProperties[ledType][key].isEntertainmentReady = isEntertainmentReady; | ||||||
|  |  | ||||||
|  |           updateBridgeDetails(devicesProperties[ledType][key]); | ||||||
|  |           if (username === "config") { | ||||||
|  |             cb(true); | ||||||
|  |           } else { | ||||||
|  |             cb(true, username); | ||||||
|  |           } | ||||||
|  |         } else { | ||||||
|  |           cb(false, username); | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         cb(false, username); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async function identify(hostAddress, port, username, name, id, id_v1) { | ||||||
|  |     const disabled = $('#btn_wiz_save').is(':disabled'); | ||||||
|  |     // Take care that new record cannot be save during background process | ||||||
|  |     $('#btn_wiz_save').prop('disabled', true); | ||||||
|  |  | ||||||
|  |     let params = { host: decodeURIComponent(hostAddress), username: username, lightName: decodeURIComponent(name), lightId: id, lightId_v1: id_v1 }; | ||||||
|  |  | ||||||
|  |     if (port !== 'undefined') { | ||||||
|  |       params.port = parseInt(port); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     await requestLedDeviceIdentification('philipshue', params); | ||||||
|  |  | ||||||
|  |     if (!window.readOnlyMode) { | ||||||
|  |       $('#btn_wiz_save').prop('disabled', disabled); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function begin() { | ||||||
|  |     const usr = utils.eV("username"); | ||||||
|  |     if (usr != "") { | ||||||
|  |       $('#user').val(usr); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const clkey = utils.eV("clientkey"); | ||||||
|  |     if (clkey != "") { | ||||||
|  |       $('#clientkey').val(clkey); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     //check if host is empty/reachable/search for bridge | ||||||
|  |     if (utils.eV("host") == "") { | ||||||
|  |       hueIPs = []; | ||||||
|  |       hueIPsinc = 0; | ||||||
|  |  | ||||||
|  |       discover(); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       const host = utils.eV("host"); | ||||||
|  |       $('#host').val(host); | ||||||
|  |  | ||||||
|  |       const port = utils.eV("port"); | ||||||
|  |       if (port > 0) { | ||||||
|  |         $('#port').val(port); | ||||||
|  |       } | ||||||
|  |       else { | ||||||
|  |         $('#port').val(''); | ||||||
|  |       } | ||||||
|  |       hueIPs.push({ host: host, port: port }); | ||||||
|  |  | ||||||
|  |       if (usr != "") { | ||||||
|  |         checkHueBridge(checkUserResult, usr); | ||||||
|  |       } else { | ||||||
|  |         checkHueBridge(checkBridgeResult); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $('#retry_bridge').off().on('click', function () { | ||||||
|  |       const host = $('#host').val(); | ||||||
|  |       const port = parseInt($('#port').val()); | ||||||
|  |  | ||||||
|  |       if (host != "") { | ||||||
|  |  | ||||||
|  |         const idx = hueIPs.findIndex(item => item.host === host && item.port === port); | ||||||
|  |         if (idx === -1) { | ||||||
|  |           hueIPs.push({ host: host, port: port }); | ||||||
|  |           hueIPsinc = hueIPs.length - 1; | ||||||
|  |         } else { | ||||||
|  |           hueIPsinc = idx; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       else { | ||||||
|  |         discover(); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       const usr = $('#user').val(); | ||||||
|  |       if (usr != "") { | ||||||
|  |         checkHueBridge(checkUserResult, usr); | ||||||
|  |       } else { | ||||||
|  |         checkHueBridge(checkBridgeResult); | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $('#retry_usr').off().on('click', function () { | ||||||
|  |       checkHueBridge(checkUserResult, $('#user').val()); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $('#wiz_hue_create_user').off().on('click', function () { | ||||||
|  |       createHueUser(); | ||||||
|  |     }); | ||||||
|  |     $('#btn_wiz_save').off().on("click", function () { | ||||||
|  |       let hueLedConfig = []; | ||||||
|  |       let finalLightIds = []; | ||||||
|  |       let channelNumber = 0; | ||||||
|  |  | ||||||
|  |       //create hue led config | ||||||
|  |       for (const key in groupLights) { | ||||||
|  |         const lightId = groupLights[key]; | ||||||
|  |  | ||||||
|  |         if ($('#hue_' + lightId).val() != "disabled") { | ||||||
|  |           finalLightIds.push(lightId); | ||||||
|  |  | ||||||
|  |           let lightName; | ||||||
|  |           if (isAPIv2Ready) { | ||||||
|  |             const light = hueLights.find(light => light.id === lightId); | ||||||
|  |             lightName = light.metadata.name; | ||||||
|  |           } else { | ||||||
|  |             lightName = hueLights[lightId].name; | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           const position = $('#hue_' + lightId).val(); | ||||||
|  |           const lightIdx = groupLights.indexOf(lightId); | ||||||
|  |           const lightLocation = groupLightsLocations[lightIdx]; | ||||||
|  |  | ||||||
|  |           let serviceID; | ||||||
|  |           if (isAPIv2Ready) { | ||||||
|  |             serviceID = lightLocation.service.rid; | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           if (position.startsWith("entertainment")) { | ||||||
|  |  | ||||||
|  |             // Layout per entertainment area definition at bridge | ||||||
|  |             let isFocusCenter = false; | ||||||
|  |             if (position === "entertainment_center") { | ||||||
|  |               isFocusCenter = true; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (isAPIv2Ready) { | ||||||
|  |  | ||||||
|  |               groupChannels.forEach((channel) => { | ||||||
|  |                 if (channel.members[0].service.rid === serviceID) { | ||||||
|  |                   const layoutObject = assignLightEntertainmentPos(isFocusCenter, channel.position, lightName, channel.channel_id); | ||||||
|  |                   hueLedConfig.push(JSON.parse(JSON.stringify(layoutObject))); | ||||||
|  |                   ++channelNumber; | ||||||
|  |                 } | ||||||
|  |               }); | ||||||
|  |             } else { | ||||||
|  |               const layoutObject = assignLightEntertainmentPos(isFocusCenter, lightLocation.position, lightName); | ||||||
|  |               hueLedConfig.push(JSON.parse(JSON.stringify(layoutObject))); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           else { | ||||||
|  |             // Layout per manual settings | ||||||
|  |             let maxSegments = 1; | ||||||
|  |  | ||||||
|  |             if (isAPIv2Ready) { | ||||||
|  |               const service = hueEntertainmentServices.find(service => service.id === serviceID); | ||||||
|  |               maxSegments = service.segments.max_segments; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (maxSegments > 1) { | ||||||
|  |               const segment = service.segments.segments; | ||||||
|  |               const layoutObjects = assignSegmentedLightPos(segment, position, lightName); | ||||||
|  |               hueLedConfig.push(...layoutObjects); | ||||||
|  |             } else { | ||||||
|  |               const layoutObject = utils.assignLightPos(position, lightName); | ||||||
|  |               hueLedConfig.push(JSON.parse(JSON.stringify(layoutObject))); | ||||||
|  |             } | ||||||
|  |             channelNumber += maxSegments; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       let sc = window.serverConfig; | ||||||
|  |       sc.leds = hueLedConfig; | ||||||
|  |  | ||||||
|  |       //Adjust gamma, brightness and compensation | ||||||
|  |       let c = sc.color.channelAdjustment[0]; | ||||||
|  |       c.gammaBlue = 1.0; | ||||||
|  |       c.gammaRed = 1.0; | ||||||
|  |       c.gammaGreen = 1.0; | ||||||
|  |       c.brightness = 100; | ||||||
|  |       c.brightnessCompensation = 0; | ||||||
|  |  | ||||||
|  |       //device config | ||||||
|  |  | ||||||
|  |       //Start with a clean configuration | ||||||
|  |       let d = {}; | ||||||
|  |       d.host = $('#host').val(); | ||||||
|  |       d.port = parseInt($('#port').val()); | ||||||
|  |       d.username = $('#user').val(); | ||||||
|  |       d.type = 'philipshue'; | ||||||
|  |       d.colorOrder = 'rgb'; | ||||||
|  |       d.lightIds = finalLightIds; | ||||||
|  |       d.transitiontime = parseInt(utils.eV("transitiontime", 1)); | ||||||
|  |       d.restoreOriginalState = utils.eV("restoreOriginalState", false); | ||||||
|  |       d.switchOffOnBlack = utils.eV("switchOffOnBlack", false); | ||||||
|  |  | ||||||
|  |       d.blackLevel = parseFloat(utils.eV("blackLevel", 0.009)); | ||||||
|  |       d.onBlackTimeToPowerOff = parseInt(utils.eV("onBlackTimeToPowerOff", 600)); | ||||||
|  |       d.onBlackTimeToPowerOn = parseInt(utils.eV("onBlackTimeToPowerOn", 300)); | ||||||
|  |       d.brightnessFactor = parseFloat(utils.eV("brightnessFactor", 1)); | ||||||
|  |  | ||||||
|  |       d.clientkey = $('#clientkey').val(); | ||||||
|  |       d.groupId = $('#groupId').val(); | ||||||
|  |       d.blackLightsTimeout = parseInt(utils.eV("blackLightsTimeout", 5000)); | ||||||
|  |       d.brightnessMin = parseFloat(utils.eV("brightnessMin", 0)); | ||||||
|  |       d.brightnessMax = parseFloat(utils.eV("brightnessMax", 1)); | ||||||
|  |       d.brightnessThreshold = parseFloat(utils.eV("brightnessThreshold", 0.0001)); | ||||||
|  |       d.handshakeTimeoutMin = parseInt(utils.eV("handshakeTimeoutMin", 300)); | ||||||
|  |       d.handshakeTimeoutMax = parseInt(utils.eV("handshakeTimeoutMax", 1000)); | ||||||
|  |       d.verbose = utils.eV("verbose"); | ||||||
|  |  | ||||||
|  |       d.autoStart = conf_editor.getEditor("root.generalOptions.autoStart").getValue(); | ||||||
|  |       d.enableAttempts = parseInt(conf_editor.getEditor("root.generalOptions.enableAttempts").getValue()); | ||||||
|  |       d.enableAttemptsInterval = parseInt(conf_editor.getEditor("root.generalOptions.enableAttemptsInterval").getValue()); | ||||||
|  |  | ||||||
|  |       d.useEntertainmentAPI = isEntertainmentReady; | ||||||
|  |       d.useAPIv2 = isAPIv2Ready; | ||||||
|  |  | ||||||
|  |       if (isEntertainmentReady) { | ||||||
|  |         d.hardwareLedCount = channelNumber; | ||||||
|  |         if (window.serverConfig.device.type !== d.type) { | ||||||
|  |           //smoothing on, if new device | ||||||
|  |           sc.smoothing = { enable: true }; | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         d.hardwareLedCount = finalLightIds.length; | ||||||
|  |         d.verbose = false; | ||||||
|  |         if (window.serverConfig.device.type !== d.type) { | ||||||
|  |           //smoothing off, if new device | ||||||
|  |           sc.smoothing = { enable: false }; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       window.serverConfig.device = d; | ||||||
|  |  | ||||||
|  |       requestWriteConfig(sc, true); | ||||||
|  |       resetWizard(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $('#btn_wiz_abort').off().on('click', resetWizard); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function createHueUser() { | ||||||
|  |     const host = hueIPs[hueIPsinc].host; | ||||||
|  |     const port = hueIPs[hueIPsinc].port; | ||||||
|  |  | ||||||
|  |     let params = { host: host }; | ||||||
|  |     if (port !== 'undefined') { | ||||||
|  |       params.port = parseInt(port); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     let retryTime = 30; | ||||||
|  |     const retryInterval = 2; | ||||||
|  |  | ||||||
|  |     const UserInterval = setInterval(function () { | ||||||
|  |  | ||||||
|  |       $('#wizp1').toggle(false); | ||||||
|  |       $('#wizp2').toggle(false); | ||||||
|  |       $('#wizp3').toggle(true); | ||||||
|  |  | ||||||
|  |       (async () => { | ||||||
|  |  | ||||||
|  |         retryTime -= retryInterval; | ||||||
|  |         $("#connectionTime").html(retryTime); | ||||||
|  |         if (retryTime <= 0) { | ||||||
|  |           abortConnection(UserInterval); | ||||||
|  |           clearInterval(UserInterval); | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |           const res = await requestLedDeviceAddAuthorization('philipshue', params); | ||||||
|  |           if (res && !res.error) { | ||||||
|  |             const response = res.info; | ||||||
|  |  | ||||||
|  |             if (jQuery.isEmptyObject(response)) { | ||||||
|  |               debugMessage(retryTime + ": link button not pressed or device not reachable"); | ||||||
|  |             } else { | ||||||
|  |               $('#wizp1').toggle(false); | ||||||
|  |               $('#wizp2').toggle(true); | ||||||
|  |               $('#wizp3').toggle(false); | ||||||
|  |  | ||||||
|  |               const username = response.username; | ||||||
|  |               if (username != 'undefined') { | ||||||
|  |                 $('#user').val(username); | ||||||
|  |                 conf_editor.getEditor("root.specificOptions.username").setValue(username); | ||||||
|  |                 conf_editor.getEditor("root.specificOptions.host").setValue(host); | ||||||
|  |                 conf_editor.getEditor("root.specificOptions.port").setValue(port); | ||||||
|  |               } | ||||||
|  |  | ||||||
|  |               if (isEntertainmentReady) { | ||||||
|  |                 const clientkey = response.clientkey; | ||||||
|  |                 if (clientkey != 'undefined') { | ||||||
|  |                   $('#clientkey').val(clientkey); | ||||||
|  |                   conf_editor.getEditor("root.specificOptions.clientkey").setValue(clientkey); | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |               checkHueBridge(checkUserResult, username); | ||||||
|  |               clearInterval(UserInterval); | ||||||
|  |             } | ||||||
|  |           } else { | ||||||
|  |             $('#wizp1').toggle(false); | ||||||
|  |             $('#wizp2').toggle(true); | ||||||
|  |             $('#wizp3').toggle(false); | ||||||
|  |             clearInterval(UserInterval); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       })(); | ||||||
|  |  | ||||||
|  |     }, retryInterval * 1000); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function get_hue_groups(username) { | ||||||
|  |     const host = hueIPs[hueIPsinc].host; | ||||||
|  |  | ||||||
|  |     if (devicesProperties['philipshue'][host] && devicesProperties['philipshue'][host][username]) { | ||||||
|  |       const ledProperties = devicesProperties['philipshue'][host][username]; | ||||||
|  |  | ||||||
|  |       if (isAPIv2Ready) { | ||||||
|  |         if (!jQuery.isEmptyObject(ledProperties.data)) { | ||||||
|  |           if (Object.keys(ledProperties.data).length > 0) { | ||||||
|  |             hueEntertainmentConfigs = ledProperties.data.filter(config => { | ||||||
|  |               return config.type === "entertainment_configuration"; | ||||||
|  |             }); | ||||||
|  |             hueEntertainmentServices = ledProperties.data.filter(config => { | ||||||
|  |               return (config.type === "entertainment" && config.renderer === true); | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } else if (!jQuery.isEmptyObject(ledProperties.groups)) { | ||||||
|  |         hueEntertainmentConfigs = []; | ||||||
|  |         let hueGroups = ledProperties.groups; | ||||||
|  |         for (const groupid in hueGroups) { | ||||||
|  |           if (hueGroups[groupid].type == 'Entertainment') { | ||||||
|  |             hueGroups[groupid].id = groupid; | ||||||
|  |             hueEntertainmentConfigs.push(hueGroups[groupid]); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (Object.keys(hueEntertainmentConfigs).length > 0) { | ||||||
|  |  | ||||||
|  |         $('.lidsb').html(""); | ||||||
|  |         $('#wh_topcontainer').toggle(false); | ||||||
|  |         $('#hue_grp_ids_t').toggle(true); | ||||||
|  |  | ||||||
|  |         for (const groupid in hueEntertainmentConfigs) { | ||||||
|  |           $('.gidsb').append(createTableRow([groupid + ' (' + hueEntertainmentConfigs[groupid].name + ')', | ||||||
|  |           '<button class="btn btn-sm btn-primary btn-group" data-groupid="' + groupid + '" data-username="' + username + '")>' | ||||||
|  |           + $.i18n('wiz_hue_e_use_group') + '</button>'])); | ||||||
|  |         } | ||||||
|  |         attachGroupButtonEvent(); | ||||||
|  |  | ||||||
|  |       } else { | ||||||
|  |         noAPISupport('wiz_hue_e_noegrpids', username); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   function attachIdentifyButtonEvent() { | ||||||
|  |     $('#wizp2_body').on('click', '.btn-identify', function () { | ||||||
|  |       const hostname = $(this).data('hostname'); | ||||||
|  |       const port = $(this).data('port'); | ||||||
|  |       const user = $(this).data('user'); | ||||||
|  |       const lightName = $(this).data('light-name'); | ||||||
|  |       const lightId = $(this).data('light-id'); | ||||||
|  |       const lightId_v1 = $(this).data('light-id-v1'); | ||||||
|  |  | ||||||
|  |       identify(hostname, port, user, lightName, lightId, lightId_v1); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |   function attachGroupButtonEvent() { | ||||||
|  |     $('#wizp2_body').on('click', '.btn-group', function () { | ||||||
|  |       const groupid = $(this).data('groupid'); | ||||||
|  |       const username = $(this).data('username'); | ||||||
|  |  | ||||||
|  |       useGroupId(groupid, username); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function noAPISupport(txt, username) { | ||||||
|  |     showNotification('danger', $.i18n('wiz_hue_e_title'), $.i18n('wiz_hue_e_noapisupport_hint')); | ||||||
|  |     conf_editor.getEditor("root.specificOptions.useEntertainmentAPI").setValue(false); | ||||||
|  |     $("#root_specificOptions_useEntertainmentAPI").trigger("change"); | ||||||
|  |     $('#btn_wiz_holder').append('<div class="bs-callout bs-callout-danger" style="margin-top:0px">' + $.i18n('wiz_hue_e_noapisupport_hint') + '</div>'); | ||||||
|  |     $('#hue_grp_ids_t').toggle(false); | ||||||
|  |     const errorMessage = txt ? $.i18n(txt) : $.i18n('wiz_hue_e_nogrpids'); | ||||||
|  |     $('<p style="font-weight:bold;color:red;">' + errorMessage + '<br />' + $.i18n('wiz_hue_e_noapisupport') + '</p>').insertBefore('#wizp2_body #hue_ids_t'); | ||||||
|  |     $('#hue_id_headline').html($.i18n('wiz_hue_desc2')); | ||||||
|  |  | ||||||
|  |     get_hue_lights(username); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function get_hue_lights(username) { | ||||||
|  |     const host = hueIPs[hueIPsinc].host; | ||||||
|  |  | ||||||
|  |     if (devicesProperties['philipshue'][host] && devicesProperties['philipshue'][host][username]) { | ||||||
|  |       const ledProperties = devicesProperties['philipshue'][host][username]; | ||||||
|  |  | ||||||
|  |       if (isAPIv2Ready) { | ||||||
|  |         if (!jQuery.isEmptyObject(ledProperties.data)) { | ||||||
|  |           if (Object.keys(ledProperties.data).length > 0) { | ||||||
|  |             hueLights = ledProperties.data.filter(config => { | ||||||
|  |               return config.type === "light"; | ||||||
|  |             }); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } else if (!jQuery.isEmptyObject(ledProperties.lights)) { | ||||||
|  |         hueLights = ledProperties.lights; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (Object.keys(hueLights).length > 0) { | ||||||
|  |         if (!isEntertainmentReady) { | ||||||
|  |           $('#wh_topcontainer').toggle(false); | ||||||
|  |         } | ||||||
|  |         $('#hue_ids_t, #btn_wiz_save').toggle(true); | ||||||
|  |  | ||||||
|  |         const lightOptions = [ | ||||||
|  |           "top", "topleft", "topright", | ||||||
|  |           "bottom", "bottomleft", "bottomright", | ||||||
|  |           "left", "lefttop", "leftmiddle", "leftbottom", | ||||||
|  |           "right", "righttop", "rightmiddle", "rightbottom", | ||||||
|  |           "entire", | ||||||
|  |           "lightPosTopLeft112", "lightPosTopLeftNewMid", "lightPosTopLeft121", | ||||||
|  |           "lightPosBottomLeft14", "lightPosBottomLeft12", "lightPosBottomLeft34", "lightPosBottomLeft11", | ||||||
|  |           "lightPosBottomLeft112", "lightPosBottomLeftNewMid", "lightPosBottomLeft121" | ||||||
|  |         ]; | ||||||
|  |  | ||||||
|  |         if (isEntertainmentReady) { | ||||||
|  |           lightOptions.unshift("entertainment_center"); | ||||||
|  |           lightOptions.unshift("entertainment"); | ||||||
|  |         } else { | ||||||
|  |           lightOptions.unshift("disabled"); | ||||||
|  |           groupLights = Object.keys(hueLights); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $('.lidsb').html(""); | ||||||
|  |  | ||||||
|  |         let pos = ""; | ||||||
|  |         for (const id in groupLights) { | ||||||
|  |           const lightId = groupLights[id]; | ||||||
|  |           let lightId_v1 = "/lights/" + lightId; | ||||||
|  |  | ||||||
|  |           let lightName; | ||||||
|  |           if (isAPIv2Ready) { | ||||||
|  |             const light = hueLights.find(light => light.id === lightId); | ||||||
|  |             lightName = light.metadata.name; | ||||||
|  |             lightId_v1 = light.id_v1; | ||||||
|  |           } else { | ||||||
|  |             lightName = hueLights[lightId].name; | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           if (isEntertainmentReady) { | ||||||
|  |             let lightLocation = {}; | ||||||
|  |             lightLocation = groupLightsLocations[id]; | ||||||
|  |             if (lightLocation) { | ||||||
|  |               if (isAPIv2Ready) { | ||||||
|  |                 pos = 0; | ||||||
|  |               } else { | ||||||
|  |                 const x = lightLocation.position.x; | ||||||
|  |                 const y = lightLocation.position.y; | ||||||
|  |                 const z = lightLocation.position.z; | ||||||
|  |  | ||||||
|  |                 let xval = (x < 0) ? "left" : "right"; | ||||||
|  |                 if (z != 1 && x >= -0.25 && x <= 0.25) xval = ""; | ||||||
|  |                 switch (z) { | ||||||
|  |                   case 1: // top / Ceiling height | ||||||
|  |                     pos = "top" + xval; | ||||||
|  |                     break; | ||||||
|  |                   case 0: // middle / TV height | ||||||
|  |                     pos = (xval == "" && y >= 0.75) ? "bottom" : xval + "middle"; | ||||||
|  |                     break; | ||||||
|  |                   case -1: // bottom / Ground height | ||||||
|  |                     pos = xval + "bottom"; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           let options = ""; | ||||||
|  |           for (const opt in lightOptions) { | ||||||
|  |             const val = lightOptions[opt]; | ||||||
|  |             const txt = (val != 'entire' && val != 'disabled') ? 'conf_leds_layout_cl_' : 'wiz_ids_'; | ||||||
|  |             options += '<option value="' + val + '"'; | ||||||
|  |             if (pos == val) options += ' selected="selected"'; | ||||||
|  |             options += '>' + $.i18n(txt + val) + '</option>'; | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           $('.lidsb').append(createTableRow([id + ' (' + lightName + ')', | ||||||
|  |           '<select id="hue_' + lightId + '" class="hue_sel_watch form-control">' | ||||||
|  |           + options | ||||||
|  |           + '</select>', | ||||||
|  |           '<button class="btn btn-sm btn-primary btn-identify" data-hostname="' + encodeURIComponent($(" #host").val()) + '" data-port="' + $('#port').val() + '" data-user="' + $("#user").val() + '" data-light-name="' + encodeURIComponent(lightName) + '" data-light-id="' + lightId + '" data-light-id-v1="' + lightId_v1 + '">' | ||||||
|  |           + $.i18n('wiz_hue_blinkblue', id) | ||||||
|  |           + '</button>'])); | ||||||
|  |         } | ||||||
|  |         attachIdentifyButtonEvent(); | ||||||
|  |  | ||||||
|  |         if (!isEntertainmentReady) { | ||||||
|  |           $('.hue_sel_watch').on("change", function () { | ||||||
|  |             let cC = 0; | ||||||
|  |             for (const key in hueLights) { | ||||||
|  |               if ($('#hue_' + key).val() != "disabled") { | ||||||
|  |                 cC++; | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             (cC == 0 || window.readOnlyMode) ? $('#btn_wiz_save').prop("disabled", true) : $('#btn_wiz_save').prop("disabled", false); | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |         $('.hue_sel_watch').trigger('change'); | ||||||
|  |       } | ||||||
|  |       else { | ||||||
|  |         const txt = '<p style="font-weight:bold;color:red;">' + $.i18n('wiz_hue_noids') + '</p>'; | ||||||
|  |         $('#wizp2_body').append(txt); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function abortConnection(UserInterval) { | ||||||
|  |     clearInterval(UserInterval); | ||||||
|  |     $('#wizp1').toggle(false); | ||||||
|  |     $('#wizp2').toggle(true); | ||||||
|  |     $('#wizp3').toggle(false); | ||||||
|  |     $("#wiz_hue_usrstate").html($.i18n('wiz_hue_failure_connection')); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return { | ||||||
|  |     start: function (e) { | ||||||
|  |       //create html | ||||||
|  |       const hue_title = 'wiz_hue_title'; | ||||||
|  |       const hue_intro1 = 'wiz_hue_e_intro1'; | ||||||
|  |       const hue_desc1 = 'wiz_hue_desc1'; | ||||||
|  |       const hue_create_user = 'wiz_hue_create_user'; | ||||||
|  |  | ||||||
|  |       $('#wiz_header').html('<i class="fa fa-magic fa-fw"></i>' + $.i18n(hue_title)); | ||||||
|  |       $('#wizp1_body').html('<h4 style="font-weight:bold;text-transform:uppercase;">' + $.i18n(hue_title) + '</h4><p>' + $.i18n(hue_intro1) + '</p>'); | ||||||
|  |       $('#wizp1_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_cont"><i class="fa fa-fw fa-check"></i>' + $.i18n('general_btn_continue') + '</button><button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_cancel') + '</button>'); | ||||||
|  |       $('#wizp2_body').html('<div id="wh_topcontainer"></div>'); | ||||||
|  |  | ||||||
|  |       let topContainer_html = '<p class="text-left" style="font-weight:bold">' + $.i18n(hue_desc1) + '</p>' + | ||||||
|  |         '<div class="row">' + | ||||||
|  |         '<div class="col-md-2">' + | ||||||
|  |         '  <p class="text-left">' + $.i18n('wiz_hue_ip') + '</p></div>' + | ||||||
|  |         '  <div class="col-md-7"><div class="input-group">' + | ||||||
|  |         '    <span class="input-group-addon" id="retry_bridge" style="cursor:pointer"><i class="fa fa-refresh"></i></span>' + | ||||||
|  |         '    <select id="hue_bridge_select" class="hue_bridge_sel_watch form-control">' + '</select>' + '</div></div>' + | ||||||
|  |         '  <div class="col-md-7"><div class="input-group">' + | ||||||
|  |         '    <span class="input-group-addon"><i class="fa fa-arrow-right"></i></span>' + | ||||||
|  |         '    <input type="text" class="input-group form-control" id="host" placeholder="' + $.i18n('wiz_hue_ip') + '"></div></div>'; | ||||||
|  |  | ||||||
|  |       if (storedAccess === 'expert') { | ||||||
|  |         topContainer_html += '<div class="col-md-3"><div class="input-group">' + | ||||||
|  |           '<span class="input-group-addon">:</span>' + | ||||||
|  |           '<input type="text" class="input-group form-control" id="port" placeholder="' + $.i18n('edt_conf_general_port_title') + '"></div></div>'; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       topContainer_html += '</div><p><span style="font-weight:bold;color:red" id="wiz_hue_ipstate"></span><span style="font-weight:bold;" id="wiz_hue_discovered"></span></p>'; | ||||||
|  |       topContainer_html += '<div class="form-group" id="usrcont" style="display:none"></div>'; | ||||||
|  |  | ||||||
|  |       $('#wh_topcontainer').append(topContainer_html); | ||||||
|  |  | ||||||
|  |       $('#usrcont').append('<div class="row"><div class="col-md-2"><p class="text-left">' + $.i18n('wiz_hue_username') + '</p ></div>' + | ||||||
|  |         '<div class="col-md-7">' + | ||||||
|  |         '<div class="input-group">' + | ||||||
|  |         '  <span class="input-group-addon" id="retry_usr" style="cursor:pointer"><i class="fa fa-refresh"></i></span>' + | ||||||
|  |         '  <input type="text" class="input-group form-control" id="user">' + | ||||||
|  |         '</div></div></div><br>' + | ||||||
|  |         '</div><input type="hidden" id="groupId">' | ||||||
|  |       ); | ||||||
|  |  | ||||||
|  |       $('#usrcont').append('<div id="hue_client_key_r" class="row"><div class="col-md-2"><p class="text-left">' + $.i18n('wiz_hue_clientkey') + | ||||||
|  |         '</p></div><div class="col-md-7"><input class="form-control" id="clientkey" type="text"></div></div><br>'); | ||||||
|  |  | ||||||
|  |       $('#usrcont').append('<p><span style="font-weight:bold;color:red" id="wiz_hue_usrstate"></span></p>' + | ||||||
|  |         '<button type="button" class="btn btn-primary" style="display:none" id="wiz_hue_create_user"> <i class="fa fa-fw fa-plus"></i>' + $.i18n(hue_create_user) + '</button>'); | ||||||
|  |  | ||||||
|  |       $('#wizp2_body').append('<div id="hue_grp_ids_t" style="display:none"><p class="text-left" style="font-weight:bold">' + $.i18n('wiz_hue_e_desc2') + '</p></div>'); | ||||||
|  |       createTable("gidsh", "gidsb", "hue_grp_ids_t"); | ||||||
|  |       $('.gidsh').append(createTableRow([$.i18n('edt_dev_spec_groupId_title'), ""], true)); | ||||||
|  |  | ||||||
|  |       $('#wizp2_body').append('<div id="hue_ids_t" style="display:none"><p class="text-left" style="font-weight:bold" id="hue_id_headline">' + $.i18n('wiz_hue_e_desc3') + '</p></div>'); | ||||||
|  |  | ||||||
|  |       createTable("lidsh", "lidsb", "hue_ids_t"); | ||||||
|  |       $('.lidsh').append(createTableRow([$.i18n('edt_dev_spec_lightid_title'), $.i18n('wiz_pos'), $.i18n('wiz_identify')], true)); | ||||||
|  |       $('#wizp2_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_save" style="display:none"><i class="fa fa-fw fa-save"></i>' + $.i18n('general_btn_save') + '</button><button type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_cancel') + '</button>'); | ||||||
|  |       $('#wizp3_body').html('<span>' + $.i18n('wiz_hue_press_link') + '</span> <br /><br /><center><span id="connectionTime"></span><br /><i class="fa fa-cog fa-spin" style="font-size:100px"></i></center>'); | ||||||
|  |  | ||||||
|  |       if (getStorage("darkMode") == "on") | ||||||
|  |         $('#wizard_logo').attr("src", 'img/hyperion/logo_negativ.png'); | ||||||
|  |  | ||||||
|  |       //open modal | ||||||
|  |       $("#wizard_modal").modal({ | ||||||
|  |         backdrop: "static", | ||||||
|  |         keyboard: false, | ||||||
|  |         show: true | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       //listen for continue | ||||||
|  |       $('#btn_wiz_cont').off().on('click', function () { | ||||||
|  |         begin(); | ||||||
|  |         $('#wizp1').toggle(false); | ||||||
|  |         $('#wizp2').toggle(true); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | })(); | ||||||
|  |  | ||||||
|  | export { philipshueWizard } | ||||||
|  |  | ||||||
							
								
								
									
										60
									
								
								assets/webconfig/js/wizards/LedDevice_utils.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								assets/webconfig/js/wizards/LedDevice_utils.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | |||||||
|  |  | ||||||
|  | const ledDeviceWizardUtils = (() => { | ||||||
|  |  | ||||||
|  |   // Layout positions | ||||||
|  |   const positionMap = { | ||||||
|  |     "top": { hmin: 0.15, hmax: 0.85, vmin: 0, vmax: 0.2 }, | ||||||
|  |     "topleft": { hmin: 0, hmax: 0.15, vmin: 0, vmax: 0.15 }, | ||||||
|  |     "topright": { hmin: 0.85, hmax: 1.0, vmin: 0, vmax: 0.15 }, | ||||||
|  |     "bottom": { hmin: 0.15, hmax: 0.85, vmin: 0.8, vmax: 1.0 }, | ||||||
|  |     "bottomleft": { hmin: 0, hmax: 0.15, vmin: 0.85, vmax: 1.0 }, | ||||||
|  |     "bottomright": { hmin: 0.85, hmax: 1.0, vmin: 0.85, vmax: 1.0 }, | ||||||
|  |     "left": { hmin: 0, hmax: 0.15, vmin: 0.15, vmax: 0.85 }, | ||||||
|  |     "lefttop": { hmin: 0, hmax: 0.15, vmin: 0, vmax: 0.5 }, | ||||||
|  |     "leftmiddle": { hmin: 0, hmax: 0.15, vmin: 0.25, vmax: 0.75 }, | ||||||
|  |     "leftbottom": { hmin: 0, hmax: 0.15, vmin: 0.5, vmax: 1.0 }, | ||||||
|  |     "right": { hmin: 0.85, hmax: 1.0, vmin: 0.15, vmax: 0.85 }, | ||||||
|  |     "righttop": { hmin: 0.85, hmax: 1.0, vmin: 0, vmax: 0.5 }, | ||||||
|  |     "rightmiddle": { hmin: 0.85, hmax: 1.0, vmin: 0.25, vmax: 0.75 }, | ||||||
|  |     "rightbottom": { hmin: 0.85, hmax: 1.0, vmin: 0.5, vmax: 1.0 }, | ||||||
|  |     "lightPosBottomLeft14": { hmin: 0, hmax: 0.25, vmin: 0.85, vmax: 1.0 }, | ||||||
|  |     "lightPosBottomLeft12": { hmin: 0.25, hmax: 0.5, vmin: 0.85, vmax: 1.0 }, | ||||||
|  |     "lightPosBottomLeft34": { hmin: 0.5, hmax: 0.75, vmin: 0.85, vmax: 1.0 }, | ||||||
|  |     "lightPosBottomLeft11": { hmin: 0.75, hmax: 1, vmin: 0.85, vmax: 1.0 }, | ||||||
|  |     "lightPosBottomLeft112": { hmin: 0, hmax: 0.5, vmin: 0.85, vmax: 1.0 }, | ||||||
|  |     "lightPosBottomLeft121": { hmin: 0.5, hmax: 1, vmin: 0.85, vmax: 1.0 }, | ||||||
|  |     "lightPosBottomLeftNewMid": { hmin: 0.25, hmax: 0.75, vmin: 0.85, vmax: 1.0 }, | ||||||
|  |     "lightPosTopLeft112": { hmin: 0, hmax: 0.5, vmin: 0, vmax: 0.15 }, | ||||||
|  |     "lightPosTopLeft121": { hmin: 0.5, hmax: 1, vmin: 0, vmax: 0.15 }, | ||||||
|  |     "lightPosTopLeftNewMid": { hmin: 0.25, hmax: 0.75, vmin: 0, vmax: 0.15 }, | ||||||
|  |     "lightPosEntire": { hmin: 0.0, hmax: 1.0, vmin: 0.0, vmax: 1.0 } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   return { | ||||||
|  |  | ||||||
|  |     //return editor Value | ||||||
|  |     eV: function (vn, defaultVal = "") { | ||||||
|  |       let editor = null; | ||||||
|  |       if (vn) { | ||||||
|  |         editor = conf_editor.getEditor("root.specificOptions." + vn); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (editor === null) { | ||||||
|  |         return defaultVal; | ||||||
|  |       } else if (defaultVal !== "" && !isNaN(defaultVal) && isNaN(editor.getValue())) { | ||||||
|  |         return defaultVal; | ||||||
|  |       } else { | ||||||
|  |         return editor.getValue(); | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     assignLightPos: function (pos, name) { | ||||||
|  |       // Retrieve the corresponding position object from the positionMap | ||||||
|  |       const i = positionMap[pos] || positionMap["lightPosEntire"]; | ||||||
|  |       i.name = name; | ||||||
|  |       return i; | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  | })(); | ||||||
|  |  | ||||||
|  | export { ledDeviceWizardUtils }; | ||||||
							
								
								
									
										300
									
								
								assets/webconfig/js/wizards/LedDevice_yeelight.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								assets/webconfig/js/wizards/LedDevice_yeelight.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,300 @@ | |||||||
|  | //**************************** | ||||||
|  | // Wizard Yeelight | ||||||
|  | //**************************** | ||||||
|  |  | ||||||
|  | import { ledDeviceWizardUtils as utils } from './LedDevice_utils.js'; | ||||||
|  |  | ||||||
|  | const yeelightWizard = (() => { | ||||||
|  |  | ||||||
|  |   const lights = []; | ||||||
|  |   let configuredLights = conf_editor.getEditor("root.specificOptions.lights").getValue(); | ||||||
|  |  | ||||||
|  |   function getHostInLights(hostname) { | ||||||
|  |     return lights.filter( | ||||||
|  |       function (lights) { | ||||||
|  |         return lights.host === hostname | ||||||
|  |       } | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function begin() { | ||||||
|  |     discover(); | ||||||
|  |  | ||||||
|  |     $('#btn_wiz_save').off().on("click", function () { | ||||||
|  |       let ledConfig = []; | ||||||
|  |       let finalLights = []; | ||||||
|  |  | ||||||
|  |       //create yeelight led config | ||||||
|  |       for (const key in lights) { | ||||||
|  |         if ($('#yee_' + key).val() !== "disabled") { | ||||||
|  |  | ||||||
|  |           let name = lights[key].name; | ||||||
|  |           // Set Name to layout-position, if empty | ||||||
|  |           if (name === "") { | ||||||
|  |             name = lights[key].host; | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           finalLights.push(lights[key]); | ||||||
|  |  | ||||||
|  |           const idx_content = utils.assignLightPos($('#yee_' + key).val(), name); | ||||||
|  |           ledConfig.push(JSON.parse(JSON.stringify(idx_content))); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       //LED layout | ||||||
|  |       window.serverConfig.leds = ledConfig; | ||||||
|  |  | ||||||
|  |       //LED device config | ||||||
|  |       const currentDeviceType = window.serverConfig.device.type; | ||||||
|  |  | ||||||
|  |       //Start with a clean configuration | ||||||
|  |       let d = {}; | ||||||
|  |  | ||||||
|  |       d.type = 'yeelight'; | ||||||
|  |       d.hardwareLedCount = finalLights.length; | ||||||
|  |       d.colorOrder = conf_editor.getEditor("root.generalOptions.colorOrder").getValue(); | ||||||
|  |       d.colorModel = parseInt(conf_editor.getEditor("root.specificOptions.colorModel").getValue()); | ||||||
|  |  | ||||||
|  |       d.transEffect = parseInt(conf_editor.getEditor("root.specificOptions.transEffect").getValue()); | ||||||
|  |       d.transTime = parseInt(conf_editor.getEditor("root.specificOptions.transTime").getValue()); | ||||||
|  |       d.extraTimeDarkness = parseInt(conf_editor.getEditor("root.specificOptions.extraTimeDarkness").getValue()); | ||||||
|  |  | ||||||
|  |       d.brightnessMin = parseInt(conf_editor.getEditor("root.specificOptions.brightnessMin").getValue()); | ||||||
|  |       d.brightnessSwitchOffOnMinimum = JSON.parse(conf_editor.getEditor("root.specificOptions.brightnessSwitchOffOnMinimum").getValue()); | ||||||
|  |       d.brightnessMax = parseInt(conf_editor.getEditor("root.specificOptions.brightnessMax").getValue()); | ||||||
|  |       d.brightnessFactor = parseFloat(conf_editor.getEditor("root.specificOptions.brightnessFactor").getValue()); | ||||||
|  |  | ||||||
|  |       d.latchTime = parseInt(conf_editor.getEditor("root.specificOptions.latchTime").getValue());; | ||||||
|  |       d.debugLevel = parseInt(conf_editor.getEditor("root.specificOptions.debugLevel").getValue()); | ||||||
|  |  | ||||||
|  |       d.lights = finalLights; | ||||||
|  |  | ||||||
|  |       window.serverConfig.device = d; | ||||||
|  |  | ||||||
|  |       if (currentDeviceType !== d.type) { | ||||||
|  |         //smoothing off, if new device | ||||||
|  |         window.serverConfig.smoothing = { enable: false }; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       requestWriteConfig(window.serverConfig, true); | ||||||
|  |       resetWizard(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $('#btn_wiz_abort').off().on('click', resetWizard); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async function discover() { | ||||||
|  |     // Get discovered lights | ||||||
|  |     const res = await requestLedDeviceDiscovery('yeelight'); | ||||||
|  |     if (res && !res.error) { | ||||||
|  |       const r = res.info; | ||||||
|  |  | ||||||
|  |       let discoveryMethod = "ssdp"; | ||||||
|  |       if (res.info.discoveryMethod) { | ||||||
|  |         discoveryMethod = res.info.discoveryMethod; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // Process devices returned by discovery | ||||||
|  |       for (const device of r.devices) { | ||||||
|  |         if (device.hostname !== "") { | ||||||
|  |           processDiscoverdDevice(device, discoveryMethod); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       // Add additional items from configuration | ||||||
|  |       for (const configuredLight of configuredLights) { | ||||||
|  |         processConfiguredLight(configuredLight); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       assign_lights(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function processDiscoverdDevice(device, discoveryMethod) { | ||||||
|  |     if (getHostInLights(device.hostname).length > 0) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const light = { | ||||||
|  |       host: device.hostname | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     if (discoveryMethod === "ssdp") { | ||||||
|  |       if (device.domain) { | ||||||
|  |         light.host += '.' + device.domain; | ||||||
|  |       } | ||||||
|  |     } else { | ||||||
|  |       light.host = device.service; | ||||||
|  |       light.name = device.name; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     light.port = device.port; | ||||||
|  |  | ||||||
|  |     if (device.txt) { | ||||||
|  |       light.model = device.txt.md; | ||||||
|  |       light.port = 55443; // Yeelight default port | ||||||
|  |     } else { | ||||||
|  |       light.name = device.other.name; | ||||||
|  |       light.model = device.other.model; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lights.push(light); | ||||||
|  |   } | ||||||
|  |   function processConfiguredLight(configuredLight) { | ||||||
|  |     const host = configuredLight.host; | ||||||
|  |     let port = configuredLight.port || 0; | ||||||
|  |  | ||||||
|  |     if (host !== "" && getHostInLights(host).length === 0) { | ||||||
|  |       const light = { | ||||||
|  |         host: host, | ||||||
|  |         port: port, | ||||||
|  |         name: configuredLight.name, | ||||||
|  |         model: "color4" | ||||||
|  |       }; | ||||||
|  |  | ||||||
|  |       lights.push(light); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function attachIdentifyButtonEvent() { | ||||||
|  |     $('#wizp2_body').on('click', '.btn-identify', function () { | ||||||
|  |       const hostname = $(this).data('hostname'); | ||||||
|  |       const port = $(this).data('port'); | ||||||
|  |       identify(hostname, port); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function assign_lights() { | ||||||
|  |     // Model mappings, see https://www.home-assistant.io/integrations/yeelight/ | ||||||
|  |     const models = ['color', 'color1', 'YLDP02YL', 'YLDP02YL', 'color2', 'YLDP06YL', 'color4', 'YLDP13YL', 'color6', 'YLDP13AYL', 'colorb', "YLDP005", 'colorc', "YLDP004-A", 'stripe', 'YLDD04YL', 'strip1', 'YLDD01YL', 'YLDD02YL', 'strip4', 'YLDD05YL', 'strip6', 'YLDD05YL']; | ||||||
|  |  | ||||||
|  |     // If records are left for configuration | ||||||
|  |     if (Object.keys(lights).length > 0) { | ||||||
|  |       $('#wh_topcontainer').toggle(false); | ||||||
|  |       $('#yee_ids_t, #btn_wiz_save').toggle(true); | ||||||
|  |  | ||||||
|  |       const lightOptions = [ | ||||||
|  |         "top", "topleft", "topright", | ||||||
|  |         "bottom", "bottomleft", "bottomright", | ||||||
|  |         "left", "lefttop", "leftmiddle", "leftbottom", | ||||||
|  |         "right", "righttop", "rightmiddle", "rightbottom", | ||||||
|  |         "entire", | ||||||
|  |         "lightPosTopLeft112", "lightPosTopLeftNewMid", "lightPosTopLeft121", | ||||||
|  |         "lightPosBottomLeft14", "lightPosBottomLeft12", "lightPosBottomLeft34", "lightPosBottomLeft11", | ||||||
|  |         "lightPosBottomLeft112", "lightPosBottomLeftNewMid", "lightPosBottomLeft121" | ||||||
|  |       ]; | ||||||
|  |  | ||||||
|  |       lightOptions.unshift("disabled"); | ||||||
|  |  | ||||||
|  |       $('.lidsb').html(""); | ||||||
|  |       let pos = ""; | ||||||
|  |  | ||||||
|  |       for (const lightid in lights) { | ||||||
|  |         const lightHostname = lights[lightid].host; | ||||||
|  |         const lightPort = lights[lightid].port; | ||||||
|  |         let lightName = lights[lightid].name; | ||||||
|  |  | ||||||
|  |         if (lightName === "") | ||||||
|  |           lightName = $.i18n('edt_dev_spec_lights_itemtitle') + '(' + lightHostname + ')'; | ||||||
|  |  | ||||||
|  |         let options = ""; | ||||||
|  |         for (const opt in lightOptions) { | ||||||
|  |           const val = lightOptions[opt]; | ||||||
|  |           const txt = (val !== 'entire' && val !== 'disabled') ? 'conf_leds_layout_cl_' : 'wiz_ids_'; | ||||||
|  |           options += '<option value="' + val + '"'; | ||||||
|  |           if (pos === val) options += ' selected="selected"'; | ||||||
|  |           options += '>' + $.i18n(txt + val) + '</option>'; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let enabled = 'enabled'; | ||||||
|  |         if (!models.includes(lights[lightid].model)) { | ||||||
|  |           enabled = 'disabled'; | ||||||
|  |           options = '<option value=disabled>' + $.i18n('wiz_yeelight_unsupported') + '</option>'; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $('.lidsb').append(createTableRow([(parseInt(lightid, 10) + 1) + '. ' + lightName, '<select id="yee_' + lightid + '" ' + enabled + ' class="yee_sel_watch form-control">' | ||||||
|  |           + options | ||||||
|  |           + '</select>', '<button class="btn btn-sm btn-primary btn-identify" data-hostname="' + lightHostname + '" data-port="' + lightPort + '")>' | ||||||
|  |           + $.i18n('wiz_identify') + '</button>'])); | ||||||
|  |       } | ||||||
|  |       attachIdentifyButtonEvent(); | ||||||
|  |  | ||||||
|  |       $('.yee_sel_watch').on("change", function () { | ||||||
|  |         let cC = 0; | ||||||
|  |         for (const key in lights) { | ||||||
|  |           if ($('#yee_' + key).val() !== "disabled") { | ||||||
|  |             cC++; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (cC === 0 || window.readOnlyMode) | ||||||
|  |           $('#btn_wiz_save').prop("disabled", true); | ||||||
|  |         else | ||||||
|  |           $('#btn_wiz_save').prop("disabled", false); | ||||||
|  |       }); | ||||||
|  |       $('.yee_sel_watch').trigger('change'); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       const noLightsTxt = '<p style="font-weight:bold;color:red;">' + $.i18n('wiz_noLights', 'lights') + '</p>'; | ||||||
|  |       $('#wizp2_body').append(noLightsTxt); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async function identify(host, port) { | ||||||
|  |  | ||||||
|  |     const disabled = $('#btn_wiz_save').is(':disabled'); | ||||||
|  |  | ||||||
|  |     // Take care that new record cannot be save during background process | ||||||
|  |     $('#btn_wiz_save').prop('disabled', true); | ||||||
|  |  | ||||||
|  |     const params = { host: host, port: port }; | ||||||
|  |     await requestLedDeviceIdentification("yeelight", params); | ||||||
|  |  | ||||||
|  |     if (!window.readOnlyMode) { | ||||||
|  |       $('#btn_wiz_save').prop('disabled', disabled); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return { | ||||||
|  |     start: function (e) { | ||||||
|  |     //create html | ||||||
|  |     const yeelight_title = 'wiz_yeelight_title'; | ||||||
|  |     const yeelight_intro1 = 'wiz_yeelight_intro1'; | ||||||
|  |  | ||||||
|  |     $('#wiz_header').html('<i class="fa fa-magic fa-fw"></i>' + $.i18n(yeelight_title)); | ||||||
|  |     $('#wizp1_body').html('<h4 style="font-weight:bold;text-transform:uppercase;">' + $.i18n(yeelight_title) + '</h4><p>' + $.i18n(yeelight_intro1) + '</p>'); | ||||||
|  |  | ||||||
|  |     $('#wizp1_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_cont"><i class="fa fa-fw fa-check"></i>' | ||||||
|  |       + $.i18n('general_btn_continue') + '</button><button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>' | ||||||
|  |       + $.i18n('general_btn_cancel') + '</button>'); | ||||||
|  |  | ||||||
|  |     $('#wizp2_body').html('<div id="wh_topcontainer"></div>'); | ||||||
|  |  | ||||||
|  |     $('#wh_topcontainer').append('<div class="form-group" id="usrcont" style="display:none"></div>'); | ||||||
|  |  | ||||||
|  |     $('#wizp2_body').append('<div id="yee_ids_t" style="display:none"><p style="font-weight:bold" id="yee_id_headline">' + $.i18n('wiz_yeelight_desc2') + '</p></div>'); | ||||||
|  |  | ||||||
|  |     createTable("lidsh", "lidsb", "yee_ids_t"); | ||||||
|  |     $('.lidsh').append(createTableRow([$.i18n('edt_dev_spec_lights_title'), $.i18n('wiz_pos'), $.i18n('wiz_identify')], true)); | ||||||
|  |     $('#wizp2_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_save" style="display:none"><i class="fa fa-fw fa-save"></i>' | ||||||
|  |       + $.i18n('general_btn_save') + '</button><buttowindow.serverConfig.device = d;n type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>' | ||||||
|  |       + $.i18n('general_btn_cancel') + '</button>'); | ||||||
|  |  | ||||||
|  |     if (getStorage("darkMode") == "on") | ||||||
|  |       $('#wizard_logo').attr("src", 'img/hyperion/logo_negativ.png'); | ||||||
|  |  | ||||||
|  |     //open modal | ||||||
|  |     $("#wizard_modal").modal({ backdrop: "static", keyboard: false, show: true }); | ||||||
|  |  | ||||||
|  |     //listen for continue | ||||||
|  |     $('#btn_wiz_cont').off().on('click', function () { | ||||||
|  |       begin(); | ||||||
|  |       $('#wizp1').toggle(false); | ||||||
|  |       $('#wizp2').toggle(true); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | }) (); | ||||||
|  |  | ||||||
|  | export { yeelightWizard }; | ||||||
|  |  | ||||||
							
								
								
									
										485
									
								
								assets/webconfig/js/wizards/colorCalibrationKodiWizard.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										485
									
								
								assets/webconfig/js/wizards/colorCalibrationKodiWizard.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,485 @@ | |||||||
|  | //**************************** | ||||||
|  | // Wizard Color calibration via Kodi | ||||||
|  | //**************************** | ||||||
|  | const colorCalibrationKodiWizard = (() => { | ||||||
|  |  | ||||||
|  |   let ws; | ||||||
|  |   const defaultKodiPort = 9090; | ||||||
|  |  | ||||||
|  |   let kodiAddress = document.location.hostname; | ||||||
|  |   let kodiPort = defaultKodiPort; | ||||||
|  |  | ||||||
|  |   const kodiUrl = new URL("ws://" + kodiAddress); | ||||||
|  |   kodiUrl.port = kodiPort; | ||||||
|  |   kodiUrl.pathname = "/jsonrpc/websocket"; | ||||||
|  |  | ||||||
|  |   let wiz_editor; | ||||||
|  |   let colorLength; | ||||||
|  |   let cobj; | ||||||
|  |   let step = 0; | ||||||
|  |   let withKodi = false; | ||||||
|  |   let profile = 0; | ||||||
|  |   let websAddress; | ||||||
|  |   let imgAddress; | ||||||
|  |   let picnr = 0; | ||||||
|  |   let id = 1; | ||||||
|  |   const vidAddress = "https://sourceforge.net/projects/hyperion-project/files/resources/vid/"; | ||||||
|  |   const availVideos = ["Sweet_Cocoon", "Caminandes_2_GranDillama", "Caminandes_3_Llamigos"]; | ||||||
|  |  | ||||||
|  |   if (getStorage("kodiAddress") != null) { | ||||||
|  |  | ||||||
|  |     kodiAddress = getStorage("kodiAddress"); | ||||||
|  |     kodiUrl.host = kodiAddress; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (getStorage("kodiPort") != null) { | ||||||
|  |     kodiPort = getStorage("kodiPort"); | ||||||
|  |     kodiUrl.port = kodiPort; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   $(window).on('beforeunload', function () { | ||||||
|  |     closeWebSocket(); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   function closeWebSocket() { | ||||||
|  |     // Check if the WebSocket is open | ||||||
|  |     if (ws && ws.readyState === WebSocket.OPEN) { | ||||||
|  |       ws.close(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function sendToKodi(type, content) { | ||||||
|  |     let command; | ||||||
|  |  | ||||||
|  |     switch (type) { | ||||||
|  |       case "msg": | ||||||
|  |         command = { "jsonrpc": "2.0", "method": "GUI.ShowNotification", "params": { "title": $.i18n('wiz_cc_title'), "message": content, "image": "info", "displaytime": 5000 }, "id": id }; | ||||||
|  |         break; | ||||||
|  |       case "stop": | ||||||
|  |         command = { "jsonrpc": "2.0", "method": "Player.Stop", "params": { "playerid": 2 }, "id": id }; | ||||||
|  |         break; | ||||||
|  |       case "playP": | ||||||
|  |         content = imgAddress + content + '.png'; | ||||||
|  |         command = { "jsonrpc": "2.0", "method": "Player.Open", "params": { "item": { "file": content } }, "id": id }; | ||||||
|  |         break; | ||||||
|  |       case "playV": | ||||||
|  |         content = vidAddress + content; | ||||||
|  |         command = { "jsonrpc": "2.0", "method": "Player.Open", "params": { "item": { "file": content } }, "id": id }; | ||||||
|  |         break; | ||||||
|  |       case "rotate": | ||||||
|  |         command = { "jsonrpc": "2.0", "method": "Player.Rotate", "params": { "playerid": 2 }, "id": id }; | ||||||
|  |         break; | ||||||
|  |       default: | ||||||
|  |         console.error('Unknown Kodi command type: ', type); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (ws.readyState === WebSocket.OPEN) { | ||||||
|  |       ws.send(JSON.stringify(command)); | ||||||
|  |       ++id; | ||||||
|  |     } else { | ||||||
|  |       console.error('WebSocket connection is not open. Unable to send command.'); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function performAction() { | ||||||
|  |     let h; | ||||||
|  |  | ||||||
|  |     if (step == 1) { | ||||||
|  |       $('#wiz_cc_desc').html($.i18n('wiz_cc_chooseid')); | ||||||
|  |       updateEditor(["id"]); | ||||||
|  |       $('#btn_wiz_back').prop("disabled", true); | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |       $('#btn_wiz_back').prop("disabled", false); | ||||||
|  |  | ||||||
|  |     if (step == 2) { | ||||||
|  |       updateEditor(["white"]); | ||||||
|  |       h = $.i18n('wiz_cc_adjustit', $.i18n('edt_conf_color_white_title')); | ||||||
|  |       if (withKodi) { | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_kodishould', $.i18n('edt_conf_color_white_title')); | ||||||
|  |         sendToKodi('playP', "white"); | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_lettvshow', $.i18n('edt_conf_color_white_title')); | ||||||
|  |       $('#wiz_cc_desc').html(h); | ||||||
|  |     } | ||||||
|  |     if (step == 3) { | ||||||
|  |       updateEditor(["gammaRed", "gammaGreen", "gammaBlue"]); | ||||||
|  |       h = '<p>' + $.i18n('wiz_cc_adjustgamma') + '</p>'; | ||||||
|  |       if (withKodi) { | ||||||
|  |         sendToKodi('playP', "HGradient"); | ||||||
|  |         h += '<button id="wiz_cc_btn_sp" class="btn btn-primary">' + $.i18n('wiz_cc_btn_switchpic') + '</button>'; | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |         h += '<p>' + $.i18n('wiz_cc_lettvshowm', "grey_1, grey_2, grey_3, HGradient, VGradient") + '</p>'; | ||||||
|  |       $('#wiz_cc_desc').html(h); | ||||||
|  |       $('#wiz_cc_btn_sp').off().on('click', function () { | ||||||
|  |         switchPicture(["VGradient", "grey_1", "grey_2", "grey_3", "HGradient"]); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |     if (step == 4) { | ||||||
|  |       updateEditor(["red"]); | ||||||
|  |       h = $.i18n('wiz_cc_adjustit', $.i18n('edt_conf_color_red_title')); | ||||||
|  |       if (withKodi) { | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_kodishould', $.i18n('edt_conf_color_red_title')); | ||||||
|  |         sendToKodi('playP', "red"); | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_lettvshow', $.i18n('edt_conf_color_red_title')); | ||||||
|  |       $('#wiz_cc_desc').html(h); | ||||||
|  |     } | ||||||
|  |     if (step == 5) { | ||||||
|  |       updateEditor(["green"]); | ||||||
|  |       h = $.i18n('wiz_cc_adjustit', $.i18n('edt_conf_color_green_title')); | ||||||
|  |       if (withKodi) { | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_kodishould', $.i18n('edt_conf_color_green_title')); | ||||||
|  |         sendToKodi('playP', "green"); | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_lettvshow', $.i18n('edt_conf_color_green_title')); | ||||||
|  |       $('#wiz_cc_desc').html(h); | ||||||
|  |     } | ||||||
|  |     if (step == 6) { | ||||||
|  |       updateEditor(["blue"]); | ||||||
|  |       h = $.i18n('wiz_cc_adjustit', $.i18n('edt_conf_color_blue_title')); | ||||||
|  |       if (withKodi) { | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_kodishould', $.i18n('edt_conf_color_blue_title')); | ||||||
|  |         sendToKodi('playP', "blue"); | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_lettvshow', $.i18n('edt_conf_color_blue_title')); | ||||||
|  |       $('#wiz_cc_desc').html(h); | ||||||
|  |     } | ||||||
|  |     if (step == 7) { | ||||||
|  |       updateEditor(["cyan"]); | ||||||
|  |       h = $.i18n('wiz_cc_adjustit', $.i18n('edt_conf_color_cyan_title')); | ||||||
|  |       if (withKodi) { | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_kodishould', $.i18n('edt_conf_color_cyan_title')); | ||||||
|  |         sendToKodi('playP', "cyan"); | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_lettvshow', $.i18n('edt_conf_color_cyan_title')); | ||||||
|  |       $('#wiz_cc_desc').html(h); | ||||||
|  |     } | ||||||
|  |     if (step == 8) { | ||||||
|  |       updateEditor(["magenta"]); | ||||||
|  |       h = $.i18n('wiz_cc_adjustit', $.i18n('edt_conf_color_magenta_title')); | ||||||
|  |       if (withKodi) { | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_kodishould', $.i18n('edt_conf_color_magenta_title')); | ||||||
|  |         sendToKodi('playP', "magenta"); | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_lettvshow', $.i18n('edt_conf_color_magenta_title')); | ||||||
|  |       $('#wiz_cc_desc').html(h); | ||||||
|  |     } | ||||||
|  |     if (step == 9) { | ||||||
|  |       updateEditor(["yellow"]); | ||||||
|  |       h = $.i18n('wiz_cc_adjustit', $.i18n('edt_conf_color_yellow_title')); | ||||||
|  |       if (withKodi) { | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_kodishould', $.i18n('edt_conf_color_yellow_title')); | ||||||
|  |         sendToKodi('playP', "yellow"); | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_lettvshow', $.i18n('edt_conf_color_yellow_title')); | ||||||
|  |       $('#wiz_cc_desc').html(h); | ||||||
|  |     } | ||||||
|  |     if (step == 10) { | ||||||
|  |       updateEditor(["backlightThreshold", "backlightColored"]); | ||||||
|  |       h = $.i18n('wiz_cc_backlight'); | ||||||
|  |       if (withKodi) { | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_kodishould', $.i18n('edt_conf_color_black_title')); | ||||||
|  |         sendToKodi('playP', "black"); | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |         h += '<br/>' + $.i18n('wiz_cc_lettvshow', $.i18n('edt_conf_color_black_title')); | ||||||
|  |       $('#wiz_cc_desc').html(h); | ||||||
|  |     } | ||||||
|  |     if (step == 11) { | ||||||
|  |       updateEditor([""], true); | ||||||
|  |       h = '<p>' + $.i18n('wiz_cc_testintro') + '</p>'; | ||||||
|  |       if (withKodi) { | ||||||
|  |         h += '<p>' + $.i18n('wiz_cc_testintrok') + '</p>'; | ||||||
|  |         sendToKodi('stop'); | ||||||
|  |         availVideos.forEach(video => { | ||||||
|  |           const txt = video.replace(/_/g, " "); | ||||||
|  |           h += `<div><button id="${video}" class="btn btn-sm btn-primary videobtn"><i class="fa fa-fw fa-play"></i> ${txt}</button></div>`; | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         h += '<div><button id="stop" class="btn btn-sm btn-danger videobtn" style="margin-bottom:15px"><i class="fa fa-fw fa-stop"></i> ' + $.i18n('wiz_cc_btn_stop') + '</button></div>'; | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |         h += '<p>' + $.i18n('wiz_cc_testintrowok') + ' <a href="https://sourceforge.net/projects/hyperion-project/files/resources/vid/" target="_blank">' + $.i18n('wiz_cc_link') + '</a></p>'; | ||||||
|  |       h += '<p>' + $.i18n('wiz_cc_summary') + '</p>'; | ||||||
|  |       $('#wiz_cc_desc').html(h); | ||||||
|  |  | ||||||
|  |       $('.videobtn').off().on('click', function (e) { | ||||||
|  |         if (e.target.id == "stop") | ||||||
|  |           sendToKodi("stop"); | ||||||
|  |         else | ||||||
|  |           sendToKodi("playV", e.target.id + '.mp4'); | ||||||
|  |  | ||||||
|  |         $(this).prop("disabled", true); | ||||||
|  |         setTimeout(function () { $('.videobtn').prop("disabled", false) }, 10000); | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       $('#btn_wiz_next').prop("disabled", true); | ||||||
|  |       $('#btn_wiz_save').toggle(true); | ||||||
|  |       window.readOnlyMode ? $('#btn_wiz_save').prop('disabled', true) : $('#btn_wiz_save').prop('disabled', false); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       $('#btn_wiz_next').prop("disabled", false); | ||||||
|  |       $('#btn_wiz_save').toggle(false); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   function switchPicture(pictures) { | ||||||
|  |     if (typeof pictures[picnr] === 'undefined') | ||||||
|  |       picnr = 0; | ||||||
|  |  | ||||||
|  |     sendToKodi('playP', pictures[picnr]); | ||||||
|  |     picnr++; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   function initializeWebSocket(cb) { | ||||||
|  |     if ("WebSocket" in window) { | ||||||
|  |  | ||||||
|  |       if (kodiUrl.port === '') { | ||||||
|  |         kodiUrl.port = defaultKodiPort; | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (!ws || ws.readyState !== WebSocket.OPEN) { | ||||||
|  |  | ||||||
|  |         // Establish WebSocket connection | ||||||
|  |         ws = new WebSocket(kodiUrl); | ||||||
|  |  | ||||||
|  |         // WebSocket onopen event | ||||||
|  |         ws.onopen = function (event) { | ||||||
|  |           withKodi = true; | ||||||
|  |           cb("opened"); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         // WebSocket onmessage event (handle incoming messages) | ||||||
|  |         ws.onmessage = function (event) { | ||||||
|  |           const response = JSON.parse(event.data); | ||||||
|  |           if (response.method === "System.OnQuit") { | ||||||
|  |             closeWebSocket(); | ||||||
|  |           } else if (response.result != undefined) { | ||||||
|  |             if (response.result !== "OK") { | ||||||
|  |               cb("error"); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         // WebSocket onerror event | ||||||
|  |         ws.onerror = function (error) { | ||||||
|  |           cb("error"); | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         // WebSocket onclose event | ||||||
|  |         ws.onclose = function (event) { | ||||||
|  |           withKodi = false; | ||||||
|  |           if (event.code === 1006) { | ||||||
|  |             // Ignore error 1006 due to Kodi issue | ||||||
|  |             console.log("WebSocket closed with error code 1006. Ignoring due to Kodi bug."); | ||||||
|  |           } | ||||||
|  |           else { | ||||||
|  |             console.error("WebSocket closed with code:", event.code); | ||||||
|  |           } | ||||||
|  |         }; | ||||||
|  |       } else { | ||||||
|  |         console.log("WebSocket connection is already open."); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       console.log("Kodi Access: WebSocket NOT supported by this browser"); | ||||||
|  |       cb("error"); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function setupEventListeners() { | ||||||
|  |     $('#btn_wiz_cancel').off().on('click', function () { | ||||||
|  |       stop(true); | ||||||
|  |     }); | ||||||
|  |     $('#wiz_cc_kodiip').off().on('change', function () { | ||||||
|  |  | ||||||
|  |       kodiAddress = encodeURIComponent($(this).val().trim()); | ||||||
|  |  | ||||||
|  |       $('#kodi_status').html(''); | ||||||
|  |       if (kodiAddress !== "") { | ||||||
|  |  | ||||||
|  |         if (!isValidHostnameOrIP(kodiAddress)) { | ||||||
|  |  | ||||||
|  |           $('#kodi_status').html('<p style="color:red;font-weight:bold;margin-top:5px">' + $.i18n('edt_msgcust_error_hostname_ip') + '</p>'); | ||||||
|  |           withKodi = false; | ||||||
|  |  | ||||||
|  |         } else { | ||||||
|  |  | ||||||
|  |           if (isValidIPv6(kodiAddress)) { | ||||||
|  |             kodiUrl.hostname = "[" + kodiAddress + "]"; | ||||||
|  |           } else { | ||||||
|  |             kodiUrl.hostname = kodiAddress; | ||||||
|  |           } | ||||||
|  |  | ||||||
|  |           $('#kodi_status').html('<p style="font-weight:bold;margin-top:5px">' + $.i18n('wiz_cc_try_connect') + '</p>'); | ||||||
|  |           $('#btn_wiz_cont').prop('disabled', true); | ||||||
|  |  | ||||||
|  |           closeWebSocket(); | ||||||
|  |           initializeWebSocket(function (cb) { | ||||||
|  |  | ||||||
|  |             if (cb == "opened") { | ||||||
|  |               setStorage("kodiAddress", kodiAddress); | ||||||
|  |               setStorage("kodiPort", defaultKodiPort); | ||||||
|  |  | ||||||
|  |               $('#kodi_status').html('<p style="color:green;font-weight:bold;margin-top:5px">' + $.i18n('wiz_cc_kodicon') + '</p>'); | ||||||
|  |               $('#btn_wiz_cont').prop('disabled', false); | ||||||
|  |  | ||||||
|  |               if (withKodi) { | ||||||
|  |                 sendToKodi("msg", $.i18n('wiz_cc_kodimsg_start')); | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |               $('#kodi_status').html('<p style="color:red;font-weight:bold;margin-top:5px">' + $.i18n('wiz_cc_kodidiscon') + '</p><p>' + $.i18n('wiz_cc_kodidisconlink') + ' <a href="https://sourceforge.net/projects/hyperion-project/files/resources/Hyperion_calibration_pictures.zip/download" target="_blank">' + $.i18n('wiz_cc_link') + '</p>'); | ||||||
|  |               withKodi = false; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $('#btn_wiz_cont').prop('disabled', false); | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     //listen for continue | ||||||
|  |     $('#btn_wiz_cont').off().on('click', function () { | ||||||
|  |       begin(); | ||||||
|  |       $('#wizp1').toggle(false); | ||||||
|  |       $('#wizp2').toggle(true); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function init() { | ||||||
|  |     colorLength = window.serverConfig.color.channelAdjustment; | ||||||
|  |     cobj = window.schema.color.properties.channelAdjustment.items.properties; | ||||||
|  |     websAddress = document.location.hostname + ':' + window.serverConfig.webConfig.port; | ||||||
|  |     imgAddress = 'http://' + websAddress + '/img/cc/'; | ||||||
|  |     setStorage("wizardactive", true); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function initProfiles() { | ||||||
|  |     //check profile count | ||||||
|  |     if (colorLength.length > 1) { | ||||||
|  |       $('#multi_cali').html('<p style="font-weight:bold;">' + $.i18n('wiz_cc_morethanone') + '</p><select id="wiz_select" class="form-control" style="width:200px;margin:auto"></select>'); | ||||||
|  |       for (let i = 0; i < colorLength.length; i++) | ||||||
|  |         $('#wiz_select').append(createSelOpt(i, i + 1 + ' (' + colorLength[i].id + ')')); | ||||||
|  |  | ||||||
|  |       $('#wiz_select').off().on('change', function () { | ||||||
|  |         profile = $(this).val(); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function createEditor() { | ||||||
|  |     wiz_editor = createJsonEditor('editor_container_wiz', { | ||||||
|  |       color: window.schema.color | ||||||
|  |     }, true, true); | ||||||
|  |  | ||||||
|  |     $('#editor_container_wiz h4').toggle(false); | ||||||
|  |     $('#editor_container_wiz .btn-group').toggle(false); | ||||||
|  |     $('#editor_container_wiz [data-schemapath="root.color.imageToLedMappingType"]').toggle(false); | ||||||
|  |     $('#editor_container_wiz [data-schemapath="root.color.reducedPixelSetFactorFactor"]').toggle(false); | ||||||
|  |     for (let i = 0; i < colorLength.length; i++) | ||||||
|  |       $('#editor_container_wiz [data-schemapath*="root.color.channelAdjustment.' + i + '."]').toggle(false); | ||||||
|  |   } | ||||||
|  |   function updateEditor(el, all) { | ||||||
|  |     for (let key in cobj) { | ||||||
|  |       if (all === true || el[0] == key || el[1] == key || el[2] == key) | ||||||
|  |         $('#editor_container_wiz [data-schemapath*=".' + profile + '.' + key + '"]').toggle(true); | ||||||
|  |       else | ||||||
|  |         $('#editor_container_wiz [data-schemapath*=".' + profile + '.' + key + '"]').toggle(false); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function stop(reload) { | ||||||
|  |     if (withKodi) { | ||||||
|  |       sendToKodi("stop"); | ||||||
|  |     } | ||||||
|  |     closeWebSocket(); | ||||||
|  |     resetWizard(reload); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function begin() { | ||||||
|  |     step = 0; | ||||||
|  |  | ||||||
|  |     $('#btn_wiz_next').off().on('click', function () { | ||||||
|  |       step++; | ||||||
|  |       performAction(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $('#btn_wiz_back').off().on('click', function () { | ||||||
|  |       step--; | ||||||
|  |       performAction(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $('#btn_wiz_abort').off().on('click', function () { | ||||||
|  |       stop(true); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $('#btn_wiz_save').off().on('click', function () { | ||||||
|  |       requestWriteConfig(wiz_editor.getValue()); | ||||||
|  |       stop(true); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     wiz_editor.on("change", function (e) { | ||||||
|  |       const val = wiz_editor.getEditor('root.color.channelAdjustment.' + profile + '').getValue(); | ||||||
|  |       const temp = JSON.parse(JSON.stringify(val)); | ||||||
|  |       delete temp.leds | ||||||
|  |       requestAdjustment(JSON.stringify(temp), "", true); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     step++ | ||||||
|  |     performAction(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return { | ||||||
|  |     start: function () { | ||||||
|  |       //create html | ||||||
|  |       $('#wiz_header').html('<i class="fa fa-magic fa-fw"></i>' + $.i18n('wiz_cc_title')); | ||||||
|  |       $('#wizp1_body').html('<h4 style="font-weight:bold;text-transform:uppercase;">' + $.i18n('wiz_cc_title') + '</h4>' + | ||||||
|  |         '<p>' + $.i18n('wiz_cc_intro1') + '</p>' + | ||||||
|  |         '<label>' + $.i18n('wiz_cc_kwebs') + '</label>' + | ||||||
|  |         '<input class="form-control" style="width:280px;margin:auto" id="wiz_cc_kodiip" type="text" placeholder="' + kodiAddress + '" value="' + kodiAddress + '" />' + | ||||||
|  |         '<span id="kodi_status"></span><span id="multi_cali"></span>' | ||||||
|  |       ); | ||||||
|  |       $('#wizp1_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_cont" disabled="disabled">' + '<i class="fa fa-fw fa-check"></i>' + $.i18n('general_btn_continue') + '</button>' + | ||||||
|  |         '<button type="button" class="btn btn-danger" id="btn_wiz_cancel" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_cancel') + '</button>' | ||||||
|  |       ); | ||||||
|  |       $('#wizp2_body').html('<div id="wiz_cc_desc" style="font-weight:bold"></div><div id="editor_container_wiz"></div>' | ||||||
|  |       ); | ||||||
|  |       $('#wizp2_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_back">' + '<i class="fa fa-fw fa-chevron-left"></i>' + $.i18n('general_btn_back') + '</button>' + | ||||||
|  |         '<button type="button" class="btn btn-primary" id="btn_wiz_next">' + $.i18n('general_btn_next') + '<i style="margin-left:4px;"class="fa fa-fw fa-chevron-right"></i>' + '</button>' + | ||||||
|  |         '<button type="button" class="btn btn-warning" id="btn_wiz_save" style="display:none"><i class="fa fa-fw fa-save"></i>' + $.i18n('general_btn_save') + '</button>' + | ||||||
|  |         '<button type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_cancel') + '</button>' | ||||||
|  |       ); | ||||||
|  |  | ||||||
|  |       if (getStorage("darkMode") == "on") | ||||||
|  |         $('#wizard_logo').prop("src", 'img/hyperion/logo_negativ.png'); | ||||||
|  |  | ||||||
|  |       //open modal | ||||||
|  |       $("#wizard_modal").modal({ | ||||||
|  |         backdrop: "static", | ||||||
|  |         keyboard: false, | ||||||
|  |         show: true | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       setupEventListeners(); | ||||||
|  |       $('#wiz_cc_kodiip').trigger("change"); | ||||||
|  |       init(); | ||||||
|  |       initProfiles(); | ||||||
|  |       createEditor(); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | })(); | ||||||
|  |  | ||||||
|  | export { colorCalibrationKodiWizard }; | ||||||
							
								
								
									
										143
									
								
								assets/webconfig/js/wizards/rgbByteOrderWizard.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								assets/webconfig/js/wizards/rgbByteOrderWizard.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | |||||||
|  | //**************************** | ||||||
|  | // Wizard RGB byte order | ||||||
|  | //**************************** | ||||||
|  |  | ||||||
|  | const rgbByteOrderWizard = (() => { | ||||||
|  |  | ||||||
|  |   let wIntveralId; | ||||||
|  |   let new_rgb_order; | ||||||
|  |  | ||||||
|  |   function changeColor() { | ||||||
|  |     let color = $("#wiz_canv_color").css('background-color'); | ||||||
|  |  | ||||||
|  |     if (color == 'rgb(255, 0, 0)') { | ||||||
|  |       $("#wiz_canv_color").css('background-color', 'rgb(0, 255, 0)'); | ||||||
|  |       requestSetColor('0', '255', '0'); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       $("#wiz_canv_color").css('background-color', 'rgb(255, 0, 0)'); | ||||||
|  |       requestSetColor('255', '0', '0'); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |   function stopWizardRGB(reload) { | ||||||
|  |     console.log("stopWizardRGB - reload: ", reload); | ||||||
|  |     clearInterval(wIntveralId); | ||||||
|  |     resetWizard(reload); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   function beginWizardRGB() { | ||||||
|  |     $("#wiz_switchtime_select").off().on('change', function () { | ||||||
|  |       clearInterval(wIntveralId); | ||||||
|  |       const time = $("#wiz_switchtime_select").val(); | ||||||
|  |       wIntveralId = setInterval(function () { changeColor(); }, time * 1000); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $('.wselect').on("change", function () { | ||||||
|  |       let rgb_order = window.serverConfig.device.colorOrder.split(""); | ||||||
|  |       const redS = $("#wiz_r_select").val(); | ||||||
|  |       const greenS = $("#wiz_g_select").val(); | ||||||
|  |       const blueS = rgb_order.toString().replace(/,/g, "").replace(redS, "").replace(greenS, ""); | ||||||
|  |  | ||||||
|  |       for (const color of rgb_order) { | ||||||
|  |         if (redS == color) | ||||||
|  |           $('#wiz_g_select option[value=' + color + ']').prop('disabled', true); | ||||||
|  |         else | ||||||
|  |           $('#wiz_g_select option[value=' + color + ']').prop('disabled', false); | ||||||
|  |         if (greenS == color) | ||||||
|  |           $('#wiz_r_select option[value=' + color + ']').prop('disabled', true); | ||||||
|  |         else | ||||||
|  |           $('#wiz_r_select option[value=' + color + ']').prop('disabled', false); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       if (redS != 'null' && greenS != 'null') { | ||||||
|  |         $('#btn_wiz_save').prop('disabled', false); | ||||||
|  |  | ||||||
|  |         for (let i = 0; i < rgb_order.length; i++) { | ||||||
|  |           if (rgb_order[i] == "r") | ||||||
|  |             rgb_order[i] = redS; | ||||||
|  |           else if (rgb_order[i] == "g") | ||||||
|  |             rgb_order[i] = greenS; | ||||||
|  |           else | ||||||
|  |             rgb_order[i] = blueS; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         rgb_order = rgb_order.toString().replace(/,/g, ""); | ||||||
|  |  | ||||||
|  |         if (redS == "r" && greenS == "g") { | ||||||
|  |           $('#btn_wiz_save').toggle(false); | ||||||
|  |           $('#btn_wiz_checkok').toggle(true); | ||||||
|  |  | ||||||
|  |           window.readOnlyMode ? $('#btn_wiz_checkok').prop('disabled', true) : $('#btn_wiz_checkok').prop('disabled', false); | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |           $('#btn_wiz_save').toggle(true); | ||||||
|  |           window.readOnlyMode ? $('#btn_wiz_save').prop('disabled', true) : $('#btn_wiz_save').prop('disabled', false); | ||||||
|  |  | ||||||
|  |           $('#btn_wiz_checkok').toggle(false); | ||||||
|  |         } | ||||||
|  |         new_rgb_order = rgb_order; | ||||||
|  |       } | ||||||
|  |       else | ||||||
|  |         $('#btn_wiz_save').prop('disabled', true); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $("#wiz_switchtime_select").append(createSelOpt('5', '5'), createSelOpt('10', '10'), createSelOpt('15', '15'), createSelOpt('30', '30')); | ||||||
|  |     $("#wiz_switchtime_select").trigger('change'); | ||||||
|  |  | ||||||
|  |     $("#wiz_r_select").append(createSelOpt("null", ""), createSelOpt('r', $.i18n('general_col_red')), createSelOpt('g', $.i18n('general_col_green')), createSelOpt('b', $.i18n('general_col_blue'))); | ||||||
|  |     $("#wiz_g_select").html($("#wiz_r_select").html()); | ||||||
|  |     $("#wiz_r_select").trigger('change'); | ||||||
|  |  | ||||||
|  |     requestSetColor('255', '0', '0'); | ||||||
|  |     setTimeout(requestSetSource, 100, 'auto'); | ||||||
|  |     setStorage("wizardactive", true); | ||||||
|  |  | ||||||
|  |     $('#btn_wiz_abort').off().on('click', function () { stopWizardRGB(true); }); | ||||||
|  |  | ||||||
|  |     $('#btn_wiz_checkok').off().on('click', function () { | ||||||
|  |       showInfoDialog('success', "", $.i18n('infoDialog_wizrgb_text')); | ||||||
|  |       stopWizardRGB(); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     $('#btn_wiz_save').off().on('click', function () { | ||||||
|  |       stopWizardRGB(); | ||||||
|  |       window.serverConfig.device.colorOrder = new_rgb_order; | ||||||
|  |       requestWriteConfig({ "device": window.serverConfig.device }); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return { | ||||||
|  |     start: function () { | ||||||
|  |       //create html | ||||||
|  |       $('#wiz_header').html('<i class="fa fa-magic fa-fw"></i>' + $.i18n('wiz_rgb_title')); | ||||||
|  |       $('#wizp1_body').html('<h4 style="font-weight:bold;text-transform:uppercase;">' + $.i18n('wiz_rgb_title') + '</h4><p>' + $.i18n('wiz_rgb_intro1') + '</p><p style="font-weight:bold;">' + $.i18n('wiz_rgb_intro2') + '</p>'); | ||||||
|  |       $('#wizp1_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_cont"><i class="fa fa-fw fa-check"></i>' + $.i18n('general_btn_continue') + '</button><button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_cancel') + '</button>'); | ||||||
|  |       $('#wizp2_body').html('<p style="font-weight:bold">' + $.i18n('wiz_rgb_expl') + '</p>'); | ||||||
|  |       $('#wizp2_body').append('<div class="form-group"><label>' + $.i18n('wiz_rgb_switchevery') + '</label><div class="input-group" style="width:100px"><select id="wiz_switchtime_select" class="form-control"></select><div class="input-group-addon">' + $.i18n('edt_append_s') + '</div></div></div>'); | ||||||
|  |       $('#wizp2_body').append('<canvas id="wiz_canv_color" width="100" height="100" style="border-radius:60px;background-color:red; display:block; margin: 10px 0;border:4px solid grey;"></canvas><label>' + $.i18n('wiz_rgb_q') + '</label>'); | ||||||
|  |       $('#wizp2_body').append('<table class="table borderless" style="width:200px"><tbody><tr><td class="ltd"><label>' + $.i18n('wiz_rgb_qrend') + '</label></td><td class="itd"><select id="wiz_r_select" class="form-control wselect"></select></td></tr><tr><td class="ltd"><label>' + $.i18n('wiz_rgb_qgend') + '</label></td><td class="itd"><select id="wiz_g_select" class="form-control wselect"></select></td></tr></tbody></table>'); | ||||||
|  |       $('#wizp2_footer').html('<button type="button" class="btn btn-primary" id="btn_wiz_save"><i class="fa fa-fw fa-save"></i>' + $.i18n('general_btn_save') + '</button><button type="button" class="btn btn-primary" id="btn_wiz_checkok" style="display:none" data-dismiss="modal"><i class="fa fa-fw fa-check"></i>' + $.i18n('general_btn_ok') + '</button><button type="button" class="btn btn-danger" id="btn_wiz_abort"><i class="fa fa-fw fa-close"></i>' + $.i18n('general_btn_cancel') + '</button>'); | ||||||
|  |  | ||||||
|  |       if (getStorage("darkMode") == "on") | ||||||
|  |         $('#wizard_logo').attr("src", 'img/hyperion/logo_negativ.png'); | ||||||
|  |  | ||||||
|  |       //open modal | ||||||
|  |       $("#wizard_modal").modal({ | ||||||
|  |         backdrop: "static", | ||||||
|  |         keyboard: false, | ||||||
|  |         show: true | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       //listen for continue | ||||||
|  |       $('#btn_wiz_cont').off().on('click', function () { | ||||||
|  |         beginWizardRGB(); | ||||||
|  |         $('#wizp1').toggle(false); | ||||||
|  |         $('#wizp2').toggle(true); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | })(); | ||||||
|  |  | ||||||
|  | export { rgbByteOrderWizard }; | ||||||
		Reference in New Issue
	
	Block a user