Webui: extend led hardware config + connection lost page (#226)

* split content and js
tune leds config

* implement connection lost page

* split js/html in huebridge

* add js action for connection lost

* extend led config
make connection loss nicer

* tune led code
add menu entry for grabber

* more tuning of webui

* switch back to botstrap textarea
add v4l to components

* add icon

* extend schema for jsoneditor

* implement ledcolors streaming with 4fps

* implement component state
This commit is contained in:
redPanther
2016-09-07 20:10:37 +02:00
committed by GitHub
parent 4c6a4a1f93
commit 2beccb0912
35 changed files with 675 additions and 279 deletions

View File

@@ -41,7 +41,6 @@ void BoblightServer::start()
emit statusChanged(_isActive);
_hyperion->registerPriority("Boblight", _priority);
}
void BoblightServer::stop()
@@ -62,11 +61,15 @@ void BoblightServer::stop()
void BoblightServer::componentStateChanged(const hyperion::Components component, bool enable)
{
if (component == COMP_BOBLIGHTSERVER && _isActive != enable)
if (component == COMP_BOBLIGHTSERVER)
{
if (enable) start();
else stop();
Info(_log, "change state to %s", (enable ? "enabled" : "disabled") );
if (_isActive != enable)
{
if (enable) start();
else stop();
Info(_log, "change state to %s", (_isActive ? "enabled" : "disabled") );
}
_hyperion->getComponentRegister().componentStateChanged(component, _isActive);
}
}

View File

@@ -9,6 +9,7 @@ SET(Hyperion_QT_HEADERS
${CURRENT_SOURCE_DIR}/LinearColorSmoothing.h
${CURRENT_HEADER_DIR}/GrabberWrapper.h
${CURRENT_HEADER_DIR}/ComponentRegister.h
)
SET(Hyperion_HEADERS
@@ -38,6 +39,7 @@ SET(Hyperion_SOURCES
${CURRENT_SOURCE_DIR}/LinearColorSmoothing.cpp
${CURRENT_SOURCE_DIR}/MessageForwarder.cpp
${CURRENT_SOURCE_DIR}/GrabberWrapper.cpp
${CURRENT_SOURCE_DIR}/ComponentRegister.cpp
)
SET(Hyperion_RESOURCES

View File

@@ -0,0 +1,27 @@
#include <hyperion/ComponentRegister.h>
#include <iostream>
ComponentRegister::ComponentRegister()
: _log(Logger::getInstance("ComponentRegister"))
{
}
ComponentRegister::~ComponentRegister()
{
}
void ComponentRegister::componentStateChanged(const hyperion::Components comp, const bool activated)
{
Info(_log, "%s: %s", componentToString(comp), (activated? "activated" : "off"));
_componentStates.emplace(comp,activated);
_componentStates[comp] = activated;
/* for(auto comp : _componentStates)
{
std::cout << hyperion::componentToIdString(comp.first) << " " << comp.second << std::endl;
}
std::cout << "\n";
*/
}

View File

@@ -17,6 +17,7 @@ GrabberWrapper::GrabberWrapper(std::string grabberName, const int priority, hype
_timer.setSingleShot(false);
_forward = _hyperion->getForwarder()->protoForwardingEnabled();
_hyperion->getComponentRegister().componentStateChanged(hyperion::COMP_BLACKBORDER, _processor->blackBorderDetectorEnabled());
connect(_hyperion, SIGNAL(componentStateChanged(hyperion::Components,bool)), this, SLOT(componentStateChanged(hyperion::Components,bool)));
connect(&_timer, SIGNAL(timeout()), this, SLOT(action()));
@@ -45,27 +46,36 @@ void GrabberWrapper::stop()
void GrabberWrapper::componentStateChanged(const hyperion::Components component, bool enable)
{
if (component == _grabberComponentId && _timer.isActive() != enable)
if (component == _grabberComponentId)
{
if (enable) start();
else stop();
_forward = _hyperion->getForwarder()->protoForwardingEnabled();
if ( enable == _timer.isActive() )
if (_timer.isActive() != enable)
{
Info(_log, "grabber change state to %s", (_timer.isActive() ? "enabled" : "disabled") );
}
else
{
WarningIf( enable, _log, "enable grabber failed");
if (enable) start();
else stop();
_forward = _hyperion->getForwarder()->protoForwardingEnabled();
if ( enable == _timer.isActive() )
{
Info(_log, "grabber change state to %s", (_timer.isActive() ? "enabled" : "disabled") );
}
else
{
WarningIf( enable, _log, "enable grabber failed");
}
}
_hyperion->getComponentRegister().componentStateChanged(component, _timer.isActive());
}
if (component == hyperion::COMP_BLACKBORDER && _processor->blackBorderDetectorEnabled() != enable)
if (component == hyperion::COMP_BLACKBORDER)
{
_processor->enableBlackBorderDetector(enable);
Info(_log, "bb detector change state to %s", (_processor->blackBorderDetectorEnabled() ? "enabled" : "disabled") );
if (_processor->blackBorderDetectorEnabled() != enable)
{
_processor->enableBlackBorderDetector(enable);
Info(_log, "bb detector change state to %s", (_processor->blackBorderDetectorEnabled() ? "enabled" : "disabled") );
}
_hyperion->getComponentRegister().componentStateChanged(component, _processor->blackBorderDetectorEnabled());
}
}

View File

@@ -507,6 +507,7 @@ LedDevice * Hyperion::createColorSmoothing(const Json::Value & smoothingConfig,
if ( ! smoothingConfig.get("enable", true).asBool() )
{
Info(log,"Smoothing disabled");
Hyperion::getInstance()->getComponentRegister().componentStateChanged(hyperion::COMP_SMOOTHING, false);
return nullptr;
}
@@ -603,12 +604,16 @@ Hyperion::Hyperion(const Json::Value &jsonConfig, const std::string configFile)
_ledString,
jsonConfig["blackborderdetector"]
);
//_hyperion->getComponentRegister().componentStateChanged(component, _processor->blackBorderDetectorEnabled());
getComponentRegister().componentStateChanged(hyperion::COMP_FORWARDER, _messageForwarder->forwardingEnabled());
// initialize the color smoothing filter
LedDevice* smoothing = createColorSmoothing(jsonConfig["smoothing"], _device);
if ( smoothing != nullptr )
{
_device = smoothing;
getComponentRegister().componentStateChanged(hyperion::COMP_SMOOTHING, ((LinearColorSmoothing*)_device)->componentState());
connect(this, SIGNAL(componentStateChanged(hyperion::Components,bool)), (LinearColorSmoothing*)_device, SLOT(componentStateChanged(hyperion::Components,bool)));
}

View File

@@ -2,6 +2,7 @@
#include <QDateTime>
#include "LinearColorSmoothing.h"
#include <hyperion/Hyperion.h>
using namespace hyperion;
@@ -21,7 +22,6 @@ LinearColorSmoothing::LinearColorSmoothing( LedDevice * ledDevice, double ledUpd
_timer.setInterval(_updateInterval);
connect(&_timer, SIGNAL(timeout()), this, SLOT(updateLeds()));
Info( _log, "Created linear-smoothing with interval: %d ms, settlingTime: %d ms, updateDelay: %d frames",
_updateInterval, settlingTime_ms, _outputDelay );
}
@@ -141,10 +141,11 @@ void LinearColorSmoothing::queueColors(const std::vector<ColorRgb> & ledColors)
void LinearColorSmoothing::componentStateChanged(const hyperion::Components component, bool enable)
{
if (component == COMP_SMOOTHING && _bypass == enable)
if (component == COMP_SMOOTHING)
{
InfoIf(_bypass == enable, _log, "change state to %s", (enable ? "enabled" : "disabled") );
Hyperion::getInstance()->getComponentRegister().componentStateChanged(component, enable);
_bypass = !enable;
Info(_log, "change state to %s", (enable ? "enabled" : "disabled") );
}
}

View File

@@ -40,7 +40,7 @@ public:
/// Switch the leds off
virtual int switchOff();
bool componentState() { return _bypass; }
bool componentState() { return !_bypass; }
private slots:
/// Timer callback which writes updated led values to the led device

View File

@@ -49,3 +49,8 @@ bool MessageForwarder::protoForwardingEnabled()
{
return ! _protoSlaves.empty();
}
bool MessageForwarder::jsonForwardingEnabled()
{
return ! _jsonSlaves.empty();
}

View File

@@ -59,16 +59,20 @@
"type":"object",
"title" : "Color Calibration",
"required" : true,
"defaultProperties": ["channelAdjustment_enable","channelAdjustment","transform_enable","transform"],
"properties":
{
"channelAdjustment_enable" :
{
"type" : "boolean"
"type" : "boolean",
"format": "checkbox",
"propertyOrder" : 1
},
"channelAdjustment" :
{
"type" : "array",
"required" : true,
"propertyOrder" : 2,
"items" :
{
"type" : "object",
@@ -239,12 +243,15 @@
},
"transform_enable" :
{
"type" : "boolean"
"type" : "boolean",
"format": "checkbox",
"propertyOrder" : 3
},
"transform" :
{
"type" : "array",
"required" : true,
"propertyOrder" : 4,
"items" :
{
"type" : "object",
@@ -376,7 +383,8 @@
"type" : "boolean",
"format": "checkbox",
"title" : "Activate",
"default" : true
"default" : true,
"propertyOrder" : 1
},
"type" :
{
@@ -433,7 +441,8 @@
"type" : "boolean",
"format": "checkbox",
"title" : "Activate",
"default" : false
"default" : false,
"propertyOrder" : 1
},
"device" :
{
@@ -565,12 +574,15 @@
"type" : "boolean",
"format": "checkbox",
"title" : "Activate",
"default" : true
"default" : true,
"propertyOrder" : 1
},
"type" :
{
"type" : "string",
"title" : "Type"
"title" : "Type",
"enum" : ["auto","dispmanx","amlogic","x11","framebuffer"],
"default" : "auto"
},
"width" :
{
@@ -667,6 +679,7 @@
"type" : "object",
"title" : "Blackbar detector",
"required": ["mode"],
"defaultProperties": ["enable","mode","threshold"],
"properties" :
{
"enable" :
@@ -674,7 +687,8 @@
"type" : "boolean",
"format": "checkbox",
"title" : "Activate",
"default" : true
"default" : true,
"propertyOrder" : 1
},
"threshold" :
{
@@ -687,26 +701,22 @@
"unknownFrameCnt" :
{
"type" : "number",
"minimum" : 0,
"advanced" : true
"minimum" : 0
},
"borderFrameCnt" :
{
"type" : "number",
"minimum" : 0,
"advanced" : true
"minimum" : 0
},
"maxInconsistentCnt" :
{
"type" : "number",
"minimum" : 0,
"advanced" : true
"minimum" : 0
},
"blurRemoveCnt" :
{
"type" : "number",
"minimum" : 0,
"advanced" : true
"minimum" : 0
},
"mode" :
{
@@ -729,7 +739,8 @@
"type" : "boolean",
"format": "checkbox",
"title" : "Activate",
"default" : false
"default" : false,
"propertyOrder" : 1
},
"kodiAddress" :
{
@@ -801,12 +812,14 @@
{
"type" : "object",
"title" : "Initial Effect",
"defaultProperties": ["background-effect","foreground-effect","foreground-duration_ms"],
"properties" :
{
"background-effect" :
{
"type" : "array",
"title" : "Background effect"
"title" : "Background effect",
"propertyOrder" : 3
},
"background-effect-args" :
{
@@ -816,7 +829,8 @@
"foreground-effect" :
{
"type" : "array",
"title" : "Boot effect"
"title" : "Boot effect",
"propertyOrder" : 1
},
"foreground-effect-args" :
{
@@ -826,7 +840,8 @@
"foreground-duration_ms" :
{
"type" : "integer",
"title" : "Boot effect duration"
"title" : "Boot effect duration",
"propertyOrder" : 2
}
},
"additionalProperties" : false
@@ -843,7 +858,8 @@
"type" : "boolean",
"format": "checkbox",
"title" : "Activate",
"required" : true
"required" : true,
"propertyOrder" : 1
},
"json" :
{
@@ -907,7 +923,8 @@
"type" : "boolean",
"format": "checkbox",
"title" : "Activate",
"default" : false
"default" : false,
"propertyOrder" : 1
},
"port" :
{
@@ -938,7 +955,8 @@
"type" : "boolean",
"format": "checkbox",
"title" : "Activate",
"default" : false
"default" : false,
"propertyOrder" : 1
},
"address" :
{
@@ -990,7 +1008,8 @@
"format": "checkbox",
"title" : "Activate",
"default" : true,
"required" : true
"required" : true,
"propertyOrder" : 1
},
"document_root" :
{
@@ -1085,7 +1104,7 @@
"colorOrder":
{
"type": "string",
"enum" : ["bgr", "rbg", "brg", "gbr", "grb"]
"enum" : ["rgb", "bgr", "rbg", "brg", "gbr", "grb"]
}
},
"additionalProperties" : false

View File

@@ -46,7 +46,10 @@ JsonClientConnection::JsonClientConnection(QTcpSocket *socket)
// connect internal signals and slots
connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed()));
connect(_socket, SIGNAL(readyRead()), this, SLOT(readData()));
connect( _hyperion, SIGNAL(componentStateChanged(hyperion::Components,bool)), this, SLOT(componentStateChanged(hyperion::Components,bool)));
connect(_hyperion, SIGNAL(componentStateChanged(hyperion::Components,bool)), this, SLOT(componentStateChanged(hyperion::Components,bool)));
_timer_ledcolors.setSingleShot(false);
connect(&_timer_ledcolors, SIGNAL(timeout()), this, SLOT(streamLedcolorsUpdate()));
}
@@ -644,6 +647,18 @@ void JsonClientConnection::handleServerInfoCommand(const Json::Value &, const st
info["ledDevices"]["available"].append(dev.first);
}
// get components
info["components"] = Json::Value(Json::arrayValue);
std::map<hyperion::Components, bool> components = _hyperion->getComponentRegister().getRegister();
for(auto comp : components)
{
Json::Value item;
item["id"] = comp.first;
item["name"] = hyperion::componentToIdString(comp.first);
item["title"] = hyperion::componentToString(comp.first);
item["enabled"] = comp.second;
info["components"].append(item);
}
// Add Hyperion Version, build time
//Json::Value & version =
@@ -977,29 +992,30 @@ void JsonClientConnection::handleComponentStateCommand(const Json::Value& messag
}
}
void JsonClientConnection::handleLedColorsCommand(const Json::Value&, const std::string &command, const int tan)
void JsonClientConnection::handleLedColorsCommand(const Json::Value& message, const std::string &command, const int tan)
{
// create result
Json::Value result;
result["success"] = true;
result["command"] = command;
result["tan"] = tan;
Json::Value & leds = result["result"] = Json::Value(Json::arrayValue);
std::string subcommand = message.get("subcommand","").asString();
_streaming_leds_reply["success"] = true;
_streaming_leds_reply["command"] = command;
_streaming_leds_reply["tan"] = tan;
const PriorityMuxer::InputInfo & priorityInfo = _hyperion->getPriorityInfo(_hyperion->getCurrentPriority());
std::vector<ColorRgb> ledBuffer = priorityInfo.ledColors;
for (ColorRgb& color : ledBuffer)
if (subcommand == "ledstream-start")
{
int idx = leds.size();
Json::Value & item = leds[idx];
item["index"] = idx;
item["red"] = color.red;
item["green"] = color.green;
item["blue"] = color.blue;
_streaming_leds_reply["command"] = command+"-ledstream-update";
_timer_ledcolors.start(125);
}
// send the result
sendMessage(result);
else if (subcommand == "ledstream-stop")
{
_timer_ledcolors.stop();
}
else
{
sendErrorReply("unknown subcommand",command,tan);
return;
}
sendSuccessReply(command+"-"+subcommand,tan);
}
void JsonClientConnection::handleNotImplemented()
@@ -1137,3 +1153,27 @@ bool JsonClientConnection::checkJson(const Json::Value & message, const QString
return true;
}
void JsonClientConnection::streamLedcolorsUpdate()
{
Json::Value & leds = _streaming_leds_reply["result"] = Json::Value(Json::arrayValue);
const PriorityMuxer::InputInfo & priorityInfo = _hyperion->getPriorityInfo(_hyperion->getCurrentPriority());
std::vector<ColorRgb> ledBuffer = priorityInfo.ledColors;
for (ColorRgb& color : ledBuffer)
{
int idx = leds.size();
Json::Value & item = leds[idx];
item["index"] = idx;
item["red"] = color.red;
item["green"] = color.green;
item["blue"] = color.blue;
}
// send the result
sendMessage(_streaming_leds_reply);
}

View File

@@ -42,6 +42,7 @@ public:
public slots:
void componentStateChanged(const hyperion::Components component, bool enable);
void streamLedcolorsUpdate();
signals:
///
@@ -61,6 +62,7 @@ private slots:
///
void socketClosed();
private:
///
/// Handle an incoming JSON message
@@ -173,7 +175,7 @@ private:
///
/// @param message the incoming message
///
void handleLedColorsCommand(const Json::Value &, const std::string &command, const int tan);
void handleLedColorsCommand(const Json::Value & message, const std::string &command, const int tan);
///
/// Handle an incoming JSON message of unknown type
@@ -251,5 +253,7 @@ private:
///
QTimer _timer_ledcolors;
Json::Value _streaming_leds_reply;
};

View File

@@ -13,7 +13,7 @@
"subcommand": {
"type" : "string",
"required" : true,
"enum" : ["ledstream_stop","ledstream_start","testled","imagestream_start","imagestream_stop"]
"enum" : ["ledstream-stop","ledstream-start","testled","imagestream-start","imagestream-stop"]
},
"oneshot": {
"type" : "bool"

View File

@@ -4,6 +4,7 @@
#include <QJsonArray>
#include <kodivideochecker/KODIVideoChecker.h>
#include <hyperion/Hyperion.h>
using namespace hyperion;
@@ -120,11 +121,15 @@ void KODIVideoChecker::stop()
void KODIVideoChecker::componentStateChanged(const hyperion::Components component, bool enable)
{
if (component == COMP_KODICHECKER && _active != enable)
if (component == COMP_KODICHECKER)
{
if (enable) start();
else stop();
Info(_log, "change state to %s", (enable ? "enabled" : "disabled") );
if (_active != enable)
{
if (enable) start();
else stop();
Info(_log, "change state to %s", (_active ? "enabled" : "disabled") );
}
Hyperion::getInstance()->getComponentRegister().componentStateChanged(component, _active);
}
}

View File

@@ -93,10 +93,14 @@ void ProtoServer::sendImageToProtoSlaves(int priority, const Image<ColorRgb> & i
void ProtoServer::componentStateChanged(const hyperion::Components component, bool enable)
{
if (component == hyperion::COMP_FORWARDER && _forwarder_enabled != enable)
if (component == hyperion::COMP_FORWARDER)
{
_forwarder_enabled = enable;
Info(_log, "forwarder change state to %s", (enable ? "enabled" : "disabled") );
if (_forwarder_enabled != enable)
{
_forwarder_enabled = enable;
Info(_log, "forwarder change state to %s", (_forwarder_enabled ? "enabled" : "disabled") );
}
_hyperion->getComponentRegister().componentStateChanged(component, _forwarder_enabled);
}
}

View File

@@ -25,15 +25,12 @@ UDPListener::UDPListener(const int priority, const int timeout, const QString& a
_bondage(shared ? QAbstractSocket::ShareAddress : QAbstractSocket::DefaultForPlatform)
{
_server = new QUdpSocket(this);
_listenAddress = address.isEmpty()
? QHostAddress::AnyIPv4
: QHostAddress(address);
_listenAddress = address.isEmpty()? QHostAddress::AnyIPv4 : QHostAddress(address);
// Set trigger for incoming connections
connect(_server, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
_hyperion->registerPriority("UDPLISTENER", _priority);
_hyperion->registerPriority("UDPLISTENER", _priority);
}
UDPListener::~UDPListener()
@@ -84,11 +81,15 @@ void UDPListener::stop()
void UDPListener::componentStateChanged(const hyperion::Components component, bool enable)
{
if (component == COMP_UDPLISTENER && _isActive != enable)
if (component == COMP_UDPLISTENER)
{
if (enable) start();
else stop();
Info(_log, "change state to %s", (enable ? "enabled" : "disabled") );
if (_isActive != enable)
{
if (enable) start();
else stop();
Info(_log, "change state to %s", (enable ? "enabled" : "disabled") );
}
_hyperion->getComponentRegister().componentStateChanged(component, enable);
}
}

View File

@@ -0,0 +1 @@

View File

@@ -108,7 +108,8 @@ void QJsonSchemaChecker::validate(const QJsonValue & value, const QJsonObject &s
; // nothing to do. value is present so always oke
else if (attribute == "id")
; // references have already been collected
else if (attribute == "title" || attribute == "description" || attribute == "default" || attribute == "format" || attribute == "advanced")
else if (attribute == "title" || attribute == "description" || attribute == "default" || attribute == "format"
|| attribute == "defaultProperties" || attribute == "propertyOrder")
; // nothing to do.
else
{