mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Fix Nanoleaf, add user auth token wizard
This commit is contained in:
parent
d7666987e9
commit
4a5f89845c
@ -566,8 +566,8 @@
|
||||
"edt_conf_webc_port_title": "HTTP Port",
|
||||
"edt_conf_webc_sslport_expl": "Port for the WebServer, RPC and WebSocket HTTPS connections",
|
||||
"edt_conf_webc_sslport_title": "HTTPS Port",
|
||||
"edt_dev_auth_key_title": "Authentication Token",
|
||||
"edt_dev_auth_key_title_info": "Authentication Token required to acccess the device",
|
||||
"edt_dev_auth_key_title": "Authorization Token",
|
||||
"edt_dev_auth_key_title_info": "Authorization Token required to acccess the device",
|
||||
"edt_dev_enum_sub_min_cool_adjust": "Subtract cool white",
|
||||
"edt_dev_enum_sub_min_warm_adjust": "Subtract warm white",
|
||||
"edt_dev_enum_subtract_minimum": "Subtract minimum",
|
||||
@ -1123,6 +1123,11 @@
|
||||
"wiz_identify_light": "Identify $1",
|
||||
"wiz_ids_disabled": "Deactivated",
|
||||
"wiz_ids_entire": "Whole picture",
|
||||
"wiz_nanoleaf_failure_auth_token": "Please press the Nanoleaf Power On/Off button within 30 seconds",
|
||||
"wiz_nanoleaf_failure_auth_token_t": "User authorization token generating timeout",
|
||||
"wiz_nanoleaf_press_onoff_button": "Please press the Power On/Off button on your Nanoleaf device for 5-7 seconds",
|
||||
"wiz_nanoleaf_user_auth_intro": "The wizard supports you in generating a user authorization token required to allowing Hyperion to access the device.",
|
||||
"wiz_nanoleaf_user_auth_title": "Authorization Token Generating Wizard",
|
||||
"wiz_noLights": "No $1 found! Please get the lights connected to the network or configure them manually.",
|
||||
"wiz_pos": "Position/State",
|
||||
"wiz_rgb_expl": "The color dot switches every x seconds the color (red, green), at the same time your LEDs switch the color too. Answer the questions at the bottom to check/correct your byte order.",
|
||||
|
@ -1060,6 +1060,13 @@ $(document).ready(function () {
|
||||
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 };
|
||||
@ -1341,6 +1348,13 @@ $(document).ready(function () {
|
||||
|
||||
if (host === "") {
|
||||
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(1);
|
||||
switch (ledType) {
|
||||
|
||||
case "nanoleaf":
|
||||
$('#btn_wiz_holder').hide();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
else {
|
||||
let params = {};
|
||||
@ -1352,6 +1366,8 @@ $(document).ready(function () {
|
||||
break;
|
||||
|
||||
case "nanoleaf":
|
||||
$('#btn_wiz_holder').show();
|
||||
|
||||
var token = conf_editor.getEditor("root.specificOptions.token").getValue();
|
||||
if (token === "") {
|
||||
return;
|
||||
@ -2165,15 +2181,8 @@ function updateElements(ledType, key) {
|
||||
case "nanoleaf":
|
||||
var ledProperties = devicesProperties[ledType][key];
|
||||
|
||||
if (ledProperties && ledProperties.panelLayout.layout) {
|
||||
//Identify non-LED type panels, e.g. Rhythm (1) and Shapes Controller (12)
|
||||
var nonLedNum = 0;
|
||||
for (const panel of ledProperties.panelLayout.layout.positionData) {
|
||||
if (panel.shapeType === 1 || panel.shapeType === 12) {
|
||||
nonLedNum++;
|
||||
}
|
||||
}
|
||||
hardwareLedCount = ledProperties.panelLayout.layout.numPanels - nonLedNum;
|
||||
if (ledProperties) {
|
||||
hardwareLedCount = ledProperties.ledCount;
|
||||
}
|
||||
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
|
||||
|
||||
|
@ -2187,3 +2187,90 @@ async function identify_atmoorb_device(orbId) {
|
||||
}
|
||||
}
|
||||
|
||||
//****************************
|
||||
// Nanoleaf Token Wizard
|
||||
//****************************
|
||||
var lights = null;
|
||||
function startWizardNanoleafUserAuth(e) {
|
||||
//create html
|
||||
var nanoleaf_user_auth_title = 'wiz_nanoleaf_user_auth_title';
|
||||
var 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');
|
||||
|
||||
//open modal
|
||||
$("#wizard_modal").modal({ backdrop: "static", keyboard: false, show: true });
|
||||
|
||||
//listen for continue
|
||||
$('#btn_wiz_cont').off().on('click', function () {
|
||||
createNanoleafUserAuthorization();
|
||||
$('#wizp1').toggle(false);
|
||||
$('#wizp3').toggle(true);
|
||||
});
|
||||
}
|
||||
|
||||
function createNanoleafUserAuthorization() {
|
||||
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
|
||||
|
||||
let params = { host: host };
|
||||
|
||||
var retryTime = 30;
|
||||
var retryInterval = 2;
|
||||
|
||||
var UserInterval = setInterval(function () {
|
||||
|
||||
$('#wizp1').toggle(false);
|
||||
$('#wizp3').toggle(true);
|
||||
|
||||
(async () => {
|
||||
|
||||
retryTime -= retryInterval;
|
||||
$("#connectionTime").html(retryTime);
|
||||
if (retryTime <= 0) {
|
||||
abortConnection(UserInterval);
|
||||
clearInterval(UserInterval);
|
||||
|
||||
showNotification('warning', $.i18n('wiz_nanoleaf_failure_auth_token'), $.i18n('wiz_nanoleaf_failure_auth_token_t'))
|
||||
|
||||
resetWizard(true);
|
||||
}
|
||||
else {
|
||||
const res = await requestLedDeviceAddAuthorization('nanoleaf', params);
|
||||
if (res && !res.error) {
|
||||
var response = res.info;
|
||||
|
||||
if (jQuery.isEmptyObject(response)) {
|
||||
debugMessage(retryTime + ": Power On/Off button not pressed or device not reachable");
|
||||
} else {
|
||||
$('#wizp1').toggle(false);
|
||||
$('#wizp3').toggle(false);
|
||||
|
||||
var token = response.auth_token;
|
||||
if (token != 'undefined') {
|
||||
conf_editor.getEditor("root.specificOptions.token").setValue(token);
|
||||
}
|
||||
clearInterval(UserInterval);
|
||||
resetWizard(true);
|
||||
}
|
||||
} else {
|
||||
$('#wizp1').toggle(false);
|
||||
$('#wizp3').toggle(false);
|
||||
clearInterval(UserInterval);
|
||||
resetWizard(true);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
}, retryInterval * 1000);
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,8 @@ const char API_EXT_MODE_STRING_V2[] = "{\"write\" : {\"command\" : \"display\",
|
||||
const char API_STATE[] = "state";
|
||||
const char API_PANELLAYOUT[] = "panelLayout";
|
||||
const char API_EFFECT[] = "effects";
|
||||
|
||||
const char API_IDENTIFY[] = "identify";
|
||||
const char API_ADD_USER[] = "new";
|
||||
const char API_EFFECT_SELECT[] = "select";
|
||||
|
||||
//Nanoleaf Control data stream
|
||||
@ -99,8 +100,15 @@ enum SHAPETYPES {
|
||||
POWER_SUPPLY= 5,
|
||||
HEXAGON_SHAPES = 7,
|
||||
TRIANGE_SHAPES = 8,
|
||||
MINI_TRIANGE_SHAPES = 8,
|
||||
SHAPES_CONTROLLER = 12
|
||||
MINI_TRIANGE_SHAPES = 9,
|
||||
SHAPES_CONTROLLER = 12,
|
||||
ELEMENTS_HEXAGONS = 14,
|
||||
ELEMENTS_HEXAGONS_CORNER = 15,
|
||||
LINES_CONECTOR = 16,
|
||||
LIGHT_LINES = 17,
|
||||
LIGHT_LINES_SINGLZONE = 18,
|
||||
CONTROLLER_CAP = 19,
|
||||
POWER_CONNECTOR = 20
|
||||
};
|
||||
|
||||
// Nanoleaf external control versions
|
||||
@ -194,6 +202,32 @@ bool LedDeviceNanoleaf::init(const QJsonObject& deviceConfig)
|
||||
return isInitOK;
|
||||
}
|
||||
|
||||
int LedDeviceNanoleaf::getHwLedCount(const QJsonObject& jsonLayout) const
|
||||
{
|
||||
int hwLedCount {0};
|
||||
|
||||
const QJsonArray positionData = jsonLayout[PANEL_POSITIONDATA].toArray();
|
||||
for(const QJsonValue & value : positionData)
|
||||
{
|
||||
QJsonObject panelObj = value.toObject();
|
||||
int panelId = panelObj[PANEL_ID].toInt();
|
||||
int panelshapeType = panelObj[PANEL_SHAPE_TYPE].toInt();
|
||||
|
||||
DebugIf(verbose,_log, "Panel [%d] - Type: [%d]", panelId, panelshapeType);
|
||||
|
||||
// Skip Rhythm and Shapes controller panels
|
||||
if (panelshapeType != RHYTM && panelshapeType != SHAPES_CONTROLLER)
|
||||
{
|
||||
++hwLedCount;
|
||||
}
|
||||
else
|
||||
{ // Reset non support/required features
|
||||
DebugIf(verbose, _log, "Rhythm/Shape Controller panel skipped.");
|
||||
}
|
||||
}
|
||||
return hwLedCount;
|
||||
}
|
||||
|
||||
bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
{
|
||||
bool isInitOK = true;
|
||||
@ -227,6 +261,8 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
QJsonObject jsonPanelLayout = jsonAllPanelInfo[API_PANELLAYOUT].toObject();
|
||||
QJsonObject jsonLayout = jsonPanelLayout[PANEL_LAYOUT].toObject();
|
||||
|
||||
_panelLedCount = getHwLedCount(jsonLayout);
|
||||
|
||||
int panelNum = jsonLayout[PANEL_NUM].toInt();
|
||||
const QJsonArray positionData = jsonLayout[PANEL_POSITIONDATA].toArray();
|
||||
|
||||
@ -256,6 +292,7 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
}
|
||||
|
||||
// Travers panels top down
|
||||
_panelIds.clear();
|
||||
for (auto posY = panelMap.crbegin(); posY != panelMap.crend(); ++posY)
|
||||
{
|
||||
// Sort panels left to right
|
||||
@ -294,7 +331,6 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
}
|
||||
}
|
||||
|
||||
this->_panelLedCount = _panelIds.size();
|
||||
_devConfig["hardwareLedCount"] = _panelLedCount;
|
||||
|
||||
Debug(_log, "PanelsNum : %d", panelNum);
|
||||
@ -314,7 +350,7 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
QString errorReason = QString("Not enough panels [%1] for configured LEDs [%2] found!")
|
||||
.arg(_panelLedCount)
|
||||
.arg(configuredLedCount);
|
||||
this->setInError(errorReason);
|
||||
this->setInError(errorReason, false);
|
||||
isInitOK = false;
|
||||
}
|
||||
else
|
||||
@ -330,7 +366,7 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
QString errorReason = QString("Start panel [%1] out of range. Start panel position can be max [%2] given [%3] panel available!")
|
||||
.arg(_startPos).arg(_panelLedCount - configuredLedCount).arg(_panelLedCount);
|
||||
|
||||
this->setInError(errorReason);
|
||||
this->setInError(errorReason, false);
|
||||
isInitOK = false;
|
||||
}
|
||||
}
|
||||
@ -436,7 +472,7 @@ QJsonObject LedDeviceNanoleaf::getProperties(const QJsonObject& params)
|
||||
|
||||
_hostName = params[CONFIG_HOST].toString("");
|
||||
_apiPort = API_DEFAULT_PORT;
|
||||
_authToken = params["token"].toString("");
|
||||
_authToken = params[CONFIG_AUTH_TOKEN].toString("");
|
||||
|
||||
Info(_log, "Get properties for %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(_hostName) );
|
||||
|
||||
@ -453,7 +489,14 @@ QJsonObject LedDeviceNanoleaf::getProperties(const QJsonObject& params)
|
||||
{
|
||||
Warning(_log, "%s get properties failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
}
|
||||
properties.insert("properties", response.getBody().object());
|
||||
QJsonObject propertiesDetails = response.getBody().object();
|
||||
if (!propertiesDetails.isEmpty())
|
||||
{
|
||||
QJsonObject jsonLayout = propertiesDetails.value(API_PANELLAYOUT).toObject().value(PANEL_LAYOUT).toObject();
|
||||
_panelLedCount = getHwLedCount(jsonLayout);
|
||||
propertiesDetails.insert("ledCount", getHwLedCount(jsonLayout));
|
||||
}
|
||||
properties.insert("properties", propertiesDetails);
|
||||
}
|
||||
|
||||
DebugIf(verbose, _log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
@ -466,8 +509,8 @@ void LedDeviceNanoleaf::identify(const QJsonObject& params)
|
||||
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
_hostName = params[CONFIG_HOST].toString("");
|
||||
_apiPort = API_DEFAULT_PORT;if (NetUtils::resolveHostToAddress(_log, _hostName, _address))
|
||||
_authToken = params["token"].toString("");
|
||||
_apiPort = API_DEFAULT_PORT;
|
||||
_authToken = params[CONFIG_AUTH_TOKEN].toString("");
|
||||
|
||||
Info(_log, "Identify %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(_hostName) );
|
||||
|
||||
@ -475,9 +518,7 @@ void LedDeviceNanoleaf::identify(const QJsonObject& params)
|
||||
{
|
||||
if ( openRestAPI() )
|
||||
{
|
||||
_restApi->setPath("identify");
|
||||
|
||||
// Perform request
|
||||
_restApi->setPath(API_IDENTIFY);
|
||||
httpResponse response = _restApi->put();
|
||||
if (response.error())
|
||||
{
|
||||
@ -487,6 +528,36 @@ void LedDeviceNanoleaf::identify(const QJsonObject& params)
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceNanoleaf::addAuthorization(const QJsonObject& params)
|
||||
{
|
||||
Debug(_log, "params: [%s]", QJsonDocument(params).toJson(QJsonDocument::Compact).constData());
|
||||
QJsonObject responseBody;
|
||||
|
||||
_hostName = params[CONFIG_HOST].toString("");
|
||||
_apiPort = API_DEFAULT_PORT;
|
||||
|
||||
Info(_log, "Generate user authorization token for %s, hostname (%s)", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(_hostName) );
|
||||
|
||||
if (NetUtils::resolveHostToAddress(_log, _hostName, _address, _apiPort))
|
||||
{
|
||||
if ( openRestAPI() )
|
||||
{
|
||||
_restApi->setBasePath(QString(API_BASE_PATH).arg(API_ADD_USER));
|
||||
httpResponse response = _restApi->post();
|
||||
if (response.error())
|
||||
{
|
||||
Warning(_log, "%s generating user authorization token failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "Generated user authorization token: \"%s\"", QSTRING_CSTR(response.getBody().object().value("auth_token").toString()) );
|
||||
responseBody = response.getBody().object();
|
||||
}
|
||||
}
|
||||
}
|
||||
return responseBody;
|
||||
}
|
||||
|
||||
bool LedDeviceNanoleaf::powerOn()
|
||||
{
|
||||
bool on = false;
|
||||
|
@ -87,6 +87,20 @@ public:
|
||||
///
|
||||
void identify(const QJsonObject& params) override;
|
||||
|
||||
/// @brief Add an API-token to the Nanoleaf device
|
||||
///
|
||||
/// Following parameters are required
|
||||
/// @code
|
||||
/// {
|
||||
/// "host" : "hostname or IP",
|
||||
/// }
|
||||
///@endcode
|
||||
///
|
||||
/// @param[in] params Parameters to query device
|
||||
/// @return A JSON structure holding the authorization keys
|
||||
///
|
||||
QJsonObject addAuthorization(const QJsonObject& params) override;
|
||||
|
||||
protected:
|
||||
|
||||
///
|
||||
@ -182,6 +196,13 @@ private:
|
||||
///
|
||||
QJsonArray discover();
|
||||
|
||||
///
|
||||
/// @brief Get number of panels that can be used as LEds.
|
||||
///
|
||||
/// @return Number of usable LED panels
|
||||
///
|
||||
int getHwLedCount(const QJsonObject& jsonLayout) const;
|
||||
|
||||
///REST-API wrapper
|
||||
ProviderRestApi* _restApi;
|
||||
int _apiPort;
|
||||
|
@ -318,7 +318,6 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "httpStatusCode: "<< httpStatusCode;
|
||||
if (httpStatusCode > 0) {
|
||||
QString httpReason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||
QString advise;
|
||||
@ -327,7 +326,7 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
||||
advise = "Check Request Body";
|
||||
break;
|
||||
case HttpStatusCode::UnAuthorized:
|
||||
advise = "Check Authentication Token (API Key)";
|
||||
advise = "Check Authorization Token (API Key)";
|
||||
break;
|
||||
case HttpStatusCode::Forbidden:
|
||||
advise = "No permission to access the given resource";
|
||||
|
Loading…
Reference in New Issue
Block a user