Initial AtmoOrb support including sample json config.

Former-commit-id: 51ad57afd39695095b5886966e8c71c4e47f269e
This commit is contained in:
RickDB 2016-03-14 20:19:19 +01:00
parent 935e400a4a
commit bd1a45216a
5 changed files with 444 additions and 0 deletions

View File

@ -0,0 +1,163 @@
// Automatically generated configuration file for 'Hyperion daemon'
// Generated by: HyperCon (The Hyperion deamon configuration file builder)
// Created with HyperCon V1.00.0 (11.03.2016)
{
/// Device configuration contains the following fields:
/// * 'name' : The user friendly name of the device (only used for display purposes)
/// * 'type' : The type of the device or leds (known types for now are
/// APA102, Adalight, AdalightAPA102, AmbiLed, Atmo, Hyperion-USBASP-WS2801, Hyperion-USBASP-WS2812, Lightberry, Lightpack, LPD6803, LPD8806, Multi-Lightpack, P9813, Paintpack, PhilipsHUE, PiBlaster, SEDU, Test, ThinkerForge, TPM2, WS2801, WS2812b, None)
/// * [device type specific configuration]
/// * 'colorOrder' : The order of the color bytes ('rgb', 'rbg', 'bgr', etc.).
///
/// * 'Specific of Philips Hue:
/// * 'username' : The name of user registred on the Philips Hue Bridge
/// * 'switchOffOnBlack': Define if Hue light switch off when black is detected
/// * 'transitiontime' : Set the time of transition between color of Hue light
"device" :
{
"name" : "MyPi",
"type" : "atmoorb",
"output" : "239.15.18.2",
"transitiontime" : 0,
"port" : 49692,
"numLeds" : 24,
"orbIds" : [1],
"switchOffOnBlack" : true,
"colorOrder" : "rgb"
},
/// Color manipulation configuration used to tune the output colors to specific surroundings.
/// The configuration contains a list of color-transforms. Each transform contains the
/// following fields:
/// * 'id' : The unique identifier of the color transformation (eg 'device_1') /// * 'leds' : The indices (or index ranges) of the leds to which this color transform applies
/// (eg '0-5, 9, 11, 12-17'). The indices are zero based. /// * 'hsv' : The manipulation in the Hue-Saturation-Value color domain with the following
/// tuning parameters:
/// - 'saturationGain' The gain adjustement of the saturation
/// - 'valueGain' The gain adjustement of the value
/// * 'red'/'green'/'blue' : The manipulation in the Red-Green-Blue color domain with the
/// following tuning parameters for each channel:
/// - 'threshold' The minimum required input value for the channel to be on
/// (else zero)
/// - 'gamma' The gamma-curve correction factor
/// - 'blacklevel' The lowest possible value (when the channel is black)
/// - 'whitelevel' The highest possible value (when the channel is white)
///
/// Next to the list with color transforms there is also a smoothing option.
/// * 'smoothing' : Smoothing of the colors in the time-domain with the following tuning
/// parameters:
/// - 'type' The type of smoothing algorithm ('linear' or 'none')
/// - 'time_ms' The time constant for smoothing algorithm in milliseconds
/// - 'updateFrequency' The update frequency of the leds in Hz
/// - 'updateDelay' The delay of the output to leds (in periods of smoothing)
"color" :
{
"transform" :
[
{
"id" : "default",
"leds" : "*",
"hsv" :
{
"saturationGain" : 1.0000,
"valueGain" : 1.0000
},
"red" :
{
"threshold" : 0.0000,
"gamma" : 2.2000,
"blacklevel" : 0.0000,
"whitelevel" : 1.0000
},
"green" :
{
"threshold" : 0.0000,
"gamma" : 2.2000,
"blacklevel" : 0.0000,
"whitelevel" : 1.0000
},
"blue" :
{
"threshold" : 0.0000,
"gamma" : 2.2000,
"blacklevel" : 0.0000,
"whitelevel" : 1.0000
}
}
],
"smoothing" :
{
"type" : "linear",
"time_ms" : 100,
"updateFrequency" : 60.0000,
"updateDelay" : 0
}
},
/// The black border configuration, contains the following items:
/// * enable : true if the detector should be activated
/// * threshold : Value below which a pixel is regarded as black (value between 0.0 and 1.0)
/// * unknownFrameCnt : Number of frames without any detection before the border is set to 0 (default 600)
/// * borderFrameCnt : Number of frames before a consistent detected border gets set (default 50)
/// * maxInconsistentCnt : Number of inconsistent frames that are ignored before a new border gets a chance to proof consistency
/// * blurRemoveCnt : Number of pixels that get removed from the detected border to cut away blur (default 1)
/// * mode : Border detection mode (values=default,classic,osd)
"blackborderdetector" :
{
"enable" : false,
"threshold" : 0.01,
"unknownFrameCnt" : 600,
"borderFrameCnt" : 50,
"maxInconsistentCnt" : 10,
"blurRemoveCnt" : 1,
"mode" : "default"
},
/// The configuration of the effect engine, contains the following items:
/// * paths : An array with absolute location(s) of directories with effects
/// * color : Set static color after boot -> set effect to "" (empty) and input the values [R,G,B] and set duration_ms NOT to 0 (use 1) instead
/// * effect : The effect selected as 'boot sequence'
/// * duration_ms : The duration of the selected effect (0=endless)
/// * priority : The priority of the selected effect/static color (default=990) HINT: lower value result in HIGHER priority!
"effects" :
{
"paths" :
[
"/opt/hyperion/effects"
]
},
/// The configuration of the Json server which enables the json remote interface
/// * port : Port at which the json server is started
"jsonServer" :
{
"port" : 19446
},
/// The configuration of the Proto server which enables the protobuffer remote interface
/// * port : Port at which the protobuffer server is started
"protoServer" :
{
"port" : 19447
},
/// The configuration for each individual led. This contains the specification of the area
/// averaged of an input image for each led to determine its color. Each item in the list
/// contains the following fields:
/// * index: The index of the led. This determines its location in the string of leds; zero
/// being the first led.
/// * hscan: The fractional part of the image along the horizontal used for the averaging
/// (minimum and maximum inclusive)
/// * vscan: The fractional part of the image along the vertical used for the averaging
/// (minimum and maximum inclusive)
"leds" :
[
{
"index" : 0,
"hscan" : { "minimum" : 0.0000, "maximum" : 1.0000 },
"vscan" : { "minimum" : 0.0000, "maximum" : 1.0000 }
}
],
"endOfJson" : "endOfJson"
}

View File

@ -17,6 +17,7 @@ SET(Leddevice_QT_HEADERS
${CURRENT_SOURCE_DIR}/LedDeviceAdalight.h ${CURRENT_SOURCE_DIR}/LedDeviceAdalight.h
${CURRENT_SOURCE_DIR}/LedDeviceAdalightApa102.h ${CURRENT_SOURCE_DIR}/LedDeviceAdalightApa102.h
${CURRENT_SOURCE_DIR}/LedDeviceAmbiLed.h ${CURRENT_SOURCE_DIR}/LedDeviceAmbiLed.h
${CURRENT_SOURCE_DIR}/LedDeviceAtmoOrb.h
${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.h ${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.h
${CURRENT_SOURCE_DIR}/LedHIDDevice.h ${CURRENT_SOURCE_DIR}/LedHIDDevice.h
${CURRENT_SOURCE_DIR}/LedDeviceRawHID.h ${CURRENT_SOURCE_DIR}/LedDeviceRawHID.h
@ -50,6 +51,7 @@ SET(Leddevice_SOURCES
${CURRENT_SOURCE_DIR}/LedDeviceAdalight.cpp ${CURRENT_SOURCE_DIR}/LedDeviceAdalight.cpp
${CURRENT_SOURCE_DIR}/LedDeviceAdalightApa102.cpp ${CURRENT_SOURCE_DIR}/LedDeviceAdalightApa102.cpp
${CURRENT_SOURCE_DIR}/LedDeviceAmbiLed.cpp ${CURRENT_SOURCE_DIR}/LedDeviceAmbiLed.cpp
${CURRENT_SOURCE_DIR}/LedDeviceAtmoOrb.cpp
${CURRENT_SOURCE_DIR}/LedDeviceRawHID.cpp ${CURRENT_SOURCE_DIR}/LedDeviceRawHID.cpp
${CURRENT_SOURCE_DIR}/LedDeviceLightpack.cpp ${CURRENT_SOURCE_DIR}/LedDeviceLightpack.cpp
${CURRENT_SOURCE_DIR}/LedDeviceMultiLightpack.cpp ${CURRENT_SOURCE_DIR}/LedDeviceMultiLightpack.cpp

View File

@ -0,0 +1,152 @@
// Local-Hyperion includes
#include "LedDeviceAtmoOrb.h"
// qt includes
#include <QtCore/qmath.h>
#include <QEventLoop>
#include <QtNetwork>
#include <QNetworkReply>
#include <QTime>
#include <stdexcept>
#include <string>
#include <set>
AtmoOrbLight::AtmoOrbLight(unsigned int id) {
// Not implemented
}
LedDeviceAtmoOrb::LedDeviceAtmoOrb(const std::string& output, bool switchOffOnBlack,
int transitiontime, int port, int numLeds, std::vector<unsigned int> orbIds) :
multicastGroup(output.c_str()), switchOffOnBlack(switchOffOnBlack), transitiontime(transitiontime),
multiCastGroupPort(port), numLeds(numLeds), orbIds(orbIds) {
manager = new QNetworkAccessManager();
groupAddress = QHostAddress(multicastGroup);
udpSocket = new QUdpSocket(this);
udpSocket->bind(multiCastGroupPort, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
if (!udpSocket->joinMulticastGroup(groupAddress))
{
joinedMulticastgroup = false;
}
else
{
joinedMulticastgroup = true;
}
}
int LedDeviceAtmoOrb::write(const std::vector<ColorRgb> & ledValues) {
// If not in multicast group return
if (!joinedMulticastgroup)
{
return 0;
}
// Iterate through colors and set Orb color.
unsigned int idx = 0;
for (const ColorRgb& color : ledValues)
{
// If color is identical skip color setter
if(color.red == lastRed && color.green == lastGreen && color.blue == lastBlue)
{
continue;
}
// Options parameter:
//
// 1 = force off
// 2 = use lamp smoothing and validate by Orb ID
// 4 = validate by Orb ID
//
if (switchOffOnBlack && color.red == 0 && color.green == 0 && color.blue == 0) {
// Force to black
for (int i = 0; i < orbIds.size(); i++) {
setColor(orbIds[i], color, 1);
}
}
else
{
// Default send color
for (int i = 0; i < orbIds.size(); i++) {
setColor(orbIds[i], color, 4);
}
}
// Store current colors
lastRed = color.red;
lastGreen = color.green;
lastBlue = color.blue;
// Next light id.
idx++;
}
return 0;
}
void LedDeviceAtmoOrb::setColor(unsigned int orbId, const ColorRgb& color, int commandType) {
QByteArray bytes;
bytes.resize(5 + 24 * 3);
// Command identifier: C0FFEE
bytes[0] = 0xC0;
bytes[1] = 0xFF;
bytes[2] = 0xEE;
// Command type
bytes[3] = 2;
// Orb ID
bytes[4] = orbId;
// RED / GREEN / BLUE
bytes[5] = color.red;
bytes[6] = color.green;
bytes[7] = color.blue;
sendCommand(bytes);
}
void LedDeviceAtmoOrb::sendCommand(const QByteArray & bytes) {
QByteArray datagram = bytes;
udpSocket->writeDatagram(datagram.data(), datagram.size(),
groupAddress, multiCastGroupPort);
}
int LedDeviceAtmoOrb::switchOff() {
// Default send color
for (int i = 0; i < orbIds.size(); i++) {
QByteArray bytes;
bytes.resize(5 + 24 * 3);
// Command identifier: C0FFEE
bytes[0] = 0xC0;
bytes[1] = 0xFF;
bytes[2] = 0xEE;
// Command type
bytes[3] = 1;
// Orb ID
bytes[4] = orbIds[i];
// RED / GREEN / BLUE
bytes[5] = 0;
bytes[6] = 0;
bytes[7] = 0;
sendCommand(bytes);
}
return 0;
}
void LedDeviceAtmoOrb::switchOn(unsigned int nLights) {
// Not implemented
}
LedDeviceAtmoOrb::~LedDeviceAtmoOrb() {
delete manager;
}

View File

@ -0,0 +1,111 @@
#pragma once
// STL includes
#include <string>
// Qt includes
#include <QObject>
#include <QString>
#include <QNetworkAccessManager>
#include <QHostAddress>
#include <QTime>
// Leddevice includes
#include <leddevice/LedDevice.h>
class QUdpSocket;
class AtmoOrbLight {
public:
unsigned int id;
///
/// Constructs the light.
///
/// @param id the orb id
AtmoOrbLight(unsigned int id);
};
/**
* Implementation for the AtmoOrb
*
* To use set the device to "atmoorb".
*
* @author RickDB (github)
*/
class LedDeviceAtmoOrb : public QObject, public LedDevice {
Q_OBJECT
public:
// Last color sent
int lastRed;
int lastGreen;
int lastBlue;
// Last command sent timer
QTime timer;
// Multicast status
bool joinedMulticastgroup;
///
/// Constructs the device.
///
/// @param output is the multicast address of Orbs
///
/// @param switchOffOnBlack kill lights for black (default: false)
///
/// @param transitiontime is optional and not used at the moment
///
/// @param port is the multicast port.
///
/// @param numLeds is the total amount of leds per Orb
///
/// @param orb ids to control
///
LedDeviceAtmoOrb(const std::string& output, bool switchOffOnBlack =
false, int transitiontime = 0, int port = 49692, int numLeds = 24, std::vector<unsigned int> orbIds = std::vector<unsigned int>());
///
/// Destructor of this device
///
virtual ~LedDeviceAtmoOrb();
///
/// Sends the given led-color values to the Orbs
///
/// @param ledValues The color-value per led
///
/// @return Zero on success else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues);
virtual int switchOff();
private:
/// Array to save the lamps.
std::vector<AtmoOrbLight> lights;
/// QNetworkAccessManager object for sending requests.
QNetworkAccessManager* manager;
QString multicastGroup;
bool switchOffOnBlack;
int transitiontime;
int multiCastGroupPort;
int numLeds;
QHostAddress groupAddress;
QUdpSocket *udpSocket;
/// Array of the light ids.
std::vector<unsigned int> orbIds;
///
/// Switches the leds on.
///
/// @param nLights the number of lights
///
void switchOn(unsigned int nLights);
// Set color
void setColor(unsigned int orbId, const ColorRgb& color, int commandType);
// Send color command
void sendCommand(const QByteArray & bytes);
};

View File

@ -37,6 +37,7 @@
#include "LedDeviceTpm2.h" #include "LedDeviceTpm2.h"
#include "LedDeviceAtmo.h" #include "LedDeviceAtmo.h"
#include "LedDeviceAdalightApa102.h" #include "LedDeviceAdalightApa102.h"
#include "LedDeviceAtmoOrb.h"
#ifdef ENABLE_WS2812BPWM #ifdef ENABLE_WS2812BPWM
#include "LedDeviceWS2812b.h" #include "LedDeviceWS2812b.h"
@ -245,6 +246,21 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
} }
device = new LedDevicePhilipsHue(output, username, switchOffOnBlack, transitiontime, lightIds); device = new LedDevicePhilipsHue(output, username, switchOffOnBlack, transitiontime, lightIds);
} }
else if (type == "atmoorb")
{
const std::string output = deviceConfig["output"].asString();
const bool switchOffOnBlack = deviceConfig.get("switchOffOnBlack", true).asBool();
const int transitiontime = deviceConfig.get("transitiontime", 1).asInt();
const int port = deviceConfig.get("port", 1).asInt();
const int numLeds = deviceConfig.get("numLeds", 1).asInt();
std::vector<unsigned int> orbIds;
for (Json::Value::ArrayIndex i = 0; i < deviceConfig["orbIds"].size(); i++) {
orbIds.push_back(deviceConfig["orbIds"][i].asInt());
}
device = new LedDeviceAtmoOrb(output, switchOffOnBlack, transitiontime, port, numLeds, orbIds);
}
else if (type == "test") else if (type == "test")
{ {
const std::string output = deviceConfig["output"].asString(); const std::string output = deviceConfig["output"].asString();