mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
Merge branch 'master' into Razer_Chroma_Support
# Conflicts: # assets/webconfig/i18n/en.json # assets/webconfig/js/content_leds.js # libsrc/leddevice/dev_net/ProviderRestApi.cpp # libsrc/leddevice/dev_net/ProviderRestApi.h
This commit is contained in:
@@ -234,20 +234,25 @@ int LedDevice::rewriteLEDs()
|
||||
return retval;
|
||||
}
|
||||
|
||||
int LedDevice::writeBlack(int numberOfBlack)
|
||||
int LedDevice::writeBlack(int numberOfWrites)
|
||||
{
|
||||
return writeColor(ColorRgb::BLACK, numberOfWrites);
|
||||
}
|
||||
|
||||
int LedDevice::writeColor(const ColorRgb& color, int numberOfWrites)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
for (int i = 0; i < numberOfBlack; i++)
|
||||
for (int i = 0; i < numberOfWrites; i++)
|
||||
{
|
||||
if ( _latchTime_ms > 0 )
|
||||
if (_latchTime_ms > 0)
|
||||
{
|
||||
// Wait latch time before writing black
|
||||
QEventLoop loop;
|
||||
QTimer::singleShot(_latchTime_ms, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
}
|
||||
_lastLedValues = std::vector<ColorRgb>(static_cast<unsigned long>(_ledCount), ColorRgb::BLACK );
|
||||
_lastLedValues = std::vector<ColorRgb>(static_cast<unsigned long>(_ledCount),color);
|
||||
rc = write(_lastLedValues);
|
||||
}
|
||||
return rc;
|
||||
@@ -265,12 +270,14 @@ bool LedDevice::switchOn()
|
||||
{
|
||||
if ( _isEnabled &&_isDeviceInitialised )
|
||||
{
|
||||
storeState();
|
||||
|
||||
if ( powerOn() )
|
||||
if ( storeState() )
|
||||
{
|
||||
_isOn = true;
|
||||
rc = true;
|
||||
if ( powerOn() )
|
||||
{
|
||||
_isOn = true;
|
||||
_isInSwitchOff = false;
|
||||
rc = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -410,28 +417,33 @@ void LedDevice::setLatchTime( int latchTime_ms )
|
||||
void LedDevice::setRewriteTime( int rewriteTime_ms )
|
||||
{
|
||||
assert(rewriteTime_ms >= 0);
|
||||
_refreshTimerInterval_ms = rewriteTime_ms;
|
||||
|
||||
if ( _refreshTimerInterval_ms > 0 )
|
||||
//Check, if refresh timer was not initialised due to getProperties/identify sceanrios
|
||||
if (_refreshTimer != nullptr)
|
||||
{
|
||||
_refreshTimerInterval_ms = rewriteTime_ms;
|
||||
|
||||
_isRefreshEnabled = true;
|
||||
|
||||
if (_refreshTimerInterval_ms <= _latchTime_ms )
|
||||
if (_refreshTimerInterval_ms > 0)
|
||||
{
|
||||
int new_refresh_timer_interval = _latchTime_ms + 10;
|
||||
Warning(_log, "latchTime(%d) is bigger/equal rewriteTime(%d), set rewriteTime to %dms", _latchTime_ms, _refreshTimerInterval_ms, new_refresh_timer_interval);
|
||||
_refreshTimerInterval_ms = new_refresh_timer_interval;
|
||||
_refreshTimer->setInterval( _refreshTimerInterval_ms );
|
||||
|
||||
_isRefreshEnabled = true;
|
||||
|
||||
if (_refreshTimerInterval_ms <= _latchTime_ms)
|
||||
{
|
||||
int new_refresh_timer_interval = _latchTime_ms + 10;
|
||||
Warning(_log, "latchTime(%d) is bigger/equal rewriteTime(%d), set rewriteTime to %dms", _latchTime_ms, _refreshTimerInterval_ms, new_refresh_timer_interval);
|
||||
_refreshTimerInterval_ms = new_refresh_timer_interval;
|
||||
_refreshTimer->setInterval(_refreshTimerInterval_ms);
|
||||
}
|
||||
|
||||
Debug(_log, "Refresh interval = %dms", _refreshTimerInterval_ms);
|
||||
_refreshTimer->setInterval(_refreshTimerInterval_ms);
|
||||
|
||||
_lastWriteTime = QDateTime::currentDateTime();
|
||||
}
|
||||
|
||||
Debug(_log, "Refresh interval = %dms",_refreshTimerInterval_ms );
|
||||
_refreshTimer->setInterval( _refreshTimerInterval_ms );
|
||||
|
||||
_lastWriteTime = QDateTime::currentDateTime();
|
||||
Debug(_log, "RewriteTime updated to %dms", _refreshTimerInterval_ms);
|
||||
}
|
||||
|
||||
Debug(_log, "RewriteTime updated to %dms", _refreshTimerInterval_ms);
|
||||
}
|
||||
|
||||
void LedDevice::printLedValues(const std::vector<ColorRgb>& ledValues)
|
||||
|
||||
@@ -118,11 +118,11 @@ bool LedDeviceLightpack::init(const QJsonObject &deviceConfig)
|
||||
QString errortext;
|
||||
if (_serialNumber.isEmpty())
|
||||
{
|
||||
errortext = QString ("No Lightpack devices were found");
|
||||
errortext = QString ("No working Lightpack devices were found");
|
||||
}
|
||||
else
|
||||
{
|
||||
errortext = QString ("No Lightpack device found with serial %1").arg( _serialNumber);
|
||||
errortext = QString ("No working Lightpack device found with serial %1").arg( _serialNumber);
|
||||
}
|
||||
this->setInError( errortext );
|
||||
}
|
||||
@@ -197,9 +197,6 @@ bool LedDeviceLightpack::searchDevice(libusb_device * device, const QString & re
|
||||
return false;
|
||||
}
|
||||
|
||||
#define UNO_VENDOR_ID 0x2341
|
||||
#define UNO_PRODUCT_ID 0x43
|
||||
|
||||
if ((deviceDescriptor.idVendor == USB_VENDOR_ID && deviceDescriptor.idProduct == USB_PRODUCT_ID) ||
|
||||
(deviceDescriptor.idVendor == USB_OLD_VENDOR_ID && deviceDescriptor.idProduct == USB_OLD_PRODUCT_ID))
|
||||
{
|
||||
@@ -375,28 +372,30 @@ int LedDeviceLightpack::openDevice(libusb_device *device, libusb_device_handle *
|
||||
Error(_log, "unable to open device(%d): %s", error, libusb_error_name(error));
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
// detach kernel driver if it is active
|
||||
if (libusb_kernel_driver_active(handle, LIGHTPACK_INTERFACE) == 1)
|
||||
else
|
||||
{
|
||||
error = libusb_detach_kernel_driver(handle, LIGHTPACK_INTERFACE);
|
||||
// detach kernel driver if it is active
|
||||
if (libusb_kernel_driver_active(handle, LIGHTPACK_INTERFACE) == 1)
|
||||
{
|
||||
error = libusb_detach_kernel_driver(handle, LIGHTPACK_INTERFACE);
|
||||
if (error != LIBUSB_SUCCESS)
|
||||
{
|
||||
Error(_log, "unable to detach kernel driver(%d): %s", error, libusb_error_name(error));
|
||||
libusb_close(handle);
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
|
||||
error = libusb_claim_interface(handle, LIGHTPACK_INTERFACE);
|
||||
if (error != LIBUSB_SUCCESS)
|
||||
{
|
||||
Error(_log, "unable to detach kernel driver(%d): %s", error, libusb_error_name(error));
|
||||
Error(_log, "unable to claim interface(%d): %s", error, libusb_error_name(error));
|
||||
libusb_attach_kernel_driver(handle, LIGHTPACK_INTERFACE);
|
||||
libusb_close(handle);
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
|
||||
error = libusb_claim_interface(handle, LIGHTPACK_INTERFACE);
|
||||
if (error != LIBUSB_SUCCESS)
|
||||
{
|
||||
Error(_log, "unable to claim interface(%d): %s", error, libusb_error_name(error));
|
||||
libusb_attach_kernel_driver(handle, LIGHTPACK_INTERFACE);
|
||||
libusb_close(handle);
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
*deviceHandle = handle;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
// Constants
|
||||
namespace {
|
||||
|
||||
const bool verbose = false;
|
||||
const bool verbose3 = false;
|
||||
const QString MULTICAST_GROUP_DEFAULT_ADDRESS = "239.255.255.250";
|
||||
const quint16 MULTICAST_GROUP_DEFAULT_PORT = 49692;
|
||||
|
||||
@@ -48,7 +50,7 @@ bool LedDeviceAtmoOrb::init(const QJsonObject &deviceConfig)
|
||||
if ( LedDevice::init(deviceConfig) )
|
||||
{
|
||||
|
||||
_multicastGroup = deviceConfig["output"].toString(MULTICAST_GROUP_DEFAULT_ADDRESS);
|
||||
_multicastGroup = deviceConfig["host"].toString(MULTICAST_GROUP_DEFAULT_ADDRESS);
|
||||
_multiCastGroupPort = static_cast<quint16>(deviceConfig["port"].toInt(MULTICAST_GROUP_DEFAULT_PORT));
|
||||
_useOrbSmoothing = deviceConfig["useOrbSmoothing"].toBool(false);
|
||||
_skipSmoothingDiff = deviceConfig["skipSmoothingDiff"].toInt(0);
|
||||
@@ -272,13 +274,13 @@ void LedDeviceAtmoOrb::setColor(int orbId, const ColorRgb &color, int commandTyp
|
||||
|
||||
void LedDeviceAtmoOrb::sendCommand(const QByteArray &bytes)
|
||||
{
|
||||
//Debug ( _log, "command: [%s] -> %s:%u", QSTRING_CSTR( QString(bytes.toHex())), QSTRING_CSTR(_groupAddress.toString()), _multiCastGroupPort );
|
||||
DebugIf(verbose3, _log, "command: [%s] -> %s:%u", QSTRING_CSTR( QString(bytes.toHex())), QSTRING_CSTR(_groupAddress.toString()), _multiCastGroupPort );
|
||||
_udpSocket->writeDatagram(bytes.data(), bytes.size(), _groupAddress, _multiCastGroupPort);
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceAtmoOrb::discover(const QJsonObject& params)
|
||||
{
|
||||
//Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
|
||||
@@ -353,14 +355,14 @@ QJsonObject LedDeviceAtmoOrb::discover(const QJsonObject& params)
|
||||
}
|
||||
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
DebugIf(verbose, _log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
return devicesDiscovered;
|
||||
}
|
||||
|
||||
void LedDeviceAtmoOrb::identify(const QJsonObject& params)
|
||||
{
|
||||
//Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
int orbId = 0;
|
||||
if ( params["id"].isString() )
|
||||
|
||||
@@ -1,52 +1,50 @@
|
||||
#include "LedDeviceCololight.h"
|
||||
|
||||
#include <utils/QStringUtils.h>
|
||||
#include <utils/WaitTime.h>
|
||||
#include <QUdpSocket>
|
||||
#include <QHostInfo>
|
||||
#include <QtEndian>
|
||||
#include <QEventLoop>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
const bool verbose = false;
|
||||
const bool verbose3 = false;
|
||||
const bool verbose = false;
|
||||
const bool verbose3 = false;
|
||||
|
||||
// Configuration settings
|
||||
// Configuration settings
|
||||
|
||||
const char CONFIG_HW_LED_COUNT[] = "hardwareLedCount";
|
||||
const char CONFIG_HW_LED_COUNT[] = "hardwareLedCount";
|
||||
|
||||
// Cololight discovery service
|
||||
const int COLOLIGHT_BEADS_PER_MODULE = 19;
|
||||
|
||||
const int API_DEFAULT_PORT = 8900;
|
||||
// Cololight discovery service
|
||||
|
||||
const char DISCOVERY_ADDRESS[] = "255.255.255.255";
|
||||
const quint16 DISCOVERY_PORT = 12345;
|
||||
const char DISCOVERY_MESSAGE[] = "Z-SEARCH * \r\n";
|
||||
constexpr std::chrono::milliseconds DEFAULT_DISCOVERY_TIMEOUT{ 5000 };
|
||||
constexpr std::chrono::milliseconds DEFAULT_READ_TIMEOUT{ 1000 };
|
||||
constexpr std::chrono::milliseconds DEFAULT_IDENTIFY_TIME{ 2000 };
|
||||
const int API_DEFAULT_PORT = 8900;
|
||||
|
||||
const char COLOLIGHT_MODEL[] = "mod";
|
||||
const char COLOLIGHT_MODEL_TYPE[] = "subkey";
|
||||
const char COLOLIGHT_MAC[] = "sn";
|
||||
const char COLOLIGHT_NAME[] = "name";
|
||||
const char DISCOVERY_ADDRESS[] = "255.255.255.255";
|
||||
const quint16 DISCOVERY_PORT = 12345;
|
||||
const char DISCOVERY_MESSAGE[] = "Z-SEARCH * \r\n";
|
||||
constexpr std::chrono::milliseconds DEFAULT_DISCOVERY_TIMEOUT{ 2000 };
|
||||
constexpr std::chrono::milliseconds DEFAULT_READ_TIMEOUT{ 1000 };
|
||||
constexpr std::chrono::milliseconds DEFAULT_IDENTIFY_TIME{ 2000 };
|
||||
|
||||
const char COLOLIGHT_MODEL_IDENTIFIER[] = "OD_WE_QUAN";
|
||||
|
||||
const int COLOLIGHT_BEADS_PER_MODULE = 19;
|
||||
const int COLOLIGHT_MIN_STRIP_SEGMENT_SIZE = 30;
|
||||
const char COLOLIGHT_MODEL[] = "mod";
|
||||
const char COLOLIGHT_MODEL_TYPE[] = "subkey";
|
||||
const char COLOLIGHT_MAC[] = "sn";
|
||||
const char COLOLIGHT_NAME[] = "name";
|
||||
|
||||
const char COLOLIGHT_MODEL_IDENTIFIER[] = "OD_WE_QUAN";
|
||||
} //End of constants
|
||||
|
||||
LedDeviceCololight::LedDeviceCololight(const QJsonObject& deviceConfig)
|
||||
: ProviderUdp(deviceConfig)
|
||||
, _modelType(-1)
|
||||
, _ledLayoutType(STRIP_LAYOUT)
|
||||
, _ledBeadCount(0)
|
||||
, _distance(0)
|
||||
, _sequenceNumber(1)
|
||||
, _modelType(-1)
|
||||
, _ledLayoutType(-1)
|
||||
, _ledBeadCount(0)
|
||||
, _distance(0)
|
||||
, _sequenceNumber(1)
|
||||
{
|
||||
_packetFixPart.append(reinterpret_cast<const char*>(PACKET_HEADER), sizeof(PACKET_HEADER));
|
||||
_packetFixPart.append(reinterpret_cast<const char*>(PACKET_SECU), sizeof(PACKET_SECU));
|
||||
@@ -94,11 +92,11 @@ bool LedDeviceCololight::initLedsConfiguration()
|
||||
QString modelTypeText;
|
||||
|
||||
switch (_modelType) {
|
||||
case 0:
|
||||
case STRIP:
|
||||
modelTypeText = "Strip";
|
||||
_ledLayoutType = STRIP_LAYOUT;
|
||||
break;
|
||||
case 1:
|
||||
case PLUS:
|
||||
_ledLayoutType = MODLUE_LAYOUT;
|
||||
modelTypeText = "Plus";
|
||||
break;
|
||||
@@ -116,33 +114,24 @@ bool LedDeviceCololight::initLedsConfiguration()
|
||||
setLedCount(_devConfig[CONFIG_HW_LED_COUNT].toInt(0));
|
||||
}
|
||||
|
||||
if (_modelType == STRIP && (getLedCount() % COLOLIGHT_MIN_STRIP_SEGMENT_SIZE != 0))
|
||||
Debug(_log, "LedCount : %d", getLedCount());
|
||||
|
||||
int configuredLedCount = _devConfig["currentLedCount"].toInt(1);
|
||||
|
||||
if (getLedCount() < configuredLedCount)
|
||||
{
|
||||
QString errorReason = QString("Hardware LED count must be multiple of %1 for Cololight Strip!")
|
||||
.arg(COLOLIGHT_MIN_STRIP_SEGMENT_SIZE);
|
||||
QString errorReason = QString("Not enough LEDs [%1] for configured LEDs in layout [%2] found!")
|
||||
.arg(getLedCount())
|
||||
.arg(configuredLedCount);
|
||||
this->setInError(errorReason);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "LedCount : %d", getLedCount());
|
||||
|
||||
int configuredLedCount = _devConfig["currentLedCount"].toInt(1);
|
||||
|
||||
if (getLedCount() < configuredLedCount)
|
||||
if (getLedCount() > configuredLedCount)
|
||||
{
|
||||
QString errorReason = QString("Not enough LEDs [%1] for configured LEDs in layout [%2] found!")
|
||||
.arg(getLedCount())
|
||||
.arg(configuredLedCount);
|
||||
this->setInError(errorReason);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (getLedCount() > configuredLedCount)
|
||||
{
|
||||
Info(_log, "%s: More LEDs [%d] than configured LEDs in layout [%d].", QSTRING_CSTR(this->getActiveDeviceType()), getLedCount(), configuredLedCount);
|
||||
}
|
||||
isInitOK = true;
|
||||
Info(_log, "%s: More LEDs [%d] than configured LEDs in layout [%d].", QSTRING_CSTR(this->getActiveDeviceType()), getLedCount(), configuredLedCount);
|
||||
}
|
||||
isInitOK = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,29 +186,42 @@ bool LedDeviceCololight::getInfo()
|
||||
QByteArray response;
|
||||
if (readResponse(response))
|
||||
{
|
||||
DebugIf(verbose, _log, "#[0x%x], Data returned: [%s]", _sequenceNumber, QSTRING_CSTR(toHex(response)));
|
||||
DebugIf(verbose,_log, "#[0x%x], Data returned: [%s]", _sequenceNumber, QSTRING_CSTR(toHex(response)));
|
||||
|
||||
quint16 ledNum = qFromBigEndian<quint16>(response.data() + 1);
|
||||
|
||||
if (ledNum != 0xFFFF)
|
||||
{
|
||||
_ledBeadCount = ledNum;
|
||||
// Cololight types are not identifyable currently
|
||||
// Work under the assumption that modules (Cololight Plus) have a number of beads and a Colologht Strip does not have a multiple of beads
|
||||
// The assumption will not hold true, if a user cuts the Strip to a multiple of beads...
|
||||
if (ledNum % COLOLIGHT_BEADS_PER_MODULE == 0)
|
||||
{
|
||||
_modelType = MODLUE_LAYOUT;
|
||||
_modelType = PLUS;
|
||||
_ledLayoutType = MODLUE_LAYOUT;
|
||||
_distance = ledNum / COLOLIGHT_BEADS_PER_MODULE;
|
||||
setLedCount(_distance);
|
||||
}
|
||||
else
|
||||
{
|
||||
_modelType = STRIP;
|
||||
_ledLayoutType = STRIP_LAYOUT;
|
||||
_distance = 0;
|
||||
setLedCount(ledNum);
|
||||
}
|
||||
isCmdOK = true;
|
||||
Debug(_log, "#LEDs found [0x%x], [%u], distance [%d]", _ledBeadCount, _ledBeadCount, _distance);
|
||||
}
|
||||
else
|
||||
{
|
||||
_modelType = STRIP;
|
||||
_modelType = -1;
|
||||
_ledLayoutType = -1;
|
||||
_distance = 0;
|
||||
setLedCount(0);
|
||||
isCmdOK = false;
|
||||
Error(_log, "Number of LEDs cannot be resolved");
|
||||
}
|
||||
|
||||
Debug(_log, "#LEDs found [0x%x], [%u], distance [%d]", _ledBeadCount, _ledBeadCount, _distance);
|
||||
|
||||
isCmdOK = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +267,7 @@ bool LedDeviceCololight::setColor(const uint32_t color)
|
||||
QByteArray response;
|
||||
if (readResponse(response))
|
||||
{
|
||||
DebugIf(verbose, _log, "#[0x%x], Data returned: [%s]", _sequenceNumber, QSTRING_CSTR(toHex(response)));
|
||||
DebugIf(verbose,_log, "#[0x%x], Data returned: [%s]", _sequenceNumber, QSTRING_CSTR(toHex(response)));
|
||||
isCmdOK = true;
|
||||
}
|
||||
}
|
||||
@@ -301,7 +303,7 @@ bool LedDeviceCololight::setState(bool isOn)
|
||||
QByteArray response;
|
||||
if (readResponse(response))
|
||||
{
|
||||
DebugIf(verbose, _log, "#[0x%x], Data returned: [%s]", _sequenceNumber, QSTRING_CSTR(toHex(response)));
|
||||
DebugIf(verbose,_log, "#[0x%x], Data returned: [%s]", _sequenceNumber, QSTRING_CSTR(toHex(response)));
|
||||
isCmdOK = true;
|
||||
}
|
||||
}
|
||||
@@ -325,7 +327,7 @@ bool LedDeviceCololight::setStateDirect(bool isOn)
|
||||
QByteArray response;
|
||||
if (readResponse(response))
|
||||
{
|
||||
DebugIf(verbose, _log, "#[0x%x], Data returned: [%s]", _sequenceNumber, QSTRING_CSTR(toHex(response)));
|
||||
DebugIf(verbose,_log, "#[0x%x], Data returned: [%s]", _sequenceNumber, QSTRING_CSTR(toHex(response)));
|
||||
isCmdOK = true;
|
||||
}
|
||||
}
|
||||
@@ -379,7 +381,7 @@ bool LedDeviceCololight::setTL1CommandMode(bool isOn)
|
||||
QByteArray response;
|
||||
if (readResponse(response))
|
||||
{
|
||||
DebugIf(verbose, _log, "#[0x%x], Data returned: [%s]", _sequenceNumber, QSTRING_CSTR(toHex(response)));
|
||||
DebugIf(verbose,_log, "#[0x%x], Data returned: [%s]", _sequenceNumber, QSTRING_CSTR(toHex(response)));
|
||||
isCmdOK = true;
|
||||
}
|
||||
}
|
||||
@@ -496,7 +498,7 @@ bool LedDeviceCololight::readResponse(QByteArray& response)
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugIf(verbose, _log, "No additional data returned");
|
||||
DebugIf(verbose,_log, "No additional data returned");
|
||||
}
|
||||
}
|
||||
isRequestOK = true;
|
||||
@@ -545,20 +547,15 @@ bool LedDeviceCololight::powerOff()
|
||||
return off;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceCololight::discover(const QJsonObject& /*params*/)
|
||||
QJsonArray LedDeviceCololight::discover()
|
||||
{
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType);
|
||||
|
||||
QJsonArray deviceList;
|
||||
|
||||
QUdpSocket udpSocket;
|
||||
|
||||
udpSocket.writeDatagram(QString(DISCOVERY_MESSAGE).toUtf8(), QHostAddress(DISCOVERY_ADDRESS), DISCOVERY_PORT);
|
||||
|
||||
if (udpSocket.waitForReadyRead(DEFAULT_DISCOVERY_TIMEOUT.count()))
|
||||
{
|
||||
while (udpSocket.waitForReadyRead(500))
|
||||
while (udpSocket.waitForReadyRead(200))
|
||||
{
|
||||
QByteArray datagram;
|
||||
|
||||
@@ -602,12 +599,13 @@ QJsonObject LedDeviceCololight::discover(const QJsonObject& /*params*/)
|
||||
}
|
||||
}
|
||||
|
||||
QJsonArray deviceList;
|
||||
QMap<QString, QMap <QString, QString>>::iterator i;
|
||||
for (i = _services.begin(); i != _services.end(); ++i)
|
||||
{
|
||||
QJsonObject obj;
|
||||
|
||||
QString ipAddress = i.key();
|
||||
const QString& ipAddress = i.key();
|
||||
obj.insert("ip", ipAddress);
|
||||
obj.insert("model", i.value().value(COLOLIGHT_MODEL));
|
||||
obj.insert("type", i.value().value(COLOLIGHT_MODEL_TYPE));
|
||||
@@ -647,27 +645,43 @@ QJsonObject LedDeviceCololight::discover(const QJsonObject& /*params*/)
|
||||
|
||||
deviceList << obj;
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceCololight::discover(const QJsonObject& /*params*/)
|
||||
{
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType);
|
||||
|
||||
QString discoveryMethod("ssdp");
|
||||
QJsonArray deviceList;
|
||||
|
||||
deviceList = discover();
|
||||
|
||||
devicesDiscovered.insert("discoveryMethod", discoveryMethod);
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
DebugIf(verbose, _log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
DebugIf(verbose,_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
return devicesDiscovered;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceCololight::getProperties(const QJsonObject& params)
|
||||
{
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
QJsonObject properties;
|
||||
|
||||
QString apiHostname = params["host"].toString("");
|
||||
QString hostName = params["host"].toString("");
|
||||
quint16 apiPort = static_cast<quint16>(params["port"].toInt(API_DEFAULT_PORT));
|
||||
|
||||
if (!apiHostname.isEmpty())
|
||||
QJsonObject propertiesDetails;
|
||||
if (!hostName.isEmpty())
|
||||
{
|
||||
QJsonObject deviceConfig;
|
||||
|
||||
deviceConfig.insert("host", apiHostname);
|
||||
deviceConfig.insert("host", hostName);
|
||||
deviceConfig.insert("port", apiPort);
|
||||
|
||||
if (ProviderUdp::init(deviceConfig))
|
||||
{
|
||||
if (getInfo())
|
||||
@@ -675,38 +689,43 @@ QJsonObject LedDeviceCololight::getProperties(const QJsonObject& params)
|
||||
QString modelTypeText;
|
||||
|
||||
switch (_modelType) {
|
||||
case 1:
|
||||
case STRIP:
|
||||
modelTypeText = "Strip";
|
||||
break;
|
||||
case PLUS:
|
||||
modelTypeText = "Plus";
|
||||
break;
|
||||
default:
|
||||
modelTypeText = "Strip";
|
||||
break;
|
||||
}
|
||||
properties.insert("modelType", modelTypeText);
|
||||
properties.insert("ledCount", static_cast<int>(getLedCount()));
|
||||
properties.insert("ledBeadCount", _ledBeadCount);
|
||||
properties.insert("distance", _distance);
|
||||
propertiesDetails.insert("modelType", modelTypeText);
|
||||
propertiesDetails.insert("ledCount", static_cast<int>(getLedCount()));
|
||||
propertiesDetails.insert("ledBeadCount", _ledBeadCount);
|
||||
propertiesDetails.insert("distance", _distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DebugIf(verbose, _log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
properties.insert("properties", propertiesDetails);
|
||||
|
||||
DebugIf(verbose,_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
void LedDeviceCololight::identify(const QJsonObject& params)
|
||||
{
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
QString apiHostname = params["host"].toString("");
|
||||
QString hostName = params["host"].toString("");
|
||||
quint16 apiPort = static_cast<quint16>(params["port"].toInt(API_DEFAULT_PORT));
|
||||
|
||||
if (!apiHostname.isEmpty())
|
||||
if (!hostName.isEmpty())
|
||||
{
|
||||
QJsonObject deviceConfig;
|
||||
|
||||
deviceConfig.insert("host", apiHostname);
|
||||
deviceConfig.insert("host", hostName);
|
||||
deviceConfig.insert("port", apiPort);
|
||||
if (ProviderUdp::init(deviceConfig))
|
||||
{
|
||||
@@ -714,9 +733,7 @@ void LedDeviceCololight::identify(const QJsonObject& params)
|
||||
{
|
||||
setEffect(THE_CIRCUS);
|
||||
|
||||
QEventLoop loop;
|
||||
QTimer::singleShot(DEFAULT_IDENTIFY_TIME.count(), &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
wait(DEFAULT_IDENTIFY_TIME);
|
||||
|
||||
setColor(ColorRgb::BLACK);
|
||||
}
|
||||
|
||||
@@ -284,6 +284,14 @@ private:
|
||||
///
|
||||
bool readResponse(QByteArray& response);
|
||||
|
||||
///
|
||||
/// @brief Discover Cololight devices available (for configuration).
|
||||
/// Cololight specific UDP Broadcast discovery
|
||||
///
|
||||
/// @return A JSON structure holding a list of devices found
|
||||
///
|
||||
QJsonArray discover();
|
||||
|
||||
// Cololight model, e.g. CololightPlus, CololightStrip
|
||||
int _modelType;
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ bool LedDeviceFadeCandy::init(const QJsonObject& deviceConfig)
|
||||
}
|
||||
else
|
||||
{
|
||||
_host = deviceConfig["output"].toString("127.0.0.1");
|
||||
_host = deviceConfig["host"].toString("127.0.0.1");
|
||||
_port = deviceConfig["port"].toInt(STREAM_DEFAULT_PORT);
|
||||
|
||||
//If host not configured the init fails
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <utils/QStringUtils.h>
|
||||
|
||||
// Qt includes
|
||||
#include <QEventLoop>
|
||||
#include <QNetworkReply>
|
||||
#include <QtEndian>
|
||||
|
||||
@@ -22,11 +21,18 @@ const bool verbose3 = false;
|
||||
const char CONFIG_ADDRESS[] = "host";
|
||||
//const char CONFIG_PORT[] = "port";
|
||||
const char CONFIG_AUTH_TOKEN[] = "token";
|
||||
const char CONFIG_RESTORE_STATE[] = "restoreOriginalState";
|
||||
const char CONFIG_BRIGHTNESS[] = "brightness";
|
||||
const char CONFIG_BRIGHTNESS_OVERWRITE[] = "overwriteBrightness";
|
||||
|
||||
const char CONFIG_PANEL_ORDER_TOP_DOWN[] = "panelOrderTopDown";
|
||||
const char CONFIG_PANEL_ORDER_LEFT_RIGHT[] = "panelOrderLeftRight";
|
||||
const char CONFIG_PANEL_START_POS[] = "panelStartPos";
|
||||
|
||||
const bool DEFAULT_IS_RESTORE_STATE = true;
|
||||
const bool DEFAULT_IS_BRIGHTNESS_OVERWRITE = true;
|
||||
const int BRI_MAX = 100;
|
||||
|
||||
// Panel configuration settings
|
||||
const char PANEL_LAYOUT[] = "layout";
|
||||
const char PANEL_NUM[] = "numPanels";
|
||||
@@ -39,9 +45,13 @@ const char PANEL_POS_Y[] = "y";
|
||||
|
||||
// List of State Information
|
||||
const char STATE_ON[] = "on";
|
||||
const char STATE_ONOFF_VALUE[] = "value";
|
||||
const char STATE_VALUE_TRUE[] = "true";
|
||||
const char STATE_VALUE_FALSE[] = "false";
|
||||
const char STATE_BRI[] = "brightness";
|
||||
const char STATE_HUE[] = "hue";
|
||||
const char STATE_SAT[] = "sat";
|
||||
const char STATE_CT[] = "ct";
|
||||
const char STATE_COLORMODE[] = "colorMode";
|
||||
const QStringList COLOR_MODES {"hs", "ct", "effect"};
|
||||
const char STATE_VALUE[] = "value";
|
||||
|
||||
// Device Data elements
|
||||
const char DEV_DATA_NAME[] = "name";
|
||||
@@ -50,10 +60,7 @@ const char DEV_DATA_MANUFACTURER[] = "manufacturer";
|
||||
const char DEV_DATA_FIRMWAREVERSION[] = "firmwareVersion";
|
||||
|
||||
// Nanoleaf Stream Control elements
|
||||
//const char STREAM_CONTROL_IP[] = "streamControlIpAddr";
|
||||
const char STREAM_CONTROL_PORT[] = "streamControlPort";
|
||||
//const char STREAM_CONTROL_PROTOCOL[] = "streamControlProtocol";
|
||||
const quint16 STREAM_CONTROL_DEFAULT_PORT = 60222; //Fixed port for Canvas;
|
||||
const quint16 STREAM_CONTROL_DEFAULT_PORT = 60222;
|
||||
|
||||
// Nanoleaf OpenAPI URLs
|
||||
const int API_DEFAULT_PORT = 16021;
|
||||
@@ -65,6 +72,8 @@ const char API_STATE[] = "state";
|
||||
const char API_PANELLAYOUT[] = "panelLayout";
|
||||
const char API_EFFECT[] = "effects";
|
||||
|
||||
const char API_EFFECT_SELECT[] = "select";
|
||||
|
||||
//Nanoleaf Control data stream
|
||||
const int STREAM_FRAME_PANEL_NUM_SIZE = 2;
|
||||
const int STREAM_FRAME_PANEL_INFO_SIZE = 8;
|
||||
@@ -72,19 +81,23 @@ const int STREAM_FRAME_PANEL_INFO_SIZE = 8;
|
||||
// Nanoleaf ssdp services
|
||||
const char SSDP_ID[] = "ssdp:all";
|
||||
const char SSDP_FILTER_HEADER[] = "ST";
|
||||
const char SSDP_CANVAS[] = "nanoleaf:nl29";
|
||||
const char SSDP_NANOLEAF[] = "nanoleaf:nl*";
|
||||
const char SSDP_LIGHTPANELS[] = "nanoleaf_aurora:light";
|
||||
} //End of constants
|
||||
|
||||
// Nanoleaf Panel Shapetypes
|
||||
enum SHAPETYPES {
|
||||
TRIANGLE,
|
||||
RHYTM,
|
||||
SQUARE,
|
||||
CONTROL_SQUARE_PRIMARY,
|
||||
CONTROL_SQUARE_PASSIVE,
|
||||
POWER_SUPPLY,
|
||||
};
|
||||
TRIANGLE = 0,
|
||||
RHYTM = 1,
|
||||
SQUARE = 2,
|
||||
CONTROL_SQUARE_PRIMARY = 3,
|
||||
CONTROL_SQUARE_PASSIVE = 4,
|
||||
POWER_SUPPLY= 5,
|
||||
HEXAGON_SHAPES = 7,
|
||||
TRIANGE_SHAPES = 8,
|
||||
MINI_TRIANGE_SHAPES = 8,
|
||||
SHAPES_CONTROLLER = 12
|
||||
};
|
||||
|
||||
// Nanoleaf external control versions
|
||||
enum EXTCONTROLVERSIONS {
|
||||
@@ -100,8 +113,8 @@ LedDeviceNanoleaf::LedDeviceNanoleaf(const QJsonObject& deviceConfig)
|
||||
, _leftRight(true)
|
||||
, _startPos(0)
|
||||
, _endPos(0)
|
||||
, _extControlVersion(EXTCTRLVER_V2),
|
||||
_panelLedCount(0)
|
||||
, _extControlVersion(EXTCTRLVER_V2)
|
||||
, _panelLedCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -127,7 +140,7 @@ bool LedDeviceNanoleaf::init(const QJsonObject& deviceConfig)
|
||||
Info(_log, "Device Nanoleaf does not require rewrites. Refresh time is ignored.");
|
||||
}
|
||||
|
||||
DebugIf(verbose, _log, "deviceConfig: [%s]", QString(QJsonDocument(_devConfig).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
DebugIf(verbose,_log, "deviceConfig: [%s]", QString(QJsonDocument(_devConfig).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
bool isInitOK = false;
|
||||
|
||||
@@ -140,6 +153,14 @@ bool LedDeviceNanoleaf::init(const QJsonObject& deviceConfig)
|
||||
Debug(_log, "RewriteTime : %d", this->getRewriteTime());
|
||||
Debug(_log, "LatchTime : %d", this->getLatchTime());
|
||||
|
||||
_isRestoreOrigState = _devConfig[CONFIG_RESTORE_STATE].toBool(DEFAULT_IS_RESTORE_STATE);
|
||||
_isBrightnessOverwrite = _devConfig[CONFIG_BRIGHTNESS_OVERWRITE].toBool(DEFAULT_IS_BRIGHTNESS_OVERWRITE);
|
||||
_brightness = _devConfig[CONFIG_BRIGHTNESS].toInt(BRI_MAX);
|
||||
|
||||
Debug(_log, "RestoreOrigState : %d", _isRestoreOrigState);
|
||||
Debug(_log, "Overwrite Brightn.: %d", _isBrightnessOverwrite);
|
||||
Debug(_log, "Set Brightness to : %d", _brightness);
|
||||
|
||||
// Read panel organisation configuration
|
||||
if (deviceConfig[CONFIG_PANEL_ORDER_TOP_DOWN].isString())
|
||||
{
|
||||
@@ -164,29 +185,29 @@ bool LedDeviceNanoleaf::init(const QJsonObject& deviceConfig)
|
||||
// TODO: Allow to handle port dynamically
|
||||
|
||||
//Set hostname as per configuration and_defaultHost default port
|
||||
_hostname = deviceConfig[CONFIG_ADDRESS].toString();
|
||||
_hostName = deviceConfig[CONFIG_ADDRESS].toString();
|
||||
_apiPort = API_DEFAULT_PORT;
|
||||
_authToken = deviceConfig[CONFIG_AUTH_TOKEN].toString();
|
||||
|
||||
//If host not configured the init failed
|
||||
if (_hostname.isEmpty())
|
||||
if (_hostName.isEmpty())
|
||||
{
|
||||
this->setInError("No target hostname nor IP defined");
|
||||
isInitOK = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (initRestAPI(_hostname, _apiPort, _authToken))
|
||||
if (initRestAPI(_hostName, _apiPort, _authToken))
|
||||
{
|
||||
// Read LedDevice configuration and validate against device configuration
|
||||
if (initLedsConfiguration())
|
||||
{
|
||||
// Set UDP streaming host and port
|
||||
_devConfig["host"] = _hostname;
|
||||
_devConfig["host"] = _hostName;
|
||||
_devConfig["port"] = STREAM_CONTROL_DEFAULT_PORT;
|
||||
|
||||
isInitOK = ProviderUdp::init(_devConfig);
|
||||
Debug(_log, "Hostname/IP : %s", QSTRING_CSTR(_hostname));
|
||||
Debug(_log, "Hostname/IP : %s", QSTRING_CSTR(_hostName));
|
||||
Debug(_log, "Port : %d", _port);
|
||||
}
|
||||
}
|
||||
@@ -206,7 +227,8 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
httpResponse response = _restApi->get();
|
||||
if (response.error())
|
||||
{
|
||||
this->setInError(response.getErrorReason());
|
||||
QString errorReason = QString("Getting device details failed with error: '%1'").arg(response.getErrorReason());
|
||||
this->setInError ( errorReason );
|
||||
isInitOK = false;
|
||||
}
|
||||
else
|
||||
@@ -243,16 +265,16 @@ bool LedDeviceNanoleaf::initLedsConfiguration()
|
||||
int panelshapeType = panelObj[PANEL_SHAPE_TYPE].toInt();
|
||||
//int panelOrientation = panelObj[PANEL_ORIENTATION].toInt();
|
||||
|
||||
DebugIf(verbose, _log, "Panel [%d] (%d,%d) - Type: [%d]", panelId, panelX, panelY, panelshapeType);
|
||||
DebugIf(verbose,_log, "Panel [%d] (%d,%d) - Type: [%d]", panelId, panelX, panelY, panelshapeType);
|
||||
|
||||
// Skip Rhythm panels
|
||||
if (panelshapeType != RHYTM)
|
||||
// Skip Rhythm and Shapes controller panels
|
||||
if (panelshapeType != RHYTM && panelshapeType != SHAPES_CONTROLLER)
|
||||
{
|
||||
panelMap[panelY][panelX] = panelId;
|
||||
}
|
||||
else
|
||||
{ // Reset non support/required features
|
||||
Info(_log, "Rhythm panel skipped.");
|
||||
Info(_log, "Rhythm/Shape Controller panel skipped.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,36 +382,24 @@ int LedDeviceNanoleaf::open()
|
||||
int retval = -1;
|
||||
_isDeviceReady = false;
|
||||
|
||||
QJsonDocument responseDoc = changeToExternalControlMode();
|
||||
// Resolve port for Light Panels
|
||||
QJsonObject jsonStreamControllInfo = responseDoc.object();
|
||||
if (!jsonStreamControllInfo.isEmpty())
|
||||
{
|
||||
//Set default streaming port
|
||||
_port = static_cast<uchar>(jsonStreamControllInfo[STREAM_CONTROL_PORT].toInt());
|
||||
}
|
||||
|
||||
if (ProviderUdp::open() == 0)
|
||||
{
|
||||
// Everything is OK, device is ready
|
||||
_isDeviceReady = true;
|
||||
retval = 0;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceNanoleaf::discover(const QJsonObject& /*params*/)
|
||||
QJsonArray LedDeviceNanoleaf::discover()
|
||||
{
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType);
|
||||
|
||||
QJsonArray deviceList;
|
||||
|
||||
// Discover Nanoleaf Devices
|
||||
SSDPDiscover discover;
|
||||
|
||||
// Search for Canvas and Light-Panels
|
||||
QString searchTargetFilter = QString("%1|%2").arg(SSDP_CANVAS, SSDP_LIGHTPANELS);
|
||||
QString searchTargetFilter = QString("%1|%2").arg(SSDP_NANOLEAF, SSDP_LIGHTPANELS);
|
||||
|
||||
discover.setSearchFilter(searchTargetFilter, SSDP_FILTER_HEADER);
|
||||
QString searchTarget = SSDP_ID;
|
||||
@@ -399,26 +409,41 @@ QJsonObject LedDeviceNanoleaf::discover(const QJsonObject& /*params*/)
|
||||
deviceList = discover.getServicesDiscoveredJson();
|
||||
}
|
||||
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceNanoleaf::discover(const QJsonObject& /*params*/)
|
||||
{
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType);
|
||||
|
||||
QString discoveryMethod("ssdp");
|
||||
QJsonArray deviceList;
|
||||
|
||||
deviceList = discover();
|
||||
|
||||
devicesDiscovered.insert("discoveryMethod", discoveryMethod);
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
DebugIf(verbose,_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
return devicesDiscovered;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceNanoleaf::getProperties(const QJsonObject& params)
|
||||
{
|
||||
Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
QJsonObject properties;
|
||||
|
||||
// Get Nanoleaf device properties
|
||||
QString host = params["host"].toString("");
|
||||
if (!host.isEmpty())
|
||||
QString hostName = params["host"].toString("");
|
||||
|
||||
if (!hostName.isEmpty())
|
||||
{
|
||||
QString authToken = params["token"].toString("");
|
||||
QString filter = params["filter"].toString("");
|
||||
|
||||
// Resolve hostname and port (or use default API port)
|
||||
QStringList addressparts = QStringUtils::split(host, ":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QStringList addressparts = QStringUtils::split(hostName, ":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QString apiHost = addressparts[0];
|
||||
int apiPort;
|
||||
|
||||
@@ -443,22 +468,22 @@ QJsonObject LedDeviceNanoleaf::getProperties(const QJsonObject& params)
|
||||
|
||||
properties.insert("properties", response.getBody().object());
|
||||
|
||||
Debug(_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
DebugIf(verbose,_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
void LedDeviceNanoleaf::identify(const QJsonObject& params)
|
||||
{
|
||||
Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
QString host = params["host"].toString("");
|
||||
if (!host.isEmpty())
|
||||
QString hostName = params["host"].toString("");
|
||||
if (!hostName.isEmpty())
|
||||
{
|
||||
QString authToken = params["token"].toString("");
|
||||
|
||||
// Resolve hostname and port (or use default API port)
|
||||
QStringList addressparts = QStringUtils::split(host, ":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QStringList addressparts = QStringUtils::split(hostName, ":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QString apiHost = addressparts[0];
|
||||
int apiPort;
|
||||
|
||||
@@ -485,44 +510,247 @@ void LedDeviceNanoleaf::identify(const QJsonObject& params)
|
||||
|
||||
bool LedDeviceNanoleaf::powerOn()
|
||||
{
|
||||
bool on = false;
|
||||
if (_isDeviceReady)
|
||||
{
|
||||
changeToExternalControlMode();
|
||||
if (changeToExternalControlMode())
|
||||
{
|
||||
QJsonObject newState;
|
||||
|
||||
//Power-on Nanoleaf device
|
||||
_restApi->setPath(API_STATE);
|
||||
_restApi->put(getOnOffRequest(true));
|
||||
QJsonObject onValue { {STATE_VALUE, true} };
|
||||
newState.insert(STATE_ON, onValue);
|
||||
|
||||
if ( _isBrightnessOverwrite)
|
||||
{
|
||||
QJsonObject briValue { {STATE_VALUE, _brightness} };
|
||||
newState.insert(STATE_BRI, briValue);
|
||||
}
|
||||
|
||||
//Power-on Nanoleaf device
|
||||
_restApi->setPath(API_STATE);
|
||||
httpResponse response = _restApi->put(newState);
|
||||
if (response.error())
|
||||
{
|
||||
QString errorReason = QString("Power-on request failed with error: '%1'").arg(response.getErrorReason());
|
||||
this->setInError ( errorReason );
|
||||
on = false;
|
||||
} else {
|
||||
on = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return on;
|
||||
}
|
||||
|
||||
bool LedDeviceNanoleaf::powerOff()
|
||||
{
|
||||
bool off = true;
|
||||
if (_isDeviceReady)
|
||||
{
|
||||
QJsonObject newState;
|
||||
|
||||
QJsonObject onValue { {STATE_VALUE, false} };
|
||||
newState.insert(STATE_ON, onValue);
|
||||
|
||||
//Power-off the Nanoleaf device physically
|
||||
_restApi->setPath(API_STATE);
|
||||
_restApi->put(getOnOffRequest(false));
|
||||
httpResponse response = _restApi->put(newState);
|
||||
if (response.error())
|
||||
{
|
||||
QString errorReason = QString("Power-off request failed with error: '%1'").arg(response.getErrorReason());
|
||||
this->setInError ( errorReason );
|
||||
off = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return off;
|
||||
}
|
||||
|
||||
QString LedDeviceNanoleaf::getOnOffRequest(bool isOn) const
|
||||
bool LedDeviceNanoleaf::storeState()
|
||||
{
|
||||
QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
|
||||
return QString("{\"%1\":{\"%2\":%3}}").arg(STATE_ON, STATE_ONOFF_VALUE, state);
|
||||
bool rc = true;
|
||||
|
||||
if ( _isRestoreOrigState )
|
||||
{
|
||||
_restApi->setPath(API_STATE);
|
||||
|
||||
httpResponse response = _restApi->get();
|
||||
if ( response.error() )
|
||||
{
|
||||
QString errorReason = QString("Storing device state failed with error: '%1'").arg(response.getErrorReason());
|
||||
setInError(errorReason);
|
||||
rc = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_originalStateProperties = response.getBody().object();
|
||||
DebugIf(verbose, _log, "state: [%s]", QString(QJsonDocument(_originalStateProperties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
QJsonObject isOn = _originalStateProperties.value(STATE_ON).toObject();
|
||||
if (!isOn.isEmpty())
|
||||
{
|
||||
_originalIsOn = isOn[STATE_VALUE].toBool();
|
||||
}
|
||||
|
||||
QJsonObject bri = _originalStateProperties.value(STATE_BRI).toObject();
|
||||
if (!bri.isEmpty())
|
||||
{
|
||||
_originalBri = bri[STATE_VALUE].toInt();
|
||||
}
|
||||
|
||||
_originalColorMode = _originalStateProperties[STATE_COLORMODE].toString();
|
||||
|
||||
switch(COLOR_MODES.indexOf(_originalColorMode)) {
|
||||
case 0:
|
||||
{
|
||||
// hs
|
||||
QJsonObject hue = _originalStateProperties.value(STATE_HUE).toObject();
|
||||
if (!hue.isEmpty())
|
||||
{
|
||||
_originalHue = hue[STATE_VALUE].toInt();
|
||||
}
|
||||
QJsonObject sat = _originalStateProperties.value(STATE_SAT).toObject();
|
||||
if (!sat.isEmpty())
|
||||
{
|
||||
_originalSat = sat[STATE_VALUE].toInt();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// ct
|
||||
QJsonObject ct = _originalStateProperties.value(STATE_CT).toObject();
|
||||
if (!ct.isEmpty())
|
||||
{
|
||||
_originalCt = ct[STATE_VALUE].toInt();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// effect
|
||||
_restApi->setPath(API_EFFECT);
|
||||
|
||||
httpResponse response = _restApi->get();
|
||||
if ( response.error() )
|
||||
{
|
||||
QString errorReason = QString("Storing device state failed with error: '%1'").arg(response.getErrorReason());
|
||||
setInError(errorReason);
|
||||
rc = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
QJsonObject effects = response.getBody().object();
|
||||
DebugIf(verbose, _log, "effects: [%s]", QString(QJsonDocument(_originalStateProperties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
_originalEffect = effects[API_EFFECT_SELECT].toString();
|
||||
_originalIsDynEffect = _originalEffect == "*Dynamic*" || _originalEffect == "*Solid*";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
QString errorReason = QString("Unknown ColorMode: '%1'").arg(_originalColorMode);
|
||||
setInError(errorReason);
|
||||
rc = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
QJsonDocument LedDeviceNanoleaf::changeToExternalControlMode()
|
||||
bool LedDeviceNanoleaf::restoreState()
|
||||
{
|
||||
bool rc = true;
|
||||
|
||||
if ( _isRestoreOrigState )
|
||||
{
|
||||
QJsonObject newState;
|
||||
switch(COLOR_MODES.indexOf(_originalColorMode)) {
|
||||
case 0:
|
||||
{ // hs
|
||||
QJsonObject hueValue { {STATE_VALUE, _originalHue} };
|
||||
newState.insert(STATE_HUE, hueValue);
|
||||
QJsonObject satValue { {STATE_VALUE, _originalSat} };
|
||||
newState.insert(STATE_SAT, satValue);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{ // ct
|
||||
QJsonObject ctValue { {STATE_VALUE, _originalCt} };
|
||||
newState.insert(STATE_CT, ctValue);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{ // effect
|
||||
if (!_originalIsDynEffect)
|
||||
{
|
||||
QJsonObject newEffect;
|
||||
newEffect[API_EFFECT_SELECT] = _originalEffect;
|
||||
_restApi->setPath(API_EFFECT);
|
||||
httpResponse response = _restApi->put(newEffect);
|
||||
if ( response.error() )
|
||||
{
|
||||
Warning (_log, "%s restoring effect failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
}
|
||||
} else {
|
||||
Warning (_log, "%s restoring effect failed with error: Cannot restore dynamic or solid effect. Turning device off", QSTRING_CSTR(_activeDeviceType));
|
||||
_originalIsOn = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Warning (_log, "%s restoring failed with error: Unknown ColorMode", QSTRING_CSTR(_activeDeviceType));
|
||||
rc = false;
|
||||
}
|
||||
|
||||
if (!_originalIsDynEffect)
|
||||
{
|
||||
QJsonObject briValue { {STATE_VALUE, _originalBri} };
|
||||
newState.insert(STATE_BRI, briValue);
|
||||
}
|
||||
|
||||
QJsonObject onValue { {STATE_VALUE, _originalIsOn} };
|
||||
newState.insert(STATE_ON, onValue);
|
||||
|
||||
_restApi->setPath(API_STATE);
|
||||
|
||||
httpResponse response = _restApi->put(newState);
|
||||
|
||||
if ( response.error() )
|
||||
{
|
||||
Warning (_log, "%s restoring state failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
rc = false;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool LedDeviceNanoleaf::changeToExternalControlMode()
|
||||
{
|
||||
QJsonDocument resp;
|
||||
return changeToExternalControlMode(resp);
|
||||
}
|
||||
|
||||
bool LedDeviceNanoleaf::changeToExternalControlMode(QJsonDocument& resp)
|
||||
{
|
||||
bool success = false;
|
||||
Debug(_log, "Set Nanoleaf to External Control (UDP) streaming mode");
|
||||
_extControlVersion = EXTCTRLVER_V2;
|
||||
//Enable UDP Mode v2
|
||||
|
||||
_restApi->setPath(API_EFFECT);
|
||||
httpResponse response = _restApi->put(API_EXT_MODE_STRING_V2);
|
||||
|
||||
return response.getBody();
|
||||
if (response.error())
|
||||
{
|
||||
QString errorReason = QString("Change to external control mode failed with error: '%1'").arg(response.getErrorReason());
|
||||
this->setInError ( errorReason );
|
||||
}
|
||||
else
|
||||
{
|
||||
resp = response.getBody();
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
int LedDeviceNanoleaf::write(const std::vector<ColorRgb>& ledValues)
|
||||
|
||||
@@ -126,6 +126,25 @@ protected:
|
||||
///
|
||||
bool powerOff() override;
|
||||
|
||||
///
|
||||
/// @brief Store the device's original state.
|
||||
///
|
||||
/// Save the device's state before hyperion color streaming starts allowing to restore state during switchOff().
|
||||
///
|
||||
/// @return True if success
|
||||
///
|
||||
bool storeState() override;
|
||||
|
||||
///
|
||||
/// @brief Restore the device's original state.
|
||||
///
|
||||
/// Restore the device's state as before hyperion color streaming started.
|
||||
/// This includes the on/off state of the device.
|
||||
///
|
||||
/// @return True, if success
|
||||
///
|
||||
bool restoreState() override;
|
||||
|
||||
private:
|
||||
|
||||
///
|
||||
@@ -149,22 +168,28 @@ private:
|
||||
///
|
||||
/// @brief Change Nanoleaf device to External Control (UDP) mode
|
||||
///
|
||||
/// @return Response from device
|
||||
///@brief
|
||||
QJsonDocument changeToExternalControlMode();
|
||||
/// @return True, if success
|
||||
bool changeToExternalControlMode();
|
||||
///
|
||||
/// @brief Change Nanoleaf device to External Control (UDP) mode
|
||||
///
|
||||
/// @param[out] response from device
|
||||
///
|
||||
/// @return True, if success
|
||||
bool changeToExternalControlMode(QJsonDocument& resp);
|
||||
|
||||
///
|
||||
/// @brief Get command to power Nanoleaf device on or off
|
||||
/// @brief Discover Nanoleaf devices available (for configuration).
|
||||
/// Nanoleaf specific ssdp discovery
|
||||
///
|
||||
/// @param isOn True, if to switch on device
|
||||
/// @return Command to switch device on/off
|
||||
/// @return A JSON structure holding a list of devices found
|
||||
///
|
||||
QString getOnOffRequest(bool isOn) const;
|
||||
QJsonArray discover();
|
||||
|
||||
///REST-API wrapper
|
||||
ProviderRestApi* _restApi;
|
||||
|
||||
QString _hostname;
|
||||
QString _hostName;
|
||||
int _apiPort;
|
||||
QString _authToken;
|
||||
|
||||
@@ -183,6 +208,21 @@ private:
|
||||
|
||||
/// Array of the panel ids.
|
||||
QVector<int> _panelIds;
|
||||
|
||||
QJsonObject _originalStateProperties;
|
||||
|
||||
bool _isBrightnessOverwrite;
|
||||
int _brightness;
|
||||
|
||||
QString _originalColorMode;
|
||||
bool _originalIsOn;
|
||||
int _originalHue;
|
||||
int _originalSat;
|
||||
int _originalCt;
|
||||
int _originalBri;
|
||||
QString _originalEffect;
|
||||
bool _originalIsDynEffect {false};
|
||||
|
||||
};
|
||||
|
||||
#endif // LEDEVICENANOLEAF_H
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace {
|
||||
bool verbose = false;
|
||||
|
||||
// Configuration settings
|
||||
const char CONFIG_ADDRESS[] = "output";
|
||||
const char CONFIG_ADDRESS[] = "host";
|
||||
//const char CONFIG_PORT[] = "port";
|
||||
const char CONFIG_USERNAME[] = "username";
|
||||
const char CONFIG_CLIENTKEY[] = "clientkey";
|
||||
@@ -115,7 +115,7 @@ CiColor CiColor::rgbToCiColor(double red, double green, double blue, const CiCol
|
||||
double cy;
|
||||
double bri;
|
||||
|
||||
if(red + green + blue > 0)
|
||||
if( (red + green + blue) > 0)
|
||||
{
|
||||
// Apply gamma correction.
|
||||
double r = (red > 0.04045) ? pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92);
|
||||
@@ -157,7 +157,7 @@ CiColor CiColor::rgbToCiColor(double red, double green, double blue, const CiCol
|
||||
|
||||
CiColor xy = { cx, cy, bri };
|
||||
|
||||
if(red + green + blue > 0)
|
||||
if( (red + green + blue) > 0)
|
||||
{
|
||||
// Check if the given XY value is within the color reach of our lamps.
|
||||
if (!isPointInLampsReach(xy, colorSpace))
|
||||
@@ -387,8 +387,11 @@ void LedDevicePhilipsHueBridge::log(const char* msg, const char* type, ...) cons
|
||||
vsnprintf(val, max_val_length, type, args);
|
||||
va_end(args);
|
||||
std::string s = msg;
|
||||
int max = 30;
|
||||
s.append(max - s.length(), ' ');
|
||||
size_t max = 30;
|
||||
if (max > s.length())
|
||||
{
|
||||
s.append(max - s.length(), ' ');
|
||||
}
|
||||
Debug( _log, "%s: %s", s.c_str(), val );
|
||||
}
|
||||
|
||||
@@ -649,7 +652,7 @@ const std::set<QString> PhilipsHueLight::GAMUT_A_MODEL_IDS =
|
||||
const std::set<QString> PhilipsHueLight::GAMUT_B_MODEL_IDS =
|
||||
{ "LCT001", "LCT002", "LCT003", "LCT007", "LLM001" };
|
||||
const std::set<QString> PhilipsHueLight::GAMUT_C_MODEL_IDS =
|
||||
{ "LCA001", "LCA002", "LCA003", "LCG002", "LCP001", "LCP002", "LCT010", "LCT011", "LCT012", "LCT014", "LCT015", "LCT016", "LCT024", "LLC020", "LST002" };
|
||||
{ "LCA001", "LCA002", "LCA003", "LCG002", "LCP001", "LCP002", "LCT010", "LCT011", "LCT012", "LCT014", "LCT015", "LCT016", "LCT024", "LCX001", "LLC020", "LST002" };
|
||||
|
||||
PhilipsHueLight::PhilipsHueLight(Logger* log, unsigned int id, QJsonObject values, unsigned int ledidx)
|
||||
: _log(log)
|
||||
@@ -859,7 +862,7 @@ bool LedDevicePhilipsHue::init(const QJsonObject &deviceConfig)
|
||||
|
||||
if( _groupId == 0 )
|
||||
{
|
||||
log( "Group-ID is invalid", "%d", _groupId );
|
||||
Error(_log, "Disabling Entertainment API as Group-ID is invalid" );
|
||||
_useHueEntertainmentAPI = false;
|
||||
}
|
||||
}
|
||||
@@ -888,7 +891,7 @@ bool LedDevicePhilipsHue::setLights()
|
||||
if( _useHueEntertainmentAPI )
|
||||
{
|
||||
_useHueEntertainmentAPI = false;
|
||||
Debug(_log, "Group-ID [%u] is not usable - Entertainment API usage was disabled!", _groupId );
|
||||
Error(_log, "Group-ID [%u] is not usable - Entertainment API usage was disabled!", _groupId );
|
||||
}
|
||||
lArray = _devConfig[ CONFIG_LIGHTIDS ].toArray();
|
||||
}
|
||||
@@ -1018,7 +1021,7 @@ bool LedDevicePhilipsHue::updateLights(const QMap<quint16, QJsonObject> &map)
|
||||
|
||||
if( lightsCount == 0 )
|
||||
{
|
||||
Debug(_log, "No usable lights found!" );
|
||||
Error(_log, "No usable lights found!" );
|
||||
isInitOK = false;
|
||||
}
|
||||
|
||||
@@ -1073,18 +1076,18 @@ bool LedDevicePhilipsHue::openStream()
|
||||
|
||||
if( isInitOK )
|
||||
{
|
||||
Info(_log, "Philips Hue Entertaiment API successful connected! Start Streaming." );
|
||||
Info(_log, "Philips Hue Entertainment API successful connected! Start Streaming." );
|
||||
_allLightsBlack = true;
|
||||
noSignalDetection();
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Philips Hue Entertaiment API not connected!" );
|
||||
Error(_log, "Philips Hue Entertainment API not connected!" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_log, "Philips Hue Entertaiment API could not initialisized!" );
|
||||
Error(_log, "Philips Hue Entertainment API could not be initialised!" );
|
||||
}
|
||||
|
||||
return isInitOK;
|
||||
@@ -1235,7 +1238,7 @@ QByteArray LedDevicePhilipsHue::prepareStreamData() const
|
||||
CiColor lightC = light.getColor();
|
||||
quint64 R = lightC.x * 0xffff;
|
||||
quint64 G = lightC.y * 0xffff;
|
||||
quint64 B = lightC.bri * 0xffff;
|
||||
quint64 B = (lightC.x || lightC.y) ? lightC.bri * 0xffff : 0;
|
||||
unsigned int id = light.getId();
|
||||
const uint8_t payload[] = {
|
||||
0x00, 0x00, static_cast<uint8_t>(id),
|
||||
@@ -1315,7 +1318,7 @@ bool LedDevicePhilipsHue::switchOff()
|
||||
stop_retry_left = 3;
|
||||
if (_useHueEntertainmentAPI)
|
||||
{
|
||||
stopStream();
|
||||
stopStream();
|
||||
}
|
||||
|
||||
return LedDevicePhilipsHueBridge::switchOff();
|
||||
@@ -1467,7 +1470,7 @@ void LedDevicePhilipsHue::setColor(PhilipsHueLight& light, CiColor& color)
|
||||
if( !_useHueEntertainmentAPI )
|
||||
{
|
||||
const int bri = qRound(qMin(254.0, _brightnessFactor * qMax(1.0, color.bri * 254.0)));
|
||||
QString stateCmd = QString("\"%1\":[%2,%3],\"%4\":%5").arg( API_XY_COORDINATES ).arg( color.x, 0, 'd', 4 ).arg( color.y, 0, 'd', 4 ).arg( API_BRIGHTNESS ).arg( bri );
|
||||
QString stateCmd = QString("{\"%1\":[%2,%3],\"%4\":%5}").arg( API_XY_COORDINATES ).arg( color.x, 0, 'd', 4 ).arg( color.y, 0, 'd', 4 ).arg( API_BRIGHTNESS ).arg( bri );
|
||||
setLightState( light.getId(), stateCmd );
|
||||
}
|
||||
else
|
||||
|
||||
@@ -4,9 +4,15 @@ const ushort TPM2_DEFAULT_PORT = 65506;
|
||||
|
||||
LedDeviceTpm2net::LedDeviceTpm2net(const QJsonObject &deviceConfig)
|
||||
: ProviderUdp(deviceConfig)
|
||||
, _tpm2_buffer(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
LedDeviceTpm2net::~LedDeviceTpm2net()
|
||||
{
|
||||
free (_tpm2_buffer);
|
||||
}
|
||||
|
||||
LedDevice* LedDeviceTpm2net::construct(const QJsonObject &deviceConfig)
|
||||
{
|
||||
return new LedDeviceTpm2net(deviceConfig);
|
||||
@@ -23,7 +29,9 @@ bool LedDeviceTpm2net::init(const QJsonObject &deviceConfig)
|
||||
{
|
||||
_tpm2_max = deviceConfig["max-packet"].toInt(170);
|
||||
_tpm2ByteCount = 3 * _ledCount;
|
||||
_tpm2TotalPackets = 1 + _tpm2ByteCount / _tpm2_max;
|
||||
_tpm2TotalPackets = (_tpm2ByteCount / _tpm2_max) + ((_tpm2ByteCount % _tpm2_max) != 0);
|
||||
|
||||
_tpm2_buffer = (uint8_t*) malloc(_tpm2_max+7);
|
||||
|
||||
isInitOK = true;
|
||||
}
|
||||
@@ -32,8 +40,6 @@ bool LedDeviceTpm2net::init(const QJsonObject &deviceConfig)
|
||||
|
||||
int LedDeviceTpm2net::write(const std::vector<ColorRgb> &ledValues)
|
||||
{
|
||||
uint8_t * tpm2_buffer = (uint8_t*) malloc(_tpm2_max+7);
|
||||
|
||||
int retVal = 0;
|
||||
|
||||
int _thisPacketBytes = 0;
|
||||
@@ -48,23 +54,22 @@ int LedDeviceTpm2net::write(const std::vector<ColorRgb> &ledValues)
|
||||
_thisPacketBytes = (_tpm2ByteCount - rawIdx < _tpm2_max) ? _tpm2ByteCount % _tpm2_max : _tpm2_max;
|
||||
// is this the last packet? ? ^^ last packet : ^^ earlier packets
|
||||
|
||||
tpm2_buffer[0] = 0x9c; // Packet start byte
|
||||
tpm2_buffer[1] = 0xda; // Packet type Data frame
|
||||
tpm2_buffer[2] = (_thisPacketBytes >> 8) & 0xff; // Frame size high
|
||||
tpm2_buffer[3] = _thisPacketBytes & 0xff; // Frame size low
|
||||
tpm2_buffer[4] = _tpm2ThisPacket++; // Packet Number
|
||||
tpm2_buffer[5] = _tpm2TotalPackets; // Number of packets
|
||||
_tpm2_buffer[0] = 0x9c; // Packet start byte
|
||||
_tpm2_buffer[1] = 0xda; // Packet type Data frame
|
||||
_tpm2_buffer[2] = (_thisPacketBytes >> 8) & 0xff; // Frame size high
|
||||
_tpm2_buffer[3] = _thisPacketBytes & 0xff; // Frame size low
|
||||
_tpm2_buffer[4] = _tpm2ThisPacket++; // Packet Number
|
||||
_tpm2_buffer[5] = _tpm2TotalPackets; // Number of packets
|
||||
}
|
||||
|
||||
tpm2_buffer [6 + rawIdx%_tpm2_max] = rawdata[rawIdx];
|
||||
_tpm2_buffer [6 + rawIdx%_tpm2_max] = rawdata[rawIdx];
|
||||
|
||||
// is this the last byte of last packet || last byte of other packets
|
||||
if ( (rawIdx == _tpm2ByteCount-1) || (rawIdx %_tpm2_max == _tpm2_max-1) )
|
||||
{
|
||||
tpm2_buffer [6 + rawIdx%_tpm2_max +1] = 0x36; // Packet end byte
|
||||
retVal &= writeBytes(_thisPacketBytes+7, tpm2_buffer);
|
||||
_tpm2_buffer [6 + rawIdx%_tpm2_max +1] = 0x36; // Packet end byte
|
||||
retVal &= writeBytes(_thisPacketBytes+7, _tpm2_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,11 @@ public:
|
||||
///
|
||||
explicit LedDeviceTpm2net(const QJsonObject &deviceConfig);
|
||||
|
||||
///
|
||||
/// @brief Destructor of the TPM2 LED-device
|
||||
///
|
||||
~LedDeviceTpm2net() override;
|
||||
|
||||
///
|
||||
/// @brief Constructs the LED-device
|
||||
///
|
||||
@@ -48,6 +53,8 @@ private:
|
||||
int _tpm2ByteCount;
|
||||
int _tpm2TotalPackets;
|
||||
int _tpm2ThisPacket;
|
||||
|
||||
uint8_t * _tpm2_buffer;
|
||||
};
|
||||
|
||||
#endif // LEDEVICETPM2NET_H
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
#include "LedDeviceUdpRaw.h"
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
|
||||
const bool verbose = false;
|
||||
|
||||
const ushort RAW_DEFAULT_PORT=5568;
|
||||
const int UDP_MAX_LED_NUM = 490;
|
||||
|
||||
} //End of constants
|
||||
|
||||
LedDeviceUdpRaw::LedDeviceUdpRaw(const QJsonObject &deviceConfig)
|
||||
: ProviderUdp(deviceConfig)
|
||||
@@ -16,8 +24,28 @@ bool LedDeviceUdpRaw::init(const QJsonObject &deviceConfig)
|
||||
{
|
||||
_port = RAW_DEFAULT_PORT;
|
||||
|
||||
// Initialise sub-class
|
||||
bool isInitOK = ProviderUdp::init(deviceConfig);
|
||||
bool isInitOK = false;
|
||||
if ( LedDevice::init(deviceConfig) )
|
||||
{
|
||||
// Initialise LedDevice configuration and execution environment
|
||||
int configuredLedCount = this->getLedCount();
|
||||
Debug(_log, "DeviceType : %s", QSTRING_CSTR( this->getActiveDeviceType() ));
|
||||
Debug(_log, "LedCount : %d", configuredLedCount);
|
||||
Debug(_log, "ColorOrder : %s", QSTRING_CSTR( this->getColorOrder() ));
|
||||
Debug(_log, "LatchTime : %d", this->getLatchTime());
|
||||
|
||||
if (configuredLedCount > UDP_MAX_LED_NUM)
|
||||
{
|
||||
QString errorReason = QString("Device type %1 can only be run with maximum %2 LEDs!").arg(this->getActiveDeviceType()).arg(UDP_MAX_LED_NUM);
|
||||
this->setInError ( errorReason );
|
||||
isInitOK = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialise sub-class
|
||||
isInitOK = ProviderUdp::init(deviceConfig);
|
||||
}
|
||||
}
|
||||
return isInitOK;
|
||||
}
|
||||
|
||||
@@ -27,3 +55,18 @@ int LedDeviceUdpRaw::write(const std::vector<ColorRgb> &ledValues)
|
||||
|
||||
return writeBytes(_ledRGBCount, dataPtr);
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceUdpRaw::getProperties(const QJsonObject& params)
|
||||
{
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
QJsonObject properties;
|
||||
|
||||
QJsonObject propertiesDetails;
|
||||
propertiesDetails.insert("maxLedCount", UDP_MAX_LED_NUM);
|
||||
|
||||
properties.insert("properties", propertiesDetails);
|
||||
|
||||
DebugIf(verbose, _log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,14 @@ public:
|
||||
///
|
||||
static LedDevice* construct(const QJsonObject &deviceConfig);
|
||||
|
||||
///
|
||||
/// @brief Get a UDP-Raw device's resource properties
|
||||
///
|
||||
/// @param[in] params Parameters to query device
|
||||
/// @return A JSON structure holding the device's properties
|
||||
///
|
||||
QJsonObject getProperties(const QJsonObject& params) override;
|
||||
|
||||
protected:
|
||||
|
||||
///
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
// Local-Hyperion includes
|
||||
#include "LedDeviceWled.h"
|
||||
|
||||
#include <ssdp/SSDPDiscover.h>
|
||||
#include <utils/QStringUtils.h>
|
||||
#include <utils/WaitTime.h>
|
||||
#include <QThread>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
|
||||
const bool verbose = false;
|
||||
|
||||
// Configuration settings
|
||||
const char CONFIG_ADDRESS[] = "host";
|
||||
const char CONFIG_RESTORE_STATE[] = "restoreOriginalState";
|
||||
const char CONFIG_BRIGHTNESS[] = "brightness";
|
||||
const char CONFIG_BRIGHTNESS_OVERWRITE[] = "overwriteBrightness";
|
||||
const char CONFIG_SYNC_OVERWRITE[] = "overwriteSync";
|
||||
|
||||
// UDP elements
|
||||
const quint16 STREAM_DEFAULT_PORT = 19446;
|
||||
const int UDP_MAX_LED_NUM = 490;
|
||||
|
||||
// WLED JSON-API elements
|
||||
const int API_DEFAULT_PORT = -1; //Use default port per communication scheme
|
||||
@@ -24,12 +34,14 @@ const char API_PATH_STATE[] = "state";
|
||||
const char STATE_ON[] = "on";
|
||||
const char STATE_VALUE_TRUE[] = "true";
|
||||
const char STATE_VALUE_FALSE[] = "false";
|
||||
const char STATE_LIVE[] = "live";
|
||||
|
||||
// WLED ssdp services
|
||||
// TODO: WLED - Update ssdp discovery parameters when available
|
||||
const char SSDP_ID[] = "ssdp:all";
|
||||
const char SSDP_FILTER[] = "(.*)";
|
||||
const char SSDP_FILTER_HEADER[] = "ST";
|
||||
const bool DEFAULT_IS_RESTORE_STATE = false;
|
||||
const bool DEFAULT_IS_BRIGHTNESS_OVERWRITE = true;
|
||||
const int BRI_MAX = 255;
|
||||
const bool DEFAULT_IS_SYNC_OVERWRITE = true;
|
||||
|
||||
constexpr std::chrono::milliseconds DEFAULT_IDENTIFY_TIME{ 2000 };
|
||||
|
||||
} //End of constants
|
||||
|
||||
@@ -37,6 +49,11 @@ LedDeviceWled::LedDeviceWled(const QJsonObject &deviceConfig)
|
||||
: ProviderUdp(deviceConfig)
|
||||
,_restApi(nullptr)
|
||||
,_apiPort(API_DEFAULT_PORT)
|
||||
,_isBrightnessOverwrite(DEFAULT_IS_BRIGHTNESS_OVERWRITE)
|
||||
,_brightness (BRI_MAX)
|
||||
,_isSyncOverwrite(DEFAULT_IS_SYNC_OVERWRITE)
|
||||
,_originalStateUdpnSend(false)
|
||||
,_originalStateUdpnRecv(true)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -53,7 +70,6 @@ LedDevice* LedDeviceWled::construct(const QJsonObject &deviceConfig)
|
||||
|
||||
bool LedDeviceWled::init(const QJsonObject &deviceConfig)
|
||||
{
|
||||
Debug(_log, "");
|
||||
bool isInitOK = false;
|
||||
|
||||
// Initialise LedDevice sub-class, ProviderUdp::init will be executed later, if connectivity is defined
|
||||
@@ -66,18 +82,35 @@ bool LedDeviceWled::init(const QJsonObject &deviceConfig)
|
||||
Debug(_log, "ColorOrder : %s", QSTRING_CSTR( this->getColorOrder() ));
|
||||
Debug(_log, "LatchTime : %d", this->getLatchTime());
|
||||
|
||||
if (configuredLedCount > UDP_MAX_LED_NUM)
|
||||
{
|
||||
QString errorReason = QString("Device type %1 can only be run with maximum %2 LEDs!").arg(this->getActiveDeviceType()).arg(UDP_MAX_LED_NUM);
|
||||
this->setInError ( errorReason );
|
||||
return false;
|
||||
}
|
||||
|
||||
_isRestoreOrigState = _devConfig[CONFIG_RESTORE_STATE].toBool(DEFAULT_IS_RESTORE_STATE);
|
||||
_isSyncOverwrite = _devConfig[CONFIG_SYNC_OVERWRITE].toBool(DEFAULT_IS_SYNC_OVERWRITE);
|
||||
_isBrightnessOverwrite = _devConfig[CONFIG_BRIGHTNESS_OVERWRITE].toBool(DEFAULT_IS_BRIGHTNESS_OVERWRITE);
|
||||
_brightness = _devConfig[CONFIG_BRIGHTNESS].toInt(BRI_MAX);
|
||||
|
||||
Debug(_log, "RestoreOrigState : %d", _isRestoreOrigState);
|
||||
Debug(_log, "Overwrite Sync. : %d", _isSyncOverwrite);
|
||||
Debug(_log, "Overwrite Brightn.: %d", _isBrightnessOverwrite);
|
||||
Debug(_log, "Set Brightness to : %d", _brightness);
|
||||
|
||||
//Set hostname as per configuration
|
||||
QString address = deviceConfig[ CONFIG_ADDRESS ].toString();
|
||||
QString hostName = deviceConfig[ CONFIG_ADDRESS ].toString();
|
||||
|
||||
//If host not configured the init fails
|
||||
if ( address.isEmpty() )
|
||||
if ( hostName.isEmpty() )
|
||||
{
|
||||
this->setInError("No target hostname nor IP defined");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
QStringList addressparts = QStringUtils::split(address,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QStringList addressparts = QStringUtils::split(hostName,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
_hostname = addressparts[0];
|
||||
if ( addressparts.size() > 1 )
|
||||
{
|
||||
@@ -100,13 +133,11 @@ bool LedDeviceWled::init(const QJsonObject &deviceConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
Debug(_log, "[%d]", isInitOK);
|
||||
return isInitOK;
|
||||
}
|
||||
|
||||
bool LedDeviceWled::initRestAPI(const QString &hostname, int port)
|
||||
{
|
||||
Debug(_log, "");
|
||||
bool isInitOK = false;
|
||||
|
||||
if ( _restApi == nullptr )
|
||||
@@ -116,38 +147,88 @@ bool LedDeviceWled::initRestAPI(const QString &hostname, int port)
|
||||
|
||||
isInitOK = true;
|
||||
}
|
||||
|
||||
Debug(_log, "[%d]", isInitOK);
|
||||
return isInitOK;
|
||||
}
|
||||
|
||||
QString LedDeviceWled::getOnOffRequest(bool isOn) const
|
||||
{
|
||||
QString state = isOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
|
||||
return QString( "{\"%1\":%2}" ).arg( STATE_ON, state);
|
||||
return QString( "\"%1\":%2,\"%3\":%4" ).arg( STATE_ON, state).arg( STATE_LIVE, state);
|
||||
}
|
||||
|
||||
QString LedDeviceWled::getBrightnessRequest(int bri) const
|
||||
{
|
||||
return QString( "\"bri\":%1" ).arg(bri);
|
||||
}
|
||||
|
||||
QString LedDeviceWled::getEffectRequest(int effect, int speed) const
|
||||
{
|
||||
return QString( "\"seg\":{\"fx\":%1,\"sx\":%2}" ).arg(effect).arg(speed);
|
||||
}
|
||||
|
||||
QString LedDeviceWled::getLorRequest(int lor) const
|
||||
{
|
||||
return QString( "\"lor\":%1" ).arg(lor);
|
||||
}
|
||||
|
||||
QString LedDeviceWled::getUdpnRequest(bool isSendOn, bool isRecvOn) const
|
||||
{
|
||||
QString send = isSendOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
|
||||
QString recv = isRecvOn ? STATE_VALUE_TRUE : STATE_VALUE_FALSE;
|
||||
return QString( "\"udpn\":{\"send\":%1,\"recv\":%2}" ).arg(send, recv);
|
||||
}
|
||||
|
||||
bool LedDeviceWled::sendStateUpdateRequest(const QString &request)
|
||||
{
|
||||
bool rc = true;
|
||||
|
||||
_restApi->setPath(API_PATH_STATE);
|
||||
|
||||
httpResponse response1 = _restApi->put(QString("{%1}").arg(request));
|
||||
if ( response1.error() )
|
||||
{
|
||||
rc = false;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
bool LedDeviceWled::powerOn()
|
||||
{
|
||||
Debug(_log, "");
|
||||
bool on = true;
|
||||
bool on = false;
|
||||
if ( _isDeviceReady)
|
||||
{
|
||||
//Power-on WLED device
|
||||
_restApi->setPath(API_PATH_STATE);
|
||||
httpResponse response = _restApi->put(getOnOffRequest(true));
|
||||
|
||||
QString cmd = getOnOffRequest(true);
|
||||
|
||||
if ( _isBrightnessOverwrite)
|
||||
{
|
||||
cmd += "," + getBrightnessRequest(_brightness);
|
||||
}
|
||||
|
||||
if (_isSyncOverwrite)
|
||||
{
|
||||
Debug( _log, "Disable synchronisation with other WLED devices");
|
||||
cmd += "," + getUdpnRequest(false, false);
|
||||
}
|
||||
|
||||
httpResponse response = _restApi->put(QString("{%1}").arg(cmd));
|
||||
if ( response.error() )
|
||||
{
|
||||
this->setInError ( response.getErrorReason() );
|
||||
QString errorReason = QString("Power-on request failed with error: '%1'").arg(response.getErrorReason());
|
||||
this->setInError ( errorReason );
|
||||
on = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
on = true;
|
||||
}
|
||||
}
|
||||
return on;
|
||||
}
|
||||
|
||||
bool LedDeviceWled::powerOff()
|
||||
{
|
||||
Debug(_log, "");
|
||||
bool off = true;
|
||||
if ( _isDeviceReady)
|
||||
{
|
||||
@@ -156,53 +237,104 @@ bool LedDeviceWled::powerOff()
|
||||
|
||||
//Power-off the WLED device physically
|
||||
_restApi->setPath(API_PATH_STATE);
|
||||
httpResponse response = _restApi->put(getOnOffRequest(false));
|
||||
|
||||
QString cmd = getOnOffRequest(false);
|
||||
|
||||
if (_isSyncOverwrite)
|
||||
{
|
||||
Debug( _log, "Restore synchronisation with other WLED devices");
|
||||
cmd += "," + getUdpnRequest(_originalStateUdpnSend, _originalStateUdpnRecv);
|
||||
}
|
||||
|
||||
httpResponse response = _restApi->put(QString("{%1}").arg(cmd));
|
||||
if ( response.error() )
|
||||
{
|
||||
this->setInError ( response.getErrorReason() );
|
||||
QString errorReason = QString("Power-off request failed with error: '%1'").arg(response.getErrorReason());
|
||||
this->setInError ( errorReason );
|
||||
off = false;
|
||||
}
|
||||
}
|
||||
return off;
|
||||
}
|
||||
|
||||
bool LedDeviceWled::storeState()
|
||||
{
|
||||
bool rc = true;
|
||||
|
||||
if ( _isRestoreOrigState || _isSyncOverwrite )
|
||||
{
|
||||
_restApi->setPath(API_PATH_STATE);
|
||||
|
||||
httpResponse response = _restApi->get();
|
||||
if ( response.error() )
|
||||
{
|
||||
QString errorReason = QString("Storing device state failed with error: '%1'").arg(response.getErrorReason());
|
||||
setInError(errorReason);
|
||||
rc = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_originalStateProperties = response.getBody().object();
|
||||
DebugIf(verbose, _log, "state: [%s]", QString(QJsonDocument(_originalStateProperties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
QJsonObject udpn = _originalStateProperties.value("udpn").toObject();
|
||||
if (!udpn.isEmpty())
|
||||
{
|
||||
_originalStateUdpnSend = udpn["send"].toBool(false);
|
||||
_originalStateUdpnRecv = udpn["recv"].toBool(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool LedDeviceWled::restoreState()
|
||||
{
|
||||
bool rc = true;
|
||||
|
||||
if ( _isRestoreOrigState )
|
||||
{
|
||||
_restApi->setPath(API_PATH_STATE);
|
||||
|
||||
_originalStateProperties[STATE_LIVE] = false;
|
||||
|
||||
httpResponse response = _restApi->put(QString(QJsonDocument(_originalStateProperties).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
if ( response.error() )
|
||||
{
|
||||
Warning (_log, "%s restoring state failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceWled::discover(const QJsonObject& /*params*/)
|
||||
{
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
|
||||
|
||||
QJsonArray deviceList;
|
||||
|
||||
// Discover WLED Devices
|
||||
SSDPDiscover discover;
|
||||
discover.skipDuplicateKeys(true);
|
||||
discover.setSearchFilter(SSDP_FILTER, SSDP_FILTER_HEADER);
|
||||
QString searchTarget = SSDP_ID;
|
||||
|
||||
if ( discover.discoverServices(searchTarget) > 0 )
|
||||
{
|
||||
deviceList = discover.getServicesDiscoveredJson();
|
||||
}
|
||||
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
DebugIf(verbose, _log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
return devicesDiscovered;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceWled::getProperties(const QJsonObject& params)
|
||||
{
|
||||
Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
QJsonObject properties;
|
||||
|
||||
// Get Nanoleaf device properties
|
||||
QString host = params["host"].toString("");
|
||||
if ( !host.isEmpty() )
|
||||
QString hostName = params["host"].toString("");
|
||||
|
||||
if ( !hostName.isEmpty() )
|
||||
{
|
||||
QString filter = params["filter"].toString("");
|
||||
|
||||
// Resolve hostname and port (or use default API port)
|
||||
QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QStringList addressparts = QStringUtils::split(hostName,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QString apiHost = addressparts[0];
|
||||
int apiPort;
|
||||
|
||||
@@ -224,51 +356,50 @@ QJsonObject LedDeviceWled::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();
|
||||
propertiesDetails.insert("maxLedCount", UDP_MAX_LED_NUM);
|
||||
|
||||
Debug(_log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
properties.insert("properties", propertiesDetails);
|
||||
|
||||
DebugIf(verbose, _log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
void LedDeviceWled::identify(const QJsonObject& /*params*/)
|
||||
void LedDeviceWled::identify(const QJsonObject& params)
|
||||
{
|
||||
#if 0
|
||||
Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
QString host = params["host"].toString("");
|
||||
if ( !host.isEmpty() )
|
||||
QString hostName = params["host"].toString("");
|
||||
|
||||
if ( !hostName.isEmpty() )
|
||||
{
|
||||
// Resolve hostname and port (or use default API port)
|
||||
QStringList addressparts = QStringUtils::split(host,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QStringList addressparts = QStringUtils::split(hostName,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QString apiHost = addressparts[0];
|
||||
int apiPort;
|
||||
|
||||
if ( addressparts.size() > 1)
|
||||
{
|
||||
apiPort = addressparts[1].toInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
apiPort = API_DEFAULT_PORT;
|
||||
}
|
||||
|
||||
// TODO: WLED::identify - Replace with valid identification code
|
||||
initRestAPI(apiHost, apiPort);
|
||||
|
||||
// initRestAPI(apiHost, apiPort);
|
||||
_isRestoreOrigState = true;
|
||||
storeState();
|
||||
|
||||
// QString resource = QString("%1/%2/%3").arg( API_LIGHTS ).arg( lightId ).arg( API_STATE);
|
||||
// _restApi->setPath(resource);
|
||||
QString request = getOnOffRequest(true) + "," + getLorRequest(1) + "," + getEffectRequest(25);
|
||||
sendStateUpdateRequest(request);
|
||||
|
||||
// QString stateCmd;
|
||||
// stateCmd += QString("\"%1\":%2,").arg( API_STATE_ON ).arg( API_STATE_VALUE_TRUE );
|
||||
// stateCmd += QString("\"%1\":\"%2\"").arg( "alert" ).arg( "select" );
|
||||
// stateCmd = "{" + stateCmd + "}";
|
||||
wait(DEFAULT_IDENTIFY_TIME);
|
||||
|
||||
// // Perform request
|
||||
// httpResponse response = _restApi->put(stateCmd);
|
||||
// if ( response.error() )
|
||||
// {
|
||||
// Warning (_log, "%s identification failed with error: '%s'", QSTRING_CSTR(_activeDeviceType), QSTRING_CSTR(response.getErrorReason()));
|
||||
// }
|
||||
restoreState();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int LedDeviceWled::write(const std::vector<ColorRgb> &ledValues)
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
|
||||
///
|
||||
/// Implementation of a WLED-device
|
||||
/// ...
|
||||
///
|
||||
///
|
||||
class LedDeviceWled : public ProviderUdp
|
||||
{
|
||||
@@ -105,6 +103,25 @@ protected:
|
||||
///
|
||||
bool powerOff() override;
|
||||
|
||||
///
|
||||
/// @brief Store the device's original state.
|
||||
///
|
||||
/// Save the device's state before hyperion color streaming starts allowing to restore state during switchOff().
|
||||
///
|
||||
/// @return True if success
|
||||
///
|
||||
bool storeState() override;
|
||||
|
||||
///
|
||||
/// @brief Restore the device's original state.
|
||||
///
|
||||
/// Restore the device's state as before hyperion color streaming started.
|
||||
/// This includes the on/off state of the device.
|
||||
///
|
||||
/// @return True, if success
|
||||
///
|
||||
bool restoreState() override;
|
||||
|
||||
private:
|
||||
|
||||
///
|
||||
@@ -124,11 +141,27 @@ private:
|
||||
///
|
||||
QString getOnOffRequest (bool isOn ) const;
|
||||
|
||||
QString getBrightnessRequest (int bri ) const;
|
||||
QString getEffectRequest(int effect, int speed=128) const;
|
||||
QString getLorRequest(int lor) const;
|
||||
QString getUdpnRequest(bool send, bool recv) const;
|
||||
|
||||
bool sendStateUpdateRequest(const QString &request);
|
||||
|
||||
///REST-API wrapper
|
||||
ProviderRestApi* _restApi;
|
||||
|
||||
QString _hostname;
|
||||
int _apiPort;
|
||||
|
||||
QJsonObject _originalStateProperties;
|
||||
|
||||
bool _isBrightnessOverwrite;
|
||||
int _brightness;
|
||||
|
||||
bool _isSyncOverwrite;
|
||||
bool _originalStateUdpnSend;
|
||||
bool _originalStateUdpnRecv;
|
||||
};
|
||||
|
||||
#endif // LEDDEVICEWLED_H
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "LedDeviceYeelight.h"
|
||||
#include "LedDeviceYeelight.h"
|
||||
|
||||
#include <ssdp/SSDPDiscover.h>
|
||||
#include <utils/QStringUtils.h>
|
||||
@@ -234,12 +234,12 @@ int YeelightLight::writeCommand( const QJsonDocument &command, QJsonArray &resul
|
||||
if ( ! _tcpSocket->waitForBytesWritten(WRITE_TIMEOUT.count()) )
|
||||
{
|
||||
QString errorReason = QString ("(%1) %2").arg(_tcpSocket->error()).arg( _tcpSocket->errorString());
|
||||
log ( 2, "Error:", "bytesWritten: [%ll], %s", bytesWritten, QSTRING_CSTR(errorReason));
|
||||
log ( 2, "Error:", "bytesWritten: [%lld], %s", bytesWritten, QSTRING_CSTR(errorReason));
|
||||
this->setInError ( errorReason );
|
||||
}
|
||||
else
|
||||
{
|
||||
log ( 3, "Success:", "Bytes written [%ll]", bytesWritten );
|
||||
log ( 3, "Success:", "Bytes written [%lld]", bytesWritten );
|
||||
|
||||
// Avoid to overrun the Yeelight Command Quota
|
||||
qint64 elapsedTime = QDateTime::currentMSecsSinceEpoch() - _lastWriteTime;
|
||||
@@ -258,7 +258,7 @@ int YeelightLight::writeCommand( const QJsonDocument &command, QJsonArray &resul
|
||||
{
|
||||
do
|
||||
{
|
||||
log ( 3, "Reading:", "Bytes available [%ll]", _tcpSocket->bytesAvailable() );
|
||||
log ( 3, "Reading:", "Bytes available [%lld]", _tcpSocket->bytesAvailable() );
|
||||
while ( _tcpSocket->canReadLine() )
|
||||
{
|
||||
QByteArray response = _tcpSocket->readLine();
|
||||
@@ -338,7 +338,7 @@ bool YeelightLight::streamCommand( const QJsonDocument &command )
|
||||
{
|
||||
int error = _tcpStreamSocket->error();
|
||||
QString errorReason = QString ("(%1) %2").arg(error).arg( _tcpStreamSocket->errorString());
|
||||
log ( 1, "Error:", "bytesWritten: [%ll], %s", bytesWritten, QSTRING_CSTR(errorReason));
|
||||
log ( 1, "Error:", "bytesWritten: [%lld], %s", bytesWritten, QSTRING_CSTR(errorReason));
|
||||
|
||||
if ( error == QAbstractSocket::RemoteHostClosedError )
|
||||
{
|
||||
@@ -353,7 +353,7 @@ bool YeelightLight::streamCommand( const QJsonDocument &command )
|
||||
}
|
||||
else
|
||||
{
|
||||
log ( 3, "Success:", "Bytes written [%ll]", bytesWritten );
|
||||
log ( 3, "Success:", "Bytes written [%lld]", bytesWritten );
|
||||
rc = true;
|
||||
}
|
||||
}
|
||||
@@ -956,7 +956,10 @@ void YeelightLight::log(int logLevel, const char* msg, const char* type, ...)
|
||||
va_end(args);
|
||||
std::string s = msg;
|
||||
uint max = 20;
|
||||
s.append(max - s.length(), ' ');
|
||||
if (max > s.length())
|
||||
{
|
||||
s.append(max - s.length(), ' ');
|
||||
}
|
||||
|
||||
Debug( _log, "%d|%15.15s| %s: %s", logLevel, QSTRING_CSTR(_name), s.c_str(), val);
|
||||
}
|
||||
@@ -1015,10 +1018,9 @@ bool LedDeviceYeelight::init(const QJsonObject &deviceConfig)
|
||||
|
||||
//Get device specific configuration
|
||||
|
||||
bool ok;
|
||||
if ( deviceConfig[ CONFIG_COLOR_MODEL ].isString() )
|
||||
{
|
||||
_outputColorModel = deviceConfig[ CONFIG_COLOR_MODEL ].toString().toInt(&ok,MODEL_RGB);
|
||||
_outputColorModel = deviceConfig[ CONFIG_COLOR_MODEL ].toString(QString(MODEL_RGB)).toInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1027,7 +1029,7 @@ bool LedDeviceYeelight::init(const QJsonObject &deviceConfig)
|
||||
|
||||
if ( deviceConfig[ CONFIG_TRANS_EFFECT ].isString() )
|
||||
{
|
||||
_transitionEffect = static_cast<YeelightLight::API_EFFECT>( deviceConfig[ CONFIG_TRANS_EFFECT ].toString().toInt(&ok, YeelightLight::API_EFFECT_SMOOTH) );
|
||||
_transitionEffect = static_cast<YeelightLight::API_EFFECT>( deviceConfig[ CONFIG_TRANS_EFFECT ].toString(QString(YeelightLight::API_EFFECT_SMOOTH)).toInt() );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1044,7 +1046,7 @@ bool LedDeviceYeelight::init(const QJsonObject &deviceConfig)
|
||||
|
||||
if ( deviceConfig[ CONFIG_DEBUGLEVEL ].isString() )
|
||||
{
|
||||
_debuglevel = deviceConfig[ CONFIG_DEBUGLEVEL ].toString().toInt();
|
||||
_debuglevel = deviceConfig[ CONFIG_DEBUGLEVEL ].toString(QString("0")).toInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1076,12 +1078,12 @@ bool LedDeviceYeelight::init(const QJsonObject &deviceConfig)
|
||||
int configuredYeelightsCount = 0;
|
||||
for (const QJsonValueRef light : configuredYeelightLights)
|
||||
{
|
||||
QString host = light.toObject().value("host").toString();
|
||||
QString hostName = light.toObject().value("host").toString();
|
||||
int port = light.toObject().value("port").toInt(API_DEFAULT_PORT);
|
||||
if ( !host.isEmpty() )
|
||||
if ( !hostName.isEmpty() )
|
||||
{
|
||||
QString name = light.toObject().value("name").toString();
|
||||
Debug(_log, "Light [%u] - %s (%s:%d)", configuredYeelightsCount, QSTRING_CSTR(name), QSTRING_CSTR(host), port );
|
||||
Debug(_log, "Light [%u] - %s (%s:%d)", configuredYeelightsCount, QSTRING_CSTR(name), QSTRING_CSTR(hostName), port );
|
||||
++configuredYeelightsCount;
|
||||
}
|
||||
}
|
||||
@@ -1107,10 +1109,10 @@ bool LedDeviceYeelight::init(const QJsonObject &deviceConfig)
|
||||
_lightsAddressList.clear();
|
||||
for (int j = 0; j < static_cast<int>( configuredLedCount ); ++j)
|
||||
{
|
||||
QString address = configuredYeelightLights[j].toObject().value("host").toString();
|
||||
QString hostName = configuredYeelightLights[j].toObject().value("host").toString();
|
||||
int port = configuredYeelightLights[j].toObject().value("port").toInt(API_DEFAULT_PORT);
|
||||
|
||||
QStringList addressparts = QStringUtils::split(address,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QStringList addressparts = QStringUtils::split(hostName,":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QString apiHost = addressparts[0];
|
||||
int apiPort = port;
|
||||
|
||||
@@ -1347,14 +1349,10 @@ bool LedDeviceYeelight::restoreState()
|
||||
return rc;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceYeelight::discover(const QJsonObject& /*params*/)
|
||||
QJsonArray LedDeviceYeelight::discover()
|
||||
{
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
|
||||
|
||||
QJsonArray deviceList;
|
||||
|
||||
// Discover Yeelight Devices
|
||||
SSDPDiscover discover;
|
||||
discover.setPort(SSDP_PORT);
|
||||
discover.skipDuplicateKeys(true);
|
||||
@@ -1365,25 +1363,36 @@ QJsonObject LedDeviceYeelight::discover(const QJsonObject& /*params*/)
|
||||
{
|
||||
deviceList = discover.getServicesDiscoveredJson();
|
||||
}
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceYeelight::discover(const QJsonObject& /*params*/)
|
||||
{
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
|
||||
|
||||
QString discoveryMethod("ssdp");
|
||||
QJsonArray deviceList;
|
||||
deviceList = discover();
|
||||
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
Debug(_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
DebugIf(verbose,_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
return devicesDiscovered;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceYeelight::getProperties(const QJsonObject& params)
|
||||
{
|
||||
Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
QJsonObject properties;
|
||||
|
||||
QString apiHostname = params["hostname"].toString("");
|
||||
QString hostName = params["hostname"].toString("");
|
||||
quint16 apiPort = static_cast<quint16>( params["port"].toInt(API_DEFAULT_PORT) );
|
||||
Debug (_log, "apiHost [%s], apiPort [%d]", QSTRING_CSTR(apiHostname), apiPort);
|
||||
|
||||
if ( !apiHostname.isEmpty() )
|
||||
if ( !hostName.isEmpty() )
|
||||
{
|
||||
YeelightLight yeelight(_log, apiHostname, apiPort);
|
||||
YeelightLight yeelight(_log, hostName, apiPort);
|
||||
|
||||
//yeelight.setDebuglevel(3);
|
||||
if ( yeelight.open() )
|
||||
@@ -1399,15 +1408,15 @@ QJsonObject LedDeviceYeelight::getProperties(const QJsonObject& params)
|
||||
|
||||
void LedDeviceYeelight::identify(const QJsonObject& params)
|
||||
{
|
||||
Debug(_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData() );
|
||||
|
||||
QString apiHostname = params["hostname"].toString("");
|
||||
QString hostName = params["hostname"].toString("");
|
||||
quint16 apiPort = static_cast<quint16>( params["port"].toInt(API_DEFAULT_PORT) );
|
||||
Debug (_log, "apiHost [%s], apiPort [%d]", QSTRING_CSTR(apiHostname), apiPort);
|
||||
Debug (_log, "apiHost [%s], apiPort [%d]", QSTRING_CSTR(hostName), apiPort);
|
||||
|
||||
if ( !apiHostname.isEmpty() )
|
||||
if ( !hostName.isEmpty() )
|
||||
{
|
||||
YeelightLight yeelight(_log, apiHostname, apiPort);
|
||||
YeelightLight yeelight(_log, hostName, apiPort);
|
||||
//yeelight.setDebuglevel(3);
|
||||
|
||||
if ( yeelight.open() )
|
||||
|
||||
@@ -591,6 +591,14 @@ private:
|
||||
///
|
||||
uint getLightsCount() const { return _lightsCount; }
|
||||
|
||||
///
|
||||
/// @brief Discover Yeelight devices available (for configuration).
|
||||
/// Yeelight specific UDP Broadcast discovery
|
||||
///
|
||||
/// @return A JSON structure holding a list of devices found
|
||||
///
|
||||
QJsonArray discover();
|
||||
|
||||
/// Array of the Yeelight addresses handled by the LED-device
|
||||
QVector<yeelightAddress> _lightsAddressList;
|
||||
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
#include <QEventLoop>
|
||||
#include <QNetworkReply>
|
||||
#include <QByteArray>
|
||||
#include <QJsonObject>
|
||||
|
||||
//std includes
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
@@ -16,6 +18,13 @@ bool verbose = false;
|
||||
|
||||
const QChar ONE_SLASH = '/';
|
||||
|
||||
const int HTTP_STATUS_NO_CONTENT = 204;
|
||||
const int HTTP_STATUS_BAD_REQUEST = 400;
|
||||
const int HTTP_STATUS_UNAUTHORIZED = 401;
|
||||
const int HTTP_STATUS_NOT_FOUND = 404;
|
||||
|
||||
constexpr std::chrono::milliseconds DEFAULT_REST_TIMEOUT{ 400 };
|
||||
|
||||
} //End of constants
|
||||
|
||||
ProviderRestApi::ProviderRestApi(const QString& host, int port, const QString& basePath)
|
||||
@@ -64,7 +73,7 @@ void ProviderRestApi::appendPath(const QString& path)
|
||||
appendPath(_path, path);
|
||||
}
|
||||
|
||||
void ProviderRestApi::appendPath(QString& path, const QString& appendPath) const
|
||||
void ProviderRestApi::appendPath ( QString& path, const QString &appendPath)
|
||||
{
|
||||
if (!appendPath.isEmpty() && appendPath != ONE_SLASH)
|
||||
{
|
||||
@@ -123,21 +132,27 @@ httpResponse ProviderRestApi::get()
|
||||
|
||||
httpResponse ProviderRestApi::get(const QUrl& url)
|
||||
{
|
||||
DebugIf(verbose,_log, "GET: [%s]", QSTRING_CSTR(url.toString()));
|
||||
|
||||
// Perform request
|
||||
QNetworkRequest request(url);
|
||||
QNetworkReply* reply = _networkManager->get(request);
|
||||
|
||||
// Connect requestFinished signal to quit slot of the loop.
|
||||
QEventLoop loop;
|
||||
QEventLoop::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
|
||||
ReplyTimeout::set(reply, DEFAULT_REST_TIMEOUT.count());
|
||||
|
||||
// Go into the loop until the request is finished.
|
||||
loop.exec();
|
||||
|
||||
httpResponse response;
|
||||
if (reply->operation() == QNetworkAccessManager::GetOperation)
|
||||
{
|
||||
response = getResponse(reply);
|
||||
if(reply->error() != QNetworkReply::NoError)
|
||||
{
|
||||
Debug(_log, "GET: [%s]", QSTRING_CSTR( url.toString() ));
|
||||
}
|
||||
response = getResponse(reply );
|
||||
}
|
||||
// Free space.
|
||||
reply->deleteLater();
|
||||
@@ -145,28 +160,37 @@ httpResponse ProviderRestApi::get(const QUrl& url)
|
||||
return response;
|
||||
}
|
||||
|
||||
httpResponse ProviderRestApi::put(const QString& body)
|
||||
httpResponse ProviderRestApi::put(const QJsonObject &body)
|
||||
{
|
||||
return put(getUrl(), body);
|
||||
return put( getUrl(), QJsonDocument(body).toJson(QJsonDocument::Compact));
|
||||
}
|
||||
|
||||
httpResponse ProviderRestApi::put(const QUrl& url, const QString& body)
|
||||
httpResponse ProviderRestApi::put(const QString &body)
|
||||
{
|
||||
DebugIf(verbose, _log, "PUT: [%s] [%s]", QSTRING_CSTR(url.toString()), QSTRING_CSTR(body));
|
||||
// Perform request
|
||||
QNetworkRequest request(_networkRequestHeaders);
|
||||
request.setUrl(url);
|
||||
return put( getUrl(), body.toUtf8() );
|
||||
}
|
||||
|
||||
QNetworkReply* reply = _networkManager->put(request, body.toUtf8());
|
||||
httpResponse ProviderRestApi::put(const QUrl &url, const QByteArray &body)
|
||||
{
|
||||
// Perform request
|
||||
QNetworkRequest request(url);
|
||||
QNetworkReply* reply = _networkManager->put(request, body);
|
||||
// Connect requestFinished signal to quit slot of the loop.
|
||||
QEventLoop loop;
|
||||
QEventLoop::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
|
||||
ReplyTimeout::set(reply, DEFAULT_REST_TIMEOUT.count());
|
||||
|
||||
// Go into the loop until the request is finished.
|
||||
loop.exec();
|
||||
|
||||
httpResponse response;
|
||||
if (reply->operation() == QNetworkAccessManager::PutOperation)
|
||||
{
|
||||
if(reply->error() != QNetworkReply::NoError)
|
||||
{
|
||||
Debug(_log, "PUT: [%s] [%s]", QSTRING_CSTR( url.toString() ),body.constData() );
|
||||
}
|
||||
response = getResponse(reply);
|
||||
}
|
||||
// Free space.
|
||||
@@ -239,14 +263,11 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
||||
|
||||
int httpStatusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
response.setHttpStatusCode(httpStatusCode);
|
||||
|
||||
DebugIf(verbose, _log, "Reply.error [%d], Reply.httpStatusCode [%d]", reply->error(), httpStatusCode);
|
||||
|
||||
response.setNetworkReplyError(reply->error());
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError)
|
||||
{
|
||||
if (httpStatusCode != 204) {
|
||||
if ( httpStatusCode != HTTP_STATUS_NO_CONTENT ){
|
||||
QByteArray replyData = reply->readAll();
|
||||
|
||||
if (!replyData.isEmpty())
|
||||
@@ -275,18 +296,19 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug(_log, "Reply.httpStatusCode [%d]", httpStatusCode );
|
||||
QString errorReason;
|
||||
if (httpStatusCode > 0) {
|
||||
QString httpReason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||
QString advise;
|
||||
switch (httpStatusCode) {
|
||||
case 400:
|
||||
switch ( httpStatusCode ) {
|
||||
case HTTP_STATUS_BAD_REQUEST:
|
||||
advise = "Check Request Body";
|
||||
break;
|
||||
case 401:
|
||||
case HTTP_STATUS_UNAUTHORIZED:
|
||||
advise = "Check Authentication Token (API Key)";
|
||||
break;
|
||||
case 404:
|
||||
case HTTP_STATUS_NOT_FOUND:
|
||||
advise = "Check Resource given";
|
||||
break;
|
||||
default:
|
||||
@@ -295,10 +317,20 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
||||
errorReason = QString("[%3 %4] - %5").arg(QString(httpStatusCode), httpReason, advise);
|
||||
}
|
||||
else {
|
||||
|
||||
errorReason = reply->errorString();
|
||||
|
||||
if ( reply->error() == QNetworkReply::OperationCanceledError )
|
||||
{
|
||||
//Do not report errors caused by request cancellation because of timeouts
|
||||
Debug(_log, "Reply: [%s]", QSTRING_CSTR(errorReason) );
|
||||
}
|
||||
else
|
||||
{
|
||||
response.setError(true);
|
||||
response.setErrorReason(errorReason);
|
||||
}
|
||||
}
|
||||
response.setError(true);
|
||||
response.setErrorReason(errorReason);
|
||||
|
||||
// Create valid body which is empty
|
||||
response.setBody(QJsonDocument());
|
||||
|
||||
@@ -10,6 +10,48 @@
|
||||
#include <QUrlQuery>
|
||||
#include <QJsonDocument>
|
||||
|
||||
#include <QBasicTimer>
|
||||
#include <QTimerEvent>
|
||||
|
||||
//Set QNetworkReply timeout without external timer
|
||||
//https://stackoverflow.com/questions/37444539/how-to-set-qnetworkreply-timeout-without-external-timer
|
||||
|
||||
class ReplyTimeout : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum HandleMethod { Abort, Close };
|
||||
ReplyTimeout(QNetworkReply* reply, const int timeout, HandleMethod method = Abort) :
|
||||
QObject(reply), m_method(method)
|
||||
{
|
||||
Q_ASSERT(reply);
|
||||
if (reply && reply->isRunning()) {
|
||||
m_timer.start(timeout, this);
|
||||
connect(reply, &QNetworkReply::finished, this, &QObject::deleteLater);
|
||||
}
|
||||
}
|
||||
static void set(QNetworkReply* reply, const int timeout, HandleMethod method = Abort)
|
||||
{
|
||||
new ReplyTimeout(reply, timeout, method);
|
||||
}
|
||||
|
||||
protected:
|
||||
QBasicTimer m_timer;
|
||||
HandleMethod m_method;
|
||||
void timerEvent(QTimerEvent * ev) override {
|
||||
if (!m_timer.isActive() || ev->timerId() != m_timer.timerId())
|
||||
return;
|
||||
auto reply = static_cast<QNetworkReply*>(parent());
|
||||
if (reply->isRunning())
|
||||
{
|
||||
if (m_method == Close)
|
||||
reply->close();
|
||||
else if (m_method == Abort)
|
||||
reply->abort();
|
||||
m_timer.stop();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// Response object for REST-API calls and JSON-responses
|
||||
///
|
||||
@@ -171,6 +213,13 @@ public:
|
||||
///
|
||||
httpResponse get(const QUrl& url);
|
||||
|
||||
/// @brief Execute PUT request
|
||||
///
|
||||
/// @param[in] body The body of the request in JSON
|
||||
/// @return Response The body of the response in JSON
|
||||
///
|
||||
httpResponse put(const QJsonObject &body);
|
||||
|
||||
///
|
||||
/// @brief Execute PUT request
|
||||
///
|
||||
@@ -186,15 +235,7 @@ public:
|
||||
/// @param[in] body The body of the request in JSON
|
||||
/// @return Response The body of the response in JSON
|
||||
///
|
||||
httpResponse put(const QUrl& url, const QString& body = "");
|
||||
|
||||
///
|
||||
/// @brief Execute POST request
|
||||
///
|
||||
/// @param[in] body The body of the request in JSON
|
||||
/// @return Response The body of the response in JSON
|
||||
///
|
||||
httpResponse post(const QString& body = "");
|
||||
httpResponse put(const QUrl &url, const QByteArray &body);
|
||||
|
||||
///
|
||||
/// @brief Execute POST request
|
||||
@@ -243,7 +284,7 @@ private:
|
||||
/// @param[in/out] path to be updated
|
||||
/// @param[in] path, element to be appended
|
||||
///
|
||||
void appendPath(QString& path, const QString& appendPath) const;
|
||||
static void appendPath (QString &path, const QString &appendPath) ;
|
||||
|
||||
Logger* _log;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
// Local Hyperion includes
|
||||
#include "ProviderUdpSSL.h"
|
||||
#include <utils/QStringUtils.h>
|
||||
|
||||
const int MAX_RETRY = 5;
|
||||
const ushort MAX_PORT_SSL = 65535;
|
||||
@@ -73,6 +74,10 @@ bool ProviderUdpSSL::init(const QJsonObject &deviceConfig)
|
||||
if( deviceConfig.contains("hs_attempts") ) _handshake_attempts = deviceConfig["hs_attempts"].toInt(5);
|
||||
|
||||
QString host = deviceConfig["host"].toString(_defaultHost);
|
||||
//Split hostname from API-port in case given
|
||||
QStringList addressparts = QStringUtils::split(host, ":", QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
QString udpHost = addressparts[0];
|
||||
|
||||
QStringList debugLevels = QStringList() << "No Debug" << "Error" << "State Change" << "Informational" << "Verbose";
|
||||
|
||||
configLog( "SSL Streamer Debug", "%s", ( _debugStreamer ) ? "yes" : "no" );
|
||||
@@ -91,24 +96,24 @@ bool ProviderUdpSSL::init(const QJsonObject &deviceConfig)
|
||||
configLog( "SSL Handshake Timeout max", "%d", _handshake_timeout_max );
|
||||
configLog( "SSL Handshake attempts", "%d", _handshake_attempts );
|
||||
|
||||
if ( _address.setAddress(host) )
|
||||
if ( _address.setAddress(udpHost) )
|
||||
{
|
||||
Debug( _log, "Successfully parsed %s as an ip address.", QSTRING_CSTR( host ) );
|
||||
Debug( _log, "Successfully parsed %s as an ip address.", QSTRING_CSTR(udpHost) );
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( _log, "Failed to parse [%s] as an ip address.", QSTRING_CSTR( host ) );
|
||||
QHostInfo info = QHostInfo::fromName(host);
|
||||
Debug( _log, "Failed to parse [%s] as an ip address.", QSTRING_CSTR(udpHost) );
|
||||
QHostInfo info = QHostInfo::fromName(udpHost);
|
||||
if ( info.addresses().isEmpty() )
|
||||
{
|
||||
Debug( _log, "Failed to parse [%s] as a hostname.", QSTRING_CSTR( host ) );
|
||||
Debug( _log, "Failed to parse [%s] as a hostname.", QSTRING_CSTR(udpHost) );
|
||||
QString errortext = QString("Invalid target address [%1]!").arg(host);
|
||||
this->setInError( errortext );
|
||||
isInitOK = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug( _log, "Successfully parsed %s as a hostname.", QSTRING_CSTR( host ) );
|
||||
Debug( _log, "Successfully parsed %s as a hostname.", QSTRING_CSTR(udpHost) );
|
||||
_address = info.addresses().first();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ int LedDeviceFile::write(const std::vector<ColorRgb> & ledValues)
|
||||
}
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
out << "]" << Qt::endl;
|
||||
out << QString("]") << Qt::endl;
|
||||
#else
|
||||
out << "]" << endl;
|
||||
#endif
|
||||
|
||||
@@ -4,11 +4,22 @@
|
||||
#include <csignal>
|
||||
|
||||
// QT includes
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
|
||||
// Local LedDevice includes
|
||||
#include "LedDevicePiBlaster.h"
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
const bool verbose = false;
|
||||
|
||||
// Pi-Blaster discovery service
|
||||
const char DISCOVERY_DIRECTORY[] = "/dev/";
|
||||
const char DISCOVERY_FILEPATTERN[] = "pi-blaster";
|
||||
|
||||
} //End of constants
|
||||
|
||||
LedDevicePiBlaster::LedDevicePiBlaster(const QJsonObject &deviceConfig)
|
||||
: LedDevice(deviceConfig)
|
||||
, _fid(nullptr)
|
||||
@@ -184,3 +195,31 @@ int LedDevicePiBlaster::write(const std::vector<ColorRgb> & ledValues)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QJsonObject LedDevicePiBlaster::discover(const QJsonObject& /*params*/)
|
||||
{
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
|
||||
|
||||
QJsonArray deviceList;
|
||||
|
||||
QDir deviceDirectory (DISCOVERY_DIRECTORY);
|
||||
QStringList deviceFilter(DISCOVERY_FILEPATTERN);
|
||||
deviceDirectory.setNameFilters(deviceFilter);
|
||||
deviceDirectory.setSorting(QDir::Name);
|
||||
QFileInfoList deviceFiles = deviceDirectory.entryInfoList(QDir::System);
|
||||
|
||||
QFileInfoList::const_iterator deviceFileIterator;
|
||||
for (deviceFileIterator = deviceFiles.constBegin(); deviceFileIterator != deviceFiles.constEnd(); ++deviceFileIterator)
|
||||
{
|
||||
QJsonObject deviceInfo;
|
||||
deviceInfo.insert("deviceName", (*deviceFileIterator).fileName());
|
||||
deviceInfo.insert("systemLocation", (*deviceFileIterator).absoluteFilePath());
|
||||
deviceList.append(deviceInfo);
|
||||
}
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
|
||||
DebugIf(verbose,_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
return devicesDiscovered;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,12 @@ public:
|
||||
/// @return LedDevice constructed
|
||||
static LedDevice* construct(const QJsonObject &deviceConfig);
|
||||
|
||||
/// @param[in] params Parameters used to overwrite discovery default behaviour
|
||||
///
|
||||
/// @return A JSON structure holding a list of devices found
|
||||
///
|
||||
QJsonObject discover(const QJsonObject& params) override;
|
||||
|
||||
protected:
|
||||
|
||||
///
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
#include "LedDeviceWS281x.h"
|
||||
#include <utils/SysInfo.h>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
const bool verbose = false;
|
||||
} //End of constants
|
||||
|
||||
LedDeviceWS281x::LedDeviceWS281x(const QJsonObject &deviceConfig)
|
||||
: LedDevice(deviceConfig)
|
||||
@@ -75,19 +81,26 @@ int LedDeviceWS281x::open()
|
||||
int retval = -1;
|
||||
_isDeviceReady = false;
|
||||
|
||||
// Try to open the LedDevice
|
||||
|
||||
ws2811_return_t rc = ws2811_init(&_led_string);
|
||||
if ( rc != WS2811_SUCCESS )
|
||||
if (!SysInfo::isUserAdmin())
|
||||
{
|
||||
QString errortext = QString ("Failed to open. Error message: %1").arg( ws2811_get_return_t_str(rc) );
|
||||
QString errortext = QString ("Hyperion must run with \"root\" privileges for this device. Current user is: \"%1\"").arg(SysInfo::userName());
|
||||
this->setInError( errortext );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Everything is OK, device is ready
|
||||
_isDeviceReady = true;
|
||||
retval = 0;
|
||||
// Try to open the LedDevice
|
||||
ws2811_return_t rc = ws2811_init(&_led_string);
|
||||
if ( rc != WS2811_SUCCESS )
|
||||
{
|
||||
QString errortext = QString ("Failed to open. Error message: %1").arg( ws2811_get_return_t_str(rc) );
|
||||
this->setInError( errortext );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Everything is OK, device is ready
|
||||
_isDeviceReady = true;
|
||||
retval = 0;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
@@ -138,3 +151,22 @@ int LedDeviceWS281x::write(const std::vector<ColorRgb> &ledValues)
|
||||
|
||||
return ws2811_render(&_led_string) ? -1 : 0;
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceWS281x::discover(const QJsonObject& /*params*/)
|
||||
{
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType);
|
||||
|
||||
QJsonArray deviceList;
|
||||
|
||||
if (SysInfo::isUserAdmin())
|
||||
{
|
||||
//Indicate the general availability of the device, if hyperion is run under root
|
||||
deviceList << QJsonObject ({{"found",true}});
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
}
|
||||
|
||||
DebugIf(verbose,_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
return devicesDiscovered;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,15 @@ public:
|
||||
///
|
||||
static LedDevice* construct(const QJsonObject &deviceConfig);
|
||||
|
||||
///
|
||||
/// @brief Discover WS281x devices available (for configuration).
|
||||
///
|
||||
/// @param[in] params Parameters used to overwrite discovery default behaviour
|
||||
///
|
||||
/// @return A JSON structure holding a list of devices found
|
||||
///
|
||||
QJsonObject discover(const QJsonObject& params) override;
|
||||
|
||||
protected:
|
||||
|
||||
///
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// hyperion local includes
|
||||
#include "LedDeviceAtmo.h"
|
||||
|
||||
namespace {
|
||||
const bool verbose = false;
|
||||
} //End of constants
|
||||
|
||||
LedDeviceAtmo::LedDeviceAtmo(const QJsonObject &deviceConfig)
|
||||
: ProviderRs232(deviceConfig)
|
||||
{
|
||||
@@ -43,3 +47,20 @@ int LedDeviceAtmo::write(const std::vector<ColorRgb> &ledValues)
|
||||
memcpy(4 + _ledBuffer.data(), ledValues.data(), _ledCount * sizeof(ColorRgb));
|
||||
return writeBytes(_ledBuffer.size(), _ledBuffer.data());
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceAtmo::getProperties(const QJsonObject& params)
|
||||
{
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
QJsonObject properties;
|
||||
|
||||
QString serialPort = params["serialPort"].toString("");
|
||||
|
||||
QJsonObject propertiesDetails;
|
||||
QJsonArray possibleLedCounts = { 5 };
|
||||
propertiesDetails.insert("ledCount", possibleLedCounts);
|
||||
|
||||
properties.insert("properties", propertiesDetails);
|
||||
|
||||
DebugIf(verbose, _log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
return properties;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,14 @@ public:
|
||||
///
|
||||
static LedDevice* construct(const QJsonObject &deviceConfig);
|
||||
|
||||
///
|
||||
/// @brief Get a Atmo device's resource properties
|
||||
///
|
||||
/// @param[in] params Parameters to query device
|
||||
/// @return A JSON structure holding the device's properties
|
||||
///
|
||||
QJsonObject getProperties(const QJsonObject& params) override;
|
||||
|
||||
private:
|
||||
|
||||
///
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
// hyperion local includes
|
||||
#include "LedDeviceKarate.h"
|
||||
|
||||
LedDeviceKarate::LedDeviceKarate(const QJsonObject &deviceConfig)
|
||||
namespace {
|
||||
const bool verbose = false;
|
||||
} //End of constants
|
||||
|
||||
LedDeviceKarate::LedDeviceKarate(const QJsonObject& deviceConfig)
|
||||
: ProviderRs232(deviceConfig)
|
||||
{
|
||||
}
|
||||
|
||||
LedDevice* LedDeviceKarate::construct(const QJsonObject &deviceConfig)
|
||||
LedDevice* LedDeviceKarate::construct(const QJsonObject& deviceConfig)
|
||||
{
|
||||
return new LedDeviceKarate(deviceConfig);
|
||||
}
|
||||
|
||||
bool LedDeviceKarate::init(const QJsonObject &deviceConfig)
|
||||
bool LedDeviceKarate::init(const QJsonObject& deviceConfig)
|
||||
{
|
||||
bool isInitOK = false;
|
||||
|
||||
// Initialise sub-class
|
||||
if ( ProviderRs232::init(deviceConfig) )
|
||||
if (ProviderRs232::init(deviceConfig))
|
||||
{
|
||||
if (_ledCount != 8 && _ledCount != 16)
|
||||
{
|
||||
//Error( _log, "%d channels configured. This should always be 16!", _ledCount);
|
||||
QString errortext = QString ("%1 channels configured. This should always be 8 or 16!").arg(_ledCount);
|
||||
QString errortext = QString("%1 channels configured. This should always be 8 or 16!").arg(_ledCount);
|
||||
this->setInError(errortext);
|
||||
isInitOK = false;
|
||||
}
|
||||
@@ -33,8 +37,8 @@ bool LedDeviceKarate::init(const QJsonObject &deviceConfig)
|
||||
_ledBuffer[2] = 0x00; // Checksum
|
||||
_ledBuffer[3] = _ledCount * 3; // Number of Databytes send
|
||||
|
||||
Debug( _log, "Karatelight header for %d leds: 0x%02x 0x%02x 0x%02x 0x%02x", _ledCount,
|
||||
_ledBuffer[0], _ledBuffer[1], _ledBuffer[2], _ledBuffer[3] );
|
||||
Debug(_log, "Karatelight header for %d leds: 0x%02x 0x%02x 0x%02x 0x%02x", _ledCount,
|
||||
_ledBuffer[0], _ledBuffer[1], _ledBuffer[2], _ledBuffer[3]);
|
||||
|
||||
isInitOK = true;
|
||||
}
|
||||
@@ -42,20 +46,37 @@ bool LedDeviceKarate::init(const QJsonObject &deviceConfig)
|
||||
return isInitOK;
|
||||
}
|
||||
|
||||
int LedDeviceKarate::write(const std::vector<ColorRgb> &ledValues)
|
||||
int LedDeviceKarate::write(const std::vector<ColorRgb>& ledValues)
|
||||
{
|
||||
for (signed iLed=0; iLed< static_cast<int>(_ledCount); iLed++)
|
||||
{
|
||||
const ColorRgb& rgb = ledValues[iLed];
|
||||
_ledBuffer[iLed*3+4] = rgb.green;
|
||||
_ledBuffer[iLed*3+5] = rgb.blue;
|
||||
_ledBuffer[iLed*3+6] = rgb.red;
|
||||
}
|
||||
for (signed iLed = 0; iLed < static_cast<int>(_ledCount); iLed++)
|
||||
{
|
||||
const ColorRgb& rgb = ledValues[iLed];
|
||||
_ledBuffer[iLed * 3 + 4] = rgb.green;
|
||||
_ledBuffer[iLed * 3 + 5] = rgb.blue;
|
||||
_ledBuffer[iLed * 3 + 6] = rgb.red;
|
||||
}
|
||||
|
||||
// Calc Checksum
|
||||
_ledBuffer[2] = _ledBuffer[0] ^ _ledBuffer[1];
|
||||
for (unsigned int i = 3; i < _ledBuffer.size(); i++)
|
||||
_ledBuffer[2] ^= _ledBuffer[i];
|
||||
_ledBuffer[2] = _ledBuffer[0] ^ _ledBuffer[1];
|
||||
for (unsigned int i = 3; i < _ledBuffer.size(); i++)
|
||||
_ledBuffer[2] ^= _ledBuffer[i];
|
||||
|
||||
return writeBytes(_ledBuffer.size(), _ledBuffer.data());
|
||||
}
|
||||
|
||||
QJsonObject LedDeviceKarate::getProperties(const QJsonObject& params)
|
||||
{
|
||||
DebugIf(verbose, _log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
QJsonObject properties;
|
||||
|
||||
QString serialPort = params["serialPort"].toString("");
|
||||
|
||||
QJsonObject propertiesDetails;
|
||||
QJsonArray possibleLedCounts = { 16, 8 };
|
||||
propertiesDetails.insert("ledCount", possibleLedCounts);
|
||||
|
||||
properties.insert("properties", propertiesDetails);
|
||||
|
||||
DebugIf(verbose, _log, "properties: [%s]", QString(QJsonDocument(properties).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
return properties;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,14 @@ public:
|
||||
/// @return LedDevice constructed
|
||||
static LedDevice* construct(const QJsonObject &deviceConfig);
|
||||
|
||||
///
|
||||
/// @brief Get a Karate device's resource properties
|
||||
///
|
||||
/// @param[in] params Parameters to query device
|
||||
/// @return A JSON structure holding the device's properties
|
||||
///
|
||||
QJsonObject getProperties(const QJsonObject& params) override;
|
||||
|
||||
private:
|
||||
|
||||
///
|
||||
|
||||
@@ -2,18 +2,30 @@
|
||||
// LedDevice includes
|
||||
#include <leddevice/LedDevice.h>
|
||||
#include "ProviderRs232.h"
|
||||
#include <utils/WaitTime.h>
|
||||
|
||||
// qt includes
|
||||
#include <QSerialPortInfo>
|
||||
#include <QEventLoop>
|
||||
#include <QDir>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
// Constants
|
||||
constexpr std::chrono::milliseconds WRITE_TIMEOUT{1000}; // device write timeout in ms
|
||||
constexpr std::chrono::milliseconds OPEN_TIMEOUT{5000}; // device open timeout in ms
|
||||
const int MAX_WRITE_TIMEOUTS = 5; // Maximum number of allowed timeouts
|
||||
const int NUM_POWEROFF_WRITE_BLACK = 2; // Number of write "BLACK" during powering off
|
||||
namespace {
|
||||
const bool verbose = false;
|
||||
|
||||
constexpr std::chrono::milliseconds WRITE_TIMEOUT{ 1000 }; // device write timeout in ms
|
||||
constexpr std::chrono::milliseconds OPEN_TIMEOUT{ 5000 }; // device open timeout in ms
|
||||
const int MAX_WRITE_TIMEOUTS = 5; // Maximum number of allowed timeouts
|
||||
const int NUM_POWEROFF_WRITE_BLACK = 2; // Number of write "BLACK" during powering off
|
||||
|
||||
constexpr std::chrono::milliseconds DEFAULT_IDENTIFY_TIME{ 500 };
|
||||
|
||||
// tty discovery service
|
||||
const char DISCOVERY_DIRECTORY[] = "/dev/";
|
||||
const char DISCOVERY_FILEPATTERN[] = "tty*";
|
||||
} //End of constants
|
||||
|
||||
ProviderRs232::ProviderRs232(const QJsonObject &deviceConfig)
|
||||
: LedDevice(deviceConfig)
|
||||
@@ -40,16 +52,26 @@ bool ProviderRs232::init(const QJsonObject &deviceConfig)
|
||||
Debug(_log, "LatchTime : %d", this->getLatchTime());
|
||||
|
||||
_deviceName = deviceConfig["output"].toString("auto");
|
||||
_isAutoDeviceName = _deviceName.toLower() == "auto";
|
||||
|
||||
// If device name was given as unix /dev/ system-location, get port name
|
||||
if ( _deviceName.startsWith(QLatin1String("/dev/")) )
|
||||
{
|
||||
_location = _deviceName;
|
||||
//Handle udev devices
|
||||
QFileInfo file_info(_deviceName);
|
||||
if (file_info.isSymLink())
|
||||
{
|
||||
_deviceName = file_info.symLinkTarget();
|
||||
}
|
||||
_deviceName = _deviceName.mid(5);
|
||||
}
|
||||
|
||||
_isAutoDeviceName = _deviceName.toLower() == "auto";
|
||||
_baudRate_Hz = deviceConfig["rate"].toInt();
|
||||
_delayAfterConnect_ms = deviceConfig["delayAfterConnect"].toInt(1500);
|
||||
|
||||
Debug(_log, "deviceName : %s", QSTRING_CSTR(_deviceName));
|
||||
Debug(_log, "DeviceName : %s", QSTRING_CSTR(_deviceName));
|
||||
DebugIf(!_location.isEmpty(), _log, "Location : %s", QSTRING_CSTR(_location));
|
||||
Debug(_log, "AutoDevice : %d", _isAutoDeviceName);
|
||||
Debug(_log, "baudRate_Hz : %d", _baudRate_Hz);
|
||||
Debug(_log, "delayAfCon ms: %d", _delayAfterConnect_ms);
|
||||
@@ -132,7 +154,14 @@ bool ProviderRs232::tryOpen(int delayAfterConnect_ms)
|
||||
|
||||
if (!_rs232Port.isOpen())
|
||||
{
|
||||
Info(_log, "Opening UART: %s", QSTRING_CSTR(_deviceName));
|
||||
if (!_location.isEmpty())
|
||||
{
|
||||
Info(_log, "Opening UART: %s (%s)", QSTRING_CSTR(_deviceName), QSTRING_CSTR(_location));
|
||||
}
|
||||
else
|
||||
{
|
||||
Info(_log, "Opening UART: %s", QSTRING_CSTR(_deviceName));
|
||||
}
|
||||
|
||||
_frameDropCounter = 0;
|
||||
|
||||
@@ -141,18 +170,16 @@ bool ProviderRs232::tryOpen(int delayAfterConnect_ms)
|
||||
Debug(_log, "_rs232Port.open(QIODevice::ReadWrite): %s, Baud rate [%d]bps", QSTRING_CSTR(_deviceName), _baudRate_Hz);
|
||||
|
||||
QSerialPortInfo serialPortInfo(_deviceName);
|
||||
|
||||
QJsonObject portInfo;
|
||||
Debug(_log, "portName: %s", QSTRING_CSTR(serialPortInfo.portName()));
|
||||
Debug(_log, "systemLocation: %s", QSTRING_CSTR(serialPortInfo.systemLocation()));
|
||||
Debug(_log, "description: %s", QSTRING_CSTR(serialPortInfo.description()));
|
||||
Debug(_log, "manufacturer: %s", QSTRING_CSTR(serialPortInfo.manufacturer()));
|
||||
Debug(_log, "productIdentifier: %s", QSTRING_CSTR(QString("0x%1").arg(serialPortInfo.productIdentifier(), 0, 16)));
|
||||
Debug(_log, "vendorIdentifier: %s", QSTRING_CSTR(QString("0x%1").arg(serialPortInfo.vendorIdentifier(), 0, 16)));
|
||||
Debug(_log, "serialNumber: %s", QSTRING_CSTR(serialPortInfo.serialNumber()));
|
||||
|
||||
if (!serialPortInfo.isNull() )
|
||||
{
|
||||
Debug(_log, "portName: %s", QSTRING_CSTR(serialPortInfo.portName()));
|
||||
Debug(_log, "systemLocation: %s", QSTRING_CSTR(serialPortInfo.systemLocation()));
|
||||
Debug(_log, "description: %s", QSTRING_CSTR(serialPortInfo.description()));
|
||||
Debug(_log, "manufacturer: %s", QSTRING_CSTR(serialPortInfo.manufacturer()));
|
||||
Debug(_log, "vendorIdentifier: %s", QSTRING_CSTR(QString("0x%1").arg(serialPortInfo.vendorIdentifier(), 0, 16)));
|
||||
Debug(_log, "productIdentifier: %s", QSTRING_CSTR(QString("0x%1").arg(serialPortInfo.productIdentifier(), 0, 16)));
|
||||
Debug(_log, "serialNumber: %s", QSTRING_CSTR(serialPortInfo.serialNumber()));
|
||||
|
||||
if ( !_rs232Port.open(QIODevice::ReadWrite) )
|
||||
{
|
||||
this->setInError(_rs232Port.errorString());
|
||||
@@ -161,8 +188,20 @@ bool ProviderRs232::tryOpen(int delayAfterConnect_ms)
|
||||
}
|
||||
else
|
||||
{
|
||||
QString errortext = QString("Invalid serial device name: [%1]!").arg(_deviceName);
|
||||
QString errortext = QString("Invalid serial device name: %1 %2!").arg(_deviceName, _location);
|
||||
this->setInError( errortext );
|
||||
|
||||
// List available device
|
||||
for (auto &port : QSerialPortInfo::availablePorts() ) {
|
||||
Debug(_log, "Avail. serial device: [%s]-(%s|%s), Manufacturer: %s, Description: %s",
|
||||
QSTRING_CSTR(port.portName()),
|
||||
QSTRING_CSTR(QString("0x%1").arg(port.vendorIdentifier(), 0, 16)),
|
||||
QSTRING_CSTR(QString("0x%1").arg(port.productIdentifier(), 0, 16)),
|
||||
QSTRING_CSTR(port.manufacturer()),
|
||||
QSTRING_CSTR(port.description())
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -215,7 +254,7 @@ int ProviderRs232::writeBytes(const qint64 size, const uint8_t *data)
|
||||
{
|
||||
if ( _rs232Port.error() == QSerialPort::TimeoutError )
|
||||
{
|
||||
Debug(_log, "Timeout after %dms: %d frames already dropped", WRITE_TIMEOUT, _frameDropCounter);
|
||||
Debug(_log, "Timeout after %dms: %d frames already dropped", WRITE_TIMEOUT.count(), _frameDropCounter);
|
||||
|
||||
++_frameDropCounter;
|
||||
|
||||
@@ -245,7 +284,7 @@ int ProviderRs232::writeBytes(const qint64 size, const uint8_t *data)
|
||||
QString ProviderRs232::discoverFirst()
|
||||
{
|
||||
// take first available USB serial port - currently no probing!
|
||||
for (auto const & port : QSerialPortInfo::availablePorts())
|
||||
for (auto & port : QSerialPortInfo::availablePorts())
|
||||
{
|
||||
if (!port.isNull() && !port.isBusy())
|
||||
{
|
||||
@@ -266,7 +305,7 @@ QJsonObject ProviderRs232::discover(const QJsonObject& /*params*/)
|
||||
// Discover serial Devices
|
||||
for (auto &port : QSerialPortInfo::availablePorts() )
|
||||
{
|
||||
if ( !port.isNull() )
|
||||
if ( !port.isNull() && port.vendorIdentifier() != 0)
|
||||
{
|
||||
QJsonObject portInfo;
|
||||
portInfo.insert("description", port.description());
|
||||
@@ -281,6 +320,71 @@ QJsonObject ProviderRs232::discover(const QJsonObject& /*params*/)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
//Check all /dev/tty* files, if they are udev-serial devices
|
||||
QDir deviceDirectory (DISCOVERY_DIRECTORY);
|
||||
QStringList deviceFilter(DISCOVERY_FILEPATTERN);
|
||||
deviceDirectory.setNameFilters(deviceFilter);
|
||||
deviceDirectory.setSorting(QDir::Name);
|
||||
QFileInfoList deviceFiles = deviceDirectory.entryInfoList(QDir::AllEntries);
|
||||
|
||||
QFileInfoList::const_iterator deviceFileIterator;
|
||||
for (deviceFileIterator = deviceFiles.constBegin(); deviceFileIterator != deviceFiles.constEnd(); ++deviceFileIterator)
|
||||
{
|
||||
if ((*deviceFileIterator).isSymLink())
|
||||
{
|
||||
QSerialPortInfo port = QSerialPortInfo(QSerialPort((*deviceFileIterator).symLinkTarget()));
|
||||
|
||||
QJsonObject portInfo;
|
||||
portInfo.insert("portName", (*deviceFileIterator).fileName());
|
||||
portInfo.insert("systemLocation", (*deviceFileIterator).absoluteFilePath());
|
||||
portInfo.insert("udev", true);
|
||||
|
||||
portInfo.insert("description", port.description());
|
||||
portInfo.insert("manufacturer", port.manufacturer());
|
||||
portInfo.insert("productIdentifier", QString("0x%1").arg(port.productIdentifier(), 0, 16));
|
||||
portInfo.insert("serialNumber", port.serialNumber());
|
||||
portInfo.insert("vendorIdentifier", QString("0x%1").arg(port.vendorIdentifier(), 0, 16));
|
||||
|
||||
deviceList.append(portInfo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
DebugIf(verbose,_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
return devicesDiscovered;
|
||||
}
|
||||
|
||||
void ProviderRs232::identify(const QJsonObject& params)
|
||||
{
|
||||
DebugIf(verbose,_log, "params: [%s]", QString(QJsonDocument(params).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
QString deviceName = params["output"].toString("");
|
||||
if (!deviceName.isEmpty())
|
||||
{
|
||||
_devConfig = params;
|
||||
init(_devConfig);
|
||||
{
|
||||
if ( open() == 0 )
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
if (writeColor(ColorRgb::RED) == 0)
|
||||
{
|
||||
wait(DEFAULT_IDENTIFY_TIME);
|
||||
|
||||
writeColor(ColorRgb::BLACK);
|
||||
wait(DEFAULT_IDENTIFY_TIME);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,20 @@ public:
|
||||
///
|
||||
~ProviderRs232() override;
|
||||
|
||||
///
|
||||
/// @brief Send an update to the RS232 device to identify it.
|
||||
///
|
||||
/// Following parameters are required
|
||||
/// @code
|
||||
/// {
|
||||
/// "deviceConfig" :
|
||||
/// }
|
||||
///@endcode
|
||||
///
|
||||
/// @param[in] params Parameters to configure device
|
||||
///
|
||||
void identify(const QJsonObject& params) override;
|
||||
|
||||
protected:
|
||||
|
||||
///
|
||||
@@ -83,6 +97,8 @@ protected:
|
||||
|
||||
/// The name of the output device
|
||||
QString _deviceName;
|
||||
/// The system location of the output device
|
||||
QString _location;
|
||||
/// The RS232 serial-device
|
||||
QSerialPort _rs232Port;
|
||||
/// The used baud-rate of the output device
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
#include "LedDeviceAPA102.h"
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
|
||||
/// The value that determines the higher bits of the APA102 brightness control field
|
||||
const int APA102_LEDFRAME_UPPER_BITS = 0xE0;
|
||||
|
||||
} //End of constants
|
||||
|
||||
|
||||
LedDeviceAPA102::LedDeviceAPA102(const QJsonObject &deviceConfig)
|
||||
: ProviderSpi(deviceConfig)
|
||||
{
|
||||
// Overwrite non supported/required features
|
||||
_latchTime_ms = 0;
|
||||
}
|
||||
|
||||
LedDevice* LedDeviceAPA102::construct(const QJsonObject &deviceConfig)
|
||||
@@ -17,32 +28,44 @@ bool LedDeviceAPA102::init(const QJsonObject &deviceConfig)
|
||||
// Initialise sub-class
|
||||
if ( ProviderSpi::init(deviceConfig) )
|
||||
{
|
||||
_brightnessControlMaxLevel = deviceConfig["brightnessControlMaxLevel"].toInt(APA102_BRIGHTNESS_MAX_LEVEL);
|
||||
Info(_log, "[%s] Setting maximum brightness to [%d] = %d%%", QSTRING_CSTR(_activeDeviceType), _brightnessControlMaxLevel, _brightnessControlMaxLevel * 100 / APA102_BRIGHTNESS_MAX_LEVEL);
|
||||
|
||||
const unsigned int startFrameSize = 4;
|
||||
const unsigned int endFrameSize = qMax<unsigned int>(((_ledCount + 15) / 16), 4);
|
||||
//Endframe, add additional 4 bytes to cover SK9922 Reset frame (in case SK9922 were sold as AP102) - has no effect on APA102
|
||||
const unsigned int endFrameSize = (_ledCount/32) * 4 + 4;
|
||||
const unsigned int APAbufferSize = (_ledCount * 4) + startFrameSize + endFrameSize;
|
||||
|
||||
_ledBuffer.resize(APAbufferSize, 0xFF);
|
||||
_ledBuffer[0] = 0x00;
|
||||
_ledBuffer[1] = 0x00;
|
||||
_ledBuffer[2] = 0x00;
|
||||
_ledBuffer[3] = 0x00;
|
||||
_ledBuffer.resize(APAbufferSize, 0x00);
|
||||
|
||||
isInitOK = true;
|
||||
|
||||
}
|
||||
return isInitOK;
|
||||
}
|
||||
|
||||
void LedDeviceAPA102::bufferWithBrightness(std::vector<uint8_t> &txBuf, const std::vector<ColorRgb> & ledValues, const int brightness) {
|
||||
const int ledCount = static_cast<int>(_ledCount);
|
||||
|
||||
for (int iLed = 0; iLed < ledCount; ++iLed)
|
||||
{
|
||||
const ColorRgb &rgb = ledValues[iLed];
|
||||
const uint8_t red = rgb.red;
|
||||
const uint8_t green = rgb.green;
|
||||
const uint8_t blue = rgb.blue;
|
||||
|
||||
/// The LED index in the buffer
|
||||
const int b = 4 + iLed * 4;
|
||||
|
||||
txBuf[b + 0] = brightness | APA102_LEDFRAME_UPPER_BITS;
|
||||
txBuf[b + 1] = blue;
|
||||
txBuf[b + 2] = green;
|
||||
txBuf[b + 3] = red;
|
||||
}
|
||||
}
|
||||
|
||||
int LedDeviceAPA102::write(const std::vector<ColorRgb> &ledValues)
|
||||
{
|
||||
for (signed iLed=0; iLed < static_cast<int>( _ledCount); ++iLed) {
|
||||
const ColorRgb& rgb = ledValues[iLed];
|
||||
_ledBuffer[4+iLed*4] = 0xFF;
|
||||
_ledBuffer[4+iLed*4+1] = rgb.red;
|
||||
_ledBuffer[4+iLed*4+2] = rgb.green;
|
||||
_ledBuffer[4+iLed*4+3] = rgb.blue;
|
||||
}
|
||||
this->bufferWithBrightness(_ledBuffer, ledValues, _brightnessControlMaxLevel);
|
||||
|
||||
return writeBytes(_ledBuffer.size(), _ledBuffer.data());
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
// hyperion includes
|
||||
#include "ProviderSpi.h"
|
||||
|
||||
/// The maximal level supported by the APA brightness control field, 31
|
||||
const int APA102_BRIGHTNESS_MAX_LEVEL = 31;
|
||||
|
||||
///
|
||||
/// Implementation of the LedDevice interface for writing to APA102 led device.
|
||||
///
|
||||
@@ -43,6 +46,18 @@ private:
|
||||
/// @return Zero on success, else negative
|
||||
///
|
||||
int write(const std::vector<ColorRgb> & ledValues) override;
|
||||
|
||||
///
|
||||
/// @brief Writes the RGB-Color values to the SPI Tx buffer setting considering a given brightness level
|
||||
///
|
||||
/// @param[in,out] txBuf The packed spi transfer buffer of the LED's color values
|
||||
/// @param[in] ledValues The RGB-color per LED
|
||||
/// @param[in] brightness The current brightness level 1 .. 31
|
||||
///
|
||||
void bufferWithBrightness(std::vector<uint8_t> &txBuf, const std::vector<ColorRgb> & ledValues, const int brightness = APA102_BRIGHTNESS_MAX_LEVEL);
|
||||
|
||||
/// The brighness level. Possibile values 1 .. 31.
|
||||
int _brightnessControlMaxLevel;
|
||||
};
|
||||
|
||||
#endif // LEDEVICEAPA102_H
|
||||
|
||||
@@ -39,10 +39,6 @@ bool LedDeviceSK9822::init(const QJsonObject &deviceConfig)
|
||||
|
||||
_ledBuffer.resize(0, 0x00);
|
||||
_ledBuffer.resize(bufferSize, 0x00);
|
||||
//_ledBuffer[0] = 0x00;
|
||||
//_ledBuffer[1] = 0x00;
|
||||
//_ledBuffer[2] = 0x00;
|
||||
//_ledBuffer[3] = 0x00;
|
||||
|
||||
isInitOK = true;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,19 @@
|
||||
#include "ProviderSpi.h"
|
||||
#include <utils/Logger.h>
|
||||
|
||||
// qt includes
|
||||
#include <QDir>
|
||||
|
||||
// Constants
|
||||
namespace {
|
||||
const bool verbose = false;
|
||||
|
||||
// SPI discovery service
|
||||
const char DISCOVERY_DIRECTORY[] = "/dev/";
|
||||
const char DISCOVERY_FILEPATTERN[] = "spidev*";
|
||||
|
||||
} //End of constants
|
||||
|
||||
ProviderSpi::ProviderSpi(const QJsonObject &deviceConfig)
|
||||
: LedDevice(deviceConfig)
|
||||
, _deviceName("/dev/spidev0.0")
|
||||
@@ -24,6 +37,7 @@ ProviderSpi::ProviderSpi(const QJsonObject &deviceConfig)
|
||||
{
|
||||
memset(&_spi, 0, sizeof(_spi));
|
||||
_latchTime_ms = 1;
|
||||
_isInSwitchOff = false;
|
||||
}
|
||||
|
||||
ProviderSpi::~ProviderSpi()
|
||||
@@ -55,6 +69,7 @@ int ProviderSpi::open()
|
||||
int retval = -1;
|
||||
QString errortext;
|
||||
_isDeviceReady = false;
|
||||
_isInSwitchOff = false;
|
||||
|
||||
const int bitsPerWord = 8;
|
||||
|
||||
@@ -131,12 +146,14 @@ int ProviderSpi::writeBytes(unsigned size, const uint8_t * data)
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t * newdata {nullptr};
|
||||
|
||||
_spi.tx_buf = __u64(data);
|
||||
_spi.len = __u32(size);
|
||||
|
||||
if (_spiDataInvert)
|
||||
{
|
||||
uint8_t * newdata = (uint8_t *)malloc(size);
|
||||
newdata = static_cast<uint8_t *>(malloc(size));
|
||||
for (unsigned i = 0; i<size; i++) {
|
||||
newdata[i] = data[i] ^ 0xff;
|
||||
}
|
||||
@@ -146,5 +163,35 @@ int ProviderSpi::writeBytes(unsigned size, const uint8_t * data)
|
||||
int retVal = ioctl(_fid, SPI_IOC_MESSAGE(1), &_spi);
|
||||
ErrorIf((retVal < 0), _log, "SPI failed to write. errno: %d, %s", errno, strerror(errno) );
|
||||
|
||||
free (newdata);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
QJsonObject ProviderSpi::discover(const QJsonObject& /*params*/)
|
||||
{
|
||||
QJsonObject devicesDiscovered;
|
||||
devicesDiscovered.insert("ledDeviceType", _activeDeviceType );
|
||||
|
||||
QJsonArray deviceList;
|
||||
|
||||
QDir deviceDirectory (DISCOVERY_DIRECTORY);
|
||||
QStringList deviceFilter(DISCOVERY_FILEPATTERN);
|
||||
deviceDirectory.setNameFilters(deviceFilter);
|
||||
deviceDirectory.setSorting(QDir::Name);
|
||||
QFileInfoList deviceFiles = deviceDirectory.entryInfoList(QDir::System);
|
||||
|
||||
QFileInfoList::const_iterator deviceFileIterator;
|
||||
for (deviceFileIterator = deviceFiles.constBegin(); deviceFileIterator != deviceFiles.constEnd(); ++deviceFileIterator)
|
||||
{
|
||||
QJsonObject deviceInfo;
|
||||
deviceInfo.insert("deviceName", (*deviceFileIterator).fileName().remove(0,6));
|
||||
deviceInfo.insert("systemLocation", (*deviceFileIterator).absoluteFilePath());
|
||||
deviceList.append(deviceInfo);
|
||||
}
|
||||
devicesDiscovered.insert("devices", deviceList);
|
||||
|
||||
DebugIf(verbose,_log, "devicesDiscovered: [%s]", QString(QJsonDocument(devicesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
return devicesDiscovered;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,12 @@ public:
|
||||
///
|
||||
int open() override;
|
||||
|
||||
/// @param[in] params Parameters used to overwrite discovery default behaviour
|
||||
///
|
||||
/// @return A JSON structure holding a list of devices found
|
||||
///
|
||||
QJsonObject discover(const QJsonObject& params) override;
|
||||
|
||||
public slots:
|
||||
///
|
||||
/// Closes the output device.
|
||||
|
||||
@@ -25,20 +25,24 @@
|
||||
},
|
||||
"lightberry_apa102_mode": {
|
||||
"type": "boolean",
|
||||
"title":"edt_dev_spec_LBap102Mode_title",
|
||||
"title": "edt_dev_spec_LBap102Mode_title",
|
||||
"default": false,
|
||||
"access" : "advanced",
|
||||
"propertyOrder" : 4
|
||||
"required": true,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 4
|
||||
},
|
||||
"latchTime": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_latchtime_title",
|
||||
"title": "edt_dev_spec_latchtime_title",
|
||||
"default": 30,
|
||||
"append" : "edt_append_ms",
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 1000,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 5
|
||||
"access": "expert",
|
||||
"options": {
|
||||
"infoText": "edt_dev_spec_latchtime_title_info"
|
||||
},
|
||||
"propertyOrder": 5
|
||||
},
|
||||
"rewriteTime": {
|
||||
"type": "integer",
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
"output": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_spipath_title",
|
||||
"enum" : ["/dev/spidev0.0","/dev/spidev0.1"],
|
||||
"default" : "/dev/spidev0.0",
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"rate": {
|
||||
@@ -21,20 +19,18 @@
|
||||
"default": false,
|
||||
"propertyOrder" : 3
|
||||
},
|
||||
"latchTime": {
|
||||
"brightnessControlMaxLevel": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_latchtime_title",
|
||||
"default": 0,
|
||||
"append" : "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 1000,
|
||||
"access" : "expert",
|
||||
"title":"edt_conf_color_brightness_title",
|
||||
"default": 31,
|
||||
"minimum": 1,
|
||||
"maximum": 31,
|
||||
"propertyOrder" : 4
|
||||
},
|
||||
"rewriteTime": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_general_rewriteTime_title",
|
||||
"default": 1000,
|
||||
"default": 0,
|
||||
"append" : "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"access" : "expert",
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"output": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_spipath_title",
|
||||
"enum" : ["/dev/spidev0.0","/dev/spidev0.1"],
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"rate": {
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
{
|
||||
"type":"object",
|
||||
"required":true,
|
||||
"properties":{
|
||||
"orbIds": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_orbIds_title",
|
||||
"default": "",
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"useOrbSmoothing": {
|
||||
"type": "boolean",
|
||||
"title":"edt_dev_spec_useOrbSmoothing_title",
|
||||
"default": true,
|
||||
"access" : "advanced",
|
||||
"propertyOrder" : 2
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_multicastGroup_title",
|
||||
"default" : "239.255.255.250",
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 3
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_port_title",
|
||||
"minimum" : 0,
|
||||
"maximum" : 65535,
|
||||
"default": 49692,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 4
|
||||
},
|
||||
"latchTime": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_latchtime_title",
|
||||
"default": 30,
|
||||
"append" : "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 1000,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 5
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"properties": {
|
||||
"orbIds": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_orbIds_title",
|
||||
"minLength": 1,
|
||||
"default": "",
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"useOrbSmoothing": {
|
||||
"type": "boolean",
|
||||
"title": "edt_dev_spec_useOrbSmoothing_title",
|
||||
"default": true,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"host": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_multicastGroup_title",
|
||||
"default": "239.255.255.250",
|
||||
"propertyOrder": 3
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_port_title",
|
||||
"minimum": 0,
|
||||
"maximum": 65535,
|
||||
"default": 49692,
|
||||
"access": "expert",
|
||||
"propertyOrder": 4
|
||||
},
|
||||
"latchTime": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_latchtime_title",
|
||||
"default": 30,
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 1000,
|
||||
"access": "expert",
|
||||
"propertyOrder": 5
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
|
||||
@@ -1,22 +1,40 @@
|
||||
{
|
||||
"type":"object",
|
||||
"required":true,
|
||||
"properties": {
|
||||
"host" : {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_targetIpHost_title",
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"latchTime": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_latchtime_title",
|
||||
"default": 0,
|
||||
"append" : "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 1000,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 2
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"properties": {
|
||||
"hostList": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_devices_discovered_title",
|
||||
"enum": [ "NONE" ],
|
||||
"options": {
|
||||
"enum_titles": [ "edt_dev_spec_devices_discovery_inprogress" ],
|
||||
"infoText": "edt_dev_spec_devices_discovered_title_info"
|
||||
},
|
||||
"required": true,
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"host": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_targetIpHost_title",
|
||||
"options": {
|
||||
"infoText": "edt_dev_spec_targetIpHost_title_info"
|
||||
},
|
||||
"required": true,
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"latchTime": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_latchtime_title",
|
||||
"default": 0,
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 1000,
|
||||
"access": "expert",
|
||||
"options": {
|
||||
"infoText": "edt_dev_spec_latchtime_title_info"
|
||||
},
|
||||
"propertyOrder": 3
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
|
||||
@@ -1,110 +1,110 @@
|
||||
{
|
||||
"type":"object",
|
||||
"required":true,
|
||||
"properties":{
|
||||
"output" : {
|
||||
"properties": {
|
||||
"host": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_targetIp_title",
|
||||
"default" : "127.0.0.1",
|
||||
"propertyOrder" : 1
|
||||
"title": "edt_dev_spec_targetIp_title",
|
||||
"default": "127.0.0.1",
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"port" : {
|
||||
"port": {
|
||||
"type": "number",
|
||||
"title":"edt_dev_spec_port_title",
|
||||
"title": "edt_dev_spec_port_title",
|
||||
"default": 7890,
|
||||
"propertyOrder" : 2
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"latchTime": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_latchtime_title",
|
||||
"title": "edt_dev_spec_latchtime_title",
|
||||
"default": 0,
|
||||
"append" : "edt_append_ms",
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 1000,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 3
|
||||
"access": "expert",
|
||||
"propertyOrder": 3
|
||||
},
|
||||
"setFcConfig": {
|
||||
"type": "boolean",
|
||||
"title":"edt_dev_spec_FCsetConfig_title",
|
||||
"title": "edt_dev_spec_FCsetConfig_title",
|
||||
"default": false,
|
||||
"propertyOrder" : 4
|
||||
"propertyOrder": 4
|
||||
},
|
||||
"manualLed": {
|
||||
"type": "boolean",
|
||||
"title":"edt_dev_spec_FCmanualControl_title",
|
||||
"title": "edt_dev_spec_FCmanualControl_title",
|
||||
"default": false,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"setFcConfig": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 5
|
||||
"propertyOrder": 5
|
||||
},
|
||||
"ledOn": {
|
||||
"type": "boolean",
|
||||
"title":"edt_dev_spec_FCledToOn_title",
|
||||
"title": "edt_dev_spec_FCledToOn_title",
|
||||
"default": false,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"setFcConfig": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 6
|
||||
"propertyOrder": 6
|
||||
},
|
||||
"interpolation": {
|
||||
"type": "boolean",
|
||||
"title":"edt_dev_spec_interpolation_title",
|
||||
"title": "edt_dev_spec_interpolation_title",
|
||||
"default": false,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"setFcConfig": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 7
|
||||
"propertyOrder": 7
|
||||
},
|
||||
"dither": {
|
||||
"type": "boolean",
|
||||
"title":"edt_dev_spec_dithering_title",
|
||||
"title": "edt_dev_spec_dithering_title",
|
||||
"default": false,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"setFcConfig": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 8
|
||||
"propertyOrder": 8
|
||||
},
|
||||
"gamma" : {
|
||||
"type" : "number",
|
||||
"title" : "edt_dev_spec_gamma_title",
|
||||
"gamma": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_gamma_title",
|
||||
"default": 1.0,
|
||||
"minimum" : 0.1,
|
||||
"minimum": 0.1,
|
||||
"maximum": 5.0,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"setFcConfig": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 9
|
||||
"propertyOrder": 9
|
||||
},
|
||||
"whitepoint" : {
|
||||
"type" : "array",
|
||||
"title" : "edt_dev_spec_whitepoint_title",
|
||||
"whitepoint": {
|
||||
"type": "array",
|
||||
"title": "edt_dev_spec_whitepoint_title",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"setFcConfig": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 10,
|
||||
"default" : [255,255,255],
|
||||
"maxItems" : 3,
|
||||
"minItems" : 3,
|
||||
"format" : "colorpicker",
|
||||
"items" : {
|
||||
"type" : "integer",
|
||||
"minimum" : 0,
|
||||
"propertyOrder": 10,
|
||||
"default": [ 255, 255, 255 ],
|
||||
"maxItems": 3,
|
||||
"minItems": 3,
|
||||
"format": "colorpicker",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"default" : 255
|
||||
"default": 255
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"output": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_spipath_title",
|
||||
"enum" : ["/dev/spidev0.0","/dev/spidev0.1"],
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"rate": {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"output": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_spipath_title",
|
||||
"enum" : ["/dev/spidev0.0","/dev/spidev0.1"],
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"rate": {
|
||||
|
||||
@@ -1,58 +1,112 @@
|
||||
{
|
||||
"type":"object",
|
||||
"required":true,
|
||||
"properties":{
|
||||
"host": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_targetIpHost_title",
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"token": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_auth_key_title",
|
||||
"propertyOrder" : 2
|
||||
},
|
||||
"title": {
|
||||
"type" : "object",
|
||||
"title":"edt_dev_spec_panelorganisation_title",
|
||||
"access" : "advanced",
|
||||
"propertyOrder" : 3
|
||||
},
|
||||
"panelOrderTopDown": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_order_top_down_title",
|
||||
"enum" : [0, 1],
|
||||
"default" : 0,
|
||||
"options" : {
|
||||
"enum_titles" : ["edt_conf_enum_top_down", "edt_conf_enum_bottom_up"]
|
||||
},
|
||||
"minimum" : 0,
|
||||
"maximum" : 1,
|
||||
"access" : "advanced",
|
||||
"propertyOrder" : 4
|
||||
},
|
||||
"panelOrderLeftRight": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_order_left_right_title",
|
||||
"enum" : [0, 1],
|
||||
"default" : 0,
|
||||
"options" : {
|
||||
"enum_titles" : ["edt_conf_enum_left_right", "edt_conf_enum_right_left"]
|
||||
},
|
||||
"minimum" : 0,
|
||||
"maximum" : 1,
|
||||
"access" : "advanced",
|
||||
"propertyOrder" : 5
|
||||
},
|
||||
"panelStartPos": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_panel_start_position",
|
||||
"step": 1,
|
||||
"minimum" : 0,
|
||||
"default": 0,
|
||||
"access" : "advanced",
|
||||
"propertyOrder" : 6
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"properties": {
|
||||
"hostList": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_devices_discovered_title",
|
||||
"enum": [ "NONE" ],
|
||||
"options": {
|
||||
"enum_titles": [ "edt_dev_spec_devices_discovery_inprogress" ],
|
||||
"infoText": "edt_dev_spec_devices_discovered_title_info"
|
||||
},
|
||||
"required": true,
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"host": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_targetIpHost_title",
|
||||
"options": {
|
||||
"infoText": "edt_dev_spec_targetIpHost_title_info"
|
||||
},
|
||||
"required": true,
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"token": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_auth_key_title",
|
||||
"options": {
|
||||
"infoText": "edt_dev_auth_key_title_info"
|
||||
},
|
||||
"propertyOrder": 3
|
||||
},
|
||||
"restoreOriginalState": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_restoreOriginalState_title",
|
||||
"default": true,
|
||||
"required": true,
|
||||
"options": {
|
||||
"infoText": "edt_dev_spec_restoreOriginalState_title_info"
|
||||
},
|
||||
"propertyOrder": 4
|
||||
},
|
||||
"overwriteBrightness": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_brightnessOverwrite_title",
|
||||
"default": true,
|
||||
"required": true,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 5
|
||||
},
|
||||
"brightness": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_brightness_title",
|
||||
"default": 100,
|
||||
"minimum": 1,
|
||||
"maximum": 100,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"overwriteBrightness": true
|
||||
}
|
||||
},
|
||||
"access": "advanced",
|
||||
"propertyOrder": 6
|
||||
},
|
||||
"title": {
|
||||
"type": "object",
|
||||
"title": "edt_dev_spec_panelorganisation_title",
|
||||
"access": "advanced",
|
||||
"propertyOrder": 7
|
||||
},
|
||||
"panelOrderTopDown": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_order_top_down_title",
|
||||
"enum": [ 0, 1 ],
|
||||
"default": 0,
|
||||
"required": true,
|
||||
"options": {
|
||||
"enum_titles": [ "edt_conf_enum_top_down", "edt_conf_enum_bottom_up" ]
|
||||
},
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 8
|
||||
},
|
||||
"panelOrderLeftRight": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_order_left_right_title",
|
||||
"enum": [ 0, 1 ],
|
||||
"default": 0,
|
||||
"required": true,
|
||||
"options": {
|
||||
"enum_titles": [ "edt_conf_enum_left_right", "edt_conf_enum_right_left" ]
|
||||
},
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 9
|
||||
},
|
||||
"panelStartPos": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_panel_start_position",
|
||||
"step": 1,
|
||||
"minimum": 0,
|
||||
"default": 0,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 10
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"output": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_spipath_title",
|
||||
"enum" : ["/dev/spidev0.0","/dev/spidev0.1"],
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"rate": {
|
||||
|
||||
@@ -1,242 +1,243 @@
|
||||
{
|
||||
"type":"object",
|
||||
"required":true,
|
||||
"properties":{
|
||||
"output": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_targetIp_title",
|
||||
"default":"",
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_username_title",
|
||||
"default": "",
|
||||
"propertyOrder" : 2
|
||||
},
|
||||
"clientkey": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_clientKey_title",
|
||||
"default" : "",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 3
|
||||
},
|
||||
"useEntertainmentAPI": {
|
||||
"type": "boolean",
|
||||
"title":"edt_dev_spec_useEntertainmentAPI_title",
|
||||
"default" : false,
|
||||
"propertyOrder" : 4
|
||||
},
|
||||
"transitiontime": {
|
||||
"type": "number",
|
||||
"title":"edt_dev_spec_transistionTime_title",
|
||||
"default" : 1,
|
||||
"append" : "x100ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": false
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 5
|
||||
},
|
||||
"switchOffOnBlack": {
|
||||
"type": "boolean",
|
||||
"title":"edt_dev_spec_switchOffOnBlack_title",
|
||||
"default" : false,
|
||||
"propertyOrder" : 6
|
||||
},
|
||||
"restoreOriginalState": {
|
||||
"type": "boolean",
|
||||
"title":"edt_dev_spec_restoreOriginalState_title",
|
||||
"default" : true,
|
||||
"propertyOrder" : 7
|
||||
},
|
||||
"lightIds": {
|
||||
"type": "array",
|
||||
"title":"edt_dev_spec_lightid_title",
|
||||
"minItems": 1,
|
||||
"uniqueItems" : true,
|
||||
"items" : {
|
||||
"type" : "string",
|
||||
"minimum" : 0,
|
||||
"title" : "edt_dev_spec_lightid_itemtitle"
|
||||
},
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": false
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 8
|
||||
},
|
||||
"groupId": {
|
||||
"type": "number",
|
||||
"title":"edt_dev_spec_groupId_title",
|
||||
"default" : 0,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 9
|
||||
},
|
||||
"blackLightsTimeout": {
|
||||
"type": "number",
|
||||
"title":"edt_dev_spec_blackLightsTimeout_title",
|
||||
"default" : 15000,
|
||||
"step": 500,
|
||||
"minimum" : 10000,
|
||||
"maximum" : 60000,
|
||||
"access" : "advanced",
|
||||
"append" : "edt_append_ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 10
|
||||
},
|
||||
"brightnessThreshold": {
|
||||
"type": "number",
|
||||
"title":"edt_dev_spec_brightnessThreshold_title",
|
||||
"default" : 0,
|
||||
"step": 0.005,
|
||||
"minimum" : 0,
|
||||
"maximum" : 1.0,
|
||||
"access" : "advanced",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 11
|
||||
},
|
||||
"brightnessFactor": {
|
||||
"type": "number",
|
||||
"title":"edt_dev_spec_brightnessFactor_title",
|
||||
"default" : 1.0,
|
||||
"step": 0.25,
|
||||
"minimum" : 0.5,
|
||||
"maximum" : 10.0,
|
||||
"access" : "advanced",
|
||||
"propertyOrder" : 12
|
||||
},
|
||||
"brightnessMin": {
|
||||
"type": "number",
|
||||
"title":"edt_dev_spec_brightnessMin_title",
|
||||
"default" : 0,
|
||||
"step": 0.05,
|
||||
"minimum" : 0,
|
||||
"maximum" : 1.0,
|
||||
"access" : "advanced",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 13
|
||||
},
|
||||
"brightnessMax": {
|
||||
"type": "number",
|
||||
"title":"edt_dev_spec_brightnessMax_title",
|
||||
"default" : 1.0,
|
||||
"step": 0.05,
|
||||
"minimum" : 0,
|
||||
"maximum" : 1.0,
|
||||
"access" : "advanced",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 14
|
||||
},
|
||||
"sslReadTimeout": {
|
||||
"type": "number",
|
||||
"title":"edt_dev_spec_sslReadTimeout_title",
|
||||
"default" : 0,
|
||||
"step": 100,
|
||||
"minimum" : 0,
|
||||
"maximum" : 30000,
|
||||
"access" : "expert",
|
||||
"append" : "edt_append_ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 15
|
||||
},
|
||||
"sslHSTimeoutMin": {
|
||||
"type": "number",
|
||||
"title":"edt_dev_spec_sslHSTimeoutMin_title",
|
||||
"default" : 400,
|
||||
"step": 100,
|
||||
"minimum" : 0,
|
||||
"maximum" : 30000,
|
||||
"access" : "expert",
|
||||
"append" : "edt_append_ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 16
|
||||
},
|
||||
"sslHSTimeoutMax": {
|
||||
"type": "number",
|
||||
"title":"edt_dev_spec_sslHSTimeoutMax_title",
|
||||
"default" : 1000,
|
||||
"step": 100,
|
||||
"minimum" : 0,
|
||||
"maximum" : 30000,
|
||||
"access" : "expert",
|
||||
"append" : "edt_append_ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 17
|
||||
},
|
||||
"verbose": {
|
||||
"type": "boolean",
|
||||
"title":"edt_dev_spec_verbose_title",
|
||||
"default" : false,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 18
|
||||
},
|
||||
"debugStreamer": {
|
||||
"type": "boolean",
|
||||
"title":"edt_dev_spec_debugStreamer_title",
|
||||
"default" : false,
|
||||
"access" : "expert",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 19
|
||||
},
|
||||
"debugLevel": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_debugLevel_title",
|
||||
"enum" : ["0", "1", "2", "3", "4"],
|
||||
"default" : "0",
|
||||
"options" : {
|
||||
"enum_titles" : ["edt_conf_enum_dl_nodebug", "edt_conf_enum_dl_error", "edt_conf_enum_dl_statechange", "edt_conf_enum_dl_informational", "edt_conf_enum_dl_verbose"],
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"minimum" : 0,
|
||||
"maximum" : 4,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 20
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"properties": {
|
||||
"host": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_targetIp_title",
|
||||
"default": "",
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_username_title",
|
||||
"default": "",
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"clientkey": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_clientKey_title",
|
||||
"default": "",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 3
|
||||
},
|
||||
"useEntertainmentAPI": {
|
||||
"type": "boolean",
|
||||
"title": "edt_dev_spec_useEntertainmentAPI_title",
|
||||
"default": true,
|
||||
"propertyOrder": 4
|
||||
},
|
||||
"transitiontime": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_transistionTime_title",
|
||||
"default": 1,
|
||||
"append": "x100ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 5
|
||||
},
|
||||
"switchOffOnBlack": {
|
||||
"type": "boolean",
|
||||
"title": "edt_dev_spec_switchOffOnBlack_title",
|
||||
"default": false,
|
||||
"propertyOrder": 6
|
||||
},
|
||||
"restoreOriginalState": {
|
||||
"type": "boolean",
|
||||
"title": "edt_dev_spec_restoreOriginalState_title",
|
||||
"default": true,
|
||||
"propertyOrder": 7
|
||||
},
|
||||
"lightIds": {
|
||||
"type": "array",
|
||||
"title": "edt_dev_spec_lightid_title",
|
||||
"minimum": 1,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"required": true,
|
||||
"title": "edt_dev_spec_lightid_itemtitle"
|
||||
},
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": false
|
||||
}
|
||||
},
|
||||
"propertyOrder": 8
|
||||
},
|
||||
"groupId": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_groupId_title",
|
||||
"default": 0,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 9
|
||||
},
|
||||
"blackLightsTimeout": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_blackLightsTimeout_title",
|
||||
"default": 15000,
|
||||
"step": 500,
|
||||
"minimum": 10000,
|
||||
"maximum": 60000,
|
||||
"access": "advanced",
|
||||
"append": "edt_append_ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 10
|
||||
},
|
||||
"brightnessThreshold": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_brightnessThreshold_title",
|
||||
"default": 0,
|
||||
"step": 0.005,
|
||||
"minimum": 0,
|
||||
"maximum": 1.0,
|
||||
"access": "advanced",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 11
|
||||
},
|
||||
"brightnessFactor": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_brightnessFactor_title",
|
||||
"default": 1.0,
|
||||
"step": 0.25,
|
||||
"minimum": 0.5,
|
||||
"maximum": 10.0,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 12
|
||||
},
|
||||
"brightnessMin": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_brightnessMin_title",
|
||||
"default": 0,
|
||||
"step": 0.05,
|
||||
"minimum": 0,
|
||||
"maximum": 1.0,
|
||||
"access": "advanced",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 13
|
||||
},
|
||||
"brightnessMax": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_brightnessMax_title",
|
||||
"default": 1.0,
|
||||
"step": 0.05,
|
||||
"minimum": 0,
|
||||
"maximum": 1.0,
|
||||
"access": "advanced",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 14
|
||||
},
|
||||
"sslReadTimeout": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_sslReadTimeout_title",
|
||||
"default": 0,
|
||||
"step": 100,
|
||||
"minimum": 0,
|
||||
"maximum": 30000,
|
||||
"access": "expert",
|
||||
"append": "edt_append_ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 15
|
||||
},
|
||||
"sslHSTimeoutMin": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_sslHSTimeoutMin_title",
|
||||
"default": 400,
|
||||
"step": 100,
|
||||
"minimum": 0,
|
||||
"maximum": 30000,
|
||||
"access": "expert",
|
||||
"append": "edt_append_ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 16
|
||||
},
|
||||
"sslHSTimeoutMax": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_sslHSTimeoutMax_title",
|
||||
"default": 1000,
|
||||
"step": 100,
|
||||
"minimum": 0,
|
||||
"maximum": 30000,
|
||||
"access": "expert",
|
||||
"append": "edt_append_ms",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 17
|
||||
},
|
||||
"verbose": {
|
||||
"type": "boolean",
|
||||
"title": "edt_dev_spec_verbose_title",
|
||||
"default": false,
|
||||
"access": "expert",
|
||||
"propertyOrder": 18
|
||||
},
|
||||
"debugStreamer": {
|
||||
"type": "boolean",
|
||||
"title": "edt_dev_spec_debugStreamer_title",
|
||||
"default": false,
|
||||
"access": "expert",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"propertyOrder": 19
|
||||
},
|
||||
"debugLevel": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_debugLevel_title",
|
||||
"enum": [ "0", "1", "2", "3", "4" ],
|
||||
"default": "0",
|
||||
"options": {
|
||||
"enum_titles": [ "edt_conf_enum_dl_nodebug", "edt_conf_enum_dl_error", "edt_conf_enum_dl_statechange", "edt_conf_enum_dl_informational", "edt_conf_enum_dl_verbose" ],
|
||||
"dependencies": {
|
||||
"useEntertainmentAPI": true
|
||||
}
|
||||
},
|
||||
"minimum": 0,
|
||||
"maximum": 4,
|
||||
"access": "expert",
|
||||
"propertyOrder": 20
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"output": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_spipath_title",
|
||||
"enum" : ["/dev/spidev0.0","/dev/spidev0.1"],
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"rate": {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"output": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_spipath_title",
|
||||
"enum" : ["/dev/spidev0.0","/dev/spidev0.1"],
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"rate": {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"output": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_spipath_title",
|
||||
"enum" : ["/dev/spidev0.0","/dev/spidev0.1"],
|
||||
"default" : "/dev/spidev0.0",
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
|
||||
@@ -1,22 +1,84 @@
|
||||
{
|
||||
"type":"object",
|
||||
"required":true,
|
||||
"properties":{
|
||||
"host" : {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_targetIpHost_title",
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"latchTime": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_latchtime_title",
|
||||
"default": 0,
|
||||
"append" : "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 1000,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 2
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
"type": "object",
|
||||
"title": "",
|
||||
"required": true,
|
||||
"properties": {
|
||||
"hostList": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_devices_discovered_title",
|
||||
"enum": [ "NONE" ],
|
||||
"options": {
|
||||
"enum_titles": [ "edt_dev_spec_devices_discovery_inprogress" ],
|
||||
"infoText": "edt_dev_spec_devices_discovered_title_info"
|
||||
},
|
||||
"required": true,
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"host": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_targetIpHost_title",
|
||||
"options": {
|
||||
"infoText": "edt_dev_spec_targetIpHost_title_info"
|
||||
},
|
||||
"required": true,
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"restoreOriginalState": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_restoreOriginalState_title",
|
||||
"default": false,
|
||||
"required": true,
|
||||
"options": {
|
||||
"infoText": "edt_dev_spec_restoreOriginalState_title_info"
|
||||
},
|
||||
"propertyOrder": 3
|
||||
},
|
||||
"overwriteSync": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_syncOverwrite_title",
|
||||
"default": true,
|
||||
"required": true,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 4
|
||||
},
|
||||
"overwriteBrightness": {
|
||||
"type": "boolean",
|
||||
"format": "checkbox",
|
||||
"title": "edt_dev_spec_brightnessOverwrite_title",
|
||||
"default": true,
|
||||
"required": true,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 5
|
||||
},
|
||||
"brightness": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_brightness_title",
|
||||
"default": 255,
|
||||
"minimum": 1,
|
||||
"maximum": 255,
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"overwriteBrightness": true
|
||||
}
|
||||
},
|
||||
"access": "advanced",
|
||||
"propertyOrder": 6
|
||||
},
|
||||
"latchTime": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_latchtime_title",
|
||||
"default": 0,
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 1000,
|
||||
"access": "expert",
|
||||
"options": {
|
||||
"infoText": "edt_dev_spec_latchtime_title_info"
|
||||
},
|
||||
"propertyOrder": 7
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"output": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_spipath_title",
|
||||
"enum" : ["/dev/spidev0.0","/dev/spidev0.1"],
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"rate": {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"output": {
|
||||
"type": "string",
|
||||
"title":"edt_dev_spec_spipath_title",
|
||||
"enum" : ["/dev/spidev0.0","/dev/spidev0.1"],
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"rate": {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"dma": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_dmaNumber_title",
|
||||
"default": 5,
|
||||
"default": 10,
|
||||
"propertyOrder" : 3
|
||||
},
|
||||
"pwmchannel": {
|
||||
|
||||
@@ -1,180 +1,176 @@
|
||||
{
|
||||
"type":"object",
|
||||
"required":true,
|
||||
"properties":{
|
||||
"colorModel": {
|
||||
"type": "integer",
|
||||
"title":"Output Type",
|
||||
"enum" : [0, 1],
|
||||
"default" : 1,
|
||||
"options" : {
|
||||
"enum_titles" : ["edt_conf_enum_hsv", "edt_conf_enum_rgb"]
|
||||
},
|
||||
"minimum" : 0,
|
||||
"maximum" : 1,
|
||||
"access" : "advanced",
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"transEffect": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_transeffect_title",
|
||||
"enum" : [0, 1],
|
||||
"default" : 0,
|
||||
"options" : {
|
||||
"enum_titles" : ["edt_conf_enum_transeffect_smooth", "edt_conf_enum_transeffect_sudden" ]
|
||||
},
|
||||
"minimum" : 0,
|
||||
"maximum" : 1,
|
||||
"access" : "advanced",
|
||||
"propertyOrder" : 2
|
||||
},
|
||||
"transTime": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_transistionTime_title",
|
||||
"default": 40,
|
||||
"append" : "ms",
|
||||
"minimum": 30,
|
||||
"maximum": 5000,
|
||||
"access" : "advanced",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"transEffect": 0
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 3
|
||||
},
|
||||
"extraTimeDarkness": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_transistionTimeExtra_title",
|
||||
"default" : 0,
|
||||
"step": 100,
|
||||
"minimum" : 0,
|
||||
"maximum" : 8000,
|
||||
"append" : "ms",
|
||||
"access" : "advanced",
|
||||
"propertyOrder" : 4
|
||||
},
|
||||
"brightnessMin": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_brightnessMin_title",
|
||||
"default" : 1,
|
||||
"step": 1,
|
||||
"minimum" : 1,
|
||||
"maximum" : 99,
|
||||
"append" : "%",
|
||||
"access" : "advanced",
|
||||
"propertyOrder" : 5
|
||||
},
|
||||
"brightnessSwitchOffOnMinimum": {
|
||||
"type": "boolean",
|
||||
"title":"edt_dev_spec_switchOffOnbelowMinBrightness_title",
|
||||
"default" : true,
|
||||
"access" : "advanced",
|
||||
"propertyOrder" : 6
|
||||
},
|
||||
"brightnessMax": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_brightnessMax_title",
|
||||
"default" : 100,
|
||||
"step": 1,
|
||||
"minimum" : 0,
|
||||
"maximum" : 100,
|
||||
"append" : "%",
|
||||
"access" : "advanced",
|
||||
"propertyOrder" : 7
|
||||
},
|
||||
"brightnessFactor": {
|
||||
"type": "number",
|
||||
"title":"edt_dev_spec_brightnessFactor_title",
|
||||
"default" : 1.0,
|
||||
"step": 0.25,
|
||||
"minimum" : 0.5,
|
||||
"maximum" : 10.0,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 8
|
||||
},
|
||||
"restoreOriginalState": {
|
||||
"type": "boolean",
|
||||
"title":"edt_dev_spec_restoreOriginalState_title",
|
||||
"default" : false,
|
||||
"propertyOrder" : 9
|
||||
},
|
||||
"lights": {
|
||||
"type": "array",
|
||||
"title":"edt_dev_spec_lights_title",
|
||||
"propertyOrder" : 9,
|
||||
"minimum" : 1,
|
||||
"uniqueItems" : true,
|
||||
"items" : {
|
||||
"type" : "object",
|
||||
"title" : "edt_dev_spec_lights_itemtitle",
|
||||
"required" : true,
|
||||
"properties" :
|
||||
{
|
||||
"host" :
|
||||
{
|
||||
"type" : "string",
|
||||
"minimum" : 7,
|
||||
"title" : "edt_dev_spec_networkDeviceName_title",
|
||||
"required" : true,
|
||||
"propertyOrder" : 1
|
||||
},
|
||||
"port" :
|
||||
{
|
||||
"type" : "integer",
|
||||
"minimum" : 0,
|
||||
"maximum" : 65535,
|
||||
"default":55443,
|
||||
"title" : "edt_dev_spec_networkDevicePort_title",
|
||||
"required" : false,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 2
|
||||
},
|
||||
"name" :
|
||||
{
|
||||
"type" : "string",
|
||||
"title" : "edt_dev_spec_lights_name",
|
||||
"minimum" : 0,
|
||||
"propertyOrder" : 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"propertyOrder" : 10
|
||||
},
|
||||
"quotaWait": {
|
||||
"type": "integer",
|
||||
"title":"Wait time (quota)",
|
||||
"default": 1000,
|
||||
"append" : "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 10000,
|
||||
"step": 100,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 11
|
||||
},
|
||||
"latchTime": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_latchtime_title",
|
||||
"default": 40,
|
||||
"append" : "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 1000,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 12
|
||||
},
|
||||
"debugLevel": {
|
||||
"type": "integer",
|
||||
"title":"edt_dev_spec_debugLevel_title",
|
||||
"enum" : [0, 1, 2, 3],
|
||||
"default" : 0,
|
||||
"options" : {
|
||||
"enum_titles" : ["edt_conf_enum_dl_nodebug", "edt_conf_enum_dl_verbose1", "edt_conf_enum_dl_verbose2", "edt_conf_enum_dl_verbose3"]
|
||||
},
|
||||
"minimum" : 0,
|
||||
"maximum" : 3,
|
||||
"access" : "expert",
|
||||
"propertyOrder" : 13
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"properties": {
|
||||
"colorModel": {
|
||||
"type": "string",
|
||||
"title": "Output Type",
|
||||
"enum": [ "0", "1" ],
|
||||
"default": "1",
|
||||
"options": {
|
||||
"enum_titles": [ "edt_conf_enum_hsv", "edt_conf_enum_rgb" ]
|
||||
},
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"transEffect": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_transeffect_title",
|
||||
"enum": [ "0", "1" ],
|
||||
"default": "0",
|
||||
"options": {
|
||||
"enum_titles": [ "edt_conf_enum_transeffect_smooth", "edt_conf_enum_transeffect_sudden" ]
|
||||
},
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"transTime": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_transistionTime_title",
|
||||
"default": 40,
|
||||
"append": "ms",
|
||||
"minimum": 30,
|
||||
"maximum": 5000,
|
||||
"access": "advanced",
|
||||
"options": {
|
||||
"dependencies": {
|
||||
"transEffect": 0
|
||||
}
|
||||
},
|
||||
"propertyOrder": 3
|
||||
},
|
||||
"extraTimeDarkness": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_transistionTimeExtra_title",
|
||||
"default": 0,
|
||||
"step": 100,
|
||||
"minimum": 0,
|
||||
"maximum": 8000,
|
||||
"append": "ms",
|
||||
"access": "advanced",
|
||||
"propertyOrder": 4
|
||||
},
|
||||
"brightnessMin": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_brightnessMin_title",
|
||||
"default": 1,
|
||||
"step": 1,
|
||||
"minimum": 1,
|
||||
"maximum": 99,
|
||||
"append": "%",
|
||||
"access": "advanced",
|
||||
"propertyOrder": 5
|
||||
},
|
||||
"brightnessSwitchOffOnMinimum": {
|
||||
"type": "boolean",
|
||||
"title": "edt_dev_spec_switchOffOnbelowMinBrightness_title",
|
||||
"default": true,
|
||||
"access": "advanced",
|
||||
"propertyOrder": 6
|
||||
},
|
||||
"brightnessMax": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_brightnessMax_title",
|
||||
"default": 100,
|
||||
"step": 1,
|
||||
"minimum": 0,
|
||||
"maximum": 100,
|
||||
"append": "%",
|
||||
"access": "advanced",
|
||||
"propertyOrder": 7
|
||||
},
|
||||
"brightnessFactor": {
|
||||
"type": "number",
|
||||
"title": "edt_dev_spec_brightnessFactor_title",
|
||||
"default": 1.0,
|
||||
"step": 0.25,
|
||||
"minimum": 0.5,
|
||||
"maximum": 10.0,
|
||||
"access": "expert",
|
||||
"propertyOrder": 8
|
||||
},
|
||||
"restoreOriginalState": {
|
||||
"type": "boolean",
|
||||
"title": "edt_dev_spec_restoreOriginalState_title",
|
||||
"default": false,
|
||||
"propertyOrder": 9
|
||||
},
|
||||
"lights": {
|
||||
"type": "array",
|
||||
"title": "edt_dev_spec_lights_title",
|
||||
"propertyOrder": 9,
|
||||
"minimum": 1,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"title": "edt_dev_spec_lights_itemtitle",
|
||||
"required": true,
|
||||
"properties": {
|
||||
"host": {
|
||||
"type": "string",
|
||||
"minLength": 7,
|
||||
"title": "edt_dev_spec_networkDeviceName_title",
|
||||
"required": true,
|
||||
"propertyOrder": 1
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 65535,
|
||||
"default": 55443,
|
||||
"title": "edt_dev_spec_networkDevicePort_title",
|
||||
"required": false,
|
||||
"access": "expert",
|
||||
"propertyOrder": 2
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_lights_name",
|
||||
"minimum": 0,
|
||||
"propertyOrder": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"propertyOrder": 10
|
||||
},
|
||||
"quotaWait": {
|
||||
"type": "integer",
|
||||
"title": "Wait time (quota)",
|
||||
"default": 1000,
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 10000,
|
||||
"step": 100,
|
||||
"access": "expert",
|
||||
"propertyOrder": 11
|
||||
},
|
||||
"latchTime": {
|
||||
"type": "integer",
|
||||
"title": "edt_dev_spec_latchtime_title",
|
||||
"default": 40,
|
||||
"append": "edt_append_ms",
|
||||
"minimum": 0,
|
||||
"maximum": 1000,
|
||||
"access": "expert",
|
||||
"propertyOrder": 12
|
||||
},
|
||||
"debugLevel": {
|
||||
"type": "string",
|
||||
"title": "edt_dev_spec_debugLevel_title",
|
||||
"enum": [ "0", "1", "2", "3" ],
|
||||
"default": "0",
|
||||
"options": {
|
||||
"enum_titles": [ "edt_conf_enum_dl_nodebug", "edt_conf_enum_dl_verbose1", "edt_conf_enum_dl_verbose2", "edt_conf_enum_dl_verbose3" ]
|
||||
},
|
||||
"minimum": 0,
|
||||
"maximum": 3,
|
||||
"access": "expert",
|
||||
"propertyOrder": 13
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user