Skip to content
Open
Show file tree
Hide file tree
Changes from 14 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 you set in Prismatik
Comment thread
zomfg marked this conversation as resolved.
Outdated
* @param {array} colors array of RGB integers representing your LEDs
* @return {array} array of new RGB integers representing your LEDs
*/
export function shine(baseColor, colors)
Comment thread
zomfg marked this conversation as resolved.
Outdated
{
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 shine(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 convinience, 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).
Comment thread
zomfg marked this conversation as resolved.
Outdated
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 shine(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 shine()
{
...
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 shine(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 shine(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 shine(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 shine(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 shine(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
}
9 changes: 9 additions & 0 deletions Software/res/moodlamps/static.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const name = "Static (default)"

// note: in static or very slow animations it's better to keep the refresh interval under 500ms to avoid bugs

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

250 is also under 500. Did you have some bugs because of this? At some point I added FAKE_GRAB_INTERVAL because some devices turn off after no input in a second.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's been a while, I think the issue was that putting a too high of a value was causing lag when changing modes, so the lower the better
and probably also with "dont send data unless changed"
I'll double check

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"putting a too high of a value was causing lag when changing modes" - switching the mode should reset the timer, so if it does cause a delay, it would be worth checking out why.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm only 99% sure about this, but I found and fixed the lag that occurred on lamp change: there was no call to update so you had to wait for the timer to fire

export const interval = 50 // ms

export function shine(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