mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Support Philips Hue APIv2 and refactoring
This commit is contained in:
parent
a5bb7e905b
commit
1b0d8a56e9
@ -86,6 +86,7 @@
|
||||
"conf_leds_layout_cl_bottomright": "Bottom Right (Corner)",
|
||||
"conf_leds_layout_cl_cornergap": "Corner Gap",
|
||||
"conf_leds_layout_cl_edgegap": "Edge Gap",
|
||||
"conf_leds_layout_cl_entertainment": "Entertainment Area",
|
||||
"conf_leds_layout_cl_gaglength": "Gap length",
|
||||
"conf_leds_layout_cl_gappos": "gap position",
|
||||
"conf_leds_layout_cl_hleddepth": "Horizontal LED depth",
|
||||
@ -618,7 +619,7 @@
|
||||
"edt_dev_spec_gpioBcm_title": "GPIO Pin",
|
||||
"edt_dev_spec_gpioMap_title": "GPIO mapping",
|
||||
"edt_dev_spec_gpioNumber_title": "GPIO number",
|
||||
"edt_dev_spec_groupId_title": "Group ID",
|
||||
"edt_dev_spec_groupId_title": "Group",
|
||||
"edt_dev_spec_header_title": "Specific Settings",
|
||||
"edt_dev_spec_interpolation_title": "Interpolation",
|
||||
"edt_dev_spec_intervall_title": "Interval",
|
||||
@ -683,6 +684,7 @@
|
||||
"edt_dev_spec_transistionTime_title": "Transition time",
|
||||
"edt_dev_spec_uid_title": "UID",
|
||||
"edt_dev_spec_universe_title": "Universe",
|
||||
"edt_dev_spec_useAPIv2_title": "Use API v2",
|
||||
"edt_dev_spec_useEntertainmentAPI_title": "Use Hue Entertainment API",
|
||||
"edt_dev_spec_useOrbSmoothing_title": "Use orb smoothing",
|
||||
"edt_dev_spec_useRgbwProtocol_title": "Use RGBW protocol",
|
||||
@ -1087,7 +1089,7 @@
|
||||
"wiz_cololight_noprops": "Not able to get device properties - Define Hardware LED count manually",
|
||||
"wiz_cololight_title": "Cololight Wizard",
|
||||
"wiz_guideyou": "The $1 will guide you through the settings. Just press the button!",
|
||||
"wiz_hue_blinkblue": "Let ID $1 light up blue",
|
||||
"wiz_hue_blinkblue": "Let it light up",
|
||||
"wiz_hue_clientkey": "Clientkey",
|
||||
"wiz_hue_create_user": "Create new User",
|
||||
"wiz_hue_desc1": "1. Hyperion searches automatically for a Hue-Bridge, in case it cannot find one you need to provide the hostname or IP-address and push the reload button. <br> 2. Provide a user ID, if you do not have one create a new one.",
|
||||
|
@ -1021,11 +1021,6 @@ $(document).ready(function () {
|
||||
var generalOptions = window.serverSchema.properties.device;
|
||||
|
||||
var ledType = $(this).val();
|
||||
|
||||
// philipshueentertainment backward fix
|
||||
if (ledType == "philipshueentertainment")
|
||||
ledType = "philipshue";
|
||||
|
||||
var specificOptions = window.serverSchema.properties.alldevices[ledType];
|
||||
|
||||
conf_editor = createJsonEditor('editor_container_leddevice', {
|
||||
@ -1060,13 +1055,10 @@ $(document).ready(function () {
|
||||
$('#btn_led_device_wiz').off();
|
||||
|
||||
if (ledType == "philipshue") {
|
||||
$('#root_specificOptions_useEntertainmentAPI').on("change", function () {
|
||||
var ledWizardType = (this.checked) ? "philipshueentertainment" : ledType;
|
||||
var data = { type: ledWizardType };
|
||||
var hue_title = (this.checked) ? 'wiz_hue_e_title' : 'wiz_hue_title';
|
||||
changeWizard(data, hue_title, startWizardPhilipsHue);
|
||||
});
|
||||
$("#root_specificOptions_useEntertainmentAPI").trigger("change");
|
||||
var ledWizardType = ledType;
|
||||
var data = { type: ledWizardType };
|
||||
var hue_title = 'wiz_hue_title';
|
||||
changeWizard(data, hue_title, startWizardPhilipsHue);
|
||||
}
|
||||
else if (ledType == "atmoorb") {
|
||||
var ledWizardType = (this.checked) ? "atmoorb" : ledType;
|
||||
|
@ -604,7 +604,7 @@ var lightPosTopLeft112 = { hmin: 0, hmax: 0.5, vmin: 0, vmax: 0.15 };
|
||||
var lightPosTopLeft121 = { hmin: 0.5, hmax: 1, vmin: 0, vmax: 0.15 };
|
||||
var lightPosTopLeftNewMid = { hmin: 0.25, hmax: 0.75, vmin: 0, vmax: 0.15 };
|
||||
|
||||
function assignLightPos(id, pos, name) {
|
||||
function assignLightPos(pos, name) {
|
||||
var i = null;
|
||||
|
||||
if (pos === "top")
|
||||
@ -695,28 +695,24 @@ devicesProperties = {};
|
||||
|
||||
var hueIPs = [];
|
||||
var hueIPsinc = 0;
|
||||
var hueLights = null;
|
||||
var hueGroups = null;
|
||||
var hueLights = [];
|
||||
var hueEntertainmentConfigs = [];
|
||||
var hueEntertainmentServices = [];
|
||||
var lightLocation = [];
|
||||
var groupLights = [];
|
||||
var groupChannels = [];
|
||||
var groupLightsLocations = [];
|
||||
var hueType = "philipshue";
|
||||
var isAPIv2Ready = true;
|
||||
var isEntertainmentReady = true;
|
||||
|
||||
function startWizardPhilipsHue(e) {
|
||||
if (typeof e.data.type != "undefined") hueType = e.data.type;
|
||||
|
||||
//create html
|
||||
|
||||
var hue_title = 'wiz_hue_title';
|
||||
var hue_intro1 = 'wiz_hue_intro1';
|
||||
var hue_intro1 = 'wiz_hue_e_intro1';
|
||||
var hue_desc1 = 'wiz_hue_desc1';
|
||||
var hue_create_user = 'wiz_hue_create_user';
|
||||
if (hueType == 'philipshueentertainment') {
|
||||
hue_title = 'wiz_hue_e_title';
|
||||
hue_intro1 = 'wiz_hue_e_intro1';
|
||||
hue_desc1 = 'wiz_hue_e_desc1';
|
||||
hue_create_user = 'wiz_hue_e_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>');
|
||||
@ -733,6 +729,9 @@ function startWizardPhilipsHue(e) {
|
||||
' <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>' +
|
||||
' <div class="col-md-3 ' + hidePort + '"><div class="input-group">' +
|
||||
' <span class="input-group-addon">:</span>' +
|
||||
@ -751,23 +750,18 @@ function startWizardPhilipsHue(e) {
|
||||
'</div><input type="hidden" id="groupId">'
|
||||
);
|
||||
|
||||
if (hueType == 'philipshueentertainment') {
|
||||
$('#usrcont').append('<div 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('<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>');
|
||||
|
||||
if (hueType == 'philipshueentertainment') {
|
||||
$('#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'), $.i18n('wiz_hue_e_use_group')], 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>');
|
||||
}
|
||||
else {
|
||||
$('#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_desc2') + '</p></div>');
|
||||
}
|
||||
$('#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>');
|
||||
@ -793,13 +787,19 @@ function startWizardPhilipsHue(e) {
|
||||
|
||||
function checkHueBridge(cb, hueUser) {
|
||||
var usr = (typeof hueUser != "undefined") ? hueUser : 'config';
|
||||
if (usr == 'config') $('#wiz_hue_discovered').html("");
|
||||
if (usr === 'config') {
|
||||
$('#wiz_hue_discovered').html("");
|
||||
}
|
||||
|
||||
if (hueIPs[hueIPsinc]) {
|
||||
var host = hueIPs[hueIPsinc].host;
|
||||
var port = hueIPs[hueIPsinc].port;
|
||||
|
||||
getProperties_hue_bridge(cb, decodeURIComponent(host), port, usr);
|
||||
|
||||
if (isAPIv2Ready) {
|
||||
$('#port').val(443);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -811,37 +811,51 @@ function checkBridgeResult(reply, usr) {
|
||||
$('#port').val(hueIPs[hueIPsinc].port)
|
||||
|
||||
$('#usrcont').toggle(true);
|
||||
checkHueBridge(checkUserResult, $('#user').val() ? $('#user').val() : "newdeveloper");
|
||||
|
||||
checkHueBridge(checkUserResult, $('#user').val());
|
||||
}
|
||||
else {
|
||||
//increment and check again
|
||||
if (hueIPs.length - 1 > hueIPsinc) {
|
||||
hueIPsinc++;
|
||||
checkHueBridge(checkBridgeResult);
|
||||
}
|
||||
else {
|
||||
$('#usrcont').toggle(false);
|
||||
$('#wiz_hue_ipstate').html($.i18n('wiz_hue_failure_ip'));
|
||||
}
|
||||
$('#usrcont').toggle(false);
|
||||
$('#wiz_hue_ipstate').html($.i18n('wiz_hue_failure_ip'));
|
||||
}
|
||||
};
|
||||
|
||||
function checkUserResult(reply, usr) {
|
||||
function checkUserResult(reply, username) {
|
||||
$('#usrcont').toggle(true);
|
||||
if (reply) {
|
||||
$('#user').val(usr);
|
||||
if (hueType == 'philipshueentertainment' && $('#clientkey').val() == "") {
|
||||
|
||||
var 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 (hueType == 'philipshue') {
|
||||
get_hue_lights();
|
||||
}
|
||||
if (hueType == 'philipshueentertainment') {
|
||||
get_hue_groups();
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -852,22 +866,73 @@ function checkUserResult(reply, usr) {
|
||||
}
|
||||
};
|
||||
|
||||
function useGroupId(id) {
|
||||
$('#groupId').val(id);
|
||||
function useGroupId(id, username) {
|
||||
$('#groupId').val(hueEntertainmentConfigs[id].id);
|
||||
if (isAPIv2Ready) {
|
||||
var group = hueEntertainmentConfigs[id];
|
||||
|
||||
//Ensure ligthIDs are strings
|
||||
groupLights = hueGroups[id].lights.map(num => {
|
||||
return String(num);
|
||||
});
|
||||
groupLights = [];
|
||||
for (const light of group.light_services) {
|
||||
groupLights.push(light.rid);
|
||||
}
|
||||
|
||||
groupLightsLocations = hueGroups[id].locations;
|
||||
get_hue_lights();
|
||||
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);
|
||||
});
|
||||
|
||||
var lightLocations = hueEntertainmentConfigs[id].locations;
|
||||
for (var locationID in lightLocations) {
|
||||
var 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 updateBridgeDetails(properties) {
|
||||
var 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_hue_bridges() {
|
||||
$('#wiz_hue_ipstate').html($.i18n('edt_dev_spec_devices_discovery_inprogress'));
|
||||
$('#wiz_hue_discovered').html("")
|
||||
|
||||
// $('#wiz_hue_discovered').html("")
|
||||
const res = await requestLedDeviceDiscovery('philipshue');
|
||||
if (res && !res.error) {
|
||||
const r = res.info;
|
||||
@ -903,11 +968,6 @@ async function discover_hue_bridges() {
|
||||
port = device.port;
|
||||
}
|
||||
|
||||
//Remap https port to http port until Hue-API v2 is supported
|
||||
if (port == 443) {
|
||||
port = 80;
|
||||
}
|
||||
|
||||
if (host) {
|
||||
|
||||
if (!hueIPs.some(item => item.host === host)) {
|
||||
@ -916,22 +976,39 @@ async function discover_hue_bridges() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$('#wiz_hue_ipstate').html("");
|
||||
$('#host').val(hueIPs[hueIPsinc].host)
|
||||
$('#port').val(hueIPs[hueIPsinc].port)
|
||||
|
||||
var usr = $('#user').val();
|
||||
if (usr != "") {
|
||||
checkHueBridge(checkUserResult, usr);
|
||||
} else {
|
||||
checkHueBridge(checkBridgeResult);
|
||||
$('#hue_bridge_select').html("");
|
||||
|
||||
for (var key in hueIPs) {
|
||||
$('#hue_bridge_select').append(createSelOpt(key, hueIPs[key].host));
|
||||
}
|
||||
|
||||
$('.hue_bridge_sel_watch').on("click", function () {
|
||||
hueIPsinc = $(this).val();
|
||||
|
||||
var name = $("#hue_bridge_select option:selected").text();
|
||||
$('#host').val(name);
|
||||
$('#port').val(hueIPs[hueIPsinc].port)
|
||||
|
||||
var usr = $('#user').val();
|
||||
if (usr != "") {
|
||||
checkHueBridge(checkUserResult, usr);
|
||||
} else {
|
||||
checkHueBridge(checkBridgeResult);
|
||||
}
|
||||
});
|
||||
|
||||
$('.hue_bridge_sel_watch').click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getProperties_hue_bridge(cb, hostAddress, port, username, resourceFilter) {
|
||||
let params = { host: hostAddress, user: username, filter: resourceFilter };
|
||||
let params = { host: hostAddress, username: username, filter: resourceFilter };
|
||||
if (port !== 'undefined') {
|
||||
params.port = parseInt(port);
|
||||
}
|
||||
@ -945,23 +1022,27 @@ async function getProperties_hue_bridge(cb, hostAddress, port, username, resourc
|
||||
}
|
||||
|
||||
// Use device's properties, if properties in chache
|
||||
if (devicesProperties[ledType][key]) {
|
||||
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) {
|
||||
var 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") {
|
||||
if (ledDeviceProperties.name && ledDeviceProperties.bridgeid && ledDeviceProperties.modelid) {
|
||||
$('#wiz_hue_discovered').html("Bridge: " + ledDeviceProperties.name + ", Modelid: " + ledDeviceProperties.modelid + ", API-Version: " + ledDeviceProperties.apiversion);
|
||||
cb(true);
|
||||
}
|
||||
cb(true);
|
||||
} else {
|
||||
devicesProperties[ledType][key] = ledDeviceProperties;
|
||||
cb(true, username);
|
||||
}
|
||||
} else {
|
||||
@ -973,12 +1054,12 @@ async function getProperties_hue_bridge(cb, hostAddress, port, username, resourc
|
||||
}
|
||||
}
|
||||
|
||||
async function identify_hue_device(hostAddress, port, username, id) {
|
||||
async function identify_hue_device(hostAddress, port, username, name, id, id_v1) {
|
||||
var 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), user: username, lightId: id };
|
||||
let params = { host: decodeURIComponent(hostAddress), username: username, lightName: decodeURIComponent(name), lightId: id, lightId_v1: id_v1 };
|
||||
|
||||
if (port !== 'undefined') {
|
||||
params.port = parseInt(port);
|
||||
@ -1003,11 +1084,9 @@ function beginWizardHue() {
|
||||
$('#user').val(usr);
|
||||
}
|
||||
|
||||
if (hueType == 'philipshueentertainment') {
|
||||
var clkey = eV("clientkey");
|
||||
if (clkey != "") {
|
||||
$('#clientkey').val(clkey);
|
||||
}
|
||||
var clkey = eV("clientkey");
|
||||
if (clkey != "") {
|
||||
$('#clientkey').val(clkey);
|
||||
}
|
||||
|
||||
//check if host is empty/reachable/search for bridge
|
||||
@ -1022,13 +1101,13 @@ function beginWizardHue() {
|
||||
$('#host').val(host);
|
||||
|
||||
var port = eV("port");
|
||||
if (port == 0) {
|
||||
$('#port').val(80);
|
||||
}
|
||||
else {
|
||||
if (port > 0) {
|
||||
$('#port').val(port);
|
||||
}
|
||||
hueIPs.unshift({ host: host, port: port });
|
||||
else {
|
||||
$('#port').val('');
|
||||
}
|
||||
hueIPs.push({ host: host, port: port });
|
||||
|
||||
if (usr != "") {
|
||||
checkHueBridge(checkUserResult, usr);
|
||||
@ -1038,18 +1117,18 @@ function beginWizardHue() {
|
||||
}
|
||||
|
||||
$('#retry_bridge').off().on('click', function () {
|
||||
var host = $('#host').val();
|
||||
var port = parseInt($('#port').val());
|
||||
|
||||
if ($('#host').val() != "") {
|
||||
if (host != "") {
|
||||
|
||||
hueIPs = [];
|
||||
hueIPsinc = 0;
|
||||
|
||||
var port = $('#port').val();
|
||||
if (isNaN(port) || port < 1 || port > 65535) {
|
||||
port = 80;
|
||||
$('#port').val(80);
|
||||
var 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;
|
||||
}
|
||||
hueIPs.push({ host: $('#host').val(), port: port });
|
||||
}
|
||||
else {
|
||||
discover_hue_bridges();
|
||||
@ -1064,29 +1143,152 @@ function beginWizardHue() {
|
||||
});
|
||||
|
||||
$('#retry_usr').off().on('click', function () {
|
||||
checkHueBridge(checkUserResult, $('#user').val() ? $('#user').val() : "newdeveloper");
|
||||
checkHueBridge(checkUserResult, $('#user').val());
|
||||
});
|
||||
|
||||
$('#wiz_hue_create_user').off().on('click', function () {
|
||||
if ($('#host').val() != "") {
|
||||
hueIPs.unshift({ host: $('#host').val(), port: $('#port').val() });
|
||||
}
|
||||
createHueUser();
|
||||
});
|
||||
|
||||
function assignLightEntertainmentPos(position, name, id) {
|
||||
|
||||
var x = position.x;
|
||||
var z = position.z;
|
||||
var h = (x + 1) / 2;
|
||||
var v = (-z + 1) / 2;
|
||||
|
||||
hmin = h - 0.05;
|
||||
hmax = h + 0.05;
|
||||
vmin = v - 0.05;
|
||||
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) {
|
||||
layoutObject.name += "_" + id;
|
||||
}
|
||||
return layoutObject;
|
||||
}
|
||||
|
||||
function assignSegmentedLightPos(segment, position, name) {
|
||||
var layoutObjects = [];
|
||||
|
||||
var segTotalLength = 0;
|
||||
for (var key in segment) {
|
||||
|
||||
segTotalLength += segment[key].length;
|
||||
}
|
||||
|
||||
var min = 0;
|
||||
var max = 1;
|
||||
var horizontal = true;
|
||||
|
||||
var layoutObject = 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;
|
||||
}
|
||||
|
||||
var step = (max - min) / segTotalLength;
|
||||
var start = min;
|
||||
|
||||
for (var 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;
|
||||
}
|
||||
|
||||
$('#btn_wiz_save').off().on("click", function () {
|
||||
var hueLedConfig = [];
|
||||
var finalLightIds = [];
|
||||
var channelNumber = 0;
|
||||
|
||||
//create hue led config
|
||||
for (var key in hueLights) {
|
||||
if (hueType == 'philipshueentertainment') {
|
||||
if (groupLights.indexOf(key) == -1) continue;
|
||||
}
|
||||
if ($('#hue_' + key).val() != "disabled") {
|
||||
finalLightIds.push(key);
|
||||
var idx_content = assignLightPos(key, $('#hue_' + key).val(), hueLights[key].name);
|
||||
hueLedConfig.push(JSON.parse(JSON.stringify(idx_content)));
|
||||
for (var key in groupLights) {
|
||||
var lightId = groupLights[key];
|
||||
|
||||
if ($('#hue_' + lightId).val() != "disabled") {
|
||||
finalLightIds.push(lightId);
|
||||
|
||||
var lightName;
|
||||
if (isAPIv2Ready) {
|
||||
light = hueLights.find(light => light.id === lightId);
|
||||
lightName = light.metadata.name;
|
||||
} else {
|
||||
lightName = hueLights[lightId].name;
|
||||
}
|
||||
|
||||
var position = $('#hue_' + lightId).val();
|
||||
var lightIdx = groupLights.indexOf(lightId)
|
||||
var lightLocation = groupLightsLocations[lightIdx];
|
||||
var serviceID
|
||||
|
||||
if (isAPIv2Ready) {
|
||||
serviceID = lightLocation.service.rid;
|
||||
}
|
||||
|
||||
if (position === "entertainment") {
|
||||
// Layout per entertainment area definition at bridge
|
||||
if (isAPIv2Ready) {
|
||||
|
||||
groupChannels.forEach((channel) => {
|
||||
if (channel.members[0].service.rid === serviceID) {
|
||||
var layoutObject = assignLightEntertainmentPos(channel.position, lightName, channel.channel_id);
|
||||
hueLedConfig.push(JSON.parse(JSON.stringify(layoutObject)));
|
||||
++channelNumber;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var layoutObject = assignLightEntertainmentPos(lightLocation.position, lightName);
|
||||
hueLedConfig.push(JSON.parse(JSON.stringify(layoutObject)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Layout per manual settings
|
||||
var maxSegments = 1;
|
||||
|
||||
if (isAPIv2Ready) {
|
||||
var service = hueEntertainmentServices.find(service => service.id === serviceID);
|
||||
maxSegments = service.segments.max_segments;
|
||||
}
|
||||
|
||||
if (maxSegments > 1) {
|
||||
var segment = service.segments.segments;
|
||||
var layoutObjects = assignSegmentedLightPos(segment, position, lightName);
|
||||
hueLedConfig.push(...layoutObjects);
|
||||
} else {
|
||||
var layoutObject = assignLightPos(position, lightName);
|
||||
hueLedConfig.push(JSON.parse(JSON.stringify(layoutObject)));
|
||||
}
|
||||
channelNumber += maxSegments;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1121,7 +1323,7 @@ function beginWizardHue() {
|
||||
d.brightnessFactor = parseFloat(eV("brightnessFactor", 1));
|
||||
|
||||
d.clientkey = $('#clientkey').val();
|
||||
d.groupId = parseInt($('#groupId').val());
|
||||
d.groupId = $('#groupId').val();
|
||||
d.blackLightsTimeout = parseInt(eV("blackLightsTimeout", 5000));
|
||||
d.brightnessMin = parseFloat(eV("brightnessMin", 0));
|
||||
d.brightnessMax = parseFloat(eV("brightnessMax", 1));
|
||||
@ -1134,8 +1336,16 @@ function beginWizardHue() {
|
||||
d.enableAttempts = parseInt(conf_editor.getEditor("root.generalOptions.enableAttempts").getValue());
|
||||
d.enableAttemptsInterval = parseInt(conf_editor.getEditor("root.generalOptions.enableAttemptsInterval").getValue());
|
||||
|
||||
if (hueType == 'philipshue') {
|
||||
d.useEntertainmentAPI = false;
|
||||
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) {
|
||||
@ -1144,15 +1354,6 @@ function beginWizardHue() {
|
||||
}
|
||||
}
|
||||
|
||||
if (hueType == 'philipshueentertainment') {
|
||||
d.useEntertainmentAPI = true;
|
||||
d.hardwareLedCount = groupLights.length;
|
||||
if (window.serverConfig.device.type !== d.type) {
|
||||
//smoothing on, if new device
|
||||
sc.smoothing = { enable: true };
|
||||
}
|
||||
}
|
||||
|
||||
window.serverConfig.device = d;
|
||||
|
||||
requestWriteConfig(sc, true);
|
||||
@ -1163,7 +1364,6 @@ function beginWizardHue() {
|
||||
}
|
||||
|
||||
function createHueUser() {
|
||||
|
||||
var host = hueIPs[hueIPsinc].host;
|
||||
var port = hueIPs[hueIPsinc].port;
|
||||
|
||||
@ -1208,7 +1408,8 @@ function createHueUser() {
|
||||
conf_editor.getEditor("root.specificOptions.host").setValue(host);
|
||||
conf_editor.getEditor("root.specificOptions.port").setValue(port);
|
||||
}
|
||||
if (hueType == 'philipshueentertainment') {
|
||||
|
||||
if (isEntertainmentReady) {
|
||||
var clientkey = response.clientkey;
|
||||
if (clientkey != 'undefined') {
|
||||
$('#clientkey').val(clientkey);
|
||||
@ -1230,37 +1431,52 @@ function createHueUser() {
|
||||
}, retryInterval * 1000);
|
||||
}
|
||||
|
||||
function get_hue_groups() {
|
||||
|
||||
function get_hue_groups(username) {
|
||||
var host = hueIPs[hueIPsinc].host;
|
||||
|
||||
if (devicesProperties['philipshue'][host]) {
|
||||
var ledProperties = devicesProperties['philipshue'][host];
|
||||
if (devicesProperties['philipshue'][host] && devicesProperties['philipshue'][host][username]) {
|
||||
var ledProperties = devicesProperties['philipshue'][host][username];
|
||||
|
||||
if (!jQuery.isEmptyObject(ledProperties)) {
|
||||
hueGroups = ledProperties.groups;
|
||||
if (Object.keys(hueGroups).length > 0) {
|
||||
|
||||
$('.lidsb').html("");
|
||||
$('#wh_topcontainer').toggle(false);
|
||||
$('#hue_grp_ids_t').toggle(true);
|
||||
|
||||
var gC = 0;
|
||||
for (var groupid in hueGroups) {
|
||||
if (hueGroups[groupid].type == 'Entertainment') {
|
||||
$('.gidsb').append(createTableRow([groupid + ' (' + hueGroups[groupid].name + ')', '<button class="btn btn-sm btn-primary" onClick=useGroupId(' + groupid + ')>' + $.i18n('wiz_hue_e_use_groupid', groupid) + '</button>']));
|
||||
gC++;
|
||||
}
|
||||
}
|
||||
if (gC == 0) {
|
||||
noAPISupport('wiz_hue_e_noegrpids');
|
||||
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 = [];
|
||||
var hueGroups = ledProperties.groups;
|
||||
for (var 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 (var groupid in hueEntertainmentConfigs) {
|
||||
$('.gidsb').append(createTableRow([groupid + ' (' + hueEntertainmentConfigs[groupid].name + ')', '<button class="btn btn-sm btn-primary" onClick=useGroupId("' + groupid + '","' + username + '")>' + $.i18n('wiz_hue_e_use_group') + '</button>']));
|
||||
}
|
||||
} else {
|
||||
noAPISupport('wiz_hue_e_noegrpids', username);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function noAPISupport(txt) {
|
||||
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");
|
||||
@ -1269,51 +1485,82 @@ function noAPISupport(txt) {
|
||||
var txt = (txt) ? $.i18n(txt) : $.i18n('wiz_hue_e_nogrpids');
|
||||
$('<p style="font-weight:bold;color:red;">' + txt + '<br />' + $.i18n('wiz_hue_e_noapisupport') + '</p>').insertBefore('#wizp2_body #hue_ids_t');
|
||||
$('#hue_id_headline').html($.i18n('wiz_hue_desc2'));
|
||||
hueType = 'philipshue';
|
||||
get_hue_lights();
|
||||
|
||||
get_hue_lights(username);
|
||||
}
|
||||
|
||||
function get_hue_lights() {
|
||||
|
||||
function get_hue_lights(username) {
|
||||
var host = hueIPs[hueIPsinc].host;
|
||||
|
||||
if (devicesProperties['philipshue'][host]) {
|
||||
var ledProperties = devicesProperties['philipshue'][host];
|
||||
if (devicesProperties['philipshue'][host] && devicesProperties['philipshue'][host][username]) {
|
||||
var ledProperties = devicesProperties['philipshue'][host][username];
|
||||
|
||||
if (!jQuery.isEmptyObject(ledProperties.lights)) {
|
||||
hueLights = ledProperties.lights;
|
||||
if (Object.keys(hueLights).length > 0) {
|
||||
if (hueType == 'philipshue') {
|
||||
$('#wh_topcontainer').toggle(false);
|
||||
if (isAPIv2Ready) {
|
||||
if (!jQuery.isEmptyObject(ledProperties.data)) {
|
||||
if (Object.keys(ledProperties.data).length > 0) {
|
||||
hueLights = ledProperties.data.filter(config => {
|
||||
return config.type === "light";
|
||||
});
|
||||
}
|
||||
$('#hue_ids_t, #btn_wiz_save').toggle(true);
|
||||
}
|
||||
} else {
|
||||
if (!jQuery.isEmptyObject(ledProperties.lights)) {
|
||||
hueLights = ledProperties.lights;
|
||||
}
|
||||
}
|
||||
|
||||
var 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 (Object.keys(hueLights).length > 0) {
|
||||
if (!isEntertainmentReady) {
|
||||
$('#wh_topcontainer').toggle(false);
|
||||
}
|
||||
$('#hue_ids_t, #btn_wiz_save').toggle(true);
|
||||
|
||||
if (hueType == 'philipshue') {
|
||||
lightOptions.unshift("disabled");
|
||||
var 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");
|
||||
} else {
|
||||
lightOptions.unshift("disabled");
|
||||
groupLights = Object.keys(hueLights);
|
||||
}
|
||||
|
||||
$('.lidsb').html("");
|
||||
|
||||
var pos = "";
|
||||
for (var id in groupLights) {
|
||||
var lightId = groupLights[id];
|
||||
var lightId_v1 = "/lights/" + lightId;
|
||||
|
||||
if (isAPIv2Ready) {
|
||||
light = hueLights.find(light => light.id === lightId);
|
||||
lightName = light.metadata.name;
|
||||
lightId_v1 = light.id_v1;
|
||||
} else {
|
||||
lightName = hueLights[lightId].name;
|
||||
}
|
||||
|
||||
$('.lidsb').html("");
|
||||
var pos = "";
|
||||
for (var lightid in hueLights) {
|
||||
if (hueType == 'philipshueentertainment') {
|
||||
if (groupLights.indexOf(lightid) == -1) continue;
|
||||
if (isEntertainmentReady) {
|
||||
var light;
|
||||
var lightLocation = {};
|
||||
|
||||
lightLocation = groupLightsLocations[id];
|
||||
if (lightLocation) {
|
||||
if (isAPIv2Ready) {
|
||||
pos = 0;
|
||||
} else {
|
||||
var x = lightLocation.position.x;
|
||||
var y = lightLocation.position.y;
|
||||
var z = lightLocation.position.z;
|
||||
|
||||
if (groupLightsLocations.hasOwnProperty(lightid)) {
|
||||
lightLocation = groupLightsLocations[lightid];
|
||||
var x = lightLocation[0];
|
||||
var y = lightLocation[1];
|
||||
var z = lightLocation[2];
|
||||
var xval = (x < 0) ? "left" : "right";
|
||||
if (z != 1 && x >= -0.25 && x <= 0.25) xval = "";
|
||||
switch (z) {
|
||||
@ -1329,37 +1576,39 @@ function get_hue_lights() {
|
||||
}
|
||||
}
|
||||
}
|
||||
var options = "";
|
||||
for (var opt in lightOptions) {
|
||||
var val = lightOptions[opt];
|
||||
var 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([lightid + ' (' + hueLights[lightid].name + ')', '<select id="hue_' + lightid + '" class="hue_sel_watch form-control">'
|
||||
+ options
|
||||
+ '</select>', '<button class="btn btn-sm btn-primary" onClick=identify_hue_device("' + encodeURIComponent($("#host").val()) + '","' + $('#port').val() + '","' + $("#user").val() + '",' + lightid + ')>' + $.i18n('wiz_hue_blinkblue', lightid) + '</button>']));
|
||||
}
|
||||
|
||||
if (hueType != 'philipshueentertainment') {
|
||||
$('.hue_sel_watch').on("change", function () {
|
||||
var cC = 0;
|
||||
for (var key in hueLights) {
|
||||
if ($('#hue_' + key).val() != "disabled") {
|
||||
cC++;
|
||||
}
|
||||
var options = "";
|
||||
for (var opt in lightOptions) {
|
||||
var val = lightOptions[opt];
|
||||
var 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" onClick=identify_hue_device("' + encodeURIComponent($("#host").val()) + '","' + $('#port').val() + '","' + $("#user").val() + '","' + encodeURIComponent(lightName) + '","' + lightId + '","' + lightId_v1 + '")>' + $.i18n('wiz_hue_blinkblue', id) + '</button>']));
|
||||
}
|
||||
|
||||
if (!isEntertainmentReady) {
|
||||
$('.hue_sel_watch').on("change", function () {
|
||||
var cC = 0;
|
||||
for (var 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 {
|
||||
var txt = '<p style="font-weight:bold;color:red;">' + $.i18n('wiz_hue_noids') + '</p>';
|
||||
$('#wizp2_body').append(txt);
|
||||
(cC == 0 || window.readOnlyMode) ? $('#btn_wiz_save').prop("disabled", true) : $('#btn_wiz_save').prop("disabled", false);
|
||||
});
|
||||
}
|
||||
$('.hue_sel_watch').trigger('change');
|
||||
}
|
||||
else {
|
||||
var txt = '<p style="font-weight:bold;color:red;">' + $.i18n('wiz_hue_noids') + '</p>';
|
||||
$('#wizp2_body').append(txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1437,7 +1686,7 @@ function beginWizardYeelight() {
|
||||
|
||||
finalLights.push(lights[key]);
|
||||
|
||||
var idx_content = assignLightPos(key, $('#yee_' + key).val(), name);
|
||||
var idx_content = assignLightPos($('#yee_' + key).val(), name);
|
||||
yeelightLedConfig.push(JSON.parse(JSON.stringify(idx_content)));
|
||||
}
|
||||
}
|
||||
@ -1733,7 +1982,7 @@ function beginWizardAtmoOrb() {
|
||||
if (lights[key].host !== "")
|
||||
name += ':' + lights[key].host;
|
||||
|
||||
var idx_content = assignLightPos(key, $('#orb_' + key).val(), name);
|
||||
var idx_content = assignLightPos($('#orb_' + key).val(), name);
|
||||
atmoorbLedConfig.push(JSON.parse(JSON.stringify(idx_content)));
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -16,31 +16,6 @@
|
||||
#include "ProviderRestApi.h"
|
||||
#include "ProviderUdpSSL.h"
|
||||
|
||||
//Streaming message header and payload definition
|
||||
const uint8_t HEADER[] =
|
||||
{
|
||||
'H', 'u', 'e', 'S', 't', 'r', 'e', 'a', 'm', //protocol
|
||||
0x01, 0x00, //version 1.0
|
||||
0x01, //sequence number 1
|
||||
0x00, 0x00, //Reserved write 0’s
|
||||
0x01, //xy Brightness
|
||||
0x00, // Reserved, write 0’s
|
||||
};
|
||||
|
||||
const uint8_t PAYLOAD_PER_LIGHT[] =
|
||||
{
|
||||
0x01, 0x00, 0x06, //light ID
|
||||
//color: 16 bpc
|
||||
0xff, 0xff,
|
||||
0xff, 0xff,
|
||||
0xff, 0xff,
|
||||
/*
|
||||
(message.R >> 8) & 0xff, message.R & 0xff,
|
||||
(message.G >> 8) & 0xff, message.G & 0xff,
|
||||
(message.B >> 8) & 0xff, message.B & 0xff
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
* A XY color point in the color space of the hue system without brightness.
|
||||
*/
|
||||
@ -145,12 +120,18 @@ public:
|
||||
/// Constructs the light.
|
||||
///
|
||||
/// @param log the logger
|
||||
/// @param bridge the bridge
|
||||
/// @param useApiV2 make use of Hue API version 2
|
||||
/// @param id the light id
|
||||
/// @param lightAttributes the light's attributes as provied by the Hue Bridge
|
||||
/// @param onBlackTimeToPowerOff Timeframe of Black output that triggers powering off the light
|
||||
/// @param onBlackTimeToPowerOn Timeframe of non Black output that triggers powering on the light
|
||||
///
|
||||
PhilipsHueLight(Logger* log, int id, QJsonObject values, int ledidx,
|
||||
int onBlackTimeToPowerOff,
|
||||
int onBlackTimeToPowerOn);
|
||||
PhilipsHueLight(Logger* log, bool useApiV2, const QString& id, const QJsonObject& lightAttributes,
|
||||
int onBlackTimeToPowerOff,
|
||||
int onBlackTimeToPowerOn);
|
||||
|
||||
void setDeviceDetails(const QJsonObject& details);
|
||||
void setEntertainmentSrvDetails(const QJsonObject& details);
|
||||
|
||||
///
|
||||
/// @param on
|
||||
@ -167,7 +148,14 @@ public:
|
||||
///
|
||||
void setColor(const CiColor& color);
|
||||
|
||||
int getId() const;
|
||||
QString getId() const;
|
||||
QString getdeviceId() const;
|
||||
QString getProduct() const;
|
||||
QString getModel() const;
|
||||
QString getName() const;
|
||||
QString getArcheType() const;
|
||||
|
||||
int getMaxSegments() const;
|
||||
|
||||
bool getOnOffState() const;
|
||||
int getTransitionTime() const;
|
||||
@ -179,7 +167,7 @@ public:
|
||||
CiColorTriangle getColorSpace() const;
|
||||
|
||||
void saveOriginalState(const QJsonObject& values);
|
||||
QString getOriginalState() const;
|
||||
QJsonObject getOriginalState() const;
|
||||
|
||||
bool isBusy();
|
||||
bool isBlack(bool isBlack);
|
||||
@ -189,24 +177,30 @@ public:
|
||||
private:
|
||||
|
||||
Logger* _log;
|
||||
/// light id
|
||||
int _id;
|
||||
int _ledidx;
|
||||
bool _useApiV2;
|
||||
|
||||
QString _id;
|
||||
QString _deviceId;
|
||||
QString _product;
|
||||
QString _model;
|
||||
QString _name;
|
||||
QString _archeType;
|
||||
QString _gamutType;
|
||||
|
||||
int _maxSegments;
|
||||
|
||||
bool _on;
|
||||
int _transitionTime;
|
||||
CiColor _color;
|
||||
bool _hasColor;
|
||||
/// darkes blue color in hue lamp GAMUT = black
|
||||
CiColor _colorBlack;
|
||||
/// The model id of the hue lamp which is used to determine the color space.
|
||||
QString _modelId;
|
||||
QString _lightname;
|
||||
CiColorTriangle _colorSpace;
|
||||
|
||||
/// The json string of the original state.
|
||||
QJsonObject _originalStateJSON;
|
||||
|
||||
QString _originalState;
|
||||
QJsonObject _originalState;
|
||||
CiColor _originalColor;
|
||||
qint64 _lastSendColorTime;
|
||||
qint64 _lastBlackTime;
|
||||
@ -242,23 +236,40 @@ public:
|
||||
QJsonDocument get(const QString& route);
|
||||
|
||||
///
|
||||
/// @brief Perform a REST-API POST
|
||||
/// @brief Perform a REST-API GET
|
||||
///
|
||||
/// @param route the route of the POST request.
|
||||
/// @param content the content of the POST request.
|
||||
/// @param routeElements the route's elements of the GET request.
|
||||
///
|
||||
QJsonDocument put(const QString& route, const QString& content, bool supressError = false);
|
||||
/// @return the content of the GET request.
|
||||
///
|
||||
QJsonDocument get(const QStringList& routeElements);
|
||||
|
||||
QJsonDocument getLightState( int lightId);
|
||||
void setLightState( int lightId = 0, const QString &state = "");
|
||||
///
|
||||
/// @brief Perform a REST-API PUT
|
||||
///
|
||||
/// @param routeElements the route's elements of the PUT request.
|
||||
/// @param content the content of the PUT request.
|
||||
/// @param supressError Treat an error as a warning
|
||||
///
|
||||
/// @return the content of the PUT request.
|
||||
///
|
||||
QJsonDocument put(const QStringList& routeElements, const QJsonObject& content, bool supressError = false);
|
||||
|
||||
QMap<int,QJsonObject> getLightMap() const;
|
||||
QJsonDocument retrieveBridgeDetails();
|
||||
QJsonObject getDeviceDetails(const QString& deviceId);
|
||||
QJsonObject getEntertainmentSrvDetails(const QString& deviceId);
|
||||
|
||||
QMap<int,QJsonObject> getGroupMap() const;
|
||||
QJsonObject getLightDetails(const QString& lightId);
|
||||
QJsonDocument setLightState(const QString& lightId, const QJsonObject& state);
|
||||
|
||||
QString getGroupName(int groupId = 0) const;
|
||||
QMap<QString,QJsonObject> getDevicesMap() const;
|
||||
QMap<QString,QJsonObject> getLightMap() const;
|
||||
QMap<QString,QJsonObject> getGroupMap() const;
|
||||
QMap<QString,QJsonObject> getEntertainmentMap() const;
|
||||
|
||||
QJsonArray getGroupLights(int groupId = 0) const;
|
||||
QString getGroupName(const QString& groupId) const;
|
||||
QStringList getGroupLights(const QString& groupId) const;
|
||||
int getGroupChannelsCount(const QString& groupId) const;
|
||||
|
||||
protected:
|
||||
|
||||
@ -289,7 +300,7 @@ protected:
|
||||
///
|
||||
/// @param[in] response from Hue-Bridge in JSON-format
|
||||
/// @param[in] suppressError Treat an error as a warning
|
||||
///
|
||||
///
|
||||
/// return True, Hue Bridge reports error
|
||||
///
|
||||
bool checkApiError(const QJsonDocument& response, bool supressError = false);
|
||||
@ -338,23 +349,41 @@ protected:
|
||||
///
|
||||
QJsonObject addAuthorization(const QJsonObject& params) override;
|
||||
|
||||
bool isApiEntertainmentReady(const QString& apiVersion);
|
||||
bool isAPIv2Ready (int swVersion);
|
||||
|
||||
int getFirmwareVerion() { return _deviceFirmwareVersion; }
|
||||
void setBridgeDetails( const QJsonDocument &doc, bool isLogging = false );
|
||||
|
||||
void setBaseApiEnvironment(bool apiV2 = true, const QString& path = "");
|
||||
|
||||
QJsonDocument getGroupDetails( const QString& groupId );
|
||||
QJsonDocument setGroupState( const QString& groupId, bool state);
|
||||
|
||||
bool isStreamOwner(const QString &streamOwner) const;
|
||||
|
||||
bool initDevicesMap();
|
||||
bool initLightsMap();
|
||||
bool initGroupsMap();
|
||||
bool initEntertainmentSrvsMap();
|
||||
|
||||
void log(const char* msg, const char* type, ...) const;
|
||||
|
||||
bool configureSsl();
|
||||
const int * getCiphersuites() const override;
|
||||
|
||||
///REST-API wrapper
|
||||
ProviderRestApi* _restApi;
|
||||
int _apiPort;
|
||||
/// User name for the API ("newdeveloper")
|
||||
QString _authToken;
|
||||
QString _applicationID;
|
||||
|
||||
bool _useHueEntertainmentAPI;
|
||||
bool _useEntertainmentAPI;
|
||||
bool _useApiV2;
|
||||
bool _isAPIv2Ready;
|
||||
|
||||
QJsonDocument getGroupState( int groupId );
|
||||
QJsonDocument setGroupState( int groupId, bool state);
|
||||
|
||||
bool isStreamOwner(const QString &streamOwner) const;
|
||||
bool initMaps();
|
||||
|
||||
void log(const char* msg, const char* type, ...) const;
|
||||
|
||||
const int * getCiphersuites() const override;
|
||||
bool _isDiyHue;
|
||||
|
||||
private:
|
||||
|
||||
@ -364,16 +393,25 @@ private:
|
||||
///
|
||||
/// @return A JSON structure holding a list of devices found
|
||||
///
|
||||
QJsonArray discover();
|
||||
QJsonArray discoverSsdp();
|
||||
|
||||
QJsonDocument getAllBridgeInfos();
|
||||
void setBridgeConfig( const QJsonDocument &doc );
|
||||
QJsonDocument retrieveDeviceDetails(const QString& deviceId = "");
|
||||
QJsonDocument retrieveLightDetails(const QString& lightId = "");
|
||||
QJsonDocument retrieveGroupDetails(const QString& groupId = "");
|
||||
QJsonDocument retrieveEntertainmentSrvDetails(const QString& deviceId = "");
|
||||
|
||||
bool retrieveApplicationId();
|
||||
|
||||
void setDevicesMap( const QJsonDocument &doc );
|
||||
void setLightsMap( const QJsonDocument &doc );
|
||||
void setGroupMap( const QJsonDocument &doc );
|
||||
void setEntertainmentSrvMap( const QJsonDocument &doc );
|
||||
|
||||
//Philips Hue Bridge details
|
||||
QString _deviceName;
|
||||
QString _deviceBridgeId;
|
||||
QString _deviceModel;
|
||||
QString _deviceFirmwareVersion;
|
||||
int _deviceFirmwareVersion;
|
||||
QString _deviceAPIVersion;
|
||||
|
||||
uint _api_major;
|
||||
@ -382,8 +420,12 @@ private:
|
||||
|
||||
bool _isHueEntertainmentReady;
|
||||
|
||||
QMap<int,QJsonObject> _lightsMap;
|
||||
QMap<int,QJsonObject> _groupsMap;
|
||||
QMap<QString,QJsonObject> _devicesMap;
|
||||
QMap<QString,QJsonObject> _lightsMap;
|
||||
QMap<QString,QJsonObject> _groupsMap;
|
||||
QMap<QString,QJsonObject> _entertainmentMap;
|
||||
|
||||
int _lightsCount;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -440,7 +482,7 @@ public:
|
||||
///
|
||||
/// @return Number of device's LEDs
|
||||
///
|
||||
unsigned int getLightsCount() const { return _lightsCount; }
|
||||
int getLightsCount() const { return _lightsCount; }
|
||||
|
||||
void setOnOffState(PhilipsHueLight& light, bool on, bool force = false);
|
||||
void setTransitionTime(PhilipsHueLight& light);
|
||||
@ -547,18 +589,18 @@ private:
|
||||
|
||||
bool setLights();
|
||||
|
||||
/// creates new PhilipsHueLight(s) based on user lightid with bridge feedback
|
||||
/// creates new PhilipsHueLight(s) based on user lightId with bridge feedback
|
||||
///
|
||||
/// @param map Map of lightid/value pairs of bridge
|
||||
/// @param map Map of lightId/value pairs of bridge
|
||||
///
|
||||
bool updateLights(const QMap<int, QJsonObject> &map);
|
||||
bool updateLights(const QMap<QString, QJsonObject> &map);
|
||||
|
||||
///
|
||||
/// @brief Set the number of LEDs supported by the device.
|
||||
///
|
||||
/// @rparam[in] Number of device's LEDs
|
||||
//
|
||||
void setLightsCount( unsigned int lightsCount);
|
||||
void setLightsCount(int lightsCount);
|
||||
|
||||
bool openStream();
|
||||
bool getStreamGroupState();
|
||||
@ -566,10 +608,8 @@ private:
|
||||
bool startStream();
|
||||
bool stopStream();
|
||||
|
||||
void writeStream(bool flush = false);
|
||||
int writeSingleLights(const std::vector<ColorRgb>& ledValues);
|
||||
|
||||
QByteArray prepareStreamData() const;
|
||||
int writeStreamData(const std::vector<ColorRgb>& ledValues, bool flush = false);
|
||||
|
||||
///
|
||||
bool _switchOffOnBlack;
|
||||
@ -582,12 +622,15 @@ private:
|
||||
bool _isInitLeds;
|
||||
|
||||
/// Array of the light ids.
|
||||
std::vector<int> _lightIds;
|
||||
QStringList _lightIds;
|
||||
/// Array to save the lamps.
|
||||
std::vector<PhilipsHueLight> _lights;
|
||||
|
||||
int _lightsCount;
|
||||
int _groupId;
|
||||
int _channelsCount;
|
||||
QString _groupId;
|
||||
QString _groupName;
|
||||
QString _streamOwner;
|
||||
|
||||
int _blackLightsTimeout;
|
||||
double _blackLevel;
|
||||
@ -595,15 +638,7 @@ private:
|
||||
int _onBlackTimeToPowerOn;
|
||||
bool _candyGamma;
|
||||
|
||||
// TODO: Check what is the correct class
|
||||
uint32_t _handshake_timeout_min;
|
||||
uint32_t _handshake_timeout_max;
|
||||
bool _stopConnection;
|
||||
|
||||
QString _groupName;
|
||||
QString _streamOwner;
|
||||
|
||||
qint64 _lastConfirm;
|
||||
int _lastId;
|
||||
QString _lastId;
|
||||
bool _groupStreamState;
|
||||
};
|
||||
|
@ -2,11 +2,18 @@
|
||||
#include "ProviderRestApi.h"
|
||||
|
||||
// Qt includes
|
||||
#include <QObject>
|
||||
#include <QEventLoop>
|
||||
#include <QNetworkReply>
|
||||
#include <QByteArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
#include <QList>
|
||||
#include <QHash>
|
||||
#include <QFile>
|
||||
|
||||
#include <QSslSocket>
|
||||
|
||||
//std includes
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
@ -30,12 +37,12 @@ ProviderRestApi::ProviderRestApi(const QString& scheme, const QString& host, int
|
||||
: _log(Logger::getInstance("LEDDEVICE"))
|
||||
, _networkManager(nullptr)
|
||||
, _requestTimeout(DEFAULT_REST_TIMEOUT)
|
||||
,_isSeflSignedCertificateAccpeted(false)
|
||||
{
|
||||
_networkManager = new QNetworkAccessManager();
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||
_networkManager->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
#endif
|
||||
|
||||
_apiUrl.setScheme(scheme);
|
||||
_apiUrl.setHost(host);
|
||||
_apiUrl.setPort(port);
|
||||
@ -46,7 +53,7 @@ ProviderRestApi::ProviderRestApi(const QString& scheme, const QString& host, int
|
||||
: ProviderRestApi(scheme, host, port, "") {}
|
||||
|
||||
ProviderRestApi::ProviderRestApi(const QString& host, int port, const QString& basePath)
|
||||
: ProviderRestApi("http", host, port, basePath) {}
|
||||
: ProviderRestApi((port == 443) ? "https" : "http", host, port, basePath) {}
|
||||
|
||||
ProviderRestApi::ProviderRestApi(const QString& host, int port)
|
||||
: ProviderRestApi(host, port, "") {}
|
||||
@ -59,18 +66,33 @@ ProviderRestApi::~ProviderRestApi()
|
||||
delete _networkManager;
|
||||
}
|
||||
|
||||
void ProviderRestApi::setScheme(const QString& scheme)
|
||||
{
|
||||
_apiUrl.setScheme(scheme);
|
||||
}
|
||||
|
||||
void ProviderRestApi::setUrl(const QUrl& url)
|
||||
{
|
||||
_apiUrl = url;
|
||||
_basePath = url.path();
|
||||
}
|
||||
|
||||
void ProviderRestApi::setBasePath(const QStringList& pathElements)
|
||||
{
|
||||
setBasePath(pathElements.join(ONE_SLASH));
|
||||
}
|
||||
|
||||
void ProviderRestApi::setBasePath(const QString& basePath)
|
||||
{
|
||||
_basePath.clear();
|
||||
appendPath(_basePath, basePath);
|
||||
}
|
||||
|
||||
void ProviderRestApi::clearBasePath()
|
||||
{
|
||||
_basePath.clear();
|
||||
}
|
||||
|
||||
void ProviderRestApi::setPath(const QStringList& pathElements)
|
||||
{
|
||||
_path.clear();
|
||||
@ -83,6 +105,11 @@ void ProviderRestApi::setPath(const QString& path)
|
||||
appendPath(_path, path);
|
||||
}
|
||||
|
||||
void ProviderRestApi::clearPath()
|
||||
{
|
||||
_path.clear();
|
||||
}
|
||||
|
||||
void ProviderRestApi::appendPath(const QString& path)
|
||||
{
|
||||
appendPath(_path, path);
|
||||
@ -204,6 +231,7 @@ httpResponse ProviderRestApi::executeOperation(QNetworkAccessManager::Operation
|
||||
QDateTime start = QDateTime::currentDateTime();
|
||||
QString opCode;
|
||||
QNetworkReply* reply;
|
||||
|
||||
switch (operation) {
|
||||
case QNetworkAccessManager::GetOperation:
|
||||
opCode = "GET";
|
||||
@ -255,11 +283,11 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
||||
HttpStatusCode httpStatusCode = static_cast<HttpStatusCode>(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
|
||||
response.setHttpStatusCode(httpStatusCode);
|
||||
response.setNetworkReplyError(reply->error());
|
||||
response.setHeaders(reply->rawHeaderPairs());
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError)
|
||||
{
|
||||
QByteArray replyData = reply->readAll();
|
||||
|
||||
if (!replyData.isEmpty())
|
||||
{
|
||||
QJsonParseError error;
|
||||
@ -268,11 +296,13 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
||||
if (error.error != QJsonParseError::NoError)
|
||||
{
|
||||
//Received not valid JSON response
|
||||
//std::cout << "Response: [" << replyData.toStdString() << "]" << std::endl;
|
||||
response.setError(true);
|
||||
response.setErrorReason(error.errorString());
|
||||
}
|
||||
else
|
||||
{
|
||||
//std::cout << "Response: [" << QString(jsonDoc.toJson(QJsonDocument::Compact)).toStdString() << "]" << std::endl;
|
||||
response.setBody(jsonDoc);
|
||||
}
|
||||
}
|
||||
@ -344,3 +374,121 @@ void ProviderRestApi::setHeader(const QByteArray &headerName, const QByteArray &
|
||||
{
|
||||
_networkRequestHeaders.setRawHeader(headerName, headerValue);
|
||||
}
|
||||
|
||||
void httpResponse::setHeaders(const QList<QNetworkReply::RawHeaderPair>& pairs)
|
||||
{
|
||||
_responseHeaders.clear();
|
||||
for (const auto &item: pairs)
|
||||
{
|
||||
_responseHeaders[item.first] = item.second;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray httpResponse::getHeader(const QByteArray header) const
|
||||
{
|
||||
return _responseHeaders.value(header);
|
||||
}
|
||||
|
||||
bool ProviderRestApi::setCaCertificate(const QString& caFileName)
|
||||
{
|
||||
bool rc {false};
|
||||
/// Add our own CA to the default SSL configuration
|
||||
QSslConfiguration configuration = QSslConfiguration::defaultConfiguration();
|
||||
|
||||
QFile caFile (caFileName);
|
||||
if (!caFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
Error(_log,"Unable to open CA-Certificate file: %s", QSTRING_CSTR(caFileName));
|
||||
return false;
|
||||
}
|
||||
|
||||
QSslCertificate cert (&caFile);
|
||||
caFile.close();
|
||||
|
||||
QList<QSslCertificate> allowedCAs;
|
||||
allowedCAs << cert;
|
||||
configuration.setCaCertificates(allowedCAs);
|
||||
|
||||
QSslConfiguration::setDefaultConfiguration(configuration);
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
if (QSslSocket::supportsSsl())
|
||||
{
|
||||
QObject::connect( _networkManager, &QNetworkAccessManager::sslErrors, this, &ProviderRestApi::onSslErrors, Qt::UniqueConnection );
|
||||
_networkManager->connectToHostEncrypted(_apiUrl.host(), _apiUrl.port(), configuration);
|
||||
rc = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void ProviderRestApi::acceptSelfSignedCertificates(bool isAccepted)
|
||||
{
|
||||
_isSeflSignedCertificateAccpeted = isAccepted;
|
||||
}
|
||||
|
||||
void ProviderRestApi::setAlternateServerIdentity(const QString& serverIdentity)
|
||||
{
|
||||
_serverIdentity = serverIdentity;
|
||||
}
|
||||
|
||||
QString ProviderRestApi::getAlternateServerIdentity() const
|
||||
{
|
||||
return _serverIdentity;
|
||||
}
|
||||
|
||||
bool ProviderRestApi::checkServerIdentity(const QSslConfiguration& sslConfig) const
|
||||
{
|
||||
bool isServerIdentified {false};
|
||||
|
||||
// Perform common name validation
|
||||
QSslCertificate serverCertificate = sslConfig.peerCertificate();
|
||||
QStringList commonName = serverCertificate.subjectInfo(QSslCertificate::CommonName);
|
||||
if ( commonName.contains(getAlternateServerIdentity(), Qt::CaseInsensitive) )
|
||||
{
|
||||
isServerIdentified = true;
|
||||
}
|
||||
|
||||
return isServerIdentified;
|
||||
}
|
||||
|
||||
void ProviderRestApi::onSslErrors(QNetworkReply* reply, const QList<QSslError>& errors)
|
||||
{
|
||||
int ignoredErrorCount {0};
|
||||
for (const QSslError &error : errors)
|
||||
{
|
||||
bool ignoreSslError{false};
|
||||
|
||||
switch (error.error()) {
|
||||
case QSslError::HostNameMismatch :
|
||||
if (checkServerIdentity(reply->sslConfiguration()) )
|
||||
{
|
||||
ignoreSslError = true;
|
||||
}
|
||||
break;
|
||||
case QSslError::SelfSignedCertificate :
|
||||
if (_isSeflSignedCertificateAccpeted)
|
||||
{
|
||||
ignoreSslError = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ignoreSslError)
|
||||
{
|
||||
++ignoredErrorCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug (_log,"SSL Error occured: [%d] %s ",error.error(), QSTRING_CSTR(error.errorString()));
|
||||
}
|
||||
}
|
||||
|
||||
if (ignoredErrorCount == errors.size())
|
||||
{
|
||||
reply->ignoreSslErrors();
|
||||
}
|
||||
}
|
||||
|
@ -10,12 +10,13 @@
|
||||
#include <QUrlQuery>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <QFile>
|
||||
#include <QBasicTimer>
|
||||
#include <QTimerEvent>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
constexpr std::chrono::milliseconds DEFAULT_REST_TIMEOUT{ 1000 };
|
||||
constexpr std::chrono::milliseconds DEFAULT_REST_TIMEOUT{ 2000 };
|
||||
|
||||
//Set QNetworkReply timeout without external timer
|
||||
//https://stackoverflow.com/questions/37444539/how-to-set-qnetworkreply-timeout-without-external-timer
|
||||
@ -28,7 +29,7 @@ public:
|
||||
enum HandleMethod { Abort, Close };
|
||||
|
||||
ReplyTimeout(QNetworkReply* reply, const int timeout, HandleMethod method = Abort) :
|
||||
QObject(reply), m_method(method), m_timedout(false)
|
||||
QObject(reply), m_method(method), m_timedout(false)
|
||||
{
|
||||
Q_ASSERT(reply);
|
||||
if (reply && reply->isRunning()) {
|
||||
@ -87,6 +88,10 @@ public:
|
||||
QJsonDocument getBody() const { return _responseBody; }
|
||||
void setBody(const QJsonDocument& body) { _responseBody = body; }
|
||||
|
||||
|
||||
QByteArray getHeader(const QByteArray header) const;
|
||||
void setHeaders(const QList<QNetworkReply::RawHeaderPair>& pairs);
|
||||
|
||||
QString getErrorReason() const { return _errorReason; }
|
||||
void setErrorReason(const QString& errorReason) { _errorReason = errorReason; }
|
||||
|
||||
@ -99,6 +104,8 @@ public:
|
||||
private:
|
||||
|
||||
QJsonDocument _responseBody {};
|
||||
QHash<QByteArray, QByteArray> _responseHeaders;
|
||||
|
||||
bool _hasError = false;
|
||||
QString _errorReason;
|
||||
|
||||
@ -131,6 +138,7 @@ class ProviderRestApi : public QObject
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
/// @brief Constructor of the REST-API wrapper
|
||||
///
|
||||
ProviderRestApi();
|
||||
@ -176,6 +184,20 @@ public:
|
||||
///
|
||||
virtual ~ProviderRestApi() override;
|
||||
|
||||
///
|
||||
/// @brief Set the API's scheme
|
||||
///
|
||||
/// @param[in] scheme
|
||||
///
|
||||
void setScheme(const QString& scheme);
|
||||
|
||||
///
|
||||
/// @brief Get the API's scheme
|
||||
///
|
||||
/// return schme
|
||||
///
|
||||
QString getScheme() { return _apiUrl.scheme(); }
|
||||
|
||||
///
|
||||
/// @brief Set an API's host
|
||||
///
|
||||
@ -190,6 +212,13 @@ public:
|
||||
///
|
||||
void setPort(const int port) { _apiUrl.setPort(port); }
|
||||
|
||||
///
|
||||
/// @brief Get the API's port
|
||||
///
|
||||
/// return port
|
||||
///
|
||||
int getPort() { return _apiUrl.port(); }
|
||||
|
||||
///
|
||||
/// @brief Set an API's url
|
||||
///
|
||||
@ -204,6 +233,13 @@ public:
|
||||
///
|
||||
QUrl getUrl() const;
|
||||
|
||||
///
|
||||
/// @brief Set an API's base path (the stable path element before addressing resources)
|
||||
///
|
||||
/// @param[in] pathElements to form a path, e.g. (clip,v2,resource) results in "/clip/v2/resource"
|
||||
///
|
||||
void setBasePath(const QStringList& pathElements);
|
||||
|
||||
///
|
||||
/// @brief Set an API's base path (the stable path element before addressing resources)
|
||||
///
|
||||
@ -211,6 +247,11 @@ public:
|
||||
///
|
||||
void setBasePath(const QString& basePath);
|
||||
|
||||
///
|
||||
/// @brief Clear an API's base path (the stable path element before addressing resources)
|
||||
///
|
||||
void clearBasePath();
|
||||
|
||||
///
|
||||
/// @brief Set an API's path to address resources
|
||||
///
|
||||
@ -218,12 +259,18 @@ public:
|
||||
///
|
||||
void setPath(const QString& path);
|
||||
|
||||
///
|
||||
/// @brief Set an API's path to address resources
|
||||
///
|
||||
/// @param[in] pathElements to form a path, e.g. (lights,1,state) results in "/lights/1/state/"
|
||||
///
|
||||
void setPath(const QStringList& pathElements);
|
||||
|
||||
///
|
||||
/// @brief Clear an API's path
|
||||
///
|
||||
void clearPath();
|
||||
|
||||
///
|
||||
/// @brief Append an API's path element to path set before
|
||||
///
|
||||
@ -252,6 +299,10 @@ public:
|
||||
///
|
||||
void setQuery(const QUrlQuery& query);
|
||||
|
||||
|
||||
QString getBasePath() {return _basePath;}
|
||||
QString getPath() {return _path;}
|
||||
|
||||
///
|
||||
/// @brief Execute GET request
|
||||
///
|
||||
@ -359,6 +410,14 @@ public:
|
||||
/// @param[in] timeout in milliseconds.
|
||||
void setTransferTimeout(std::chrono::milliseconds timeout = DEFAULT_REST_TIMEOUT) { _requestTimeout = timeout; }
|
||||
|
||||
|
||||
bool setCaCertificate(const QString& caFileName);
|
||||
|
||||
void acceptSelfSignedCertificates(bool accept);
|
||||
|
||||
void setAlternateServerIdentity(const QString& serverIdentity);
|
||||
QString getAlternateServerIdentity() const;
|
||||
|
||||
///
|
||||
/// @brief Set the common logger for LED-devices.
|
||||
///
|
||||
@ -366,6 +425,10 @@ public:
|
||||
///
|
||||
void setLogger(Logger* log) { _log = log; }
|
||||
|
||||
protected slots:
|
||||
/// Handle the SSLErrors
|
||||
void onSslErrors(QNetworkReply* reply, const QList<QSslError>& errors);
|
||||
|
||||
private:
|
||||
|
||||
///
|
||||
@ -379,9 +442,11 @@ private:
|
||||
|
||||
httpResponse executeOperation(QNetworkAccessManager::Operation op, const QUrl& url, const QByteArray& body = {});
|
||||
|
||||
bool checkServerIdentity(const QSslConfiguration& sslConfig) const;
|
||||
|
||||
Logger* _log;
|
||||
|
||||
// QNetworkAccessManager object for sending REST-requests.
|
||||
/// QNetworkAccessManager object for sending REST-requests.
|
||||
QNetworkAccessManager* _networkManager;
|
||||
std::chrono::milliseconds _requestTimeout;
|
||||
|
||||
@ -394,6 +459,9 @@ private:
|
||||
QUrlQuery _query;
|
||||
|
||||
QNetworkRequest _networkRequestHeaders;
|
||||
|
||||
QString _serverIdentity;
|
||||
bool _isSeflSignedCertificateAccpeted;
|
||||
};
|
||||
|
||||
#endif // PROVIDERRESTKAPI_H
|
||||
|
@ -150,6 +150,11 @@ const int *ProviderUdpSSL::getCiphersuites() const
|
||||
return mbedtls_ssl_list_ciphersuites();
|
||||
}
|
||||
|
||||
void ProviderUdpSSL::setPSKidentity(const QString& pskIdentity)
|
||||
{
|
||||
_psk_identity = pskIdentity;
|
||||
}
|
||||
|
||||
bool ProviderUdpSSL::initNetwork()
|
||||
{
|
||||
if ((!_isDeviceReady || _streamPaused) && _streamReady)
|
||||
@ -334,6 +339,11 @@ void ProviderUdpSSL::freeSSLConnection()
|
||||
}
|
||||
}
|
||||
|
||||
void ProviderUdpSSL::writeBytes(QByteArray data, bool flush)
|
||||
{
|
||||
writeBytes(static_cast<uint>(data.size()), reinterpret_cast<unsigned char*>(data.data()), flush);
|
||||
}
|
||||
|
||||
void ProviderUdpSSL::writeBytes(unsigned int size, const uint8_t* data, bool flush)
|
||||
{
|
||||
if (!_streamReady || _streamPaused)
|
||||
|
@ -100,6 +100,14 @@ protected:
|
||||
///
|
||||
void stopConnection();
|
||||
|
||||
///
|
||||
/// Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the
|
||||
/// values are latched.
|
||||
///
|
||||
/// @param[in] data The data
|
||||
///
|
||||
void writeBytes(QByteArray data, bool flush = false);
|
||||
|
||||
///
|
||||
/// Writes the given bytes/bits to the UDP-device and sleeps the latch time to ensure that the
|
||||
/// values are latched.
|
||||
@ -116,6 +124,8 @@ protected:
|
||||
///
|
||||
virtual const int * getCiphersuites() const;
|
||||
|
||||
void setPSKidentity(const QString& pskIdentity);
|
||||
|
||||
private:
|
||||
|
||||
bool initConnection();
|
||||
|
@ -35,26 +35,45 @@
|
||||
},
|
||||
"propertyOrder": 4
|
||||
},
|
||||
"useAPIv2": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_useAPIv2_title",
|
||||
"default": false,
|
||||
"options": {
|
||||
"hidden": true
|
||||
},
|
||||
"access": "expert",
|
||||
"propertyOrder": 5
|
||||
},
|
||||
"useEntertainmentAPI": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_useEntertainmentAPI_title",
|
||||
"default": true,
|
||||
"propertyOrder": 5
|
||||
"options": {
|
||||
"hidden": true
|
||||
},
|
||||
"propertyOrder": 6
|
||||
},
|
||||
"switchOffOnBlack": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_switchOffOnBlack_title",
|
||||
"default": false,
|
||||
"propertyOrder": 6
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 7
|
||||
},
|
||||
"restoreOriginalState": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_restoreOriginalState_title",
|
||||
"default": false,
|
||||
"propertyOrder": 7
|
||||
"propertyOrder": 8
|
||||
},
|
||||
"blackLevel": {
|
||||
"type": "number",
|
||||
@ -64,7 +83,12 @@
|
||||
"step": 0.01,
|
||||
"minimum": 0.001,
|
||||
"maximum": 1.0,
|
||||
"propertyOrder": 8
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 9
|
||||
},
|
||||
"onBlackTimeToPowerOff": {
|
||||
"type": "integer",
|
||||
@ -76,7 +100,12 @@
|
||||
"maximum": 100000,
|
||||
"default": 600,
|
||||
"required": true,
|
||||
"propertyOrder": 9
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 10
|
||||
},
|
||||
"onBlackTimeToPowerOn": {
|
||||
"type": "integer",
|
||||
@ -88,14 +117,24 @@
|
||||
"maximum": 100000,
|
||||
"default": 300,
|
||||
"required": true,
|
||||
"propertyOrder": 9
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 11
|
||||
},
|
||||
"candyGamma": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_candyGamma_title",
|
||||
"default": true,
|
||||
"propertyOrder": 10
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 12
|
||||
},
|
||||
"lightIds": {
|
||||
"type": "array",
|
||||
@ -112,20 +151,23 @@
|
||||
"useEntertainmentAPI": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 11
|
||||
"propertyOrder": 13
|
||||
},
|
||||
"groupId": {
|
||||
"type": "number",
|
||||
"format": "stepper",
|
||||
"step": 1,
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_groupId_title",
|
||||
"default": 0,
|
||||
"default": "",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 12
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 14
|
||||
},
|
||||
"brightnessFactor": {
|
||||
"type": "number",
|
||||
@ -136,7 +178,12 @@
|
||||
"minimum": 0.5,
|
||||
"maximum": 10.0,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 13
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 15
|
||||
},
|
||||
"handshakeTimeoutMin": {
|
||||
"type": "number",
|
||||
@ -154,7 +201,7 @@
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 14
|
||||
"propertyOrder": 16
|
||||
},
|
||||
"handshakeTimeoutMax": {
|
||||
"type": "number",
|
||||
@ -172,7 +219,7 @@
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 15
|
||||
"propertyOrder": 17
|
||||
},
|
||||
"verbose": {
|
||||
"type": "boolean",
|
||||
@ -180,7 +227,7 @@
|
||||
"title": "edt_dev_spec_verbose_title",
|
||||
"default": false,
|
||||
"access": "expert",
|
||||
"propertyOrder": 16
|
||||
"propertyOrder": 18
|
||||
},
|
||||
"transitiontime": {
|
||||
"type": "number",
|
||||
@ -195,24 +242,29 @@
|
||||
"useEntertainmentAPI": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 17
|
||||
"propertyOrder": 19
|
||||
},
|
||||
"blackLightsTimeout": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_blackLightsTimeout_title",
|
||||
"default": 5000,
|
||||
"options": {
|
||||
"hidden": true
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 18
|
||||
"propertyOrder": 20
|
||||
},
|
||||
"brightnessThreshold": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_brightnessThreshold_title",
|
||||
"default": 0.0001,
|
||||
"options": {
|
||||
"hidden": true
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 19
|
||||
"propertyOrder": 21
|
||||
},
|
||||
"brightnessMin": {
|
||||
"type": "number",
|
||||
@ -223,9 +275,11 @@
|
||||
"maximum": 1.0,
|
||||
"access": "advanced",
|
||||
"options": {
|
||||
"hidden": true
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 20
|
||||
"propertyOrder": 22
|
||||
},
|
||||
"brightnessMax": {
|
||||
"type": "number",
|
||||
@ -236,9 +290,11 @@
|
||||
"maximum": 1.0,
|
||||
"access": "advanced",
|
||||
"options": {
|
||||
"hidden": true
|
||||
"dependencies": {
|
||||
"useAPIv2": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 21
|
||||
"propertyOrder": 23
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
|
14
resources/ssl/philips_hue_ca.pem
Normal file
14
resources/ssl/philips_hue_ca.pem
Normal file
@ -0,0 +1,14 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICMjCCAdigAwIBAgIUO7FSLbaxikuXAljzVaurLXWmFw4wCgYIKoZIzj0EAwIw
|
||||
OTELMAkGA1UEBhMCTkwxFDASBgNVBAoMC1BoaWxpcHMgSHVlMRQwEgYDVQQDDAty
|
||||
b290LWJyaWRnZTAiGA8yMDE3MDEwMTAwMDAwMFoYDzIwMzgwMTE5MDMxNDA3WjA5
|
||||
MQswCQYDVQQGEwJOTDEUMBIGA1UECgwLUGhpbGlwcyBIdWUxFDASBgNVBAMMC3Jv
|
||||
b3QtYnJpZGdlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjNw2tx2AplOf9x86
|
||||
aTdvEcL1FU65QDxziKvBpW9XXSIcibAeQiKxegpq8Exbr9v6LBnYbna2VcaK0G22
|
||||
jOKkTqOBuTCBtjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNV
|
||||
HQ4EFgQUZ2ONTFrDT6o8ItRnKfqWKnHFGmQwdAYDVR0jBG0wa4AUZ2ONTFrDT6o8
|
||||
ItRnKfqWKnHFGmShPaQ7MDkxCzAJBgNVBAYTAk5MMRQwEgYDVQQKDAtQaGlsaXBz
|
||||
IEh1ZTEUMBIGA1UEAwwLcm9vdC1icmlkZ2WCFDuxUi22sYpLlwJY81Wrqy11phcO
|
||||
MAoGCCqGSM49BAMCA0gAMEUCIEBYYEOsa07TH7E5MJnGw557lVkORgit2Rm1h3B2
|
||||
sFgDAiEA1Fj/C3AN5psFMjo0//mrQebo0eKd3aWRx+pQY08mk48=
|
||||
-----END CERTIFICATE-----
|
Loading…
Reference in New Issue
Block a user