Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c63c85d
src.pro: enable qml
zomfg Jun 21, 2020
65b607d
mood lamps: replaced cpp lamps with js engine
zomfg Jun 24, 2020
f16c86f
mood lamps: lamp scripts
zomfg Jun 24, 2020
770b5ca
mood lamps: readme
zomfg Jun 24, 2020
7d1d309
mood lamps: readme update
zomfg Jun 24, 2020
455b07d
readme: linux: required package for QML
zomfg Jun 25, 2020
5a1aac0
mood lamps: readme: warning about altered scripts and updates
zomfg Jun 25, 2020
60a28c8
mood lamps: improved Breathing
zomfg Jun 25, 2020
bc63e22
mood lamp manager: expose QT_VERSION
zomfg Jun 25, 2020
235b2ad
mood lamp manager: qt 5.12 tweaks
zomfg Jun 25, 2020
d1e0378
mood lamps: moved scripts to resources + deployment
zomfg Jun 27, 2020
26eb5d3
lightpack application: removed redundant mood lamp initFormSettings
zomfg Jun 27, 2020
f1cf733
settings window: mood lamps: refresh/open script dir buttons
zomfg Jun 27, 2020
2aa226b
settings window: mood lamps: scripting readme link
zomfg Jun 27, 2020
b752756
settings window: moodlamps: updated tabstops
zomfg Aug 13, 2020
b6513da
settings window: moodlamps: button tooltips
zomfg Aug 13, 2020
7905351
mood lamps: renamed shine() to tick()
zomfg Aug 13, 2020
4f4348a
mood lamps: typos
zomfg Aug 13, 2020
9a63512
mood lamp manager: removed old header
zomfg Aug 13, 2020
8496b9a
mood lamp manager: update colors on lamp change
zomfg Aug 13, 2020
beb41dc
mood lamps: static lamp: removed note about recommended timing
zomfg Aug 13, 2020
e5980fc
mood lamp manager: interval value check on load
zomfg Aug 13, 2020
c8c99b8
settings: migrate moodlamp names
zomfg Aug 13, 2020
327dd5a
mood lamp settings: base color tooltips
zomfg Aug 14, 2020
624b07a
mood lamp manager: pass base color to liquid color gen
zomfg Aug 14, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ handle other devices with Prismatik such as Adalight, Ardulight, or even Alienwa

![SoundViz](screenshots/soundviz_win.png)

* Mood Lamps
* Scriptable Mood Lamps ([see here how](/Software/res/moodlamps/))

![Mood Lamps](screenshots/moodlamps_win.png)

Expand Down Expand Up @@ -104,6 +104,7 @@ handle other devices with Prismatik such as Adalight, Ardulight, or even Alienwa
You will need the following packages, usually all of them are in distro's repository:
* qt5-default
* libqt5serialport5-dev
* qtdeclarative5-dev <!--- qml module -->
* build-essential
* libgtk2.0-dev
* libusb-1.0-0-dev
Expand Down
7 changes: 7 additions & 0 deletions Software/res/LightpackResources.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,12 @@
<file>icons/Prismatik-pixmap.png</file>
<file>text/cast.html</file>
<file>icons/persist.png</file>

<file>moodlamps/static.mjs</file>
<file>moodlamps/rgb_is_life.mjs</file>
<file>moodlamps/rgb_cycle.mjs</file>
<file>moodlamps/breathing.mjs</file>
<file>moodlamps/fire.mjs</file>
<file>moodlamps/random_noise.mjs</file>
</qresource>
</RCC>
144 changes: 144 additions & 0 deletions Software/res/moodlamps/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
Moodlamp scripting
---------
Prismatik uses [QJSEngine](https://doc.qt.io/qt-5/qjsengine.html) for parsing and running moodlamp scripts, so if you are familiar with JavaScript you'll be fine.

The scripts live in your user's Prismatik folder:
- Windows: `C:\Users\<you>\Prismatik\moodlamps`
- macOS & Linux: `~/.Prismatik/moodlamps`

File name should obey these rules:
- allowed characters `a-z`, `0-9` and `_` (underscore)
- `.mjs` extension
- unique name

For ex: `my_cool_lamp.mjs`


**The bare minimum template:**
```js
export const name = "your lamp name here"
export const interval = 50 // frame time in ms

/**
* @param {integer} baseColor integer RGB value of the base color that is selected in Prismatik
* @param {array} colors array of RGB integers representing your LEDs
* @return {array} array of new RGB integers representing your LEDs
*/
export function tick(baseColor, colors)
{
for (let i = 0; i < colors.length; i++)
{
// your code here, for ex:
colors[i] = baseColor // will set all LEDs to the color you set in Prismatik
}

return colors
}
```


**More examples:**

```js
export const name = "your lamp name here"
export const interval = 50 // frame time in ms

// if you need a global constant
const myBlueConst = 0x0000FF // blue color

// if you need a variable that persists between frames (a counter for ex), declare it here
let someVariable = 0

export function tick(baseColor, colors)
{
for (let i = 0; i < colors.length; i++)
{
// to set all your LEDs to white
colors[i] = 0xFFFFFF
// or to blue
colors[i] = myBlueConst

// random colors for each LED
colors[i] = Math.round(Math.random() * 0xFFFFFF)
}

someVariable++ // counter from above

return colors
}

```

**Recommended way:**

For convenience, Prismatik partially exposes `QColor` class, see [here for documentation](https://doc.qt.io/qt-5/qcolor.html) and [here for exposed methods](/Software/src/QColorMetaWrapper.hpp).
Prismatik uses `QColor` internally so for best compatibility it's better to use this class.
Raw RGB values should work, but use them at your own risk.

Example from `rgb_is_life.js`:
```js
export const name = "RGB is Life"
export const interval = 33 // ms

const speed = 1.5

let m_frames = 0

export function tick(baseColor, colors)
{
// transform base RGB value into QColor
baseColor = new QColor(baseColor)

// RGB transition math
const degrees = 360.0 / colors.length
const step = speed * m_frames++
for (let i = 0; i < colors.length; i++)
{
// transform current color for a given LED into QColor
let color = new QColor(colors[i])

// increment baseColor's hue value for that smooth RGB rotation
const hue = baseColor.hslHue() + degrees * i + step

// set new hue while preserving baseColor's saturation and lightness
color.setHsl(hue, baseColor.hslSaturation(), baseColor.lightness())

// assign the new color
// colors[] has to contain .rgb() values
colors[i] = color.rgb()
}
if (step > 360)
m_frames = 0

return colors
}
```


To see your changes use the reload button or relaunch the app.


**Warnig:** scripts bundled with Prismatik are overwritten on launch, if you want to customize Prismatik's scripts, make sure to make a copy of them and modify those.


**Logging and console**

If you enable logs in Prismatik your JS errors will be logged to
- Windows: `C:\Users\<you>\Prismatik\Logs`
- macOS & Linux: `~/.Prismatik/Logs`


You can also enable the [Console API](https://doc.qt.io/qt-5/qtquick-debugging.html#console-api)

```js
export const enableConsole = true
...
export function tick()
{
...
console.log("some debug")
...
}
```

Don't forget to remove when you are done
22 changes: 22 additions & 0 deletions Software/res/moodlamps/breathing.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export const name = "Breathing"
export const interval = 40 // ms

let lightnessInc = -1
let lightness = -1

export function tick(baseColor, colors)
{
baseColor = new QColor(baseColor)
if (lightness == -1)
lightness = baseColor.lightness()
let color = new QColor()
if (lightness <= baseColor.lightness() / 2 && lightnessInc == -1)
lightnessInc = 1
else if (lightness >= baseColor.lightness() - 1 && lightnessInc == 1)
lightnessInc = -1

lightness += lightnessInc

color.setHsl(baseColor.hslHue(), baseColor.hslSaturation(), lightness)
return colors.fill(color.rgb())
}
74 changes: 74 additions & 0 deletions Software/res/moodlamps/fire.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
export const name = "Fire"
export const interval = 33 // ms

let m_lightness = []
let m_center = 0

const Cooling = 8
const SparkMax = 160
const SparkMin = 100
const DefaultLightness = 127

function randomBounded(max) {
return Math.floor(Math.random() * Math.floor(max))
}

export function tick(baseColor, colors)
{
if (colors.length < 2)
return colors

if (colors.length > m_lightness.length) {
for (let i = m_lightness.length; i < colors.length; ++i)
m_lightness.push(DefaultLightness)
}

// heavily inspired by FastLED Fire2012 demo
// https://github.com/FastLED/FastLED/blob/master/examples/Fire2012/Fire2012.ino

const centerMax = Math.round(colors.length / 4)
const middleLed = Math.floor(colors.length / 2)
const sparkCount = Math.round(colors.length / 12)

m_center += randomBounded(2) ? -1 : 1
m_center = Math.max(-centerMax, Math.min(centerMax, m_center))

for (let i = 0; i < middleLed + m_center; ++i) {
const minLightnessReduction = Cooling * Math.pow(i / (middleLed + m_center), 3)
const maxLightnessReduction = minLightnessReduction * 2 // useless?
const lightnessReduction = minLightnessReduction + randomBounded(Math.max(1, maxLightnessReduction - minLightnessReduction)) + Cooling / 3
m_lightness[i] = Math.round(Math.max(0, m_lightness[i] - lightnessReduction)) % 256
}

for (let i = colors.length - 1; i >= middleLed + m_center; --i) {
const minLightnessReduction = Cooling * Math.pow((colors.length - 1 - i) / (middleLed - m_center), 3)
const maxLightnessReduction = minLightnessReduction * 2 // useless?
const lightnessReduction = minLightnessReduction + randomBounded(Math.max(1, maxLightnessReduction - minLightnessReduction)) + Cooling / 3
m_lightness[i] = Math.round(Math.max(0, m_lightness[i] - lightnessReduction)) % 256
}

for (let k = middleLed + m_center; k > 1; --k)
m_lightness[k] = Math.round((m_lightness[k - 1] + m_lightness[k - 2] * 2) / 3) % 256

for (let k = middleLed + m_center; k < colors.length - 2; ++k)
m_lightness[k] = Math.round((m_lightness[k + 1] + m_lightness[k + 2] * 2) / 3) % 256


if (randomBounded(2) == 0) {
const y = randomBounded(Math.max(1, sparkCount))
m_lightness[y] = Math.max(SparkMax, m_lightness[y] + (SparkMin + randomBounded(SparkMax - SparkMin))) % 256
}
if (randomBounded(2) == 0) {
const z = colors.length - 1 - randomBounded(Math.max(1, sparkCount))
m_lightness[z] = Math.max(SparkMax, m_lightness[z] + (SparkMin + randomBounded(SparkMax - SparkMin))) % 256
}

let color = new QColor(baseColor)
for (let i = 0; i < colors.length; i++)
{
color.setHsl(color.hslHue(), color.hslSaturation(), m_lightness[i])
colors[i] = color.rgb()
}

return colors
}
12 changes: 12 additions & 0 deletions Software/res/moodlamps/random_noise.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const name = "Random noise"
export const interval = 50 // ms

export function tick(baseColor, colors)
{
let color = new QColor(baseColor)
for (let i = 0; i < colors.length; i++) {
color.setRgb(Math.random() * 0xFFFFFF)
colors[i] = color.rgb()
}
return colors
}
15 changes: 15 additions & 0 deletions Software/res/moodlamps/rgb_cycle.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const name = "RGB Cycle"
export const interval = 33 // ms

let degrees = 0

export function tick(baseColor, colors)
{
baseColor = new QColor(baseColor)
baseColor.setHsl(baseColor.hslHue() + degrees++, baseColor.hslSaturation(), baseColor.lightness())

if (degrees > 360)
degrees = 0

return colors.fill(baseColor.rgb())
}
22 changes: 22 additions & 0 deletions Software/res/moodlamps/rgb_is_life.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export const name = "RGB is Life"
export const interval = 33 // ms
const speed = 1.5

let m_frames = 0

export default function tick(baseColor, colors)
{
baseColor = new QColor(baseColor)
const degrees = 360.0 / colors.length
const step = speed * m_frames++
for (let i = 0; i < colors.length; i++)
{
let color = new QColor(colors[i])
color.setHsl(baseColor.hslHue() + degrees * i + step, baseColor.hslSaturation(), baseColor.lightness())
colors[i] = color.rgb()
}
if (step > 360)
m_frames = 0

return colors
}
7 changes: 7 additions & 0 deletions Software/res/moodlamps/static.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const name = "Static (default)"
export const interval = 50 // ms

export function tick(baseColor, colors)
{
return colors.fill(baseColor)
}
15 changes: 10 additions & 5 deletions Software/src/LightpackApplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -699,8 +699,7 @@ void LightpackApplication::initGrabManager()
DEBUG_LOW_LEVEL << Q_FUNC_INFO;
m_grabManager = new GrabManager(NULL);

m_moodlampManager = new MoodLampManager(NULL);
m_moodlampManager->initFromSettings();
m_moodlampManager = new MoodLampManager(m_applicationDirPath);

#ifdef SOUNDVIZ_SUPPORT
m_soundManager = SoundManagerBase::create(m_settingsWindow->winId(), NULL);
Expand All @@ -727,7 +726,7 @@ void LightpackApplication::initGrabManager()
connect(settings(), SIGNAL(moodLampColorChanged(QColor)), m_moodlampManager, SLOT(setCurrentColor(QColor)));
connect(settings(), SIGNAL(moodLampSpeedChanged(int)), m_moodlampManager, SLOT(setLiquidModeSpeed(int)));
connect(settings(), SIGNAL(moodLampLiquidModeChanged(bool)), m_moodlampManager, SLOT(setLiquidMode(bool)));
connect(settings(), SIGNAL(moodLampLampChanged(int)), m_moodlampManager, SLOT(setCurrentLamp(int)));
connect(settings(), SIGNAL(moodLampLampChanged(const QString&)), m_moodlampManager, SLOT(setCurrentLamp(const QString&)));
connect(settings(), SIGNAL(sendDataOnlyIfColorsChangesChanged(bool)), m_moodlampManager, SLOT(setSendDataOnlyIfColorsChanged(bool)));

#ifdef SOUNDVIZ_SUPPORT
Expand Down Expand Up @@ -774,8 +773,9 @@ void LightpackApplication::initGrabManager()
}
#endif

connect(m_settingsWindow, SIGNAL(requestMoodLampLamps()), m_moodlampManager, SLOT(requestLampList()));
connect(m_moodlampManager, SIGNAL(lampList(const QList<MoodLampLampInfo> &, int)), m_settingsWindow, SLOT(updateAvailableMoodLampLamps(const QList<MoodLampLampInfo> &, int)));
connect(m_settingsWindow, SIGNAL(requestMoodLampLamps(bool)), m_moodlampManager, SLOT(requestLampList(bool)));
connect(m_moodlampManager, SIGNAL(lampList(const QList<MoodLampLampInfo> &)), m_settingsWindow, SLOT(updateAvailableMoodLampLamps(const QList<MoodLampLampInfo> &)));
connect(m_settingsWindow, SIGNAL(openMoodLampScriptDir()), this, SLOT(openMoodLampScriptDir()));
}

connect(m_grabManager, SIGNAL(ambilightTimeOfUpdatingColors(double)), m_pluginInterface, SLOT(refreshAmbilightEvaluated(double)));
Expand All @@ -793,6 +793,11 @@ void LightpackApplication::initGrabManager()
#endif
}

void LightpackApplication::openMoodLampScriptDir()
{
QDesktopServices::openUrl(QUrl(m_moodlampManager->scriptDir()));
}

void LightpackApplication::commitData(QSessionManager &sessionManager)
{
Q_UNUSED(sessionManager);
Expand Down
1 change: 1 addition & 0 deletions Software/src/LightpackApplication.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ private slots:
void onFocusChanged(QWidget *, QWidget *);
void quitFromWizard(int result);
void onSessionChange(int change);
void openMoodLampScriptDir();

private:
void processCommandLineArguments();
Expand Down
Loading