Systemd changes | root script | URL support for gif effects (#1319)

* Systemd changes and URL option for Gif Effects
* Add grayscale to gif effect
* WebUI adjustments
* Rename version to .version
* Copy runHyperionAsRoot.sh to rpi packages
* Pack script into all unix packages
* Start hyperion only after network is available
* Snap builds removed due to poor server connection
* Flexible updateHyperionUser.sh
* updateHyperionUser script entered in the package
* Print help on none sudo execute
* Corrected embedded Python location
* Replacement for the QWindowsScreen grabWindow function
* Updated to latest 2.x mbedtls version 2.27

Co-authored-by: LordGrey <lordgrey.emmel@gmail.com>
This commit is contained in:
Markus 2021-10-02 18:02:52 +02:00 committed by GitHub
parent f269268def
commit eb96553975
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 776 additions and 573 deletions

View File

@ -35,7 +35,7 @@ jobs:
- name: Build package - name: Build package
shell: bash shell: bash
run: | run: |
tr -d '\n' < version > temp && mv temp version tr -d '\n' < .version > temp && mv temp .version
mkdir -p "${GITHUB_WORKSPACE}/deploy" mkdir -p "${GITHUB_WORKSPACE}/deploy"
docker run --rm \ docker run --rm \
-v "${GITHUB_WORKSPACE}/deploy:/deploy" \ -v "${GITHUB_WORKSPACE}/deploy:/deploy" \
@ -43,7 +43,7 @@ jobs:
ghcr.io/hyperion-project/${{ matrix.architecture }}:$(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]') \ ghcr.io/hyperion-project/${{ matrix.architecture }}:$(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]') \
/bin/bash -c "cd /source && \ /bin/bash -c "cd /source && \
mkdir -p debian/source && echo '3.0 (quilt)' > debian/source/format && \ mkdir -p debian/source && echo '3.0 (quilt)' > debian/source/format && \
dch --create --distribution $(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]') --package 'hyperion' -v '$(cat version)~$(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]')' '${{ github.event.commits[0].message }}' && \ dch --create --distribution $(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]') --package 'hyperion' -v '$(cat .version)~$(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]')' '${{ github.event.commits[0].message }}' && \
cp -fr LICENSE debian/copyright && \ cp -fr LICENSE debian/copyright && \
sed 's/@BUILD_DEPENDS@/${{ matrix.build-depends }}/g; s/@DEPENDS@/${{ matrix.package-depends }}/g; s/@ARCHITECTURE@/${{ matrix.architecture }}/g' debian/control.in > debian/control && \ sed 's/@BUILD_DEPENDS@/${{ matrix.build-depends }}/g; s/@DEPENDS@/${{ matrix.package-depends }}/g; s/@ARCHITECTURE@/${{ matrix.architecture }}/g' debian/control.in > debian/control && \
tar cf ../hyperion_2.0.0.orig.tar . && \ tar cf ../hyperion_2.0.0.orig.tar . && \

View File

@ -36,12 +36,12 @@ jobs:
with: with:
submodules: true submodules: true
# Append PR number to version # Append PR number to .version
- name: Append PR number to version - name: Append PR number to version
shell: bash shell: bash
run: | run: |
tr -d '\n' < version > temp && mv temp version tr -d '\n' < .version > temp && mv temp .version
echo -n "+PR${{ github.event.pull_request.number }}" >> version echo -n "+PR${{ github.event.pull_request.number }}" >> .version
# Build packages # Build packages
- name: Build packages - name: Build packages
@ -82,12 +82,12 @@ jobs:
with: with:
submodules: true submodules: true
# Append PR number to version # Append PR number to .version
- name: Append PR number to version - name: Append PR number to version
shell: bash shell: bash
run: | run: |
tr -d '\n' < version > temp && mv temp version tr -d '\n' < .version > temp && mv temp .version
echo -n "+PR${{ github.event.pull_request.number }}" >> version echo -n "+PR${{ github.event.pull_request.number }}" >> .version
# Install dependencies # Install dependencies
- name: Install dependencies - name: Install dependencies
@ -131,12 +131,12 @@ jobs:
with: with:
submodules: true submodules: true
# Append PR number to version # Append PR number to .version
- name: Append PR number to version - name: Append PR number to version
shell: bash shell: bash
run: | run: |
tr -d '\n' < version > temp && mv temp version tr -d '\n' < .version > temp && mv temp .version
echo -n "+PR${{ github.event.pull_request.number }}" >> version echo -n "+PR${{ github.event.pull_request.number }}" >> .version
- name: Cache Qt - name: Cache Qt
uses: actions/cache@v2 uses: actions/cache@v2
@ -199,27 +199,3 @@ jobs:
with: with:
name: windows name: windows
path: windows path: windows
##########################
#### Snap (x86_64) #######
##########################
snap:
name: Snap (x86_64)
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Build snap package
- name: Build snap
id: build
uses: snapcore/action-build@v1
# Upload snap artifact (only on tagged commit)
- name: Upload snap artifact
uses: actions/upload-artifact@v2
with:
name: snap
path: ${{ steps.build.outputs.snap }}

View File

@ -156,31 +156,6 @@ jobs:
with: with:
path: build/Hyperion-* path: build/Hyperion-*
##########################
#### Snap (x86_64) #######
##########################
snap:
name: Snap (x86_64)
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Build snap package
- name: Build snap
id: build
uses: snapcore/action-build@v1
# Upload snap artifact (only on tagged commit)
- name: Upload snap artifact
if: startsWith(github.event.ref, 'refs/tags')
uses: actions/upload-artifact@v2
with:
name: snap
path: ${{ steps.build.outputs.snap }}
####################################### #######################################
###### Publish GitHub Releases ######## ###### Publish GitHub Releases ########
####################################### #######################################
@ -195,10 +170,10 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
# generate environment variables # generate environment variables
- name: Generate environment variables from version and tag - name: Generate environment variables from .version and tag
run: | run: |
echo "TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV echo "TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
echo "VERSION=$(tr -d '\n' < version)" >> $GITHUB_ENV echo "VERSION=$(tr -d '\n' < .version)" >> $GITHUB_ENV
echo "preRelease=false" >> $GITHUB_ENV echo "preRelease=false" >> $GITHUB_ENV
# If version contains alpha or beta, mark draft release as pre-release # If version contains alpha or beta, mark draft release as pre-release
@ -223,35 +198,3 @@ jobs:
prerelease: ${{ env.preRelease }} prerelease: ${{ env.preRelease }}
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
############################
###### Snap Release ########
############################
snap_publish:
name: Publish Snap Release
if: startsWith(github.event.ref, 'refs/tags')
needs: [snap]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
# Download snap from snap job
- name: Download snap from snap build
id: download-artifact
uses: actions/download-artifact@v2
with:
name: snap
# Get file name of the snap
- name: Get file name of the snap
run: echo "snap=$(ls ${{ steps.download-artifact.outputs.download-path }}/hyperion-ng_*.snap)" >> $GITHUB_ENV
# Publish snap build to edge channel
- name: Publish snap build to edge channel
uses: snapcore/action-publish@v1
with:
store_login: ${{ secrets.SNAP_STORE_LOGIN }}
snap: ${{ env.snap }}
release: edge

View File

View File

@ -6,7 +6,7 @@ PROJECT(hyperion)
# Parse semantic version of version file and write version to config # Parse semantic version of version file and write version to config
include (${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cmake) include (${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cmake)
file (STRINGS "version" HYPERION_VERSION) file (STRINGS ".version" HYPERION_VERSION)
SetVersionNumber(HYPERION ${HYPERION_VERSION}) SetVersionNumber(HYPERION ${HYPERION_VERSION})
set(DEFAULT_JSON_CONFIG_FILE ${CMAKE_CURRENT_SOURCE_DIR}/config/hyperion.config.json.default) set(DEFAULT_JSON_CONFIG_FILE ${CMAKE_CURRENT_SOURCE_DIR}/config/hyperion.config.json.default)
file(READ ${DEFAULT_JSON_CONFIG_FILE} DEFAULT_JSON_CONFIG_VAR) file(READ ${DEFAULT_JSON_CONFIG_FILE} DEFAULT_JSON_CONFIG_VAR)

View File

@ -299,6 +299,10 @@ select.form-control {
color: #DDDDDD; color: #DDDDDD;
} }
.radio__field:checked ~ .radio__icon::before {
background: #fff;
}
.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle) { .btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle) {
border-bottom-right-radius: 4px; border-bottom-right-radius: 4px;
border-top-right-radius: 4px; border-top-right-radius: 4px;

View File

@ -764,6 +764,88 @@ li a:active:after {
padding: 15px; padding: 15px;
} }
/*https://github.com/json-editor/json-editor/blob/2e005a2bd34c05803702d8bc1347efde7a4926ce/docs/radio.html#L37 radiobox for Json-Editor*/
.radio {
position: relative;
display: inline-block;
min-width: 1.625rem;
min-height: 1.625rem;
max-width: 100%;
}
.radio:first-child {
margin-right: 20px;
}
.radio__field {
display: none;
}
.radio__icon {
position: absolute;
top: -0.125rem;
left: -0.125rem;
width: 1.875rem;
height: 1.875rem;
-webkit-box-sizing: border-box;
box-sizing: border-box;
border: 0.125rem solid #616161;
border-radius: 50%;
cursor: pointer;
}
.radio__icon::before {
position: absolute;
content: '';
top: 50%;
left: 50%;
width: 0.75rem;
height: 0.75rem;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
border-radius: 50%;
visibility: hidden;
}
.radio__icon::after {
position: absolute;
content: '';
top: -0.4375rem;
right: -0.4375rem;
bottom: -0.4375rem;
left: -0.4375rem;
}
.radio__label {
line-height: 1.5rem;
display: block;
padding: 0.0625rem 0 0 2.375rem;
cursor: pointer;
}
.radio__field:checked ~ .radio__icon::before {
background: #616161;
visibility: visible;
}
/* disabled state css */
.radio__field:disabled:checked ~ .radio__icon,
.radio__field:disabled:not(:checked) ~ .radio__icon {
cursor: default;
border-color: #959899;
}
.radio__field:disabled:checked ~ .radio__label,
.radio__field:disabled:not(:checked) ~ .radio__label {
cursor: default;
color: #959899;
}
.radio__field:disabled:checked ~ .radio__icon::before,
.radio__field:disabled:not(:checked) ~ .radio__icon::before {
cursor: default;
background-color: #959899;
}
/*https://github.com/flatlogic/awesome-bootstrap-checkbox slighty edited for our purposes*/ /*https://github.com/flatlogic/awesome-bootstrap-checkbox slighty edited for our purposes*/
.checkbox { .checkbox {

View File

@ -636,12 +636,17 @@
"edt_eff_flag_header": "Flaggen", "edt_eff_flag_header": "Flaggen",
"edt_eff_flag_header_desc": "Verpasse deinen LEDs die Farben deines Landes. Du kannst mehr als eine Flagge auswählen, je nach Intervall werden diese dann abwechselnd angezeigt.", "edt_eff_flag_header_desc": "Verpasse deinen LEDs die Farben deines Landes. Du kannst mehr als eine Flagge auswählen, je nach Intervall werden diese dann abwechselnd angezeigt.",
"edt_eff_fps": "Bilder pro Sekunde", "edt_eff_fps": "Bilder pro Sekunde",
"edt_eff_grayscale": "Graustufen",
"edt_eff_frequency": "Frequenz", "edt_eff_frequency": "Frequenz",
"edt_eff_gif_header": "GIFs", "edt_eff_gif_header": "GIFs",
"edt_eff_gif_header_desc": "Dieser Effekt spielt .gif Dateien ab. Bietet die Möglichkeit kleine GIF-Videos abzuspielen.", "edt_eff_gif_header_desc": "Dieser Effekt spielt .gif Dateien ab. Bietet die Möglichkeit kleine GIF-Videos abzuspielen.",
"edt_eff_height": "Höhe", "edt_eff_height": "Höhe",
"edt_eff_huechange": "Farbänderung", "edt_eff_huechange": "Farbänderung",
"edt_eff_image_source": "Bildquelle",
"edt_eff_image_source_file": "Lokale Datei",
"edt_eff_image_source_url": "URL",
"edt_eff_image": "Bilddatei", "edt_eff_image": "Bilddatei",
"edt_eff_url": "Bildadresse",
"edt_eff_initial_blink": "Blinken beim Start", "edt_eff_initial_blink": "Blinken beim Start",
"edt_eff_interval": "Intervall", "edt_eff_interval": "Intervall",
"edt_eff_knightrider_header": "Knight Rider", "edt_eff_knightrider_header": "Knight Rider",
@ -1018,4 +1023,4 @@
"wiz_yeelight_intro1": "Dieser Assistent hilft dir bei der Konfiguration von Hyperion für Yeelight. Zu den Funktionen zählen ein automatisches Finden der Yeelights, die einzelnen Lampen unterschiedlichen Bereichen im Bild zuzuordnen und weitere Einstellungen von Hyperion automatisch anzupassen. Kurz gesagt: Komplette Einrichtung mit ein paar Klicks.", "wiz_yeelight_intro1": "Dieser Assistent hilft dir bei der Konfiguration von Hyperion für Yeelight. Zu den Funktionen zählen ein automatisches Finden der Yeelights, die einzelnen Lampen unterschiedlichen Bereichen im Bild zuzuordnen und weitere Einstellungen von Hyperion automatisch anzupassen. Kurz gesagt: Komplette Einrichtung mit ein paar Klicks.",
"wiz_yeelight_title": "Yeelight Einrichtungsassistent", "wiz_yeelight_title": "Yeelight Einrichtungsassistent",
"wiz_yeelight_unsupported": "Nicht unterstützt" "wiz_yeelight_unsupported": "Nicht unterstützt"
} }

View File

@ -643,11 +643,16 @@
"edt_eff_flag_header_desc": "Let your LEDs shine bright in the colours of your country. You can select more than one flag and they will change based on the interval time.", "edt_eff_flag_header_desc": "Let your LEDs shine bright in the colours of your country. You can select more than one flag and they will change based on the interval time.",
"edt_eff_fps": "Frames per seconds", "edt_eff_fps": "Frames per seconds",
"edt_eff_frequency": "Frequency", "edt_eff_frequency": "Frequency",
"edt_eff_grayscale": "Grayscale",
"edt_eff_gif_header": "GIF's", "edt_eff_gif_header": "GIF's",
"edt_eff_gif_header_desc": "This effect plays .gif files, provide a simple video like loop as effect.", "edt_eff_gif_header_desc": "This effect plays .gif files, provide a simple video like loop as effect.",
"edt_eff_height": "Height", "edt_eff_height": "Height",
"edt_eff_huechange": "Color change", "edt_eff_huechange": "Color change",
"edt_eff_image_source": "Image source",
"edt_eff_image_source_file": "Local file",
"edt_eff_image_source_url": "URL",
"edt_eff_image": "Image file", "edt_eff_image": "Image file",
"edt_eff_url": "Image adress",
"edt_eff_initial_blink" : "Flash for attention", "edt_eff_initial_blink" : "Flash for attention",
"edt_eff_interval": "Interval", "edt_eff_interval": "Interval",
"edt_eff_knightrider_header": "Knight Rider", "edt_eff_knightrider_header": "Knight Rider",

View File

@ -55,7 +55,11 @@ $(document).ready(function () {
testrun = true; testrun = true;
var args = effects_editor.getEditor('root.args'); var args = effects_editor.getEditor('root.args');
requestTestEffect(effectName, effectPyScript, JSON.stringify(args.getValue()), imageData); if ($('input[type=radio][value=url]').is(':checked')) {
requestTestEffect(effectName, effectPyScript, JSON.stringify(args.getValue()), "");
} else {
requestTestEffect(effectName, effectPyScript, JSON.stringify(args.getValue()), imageData);
}
}; };
// Specify upload handler for image files // Specify upload handler for image files
@ -133,7 +137,12 @@ $(document).ready(function () {
// Save Effect // Save Effect
$('#btn_write').off().on('click', function () { $('#btn_write').off().on('click', function () {
requestWriteEffect(effectName, effectPyScript, JSON.stringify(effects_editor.getValue()), imageData); if ($('input[type=radio][value=url]').is(':checked')) {
requestWriteEffect(effectName, effectPyScript, JSON.stringify(effects_editor.getValue()), "");
} else {
requestWriteEffect(effectName, effectPyScript, JSON.stringify(effects_editor.getValue()), imageData);
}
$(window.hyperion).one("cmd-create-effect", function (event) { $(window.hyperion).one("cmd-create-effect", function (event) {
if (event.response.success) if (event.response.success)
showInfoDialog('success', "", $.i18n('infoDialog_effconf_created_text', effectName)); showInfoDialog('success', "", $.i18n('infoDialog_effconf_created_text', effectName));

View File

@ -1421,7 +1421,7 @@ JSONEditor.AbstractEditor = Class.extend({
this.template_engine = this.jsoneditor.template; this.template_engine = this.jsoneditor.template;
this.iconlib = this.jsoneditor.iconlib; this.iconlib = this.jsoneditor.iconlib;
this.access = this.jsoneditor.access; this.access = this.jsoneditor.access;
this.translate = this.jsoneditor.translate || JSONEditor.defaults.translate; this.translate = this.jsoneditor.translate || JSONEditor.defaults.translate;
this.original_schema = options.schema; this.original_schema = options.schema;
@ -1447,7 +1447,7 @@ JSONEditor.AbstractEditor = Class.extend({
if (!deps) { if (!deps) {
return; return;
} }
var self = this; var self = this;
Object.keys(deps).forEach(function(dependency) { Object.keys(deps).forEach(function(dependency) {
var path = self.path.split('.'); var path = self.path.split('.');
@ -1464,13 +1464,13 @@ JSONEditor.AbstractEditor = Class.extend({
if (this.path === path || !wrapper) { if (this.path === path || !wrapper) {
return; return;
} }
var self = this; var self = this;
var editor = this.jsoneditor.getEditor(path); var editor = this.jsoneditor.getEditor(path);
var value = editor ? editor.getValue() : undefined; var value = editor ? editor.getValue() : undefined;
var previousStatus = this.dependenciesFulfilled; var previousStatus = this.dependenciesFulfilled;
this.dependenciesFulfilled = false; this.dependenciesFulfilled = false;
if (!editor || !editor.dependenciesFulfilled) { if (!editor || !editor.dependenciesFulfilled) {
this.dependenciesFulfilled = false; this.dependenciesFulfilled = false;
} else if (Array.isArray(choices)) { } else if (Array.isArray(choices)) {
@ -1504,7 +1504,7 @@ JSONEditor.AbstractEditor = Class.extend({
this.dependenciesFulfilled = !value; this.dependenciesFulfilled = !value;
} }
} }
if (this.dependenciesFulfilled !== previousStatus) { if (this.dependenciesFulfilled !== previousStatus) {
this.notify(); this.notify();
} }
@ -1535,7 +1535,7 @@ JSONEditor.AbstractEditor = Class.extend({
this.updateHeaderText(); this.updateHeaderText();
this.register(); this.register();
this.onWatchedFieldChange(); this.onWatchedFieldChange();
//hide input fields, if they didn't match the current access level //hide input fields, if they didn't match the current access level
var storedAccess = this.access var storedAccess = this.access
if(this.schema.access){ if(this.schema.access){
@ -1544,12 +1544,12 @@ JSONEditor.AbstractEditor = Class.extend({
else if(this.schema.access == 'advanced' && storedAccess == 'default') else if(this.schema.access == 'advanced' && storedAccess == 'default')
{ {
this.container.style.display = "none"; this.container.style.display = "none";
} }
else if(this.schema.access == 'expert' && storedAccess != 'expert') else if(this.schema.access == 'expert' && storedAccess != 'expert')
{ {
this.container.style.display = "none"; this.container.style.display = "none";
//this.disable(); //this.disable();
} }
} }
}, },
@ -2046,7 +2046,7 @@ JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({
if(this.schema.append) this.append = this.theme.getFormInputAppend(this.getAppend()); if(this.schema.append) this.append = this.theme.getFormInputAppend(this.getAppend());
this.placeholder = this.schema.default; this.placeholder = this.schema.default;
this.format = this.schema.format; this.format = this.schema.format;
if(!this.format && this.schema.media && this.schema.media.type) { if(!this.format && this.schema.media && this.schema.media.type) {
this.format = this.schema.media.type.replace(/(^(application|text)\/(x-)?(script\.)?)|(-source$)/g,''); this.format = this.schema.media.type.replace(/(^(application|text)\/(x-)?(script\.)?)|(-source$)/g,'');
@ -2057,7 +2057,7 @@ JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({
if(this.options.format) { if(this.options.format) {
this.format = this.options.format; this.format = this.options.format;
} }
// Specific format // Specific format
if(this.format) { if(this.format) {
// Text Area // Text Area
@ -2146,7 +2146,7 @@ JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({
} }
// Number or integer adds html5 tag 'number' // Number or integer adds html5 tag 'number'
else if (this.schema.type == "number" || this.schema.type == "integer"){ else if (this.schema.type == "number" || this.schema.type == "integer"){
var min = this.schema.minimum var min = this.schema.minimum
var max = this.schema.maximum var max = this.schema.maximum
var step = this.schema.step var step = this.schema.step
@ -2240,7 +2240,7 @@ JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({
if(this.format) this.input.setAttribute('data-schemaformat',this.format); if(this.format) this.input.setAttribute('data-schemaformat',this.format);
if(this.defaultValue) this.input.setAttribute('data-schemaformat',this.format); if(this.defaultValue) this.input.setAttribute('data-schemaformat',this.format);
if(this.formname && this.label)this.label.setAttribute('for',this.formname); if(this.formname && this.label)this.label.setAttribute('for',this.formname);
this.control = this.theme.getFormControl(this.label, this.input, this.description, this.append, this.placeholder); this.control = this.theme.getFormControl(this.label, this.input, this.description, this.append, this.placeholder);
this.container.appendChild(this.control); this.container.appendChild(this.control);
@ -5104,7 +5104,7 @@ JSONEditor.defaults.editors.select = JSONEditor.AbstractEditor.extend({
if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle()); if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description); if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);
if(this.schema.append) this.append = this.theme.getFormInputAppend(this.getAppend()); if(this.schema.append) this.append = this.theme.getFormInputAppend(this.getAppend());
if(this.options.compact) this.container.className += ' compact'; if(this.options.compact) this.container.className += ' compact';
this.input = this.theme.getSelectInput(this.enum_options); this.input = this.theme.getSelectInput(this.enum_options);
@ -5122,7 +5122,7 @@ JSONEditor.defaults.editors.select = JSONEditor.AbstractEditor.extend({
}); });
if(this.formname)this.label.setAttribute('for',this.formname); if(this.formname)this.label.setAttribute('for',this.formname);
this.control = this.theme.getFormControl(this.label, this.input, this.description); this.control = this.theme.getFormControl(this.label, this.input, this.description);
this.container.appendChild(this.control); this.container.appendChild(this.control);
@ -6227,6 +6227,121 @@ JSONEditor.defaults.editors.arraySelectize = JSONEditor.AbstractEditor.extend({
} }
}); });
// Imported from Version 1.4.0.beta.0 | https://cdn.jsdelivr.net/npm/@json-editor/json-editor@1.4.0-beta.0/dist/jsoneditor.js
JSONEditor.defaults.editors.radio = JSONEditor.defaults.editors.string.extend({
build: function () {
var self = this;
if(!this.options.compact) this.header = this.label = this.theme.getFormInputLabel(this.getTitle());
if(this.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);
if(this.options.infoText) this.infoButton = this.theme.getInfoButton(this.options.infoText);
if(this.options.compact) this.container.classList.add('compact');
this.radioContainer = document.createElement('div');
this.enum_values = this.schema.enum;
this.enum_titles = this.options.enum_titles || [];
this.radioGroup = [];
var radioInputEventhandler = function(e) {
e.preventDefault();
e.stopPropagation();
self.setValue(this.value);
self.onChange(true);
};
for(var i = 0; i < this.enum_values.length; i++) {
var id = this.key + '-' + i;
// form radio elements
var radioInput = this.theme.getFormInputField('radio');
radioInput.name = this.formname;
radioInput.value = this.enum_values[i];
radioInput.id = id;
radioInput.classList.add('radio__field');
radioInput.addEventListener('change', radioInputEventhandler, false);
this.radioGroup.push(radioInput);
// form-label for radio elements
var radioLabel = document.createElement('label');
radioLabel.htmlFor = id;
radioLabel.classList.add('radio');
// contains the displayed text to the label
var radioLabelText = document.createElement('span');
radioLabelText.innerText = $.i18n(this.options.enum_titles[i]) || this.enum_values[i];
radioLabelText.classList.add('radio__label');
// permits the addition of styles for the radio itself (if you want it to look differently than browser default)
var radioLabelIcon = document.createElement('span');
radioLabelIcon.classList.add('radio__icon');
radioLabel.appendChild(radioInput);
radioLabel.appendChild(radioLabelIcon);
radioLabel.appendChild(radioLabelText);
this.radioContainer.appendChild(radioLabel);
}
if(this.schema.readOnly || this.schema.readonly) {
this.always_disabled = true;
for (var j = 0; j < this.radioGroup.length; j++) {
this.radioGroup[j].disabled = true;
}
this.radioContainer.classList.add('readonly');
}
var radioContainerWrapper = this.theme.getContainer();
radioContainerWrapper.appendChild(this.radioContainer);
this.input = radioContainerWrapper;
this.control = this.theme.getFormControl(this.label, radioContainerWrapper, this.description, this.infoButton);
this.container.appendChild(this.control);
},
enable: function() {
if(!this.always_disabled) {
for (var i = 0; i<this.radioGroup.length; i++) {
this.radioGroup[i].disabled = false;
}
this.radioContainer.classList.remove('readonly');
this._super();
}
},
disable: function(always_disabled) {
if(always_disabled) this.always_disabled = true;
for (var i = 0; i<this.radioGroup.length; i++) {
this.radioGroup[i].disabled = true;
}
this.radioContainer.classList.add('readonly');
this._super();
},
destroy: function() {
if(this.radioContainer.parentNode && this.radioContainer.parentNode.parentNode) this.radioContainer.parentNode.parentNode.removeChild(this.radioContainer.parentNode);
if(this.label && this.label.parentNode) this.label.parentNode.removeChild(this.label);
if(this.description && this.description.parentNode) this.description.parentNode.removeChild(this.description);
this._super();
},
getNumColumns: function() {
return 2;
},
setValue: function (val) {
for(var i = 0; i < this.radioGroup.length; i++) {
if(this.radioGroup[i].value == val) {
this.radioGroup[i].checked = true;
this.value = val;
if(this.options.displayValue) {
this.displayRating.innerHTML = this.value;
}
this.onChange();
break;
}
}
}
});
// colorpicker creation and handling, build on top of strings editor // colorpicker creation and handling, build on top of strings editor
JSONEditor.defaults.editors.colorPicker = JSONEditor.defaults.editors.string.extend({ JSONEditor.defaults.editors.colorPicker = JSONEditor.defaults.editors.string.extend({
getValue: function() { getValue: function() {
@ -6253,9 +6368,9 @@ JSONEditor.defaults.editors.colorPicker = JSONEditor.defaults.editors.string.ext
$(this.input).colorpicker('updatePicker', rgb2hex(val)); $(this.input).colorpicker('updatePicker', rgb2hex(val));
$(this.input).colorpicker('updateComponent', 'rgb('+val+')'); $(this.input).colorpicker('updateComponent', 'rgb('+val+')');
}, },
build: function() { build: function() {
this._super(); this._super();
var myinput = this; var myinput = this;
@ -6277,12 +6392,12 @@ JSONEditor.defaults.editors.colorPicker = JSONEditor.defaults.editors.string.ext
$("#event_catcher").detach().insertAfter(myinput.input); $("#event_catcher").detach().insertAfter(myinput.input);
$("#event_catcher").attr("id", "selector"); $("#event_catcher").attr("id", "selector");
$(this.input).colorpicker().on('changeColor', function(e) { $(this.input).colorpicker().on('changeColor', function(e) {
$(myinput).val(e.color.toRGB()).change(); $(myinput).val(e.color.toRGB()).change();
}); });
}, },
destroy: function() { destroy: function() {
$(this.input).colorpicker('destroy'); $(this.input).colorpicker('destroy');
} }
@ -6306,9 +6421,9 @@ JSONEditor.defaults.editors.colorPickerRGBA = JSONEditor.defaults.editors.string
// $(this.input).colorpicker('updatePicker', rgb2hex(val)); // $(this.input).colorpicker('updatePicker', rgb2hex(val));
$(this.input).colorpicker('updateComponent', 'rgba('+val+')'); $(this.input).colorpicker('updateComponent', 'rgba('+val+')');
}, },
build: function() { build: function() {
this._super(); this._super();
var myinput = this; var myinput = this;
@ -6333,12 +6448,12 @@ JSONEditor.defaults.editors.colorPickerRGBA = JSONEditor.defaults.editors.string
$("#event_catcher").detach().insertAfter(myinput.input); $("#event_catcher").detach().insertAfter(myinput.input);
$("#event_catcher").attr("id", "selector"); $("#event_catcher").attr("id", "selector");
$(this.input).colorpicker().on('changeColor', function(e) { $(this.input).colorpicker().on('changeColor', function(e) {
$(myinput).val(e.color.toRGB()).change(); $(myinput).val(e.color.toRGB()).change();
}); });
}, },
destroy: function() { destroy: function() {
$(this.input).colorpicker('destroy'); $(this.input).colorpicker('destroy');
} }
@ -6508,7 +6623,7 @@ JSONEditor.AbstractTheme = Class.extend({
}, },
getRangeInput: function(min,max,step) { getRangeInput: function(min,max,step) {
if (typeof step == "undefined") step = 1; if (typeof step == "undefined") step = 1;
var el = this.getFormInputField('number'); var el = this.getFormInputField('number');
if (typeof min != "undefined") el.setAttribute('min',min); if (typeof min != "undefined") el.setAttribute('min',min);
if (typeof max != "undefined") el.setAttribute('max',max); if (typeof max != "undefined") el.setAttribute('max',max);
@ -6748,13 +6863,13 @@ JSONEditor.defaults.themes.bootstrap3 = JSONEditor.AbstractTheme.extend({
getFormControl: function(label, input, description, append, placeholder) { getFormControl: function(label, input, description, append, placeholder) {
var group = document.createElement('div'); var group = document.createElement('div');
var subgroup = document.createElement('div'); var subgroup = document.createElement('div');
if(placeholder) if(placeholder)
input.setAttribute('placeholder',placeholder); input.setAttribute('placeholder',placeholder);
if (input.type === 'checkbox'){ if (input.type === 'checkbox'){
var helplabel = document.createElement("label") var helplabel = document.createElement("label")
group.className += ' form-group'; group.className += ' form-group';
group.style.minHeight = "30px"; group.style.minHeight = "30px";
label.className += ' col-form-label col-sm-5 col-md-3 col-lg-5 col-xxl-4'; label.className += ' col-form-label col-sm-5 col-md-3 col-lg-5 col-xxl-4';
@ -6790,7 +6905,7 @@ JSONEditor.defaults.themes.bootstrap3 = JSONEditor.AbstractTheme.extend({
subgroup.className += ' input-group col-sm-7 col-md-9 col-lg-7 col-xxl-8'; subgroup.className += ' input-group col-sm-7 col-md-9 col-lg-7 col-xxl-8';
subgroup.appendChild(input); subgroup.appendChild(input);
} }
if(description) group.appendChild(description); if(description) group.appendChild(description);
@ -7096,10 +7211,10 @@ JSONEditor.defaults.template = 'default';
JSONEditor.defaults.options = {}; JSONEditor.defaults.options = {};
// String translate function // String translate function
JSONEditor.defaults.translate = function(key, variables) { JSONEditor.defaults.translate = function(key, variables) {
return $.i18n(key, variables); return $.i18n(key, variables);
}; };
// Miscellaneous Plugin Settings // Miscellaneous Plugin Settings
@ -7174,7 +7289,12 @@ JSONEditor.defaults.resolvers.unshift(function(schema) {
}); });
// Use the `select` editor for dynamic enumSource enums // Use the `select` editor for dynamic enumSource enums
JSONEditor.defaults.resolvers.unshift(function(schema) { JSONEditor.defaults.resolvers.unshift(function(schema) {
if(schema.enumSource) return (JSONEditor.plugins.selectize.enable) ? 'selectize' : 'select'; if(schema.enumSource) {
if(schema.format === "radio") {
return "radio";
}
return (JSONEditor.plugins.selectize.enable) ? 'selectize' : 'select';
}
}); });
// Use the `enum` or `select` editors for schemas with enumerated properties // Use the `enum` or `select` editors for schemas with enumerated properties
JSONEditor.defaults.resolvers.unshift(function(schema) { JSONEditor.defaults.resolvers.unshift(function(schema) {
@ -7183,6 +7303,11 @@ JSONEditor.defaults.resolvers.unshift(function(schema) {
return "enum"; return "enum";
} }
else if(schema.type === "number" || schema.type === "integer" || schema.type === "string") { else if(schema.type === "number" || schema.type === "integer" || schema.type === "string") {
if(schema.format === "radio") {
return "radio";
}
return (JSONEditor.plugins.selectize.enable) ? 'selectize' : 'select'; return (JSONEditor.plugins.selectize.enable) ? 'selectize' : 'select';
} }
} }

View File

@ -0,0 +1,92 @@
#!/bin/bash -e
# help print function
function printHelp {
echo "The script updates the user Hyperion is executed by system and service manager (after start-up).
Without arguments it will configure Hyperion being executed under the current user.
The script must be executed as root, i.e. sudo $0
Options:
-u username The user name Hyperion to executed to be with
-h Shows this help message and exits"
}
function prompt () {
while true; do
read -p "$1 " yn
if [[ $yn = "" ]]; then
echo "Please answer Yes or No."
else
case "${yn:-Y}" in
[Yes]* ) return 1;;
[No]* ) return 0;;
* ) echo "Please answer Yes or No.";;
esac
fi
done
}
# Default username
USERNAME=${SUDO_USER}
while getopts u:h option
do
case "${option}"
in
u) USERNAME=${OPTARG};;
v) _VERBOSE=1;;
h) printHelp; exit 0;;
esac
done
if [ "`id -u`" -ne 0 ]; then
printHelp
exit 99
fi
if ! id ${USERNAME} >/dev/null 2>&1; then
echo "The given username \"${USERNAME}\" does not exist. Exiting..."
exit 99
fi
echo "Configure the hyperion daemon to be executed under user: ${USERNAME}"
if [ ${USERNAME} == "root" ]; then
echo ''
echo 'You asked to run Hyperion with root privileges. This poses a security risk!'
echo 'It is recommended not to do so unless there are good reasons (e.g. WS281x usage).'
if prompt 'Are you sure you want to run Hyperion under root? [Yes/No]'; then
echo 'No updates will be done. Exiting...'
exit 99
fi
fi
#Disable current service
CURRENT_SERVICE=$(systemctl --type service | { grep -o "hyperion.*@.*\.service" || true; })
if [[ ! -z ${CURRENT_SERVICE} ]]; then
CURRENT_SERVICE_USER=$(expr "${CURRENT_SERVICE}" : 'hyperion.*@\(.*\).service')
if [ "${USERNAME}" == "${CURRENT_SERVICE_USER}" ]; then
echo "Hyperion is already running under the user: ${USERNAME}. No updates required."
exit 0;
fi
echo "Disable current service: ${CURRENT_SERVICE}"
systemctl is-active --quiet ${CURRENT_SERVICE} && systemctl disable --quiet ${CURRENT_SERVICE} --now >/dev/null 2>&1
fi
HYPERION="hyperion"
#Downward compatibility
if [[ ${CURRENT_SERVICE} == hyperiond* ]]; then
HYPERION="hyperiond"
fi
#Enable new service
NEW_SERVICE="${HYPERION}@${USERNAME}.service"
echo "Restarting Hyperion Service: ${NEW_SERVICE}"
systemctl enable --quiet ${NEW_SERVICE} --now >/dev/null 2>&1
# Update HyperBian splash screen
sed -i "s/${CURRENT_SERVICE}/${NEW_SERVICE}/" /etc/update-motd.d/10-hyperbian >/dev/null 2>&1
echo "Done."
exit 0

View File

@ -1,6 +1,10 @@
[Unit] [Unit]
Description=Hyperion ambient light systemd service for user %i Description=Hyperion ambient light systemd service for user %i
After=network.target Documentation=https://docs.hyperion-project.org
Requisite=network.target
Wants=network-online.target
After=network-online.target
After=systemd-resolved.service
[Service] [Service]
ExecStart=/usr/bin/hyperiond ExecStart=/usr/bin/hyperiond

View File

@ -158,12 +158,12 @@ macro(DeployUnix TARGET)
endif() endif()
# Copy Python modules to 'share/hyperion/lib/python' and ignore the unnecessary stuff listed below # Copy Python modules to 'share/hyperion/lib/pythonXX' and ignore the unnecessary stuff listed below
if (PYTHON_MODULES_DIR) if (PYTHON_MODULES_DIR)
install( install(
DIRECTORY ${PYTHON_MODULES_DIR}/ DIRECTORY ${PYTHON_MODULES_DIR}/
DESTINATION "share/hyperion/lib/python" DESTINATION "share/hyperion/lib/python${PYTHON_VERSION_MAJOR_MINOR}"
COMPONENT "Hyperion" COMPONENT "Hyperion"
PATTERN "*.pyc" EXCLUDE # compiled bytecodes PATTERN "*.pyc" EXCLUDE # compiled bytecodes
PATTERN "__pycache__" EXCLUDE # any cache PATTERN "__pycache__" EXCLUDE # any cache

View File

@ -1,61 +0,0 @@
#!/bin/sh
echo "---Hyperion ambient light preinst ---"
# search for users in system, returns first entry
FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root"
# stop running daemon before we install
if pgrep hyperiond > /dev/null 2>&1
then
if grep -m1 systemd /proc/1/comm > /dev/null
then
echo "--> stop init deamon: systemd"
# systemd
systemctl stop hyperion hyperiond"@${FOUND_USR}" 2> /dev/null
elif [ -e /sbin/initctl ]
then
echo "--> stop init deamon: upstart"
# upstart
initctl stop hyperiond
else
echo "--> stop init deamon: sysV"
# sysV
service hyperiond stop 2>/dev/null
fi
fi
# In case we don't use a service kill all instances
killall hyperiond 2> /dev/null
# overwrite last return code
exit 0
#$USR=hyperionIS;
#addToGroup()
##{
# getent group $1 && adduser $USR $1;
#}
#check if user exists
#if id $USR >/dev/null 2>&1; then
# echo "--> hyperion user exists, skip creation";
#else
## create user
# echo "--> Create Hyperion user";
# adduser --system --group $USR;
#fi
# add user to groups if required
## secondary user groups that are required to access system things
#addToGroup(dialout);
#addToGroup(video);
#addToGroup(audio);
#addToGroup(systemd-journal);
# platform specific groups
#addToGroup(i2c);
#addToGroup(spi);
#addToGroup(gpio);

View File

@ -1,5 +1,14 @@
#!/bin/sh #!/bin/sh
echo "--- postinst called with args= " $1 $2
# If $1=configure and $2 is set, this is an upgrade
if [ "$1" = configure ] && [ "$2" != "" ]; then
IS_UPGRADE=true
else
IS_UPGRADE=false
fi
install_file() install_file()
{ {
src="$1" src="$1"
@ -15,13 +24,11 @@ install_file()
fi fi
} }
echo "--- Hyperion ambient light postinstall ---" echo "--- Hyperion ambient light postinstall ---"
#check system #check system
CPU_RPI=`grep -m1 -c 'BCM2708\|BCM2709\|BCM2710\|BCM2835\|BCM2836\|BCM2837\|BCM2711' /proc/cpuinfo` CPU_RPI=`grep -m1 -c 'BCM2708\|BCM2709\|BCM2710\|BCM2835\|BCM2836\|BCM2837\|BCM2711' /proc/cpuinfo`
CPU_X32X64=`uname -m | grep 'x86_32\|i686\|x86_64' | wc -l` CPU_X32X64=`uname -m | grep 'x86_32\|i686\|x86_64' | wc -l`
OS_HYPERBIAN=`grep ID /etc/os-release | grep -m1 -c HyperBian`
#Check for a bootloader as Berryboot #Check for a bootloader as Berryboot
BOOT_BERRYBOOT=$(grep -m1 -c '\(/var/media\|/media/pi\)/berryboot' /etc/mtab) BOOT_BERRYBOOT=$(grep -m1 -c '\(/var/media\|/media/pi\)/berryboot' /etc/mtab)
@ -32,54 +39,38 @@ NET_IP=`hostname -I | cut -d " " -f1`
# search for users in system, returns first entry # search for users in system, returns first entry
FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root"
# determine if we should use a service
ENABLE_SERVICE=0
STARTUP_MSG="echo ---> You can start Hyperion from your menu now"
if [ $CPU_RPI -eq 1 ]; then
ENABLE_SERVICE=1
STARTUP_MSG="echo ---> Hyperion has been installed as service, it will start on each system startup"
fi
start_msg="" start_msg=""
restart_msg="" restart_msg=""
if grep -m1 systemd /proc/1/comm > /dev/null # service registration if no gui is present (only on initial installation and not upgrade)
then if [ "$IS_UPGRADE" = false ]; then
echo "---> init deamon: systemd" if [ -z "${DISPLAY}" ] && [ -z "${WAYLAND_DISPLAY}" ] && [ -z "${XDG_CURRENT_DESKTOP}" ]; then
# systemd STARTUP_MSG="echo ---> Hyperion has been installed as service, it will start on each system startup"
if [ $OS_HYPERBIAN -eq 1 ]; then
# start service only if grep -m1 systemd /proc/1/comm > /dev/null
echo "--> Service file already exists, skip creation" then
start_msg="--> systemctl start hyperion" # systemd
systemctl start hyperion echo "---> init deamon: systemd"
else install_file /usr/share/hyperion/service/hyperion.systemd /etc/systemd/system/hyperion@.service
install_file /usr/share/hyperion/service/hyperion.systemd /etc/systemd/system/hyperiond@.service systemctl enable hyperion"@${FOUND_USR}".service
# service registration just on Raspberry Pi, probably need to ask the user how we should use the service. TODO service start in user login scope eg for x11?! start_msg="--> systemctl start hyperion for user ${FOUND_USR}"
if [ $ENABLE_SERVICE -eq 1 ]; then systemctl start hyperion"@${FOUND_USR}"
systemctl enable hyperiond"@${FOUND_USR}".service elif [ -e /sbin/initctl ]
start_msg="--> systemctl start hyperiond for user ${FOUND_USR}" then
systemctl start hyperiond"@${FOUND_USR}" # upstart
echo "---> init deamon: upstart"
install_file /usr/share/hyperion/service/hyperion.initctl /etc/init/hyperion.conf && initctl reload-configuration
start_msg="--> initctl start hyperion"
initctl start hyperion
else
# sysV
echo "---> init deamon: sysV"
install_file /usr/share/hyperion/service/hyperion.init /etc/init.d/hyperion && chmod +x /etc/init.d/hyperion && update-rc.d hyperion defaults 98 02
start_msg="---> service hyperion start"
service hyperion start
fi fi
fi else
STARTUP_MSG="echo ---> You can start Hyperion from your menu now"
elif [ -e /sbin/initctl ]
then
echo "---> init deamon: upstart"
# upstart
if [ $ENABLE_SERVICE -eq 1 ]; then
install_file /usr/share/hyperion/service/hyperiond.initctl /etc/init/hyperion.conf && initctl reload-configuration
start_msg="--> initctl start hyperiond"
initctl start hyperiond
fi
else
echo "---> init deamon: sysV"
# sysV
if [ $ENABLE_SERVICE -eq 1 ]; then
install_file /usr/share/hyperion/service/hyperion.init /etc/init.d/hyperiond && chmod +x /etc/init.d/hyperiond && update-rc.d hyperiond defaults 98 02
start_msg="---> service hyperiond start"
service hyperiond start
fi fi
fi fi
@ -87,25 +78,30 @@ fi
rm -r /usr/share/hyperion/service rm -r /usr/share/hyperion/service
#link binarys and set exec bit #link binarys and set exec bit
BINSP=/usr/share/hyperion/bin BINSP=/usr/share/hyperion
BINTP=/usr/bin BINTP=/usr/bin
chmod +x -R $BINSP chmod +x -R $BINSP/bin
ln -fs $BINSP/hyperiond $BINTP/hyperiond ln -fs $BINSP/bin/hyperiond $BINTP/hyperiond
ln -fs $BINSP/hyperion-remote $BINTP/hyperion-remote ln -fs $BINSP/bin/hyperion-remote $BINTP/hyperion-remote
ln -fs $BINSP/hyperion-v4l2 $BINTP/hyperion-v4l2 ln -fs $BINSP/bin/hyperion-v4l2 $BINTP/hyperion-v4l2
ln -fs $BINSP/hyperion-framebuffer $BINTP/hyperion-framebuffer 2>/dev/null ln -fs $BINSP/bin/hyperion-framebuffer $BINTP/hyperion-framebuffer 2>/dev/null
ln -fs $BINSP/hyperion-dispmanx $BINTP/hyperion-dispmanx 2>/dev/null ln -fs $BINSP/bin/hyperion-dispmanx $BINTP/hyperion-dispmanx 2>/dev/null
ln -fs $BINSP/hyperion-x11 $BINTP/hyperion-x11 2>/dev/null ln -fs $BINSP/bin/hyperion-x11 $BINTP/hyperion-x11 2>/dev/null
ln -fs $BINSP/hyperion-xcb $BINTP/hyperion-xcb 2>/dev/null ln -fs $BINSP/bin/hyperion-xcb $BINTP/hyperion-xcb 2>/dev/null
ln -fs $BINSP/hyperion-aml $BINTP/hyperion-aml 2>/dev/null ln -fs $BINSP/bin/hyperion-aml $BINTP/hyperion-aml 2>/dev/null
ln -fs $BINSP/hyperion-qt $BINTP/hyperion-qt 2>/dev/null ln -fs $BINSP/bin/hyperion-qt $BINTP/hyperion-qt 2>/dev/null
# install desktop icons / not on HyperBian #create symlink for updateHyperionUser.sh script
if [ $OS_HYPERBIAN -ne 1 ]; then ln -fs $BINSP/scripts/updateHyperionUser.sh $BINTP/updateHyperionUser 2>/dev/null
echo "---> Install Hyperion desktop icons"
mkdir /usr/share/pixmaps/hyperion 2>/dev/null # install desktop icons (only on initial installation and not upgrade)
cp /usr/share/hyperion/desktop/*.png /usr/share/pixmaps/hyperion 2>/dev/null if [ "$IS_UPGRADE" = false ]; then
desktop-file-install /usr/share/hyperion/desktop/hyperiond.desktop 2>/dev/null if hash desktop-file-install 2>/dev/null; then
echo "---> Install Hyperion desktop icons"
mkdir /usr/share/pixmaps/hyperion 2>/dev/null
cp /usr/share/hyperion/desktop/*.png /usr/share/pixmaps/hyperion 2>/dev/null
desktop-file-install /usr/share/hyperion/desktop/hyperiond.desktop 2>/dev/null
fi
fi fi
# cleanup desktop icons # cleanup desktop icons
@ -133,7 +129,11 @@ fi
echo ${start_msg} echo ${start_msg}
echo "-----------------------------------------------------------------------------" echo "-----------------------------------------------------------------------------"
echo "---> Hyperion has been installed/updated!" if [ "$IS_UPGRADE" = true ]; then
echo "---> Hyperion has been upgraded!"
else
echo "---> Hyperion has been installed!"
fi
echo "---> " echo "---> "
$STARTUP_MSG $STARTUP_MSG
echo "---> For configuration, visit with your browser: ${NET_IP}:8090" echo "---> For configuration, visit with your browser: ${NET_IP}:8090"

View File

@ -0,0 +1,33 @@
#!/bin/sh
echo "---Hyperion ambient light preinst ---"
# search for users in system, returns first entry
FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root"
# stop running daemon before we install/upgrade
if pgrep hyperiond > /dev/null 2>&1
then
if grep -m1 systemd /proc/1/comm > /dev/null
then
echo "--> stop init deamon: systemd"
# systemd
systemctl stop hyperion hyperiond"@${FOUND_USR}" hyperion"@${FOUND_USR}" 2> /dev/null
elif [ -e /sbin/initctl ]
then
echo "--> stop init deamon: upstart"
# upstart
initctl stop hyperiond 2>/dev/null
initctl stop hyperion 2>/dev/null
else
echo "--> stop init deamon: sysV"
# sysV
service hyperiond stop 2>/dev/null
service hyperion stop 2>/dev/null
fi
fi
# In case we don't use a service kill all instances
killall hyperiond 2> /dev/null
exit 0

View File

@ -2,6 +2,17 @@
echo "---Hyperion ambient light prerm ---" echo "---Hyperion ambient light prerm ---"
UPGRADE="$1"
if [ "$2" = "in-favour" ]; then
# Treat conflict remove as an upgrade.
UPGRADE="upgrade"
fi
# Don't clean-up just for an upgrade.`
if [ "$action" = "upgrade" ] ; then
exit 0
fi
# search for users in system, returns first entry # search for users in system, returns first entry
FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root" FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root"
@ -13,17 +24,18 @@ if grep -m1 systemd /proc/1/comm > /dev/null
then then
echo "---> stop init deamon: systemd" echo "---> stop init deamon: systemd"
# systemd # systemd
$HYPERION_RUNNING && systemctl stop hyperion hyperiond"@${FOUND_USR}" 2> /dev/null $HYPERION_RUNNING && systemctl stop hyperion hyperiond"@${FOUND_USR}" hyperion"@${FOUND_USR}" 2> /dev/null
# disable user specific symlink / not on HyperBian # disable user specific symlink
echo "---> Disable service and remove entry" echo "---> Disable service and remove entry"
systemctl -q disable hyperiond"@${FOUND_USR}" 2> /dev/null systemctl -q disable hyperion hyperiond"@${FOUND_USR}" hyperion"@${FOUND_USR}" 2> /dev/null
rm -v /etc/systemd/system/hyperiond@.service 2>/dev/null rm -v /etc/systemd/system/hyperion.service /etc/systemd/system/hyperiond@.service /etc/systemd/system/hyperion@.service 2>/dev/null
elif [ -e /sbin/initctl ] elif [ -e /sbin/initctl ]
then then
echo "---> stop init deamon: upstart" echo "---> stop init deamon: upstart"
# upstart # upstart
$HYPERION_RUNNING && initctl stop hyperiond $HYPERION_RUNNING && initctl stop hyperiond
$HYPERION_RUNNING && initctl stop hyperion
echo "---> Remove upstart service" echo "---> Remove upstart service"
rm -v /etc/init/hyperion* 2>/dev/null rm -v /etc/init/hyperion* 2>/dev/null
initctl reload-configuration initctl reload-configuration
@ -32,6 +44,7 @@ else
echo "---> stop init deamon: sysV" echo "---> stop init deamon: sysV"
# sysV # sysV
$HYPERION_RUNNING && service hyperiond stop 2>/dev/null $HYPERION_RUNNING && service hyperiond stop 2>/dev/null
$HYPERION_RUNNING && service hyperion stop 2>/dev/null
echo "---> Remove sysV service" echo "---> Remove sysV service"
update-rc.d -f hyperion remove update-rc.d -f hyperion remove
rm /etc/init.d/hyperion* 2>/dev/null rm /etc/init.d/hyperion* 2>/dev/null

View File

@ -65,7 +65,7 @@ ENDIF()
# Specific CPack Package Generators # Specific CPack Package Generators
# https://cmake.org/Wiki/CMake:CPackPackageGenerators # https://cmake.org/Wiki/CMake:CPackPackageGenerators
# .deb files for apt # .deb files for apt
SET ( CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/cmake/debian/preinst;${CMAKE_CURRENT_SOURCE_DIR}/cmake/debian/postinst;${CMAKE_CURRENT_SOURCE_DIR}/cmake/debian/prerm" ) SET ( CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/cmake/package-scripts/preinst;${CMAKE_CURRENT_SOURCE_DIR}/cmake/package-scripts/postinst;${CMAKE_CURRENT_SOURCE_DIR}/cmake/package-scripts/prerm" )
SET ( CPACK_DEBIAN_PACKAGE_DEPENDS "libcec6 | libcec4" ) SET ( CPACK_DEBIAN_PACKAGE_DEPENDS "libcec6 | libcec4" )
SET ( CPACK_DEBIAN_PACKAGE_SECTION "Miscellaneous" ) SET ( CPACK_DEBIAN_PACKAGE_SECTION "Miscellaneous" )
@ -75,9 +75,9 @@ SET ( CPACK_RPM_PACKAGE_RELEASE 1 )
SET ( CPACK_RPM_PACKAGE_LICENSE "MIT" ) SET ( CPACK_RPM_PACKAGE_LICENSE "MIT" )
SET ( CPACK_RPM_PACKAGE_GROUP "Applications" ) SET ( CPACK_RPM_PACKAGE_GROUP "Applications" )
SET ( CPACK_RPM_PACKAGE_REQUIRES "libcec >= 4.0.0" ) SET ( CPACK_RPM_PACKAGE_REQUIRES "libcec >= 4.0.0" )
SET ( CPACK_RPM_PRE_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/rpm/preinst" ) SET ( CPACK_RPM_PRE_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/package-scripts/preinst" )
SET ( CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/rpm/postinst" ) SET ( CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/package-scripts/postinst" )
SET ( CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/rpm/prerm" ) SET ( CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/package-scripts/prerm" )
# OSX "Bundle" generator TODO Add more osx generators # OSX "Bundle" generator TODO Add more osx generators
# https://cmake.org/cmake/help/v3.10/module/CPackBundle.html # https://cmake.org/cmake/help/v3.10/module/CPackBundle.html

View File

@ -1,148 +0,0 @@
#!/bin/sh
install_file()
{
src="$1"
dest="$2"
if [ ! -e "$dest" ]
then
cp "$src" "${dest}"
return 1
else
echo "--> Service file already exists, skip creation"
return 0
fi
}
echo "---Hyperion ambient light postinstall ---"
#check system
CPU_RPI=`grep -m1 -c 'BCM2708\|BCM2709\|BCM2710\|BCM2835' /proc/cpuinfo`
CPU_X32X64=`uname -m | grep 'x86_32\|i686\|x86_64' | wc -l`
#Check for a bootloader as Berryboot
BOOT_BERRYBOOT=$(grep -m1 -c '\(/var/media\|/media/pi\)/berryboot' /etc/mtab)
#get current system ip
NET_IP=`hostname -I | cut -d " " -f1`
# search for users in system, returns first entry
FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root"
# determine if we should use a service
ENABLE_SERVICE=0
STARTUP_MSG="echo ---> You can start Hyperion from your menu now"
if [ $CPU_RPI -eq 1 ]; then
ENABLE_SERVICE=1
STARTUP_MSG="echo ---> Hyperion has been installed as service, it will start on each system startup"
fi
start_msg=""
restart_msg=""
if grep -m1 systemd /proc/1/comm > /dev/null
then
echo "---> init deamon: systemd"
# systemd
install_file /usr/share/hyperion/service/hyperion.systemd /etc/systemd/system/hyperiond@.service
# service registration just on Raspberry Pi, probably need to ask the user how we should use the service. TODO service start in user login scope eg for x11?!
if [ $ENABLE_SERVICE -eq 1 ]; then
systemctl enable hyperiond"@${FOUND_USR}".service
start_msg="--> systemctl start hyperiond for user ${FOUND_USR}"
systemctl start hyperiond"@${FOUND_USR}"
fi
elif [ -e /sbin/initctl ]
then
echo "---> init deamon: upstart"
# upstart
if [ $ENABLE_SERVICE -eq 1 ]; then
install_file /usr/share/hyperion/service/hyperiond.initctl /etc/init/hyperion.conf && initctl reload-configuration
start_msg="--> initctl start hyperiond"
initctl start hyperiond
fi
else
echo "---> init deamon: sysV"
# sysV
if [ $ENABLE_SERVICE -eq 1 ]; then
install_file /usr/share/hyperion/service/hyperion.init /etc/init.d/hyperiond && chmod +x /etc/init.d/hyperiond && update-rc.d hyperiond defaults 98 02
start_msg="---> service hyperiond start"
service hyperiond start
fi
fi
#cleanup
rm -r /usr/share/hyperion/service
#link binarys and set exec bit
BINSP=/usr/share/hyperion/bin
BINTP=/usr/bin
chmod +x -R $BINSP
ln -fs $BINSP/hyperiond $BINTP/hyperiond
ln -fs $BINSP/hyperion-remote $BINTP/hyperion-remote
ln -fs $BINSP/hyperion-v4l2 $BINTP/hyperion-v4l2
ln -fs $BINSP/hyperion-framebuffer $BINTP/hyperion-framebuffer 2>/dev/null
ln -fs $BINSP/hyperion-dispmanx $BINTP/hyperion-dispmanx 2>/dev/null
ln -fs $BINSP/hyperion-x11 $BINTP/hyperion-x11 2>/dev/null
ln -fs $BINSP/hyperion-xcb $BINTP/hyperion-xcb 2>/dev/null
ln -fs $BINSP/hyperion-aml $BINTP/hyperion-aml 2>/dev/null
ln -fs $BINSP/hyperion-qt $BINTP/hyperion-qt 2>/dev/null
# install desktop icons
echo "---> Install Hyperion desktop icons"
mkdir /usr/share/pixmaps/hyperion 2>/dev/null
cp /usr/share/hyperion/desktop/*.png /usr/share/pixmaps/hyperion 2>/dev/null
desktop-file-install /usr/share/hyperion/desktop/hyperiond.desktop 2>/dev/null
# cleanup desktop icons
rm -r /usr/share/hyperion/desktop 2>/dev/null
#Check, if dtparam=spi=on is in place
if [ $CPU_RPI -eq 1 ]; then
BOOT_DIR="/boot"
if [ $BOOT_BERRYBOOT -eq 1 ]; then
BOOT_DIR=$(sed -ne "s#/dev/mmcblk0p1 \([^ ]*\) vfat rw,.*#\1#p" /etc/mtab)
fi
if [ -z "$BOOT_DIR" -o ! -f "$BOOT_DIR/config.txt" ]; then
echo '---> Warning: RPi using BERRYBOOT found but can not locate where config.txt is to enable SPI. (BOOT_DIR='"$BOOT_DIR)"
SPIOK=1 # Not sure if OK, but don't ask to reboot
else
SPIOK=`grep '^\dtparam=spi=on' "$BOOT_DIR/config.txt" | wc -l`
if [ $SPIOK -ne 1 ]; then
echo '---> Raspberry Pi found, but SPI is not set, we write "dtparam=spi=on" to '"$BOOT_DIR/config.txt"
sed -i '$a dtparam=spi=on' "$BOOT_DIR/config.txt"
REBOOTMESSAGE="echo Please reboot your Raspberry Pi, we inserted dtparam=spi=on to $BOOT_DIR/config.txt"
fi
fi
fi
echo ${start_msg}
echo "-----------------------------------------------------------------------------"
echo "---> Hyperion has been installed/updated!"
echo "---> "
$STARTUP_MSG
echo "---> For configuration, visit with your browser: ${NET_IP}:8090"
echo "---> or if already used by another service try: ${NET_IP}:8091"
$REBOOTMESSAGE
echo "-----------------------------------------------------------------------------"
echo "Webpage: www.hyperion-project.org"
echo "Forum: www.hyperion-project.org"
echo "Documenation: docs.hyperion-project.org"
echo "-----------------------------------------------------------------------------"
if [ -e /opt/hyperion/ ]
then
echo
echo "---------------------------------------------------------------------------------"
echo "- It seemd that you have an older version of hyperion installed in /opt/hyperion -"
echo "- please remove it to avoid problems -"
echo "---------------------------------------------------------------------------------"
fi
exit 0

View File

@ -1,61 +0,0 @@
#!/bin/sh
echo "---Hyperion ambient light preinst ---"
# search for users in system, returns first entry
FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root"
# stop running daemon before we install
if pgrep hyperiond > /dev/null 2>&1
then
if grep -m1 systemd /proc/1/comm > /dev/null
then
echo "--> stop init deamon: systemd"
# systemd
systemctl stop hyperiond"@${FOUND_USR}" 2> /dev/null
elif [ -e /sbin/initctl ]
then
echo "--> stop init deamon: upstart"
# upstart
initctl stop hyperiond
else
echo "--> stop init deamon: sysV"
# sysV
service hyperiond stop 2>/dev/null
fi
fi
# In case we don't use a service kill all instances
killall hyperiond 2> /dev/null
exit 0
#$USR=hyperionIS;
#addToGroup()
##{
# getent group $1 && adduser $USR $1;
#}
#check if user exists
#if id $USR >/dev/null 2>&1; then
# echo "--> hyperion user exists, skip creation";
#else
## create user
# echo "--> Create Hyperion user";
# adduser --system --group $USR;
#fi
# add user to groups if required
## secondary user groups that are required to access system things
#addToGroup(dialout);
#addToGroup(video);
#addToGroup(audio);
#addToGroup(systemd-journal);
# platform specific groups
#addToGroup(i2c);
#addToGroup(spi);
#addToGroup(gpio);

View File

@ -1,50 +0,0 @@
#!/bin/sh
echo "---Hyperion ambient light prerm ---"
# search for users in system, returns first entry
FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root"
# stop running daemon before we delete it
HYPERION_RUNNING=false
pgrep hyperiond > /dev/null 2>&1 && HYPERION_RUNNING=true
if grep -m1 systemd /proc/1/comm > /dev/null
then
echo "---> stop init deamon: systemd"
# systemd
$HYPERION_RUNNING && systemctl stop hyperiond"@${FOUND_USR}" 2> /dev/null
# disable user specific symlink
echo "---> Disable service and remove entry"
systemctl -q disable hyperiond"@${FOUND_USR}"
rm -v /etc/systemd/system/hyperiond@.service 2>/dev/null
elif [ -e /sbin/initctl ]
then
echo "---> stop init deamon: upstart"
# upstart
$HYPERION_RUNNING && initctl stop hyperiond
echo "---> Remove upstart service"
rm -v /etc/init/hyperion* 2>/dev/null
initctl reload-configuration
else
echo "---> stop init deamon: sysV"
# sysV
$HYPERION_RUNNING && service hyperiond stop 2>/dev/null
echo "---> Remove sysV service"
update-rc.d -f hyperion remove
rm /etc/init.d/hyperion* 2>/dev/null
fi
# In case we don't use a service kill all instances
killall hyperiond 2> /dev/null
# delete desktop icons; desktop-file-edit is a workaround to hide the entry and delete it afterwards manual.
# TODO Better way for deletion and keep the desktop in sync without logout/login or desktop dependend cmds?
echo "---> Delete Hyperion desktop icons"
desktop-file-edit --set-key=NoDisplay --set-value=true /usr/share/applications/hyperiond.desktop 2>/dev/null
rm -v /usr/share/applications/hyperion* 2>/dev/null
rm -rv /usr/share/pixmaps/hyperion 2>/dev/null
exit 0

8
debian/rules vendored
View File

@ -3,7 +3,7 @@ export DH_VERBOSE = 1
BUILDDIR = build BUILDDIR = build
build: build:
mkdir $(BUILDDIR); mkdir $(BUILDDIR);
cd $(BUILDDIR); cmake -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../debian/tmp/usr .. cd $(BUILDDIR); cmake -DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../debian/tmp/usr ..
make -j4 -C $(BUILDDIR) make -j4 -C $(BUILDDIR)
@ -15,11 +15,11 @@ binary-indep:
binary-arch: binary-arch:
cd $(BUILDDIR); cmake -P cmake_install.cmake cd $(BUILDDIR); cmake -P cmake_install.cmake
mkdir debian/tmp/DEBIAN mkdir debian/tmp/DEBIAN
cp cmake/debian/postinst debian/tmp/DEBIAN cp cmake/package-scripts/postinst debian/tmp/DEBIAN
chmod 0775 debian/tmp/DEBIAN/postinst chmod 0775 debian/tmp/DEBIAN/postinst
cp cmake/debian/preinst debian/tmp/DEBIAN cp cmake/package-scripts/preinst debian/tmp/DEBIAN
chmod 0775 debian/tmp/DEBIAN/preinst chmod 0775 debian/tmp/DEBIAN/preinst
cp cmake/debian/prerm debian/tmp/DEBIAN cp cmake/package-scripts/prerm debian/tmp/DEBIAN
chmod 0775 debian/tmp/DEBIAN/prerm chmod 0775 debian/tmp/DEBIAN/prerm
dpkg-gencontrol -phyperion dpkg-gencontrol -phyperion
dpkg --build debian/tmp .. dpkg --build debian/tmp ..

View File

@ -14,7 +14,7 @@ include(ExternalProject)
ExternalProject_Add( ExternalProject_Add(
mbedtls mbedtls
GIT_REPOSITORY "https://github.com/ARMmbed/mbedtls.git" GIT_REPOSITORY "https://github.com/ARMmbed/mbedtls.git"
GIT_TAG "v2.25.0" # Reset to origin/master if issue https://github.com/ARMmbed/mbedtls/issues/4233 if fixed GIT_TAG "v2.27.0" # Latest 2.x Version
BUILD_ALWAYS OFF BUILD_ALWAYS OFF
DOWNLOAD_DIR "${DOWNLOAD_DIR}" DOWNLOAD_DIR "${DOWNLOAD_DIR}"
SOURCE_DIR "${SOURCE_DIR}" SOURCE_DIR "${SOURCE_DIR}"

View File

@ -233,7 +233,7 @@ if (NOT USE_SYSTEM_MBEDTLS_LIBS)
FetchContent_Declare( FetchContent_Declare(
mbedtls mbedtls
GIT_REPOSITORY https://github.com/ARMmbed/mbedtls.git GIT_REPOSITORY https://github.com/ARMmbed/mbedtls.git
GIT_TAG "v2.25.0" # Reset to origin/master if issue https://github.com/ARMmbed/mbedtls/issues/4233 if fixed GIT_TAG "v2.27.0" # Latest 2.x Version
BUILD_ALWAYS OFF BUILD_ALWAYS OFF
GIT_PROGRESS 1 GIT_PROGRESS 1
DOWNLOAD_DIR "${MBEDTLS_DOWNLOAD_DIR}" DOWNLOAD_DIR "${MBEDTLS_DOWNLOAD_DIR}"

View File

@ -3,8 +3,14 @@
"script" : "gif.py", "script" : "gif.py",
"args" : "args" :
{ {
"image" : ":fire.gif", "cropBottom": 0,
"cropLeft": 0,
"cropRight": 0,
"cropTop": 0,
"file" : ":fire.gif",
"fps" :25, "fps" :25,
"grayscale": false,
"imageSource": "file",
"reverse" : false "reverse" : false
} }
} }

View File

@ -1,18 +1,27 @@
import hyperion, time import hyperion, time
# Get the parameters # Get the parameters
imageFile = hyperion.args.get('image') imageData = hyperion.args.get('url') if hyperion.args.get('imageSource', "") == "url" else hyperion.args.get('file')
framesPerSecond = float(hyperion.args.get('fps', 25)) framesPerSecond = float(hyperion.args.get('fps', 25))
reverse = bool(hyperion.args.get('reverse', False)) reverse = bool(hyperion.args.get('reverse', False))
cropLeft = int(hyperion.args.get('cropLeft', 0))
cropTop = int(hyperion.args.get('cropTop', 0))
cropRight = int(hyperion.args.get('cropRight', 0))
cropBottom = int(hyperion.args.get('cropBottom', 0))
grayscale = bool(hyperion.args.get('grayscale', False))
sleepTime = 1./framesPerSecond sleepTime = 1./framesPerSecond
imageList = [] imageFrameList = []
if imageFile: if imageData:
imageList = [reversed(hyperion.getImage(imageFile))] if reverse else hyperion.getImage(imageFile) if reverse:
imageFrameList = reversed(hyperion.getImage(imageData, cropLeft, cropTop, cropRight, cropBottom, grayscale))
else:
imageFrameList = hyperion.getImage(imageData, cropLeft, cropTop, cropRight, cropBottom, grayscale)
# Start the write data loop # Start the write data loop
while not hyperion.abort() and imageList: while not hyperion.abort() and imageFrameList:
for image in imageList: for image in imageFrameList:
hyperion.setImage(image["imageWidth"], image["imageHeight"], image["imageData"]) if not hyperion.abort():
time.sleep(sleepTime) hyperion.setImage(image["imageWidth"], image["imageHeight"], image["imageData"])
time.sleep(sleepTime)

View File

@ -3,8 +3,14 @@
"script" : "gif.py", "script" : "gif.py",
"args" : "args" :
{ {
"image" : ":lights.gif", "cropBottom": 0,
"cropLeft": 0,
"cropRight": 0,
"cropTop": 0,
"file" : ":lights.gif",
"fps" :25, "fps" :25,
"reverse" : false "grayscale": false,
"imageSource": "file",
"reverse": false
} }
} }

16
effects/matrix.json Normal file
View File

@ -0,0 +1,16 @@
{
"name" : "Matrix",
"script" : "gif.py",
"args" :
{
"cropBottom": 0,
"cropLeft": 0,
"cropRight": 0,
"cropTop": 0,
"fps": 30,
"grayscale": false,
"imageSource": "url",
"reverse": false,
"url": "https://i.gifer.com/1j6F.gif"
}
}

View File

@ -4,17 +4,79 @@
"title":"edt_eff_gif_header", "title":"edt_eff_gif_header",
"required":true, "required":true,
"properties": { "properties": {
"image": { "imageSource": {
"type": "string",
"title": "edt_eff_image_source",
"format": "radio",
"enum": ["url", "file"],
"options" : {
"enum_titles" : ["edt_eff_image_source_url", "edt_eff_image_source_file"]
},
"propertyOrder" : 1
},
"url": {
"type": "string",
"title":"edt_eff_url",
"default": "",
"options": {
"dependencies": {
"imageSource": "url"
}
},
"propertyOrder" : 2
},
"file": {
"type": "string", "type": "string",
"title":"edt_eff_image", "title":"edt_eff_image",
"format" : "url", "format" : "url",
"options" : "options" :
{ {
"upload" : true, "upload" : true,
"auto_upload" : true "auto_upload" : true,
"dependencies": {
"imageSource": "file"
}
}, },
"default": "", "default": "",
"propertyOrder" : 1 "propertyOrder" : 3
},
"cropLeft": {
"type": "integer",
"title":"edt_conf_v4l2_cropLeft_title",
"default": 0,
"minimum" : 0,
"step" : 1,
"propertyOrder" : 4
},
"cropTop": {
"type": "integer",
"title":"edt_conf_v4l2_cropTop_title",
"default": 0,
"minimum" : 0,
"step" : 1,
"propertyOrder" : 5
},
"cropRight": {
"type": "integer",
"title":"edt_conf_v4l2_cropRight_title",
"default": 0,
"minimum" : 0,
"step" : 1,
"propertyOrder" : 6
},
"cropBottom": {
"type": "integer",
"title":"edt_conf_v4l2_cropBottom_title",
"default": 0,
"minimum" : 0,
"step" : 1,
"propertyOrder" : 7
},
"grayscale": {
"type": "boolean",
"title":"edt_eff_grayscale",
"default": false,
"propertyOrder" : 8
}, },
"fps": { "fps": {
"type": "integer", "type": "integer",
@ -23,13 +85,13 @@
"minimum" : 1, "minimum" : 1,
"maximum" : 100, "maximum" : 100,
"step" : 1, "step" : 1,
"propertyOrder" : 2 "propertyOrder" : 9
}, },
"reverse": { "reverse": {
"type": "boolean", "type": "boolean",
"title":"edt_eff_reversedirection", "title":"edt_eff_reversedirection",
"default": false, "default": false,
"propertyOrder" : 3 "propertyOrder" : 10
} }
}, },
"additionalProperties": false "additionalProperties": false

View File

@ -8,8 +8,10 @@
class Effect; class Effect;
class EffectModule class EffectModule: public QObject
{ {
Q_OBJECT
public: public:
// Python 3 module def // Python 3 module def
static struct PyModuleDef moduleDef; static struct PyModuleDef moduleDef;

View File

@ -79,6 +79,15 @@ public:
/// ///
bool open(); bool open();
#ifdef _WIN32
///
/// @brief Replacement for the virtual QWindowsScreen Function grabWindow (only on Windows).
///
/// @return QPixmap
///
QPixmap grabWindow(quintptr window, int xIn, int yIn, int width, int height) const;
#endif
private slots: private slots:
/// ///

View File

@ -70,9 +70,9 @@ QString EffectFileHandler::deleteEffect(const QString& effectName)
{ {
if (effectConfigurationFile.exists()) if (effectConfigurationFile.exists())
{ {
if ((it->script == ":/effects/gif.py") && !it->args.value("image").toString("").isEmpty()) if ((it->script == ":/effects/gif.py") && !it->args.value("file").toString("").isEmpty())
{ {
QFileInfo effectImageFile(it->args.value("image").toString()); QFileInfo effectImageFile(it->args.value("file").toString());
if (effectImageFile.exists()) if (effectImageFile.exists())
{ {
QFile::remove(effectImageFile.absoluteFilePath()); QFile::remove(effectImageFile.absoluteFilePath());
@ -159,19 +159,26 @@ QString EffectFileHandler::saveEffect(const QJsonObject& message)
newFileName.setFile(f); newFileName.setFile(f);
} }
if (!message["imageData"].toString("").isEmpty() && !message["args"].toObject().value("image").toString("").isEmpty()) if (!message["imageData"].toString("").isEmpty() && !message["args"].toObject().value("file").toString("").isEmpty())
{ {
QJsonObject args = message["args"].toObject(); QJsonObject args = message["args"].toObject();
QString imageFilePath = effectArray[0].toString().replace("$ROOT", _rootPath) + '/' + args.value("image").toString(); QString imageFilePath = effectArray[0].toString().replace("$ROOT", _rootPath) + '/' + args.value("file").toString();
QFileInfo imageFileName(imageFilePath); QFileInfo imageFileName(imageFilePath);
if (!FileUtils::writeFile(imageFileName.absoluteFilePath(), QByteArray::fromBase64(message["imageData"].toString("").toUtf8()), _log)) if (!FileUtils::writeFile(imageFileName.absoluteFilePath(), QByteArray::fromBase64(message["imageData"].toString("").toUtf8()), _log))
{ {
return "Error while saving image file '" + message["args"].toObject().value("image").toString() + ", please check the Hyperion Log"; return "Error while saving image file '" + message["args"].toObject().value("file").toString() + ", please check the Hyperion Log";
} }
//Update json with image file location //Update json with image file location
args["image"] = imageFilePath; args["file"] = imageFilePath;
effectJson["args"] = args;
}
if (message["args"].toObject().value("imageSource").toString("") == "url" || message["args"].toObject().value("imageSource").toString("") == "file")
{
QJsonObject args = message["args"].toObject();
args.remove(args.value("imageSource").toString("") == "url" ? "file" : "url");
effectJson["args"] = args; effectJson["args"] = args;
} }

View File

@ -12,6 +12,10 @@
#include <QDateTime> #include <QDateTime>
#include <QImageReader> #include <QImageReader>
#include <QBuffer> #include <QBuffer>
#include <QUrl>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QEventLoop>
// Get the effect from the capsule // Get the effect from the capsule
#define getEffect() static_cast<Effect*>((Effect*)PyCapsule_Import("hyperion.__effectObj", 0)) #define getEffect() static_cast<Effect*>((Effect*)PyCapsule_Import("hyperion.__effectObj", 0))
@ -219,31 +223,57 @@ PyObject* EffectModule::wrapSetImage(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args) PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
{ {
QString file;
QBuffer buffer; QBuffer buffer;
QImageReader reader; QImageReader reader;
char *source;
int cropLeft = 0, cropTop = 0, cropRight = 0, cropBottom = 0;
bool grayscale = false;
if (getEffect()->_imageData.isEmpty()) if (getEffect()->_imageData.isEmpty())
{ {
Q_INIT_RESOURCE(EffectEngine); Q_INIT_RESOURCE(EffectEngine);
char *source; if(!PyArg_ParseTuple(args, "s|iiiii", &source, &cropLeft, &cropTop, &cropRight, &cropBottom, &grayscale))
if(!PyArg_ParseTuple(args, "s", &source))
{ {
PyErr_SetString(PyExc_TypeError, "String required"); PyErr_SetString(PyExc_TypeError, "String required");
return nullptr; return nullptr;
} }
file = QString::fromUtf8(source); const QUrl url = QUrl(source);
if (url.isValid())
{
QNetworkAccessManager *networkManager = new QNetworkAccessManager();
QNetworkReply * networkReply = networkManager->get(QNetworkRequest(url));
if (file.mid(0, 1) == ":") QEventLoop eventLoop;
file = ":/effects/"+file.mid(1); connect(networkReply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
reader.setDecideFormatFromContent(true); if (networkReply->error() == QNetworkReply::NoError)
reader.setFileName(file); {
buffer.setData(networkReply->readAll());
buffer.open(QBuffer::ReadOnly);
reader.setDecideFormatFromContent(true);
reader.setDevice(&buffer);
}
delete networkReply;
delete networkManager;
}
else
{
QString file = QString::fromUtf8(source);
if (file.mid(0, 1) == ":")
file = ":/effects/"+file.mid(1);
reader.setDecideFormatFromContent(true);
reader.setFileName(file);
}
} }
else else
{ {
PyArg_ParseTuple(args, "|siiiii", &source, &cropLeft, &cropTop, &cropRight, &cropBottom, &grayscale);
buffer.setData(QByteArray::fromBase64(getEffect()->_imageData.toUtf8())); buffer.setData(QByteArray::fromBase64(getEffect()->_imageData.toUtf8()));
buffer.open(QBuffer::ReadOnly); buffer.open(QBuffer::ReadOnly);
reader.setDecideFormatFromContent(true); reader.setDecideFormatFromContent(true);
@ -260,19 +290,33 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
if (reader.canRead()) if (reader.canRead())
{ {
QImage qimage = reader.read(); QImage qimage = reader.read();
int width = qimage.width(); int width = qimage.width();
int height = qimage.height(); int height = qimage.height();
if (cropLeft > 0 || cropTop > 0 || cropRight > 0 || cropBottom > 0)
{
if (cropLeft + cropRight >= width || cropTop + cropBottom >= height)
{
QString errorStr = QString("Rejecting invalid crop values: left: %1, right: %2, top: %3, bottom: %4, higher than height/width %5/%6").arg(cropLeft).arg(cropRight).arg(cropTop).arg(cropBottom).arg(height).arg(width);
PyErr_SetString(PyExc_RuntimeError, qPrintable(errorStr));
return nullptr;
}
qimage = qimage.copy(cropLeft, cropTop, width - cropLeft - cropRight, height - cropTop - cropBottom);
width = qimage.width();
height = qimage.height();
}
QByteArray binaryImage; QByteArray binaryImage;
for (int i = 0; i<height; ++i) for (int i = 0; i<height; i++)
{ {
const QRgb *scanline = reinterpret_cast<const QRgb *>(qimage.scanLine(i)); const QRgb *scanline = reinterpret_cast<const QRgb *>(qimage.scanLine(i));
for (int j = 0; j< width; ++j) const QRgb *end = scanline + qimage.width();
for (; scanline != end; scanline++)
{ {
binaryImage.append((char) qRed(scanline[j])); binaryImage.append(!grayscale ? (char) qRed(scanline[0]) : (char) qGray(scanline[0]));
binaryImage.append((char) qGreen(scanline[j])); binaryImage.append(!grayscale ? (char) qGreen(scanline[1]) : (char) qGray(scanline[1]));
binaryImage.append((char) qBlue(scanline[j])); binaryImage.append(!grayscale ? (char) qBlue(scanline[2]) : (char) qGray(scanline[2]));
} }
} }
PyList_SET_ITEM(result, i, Py_BuildValue("{s:i,s:i,s:O}", "imageWidth", width, "imageHeight", height, "imageData", PyByteArray_FromStringAndSize(binaryImage.constData(),binaryImage.size()))); PyList_SET_ITEM(result, i, Py_BuildValue("{s:i,s:i,s:O}", "imageWidth", width, "imageHeight", height, "imageData", PyByteArray_FromStringAndSize(binaryImage.constData(),binaryImage.size())));
@ -283,6 +327,7 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
return nullptr; return nullptr;
} }
} }
return result; return result;
} }
else else

View File

@ -11,6 +11,10 @@
#include <QJsonArray> #include <QJsonArray>
#include <QJsonDocument> #include <QJsonDocument>
#ifdef _WIN32
#include <Windows.h>
#endif
// Constants // Constants
namespace { namespace {
const bool verbose = false; const bool verbose = false;
@ -156,6 +160,58 @@ void QtGrabber::geometryChanged(const QRect &geo)
updateScreenDimensions(true); updateScreenDimensions(true);
} }
#ifdef _WIN32
extern QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int format = 0);
QPixmap QtGrabber::grabWindow(quintptr window, int xIn, int yIn, int width, int height) const
{
QSize windowSize;
int x = xIn;
int y = yIn;
HWND hwnd = reinterpret_cast<HWND>(window);
if (hwnd)
{
RECT r;
GetClientRect(hwnd, &r);
windowSize = QSize(r.right - r.left, r.bottom - r.top);
}
else
{
hwnd = GetDesktopWindow();
const QRect screenGeometry = _screen->geometry();
windowSize = screenGeometry.size();
x += screenGeometry.x();
y += screenGeometry.y();
}
if (width < 0)
width = windowSize.width() - x;
if (height < 0)
height = windowSize.height() - y;
// Create and setup bitmap
HDC display_dc = GetDC(nullptr);
HDC bitmap_dc = CreateCompatibleDC(display_dc);
HBITMAP bitmap = CreateCompatibleBitmap(display_dc, width, height);
HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
// copy data
HDC window_dc = GetDC(hwnd);
BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY);
// clean up all but bitmap
ReleaseDC(hwnd, window_dc);
SelectObject(bitmap_dc, null_bitmap);
DeleteDC(bitmap_dc);
const QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap);
DeleteObject(bitmap);
ReleaseDC(nullptr, display_dc);
return pixmap;
}
#endif
int QtGrabber::grabFrame(Image<ColorRgb> & image) int QtGrabber::grabFrame(Image<ColorRgb> & image)
{ {
int rc = 0; int rc = 0;
@ -170,7 +226,11 @@ int QtGrabber::grabFrame(Image<ColorRgb> & image)
if (_isEnabled) if (_isEnabled)
{ {
#ifdef _WIN32
QPixmap originalPixmap = grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max);
#else
QPixmap originalPixmap = _screen->grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max); QPixmap originalPixmap = _screen->grabWindow(0, _src_x, _src_y, _src_x_max, _src_y_max);
#endif
if (originalPixmap.isNull()) if (originalPixmap.isNull())
{ {
rc = -1; rc = -1;

View File

@ -23,17 +23,26 @@ PythonInit::PythonInit()
// register modules // register modules
EffectModule::registerHyperionExtensionModule(); EffectModule::registerHyperionExtensionModule();
// Set Program name
Py_SetProgramName(L"Hyperion");
// set Python module path when exists // set Python module path when exists
QString py_patch = QDir::cleanPath(qApp->applicationDirPath() + "/../lib/python"); QString py_patch = QDir::cleanPath(qApp->applicationDirPath() + "/../lib/python" + STRINGIFY(PYTHON_VERSION_MAJOR_MINOR));
QString py_file = QDir::cleanPath(qApp->applicationDirPath() + "/python" + STRINGIFY(PYTHON_VERSION_MAJOR_MINOR) + ".zip"); QString py_file = QDir::cleanPath(qApp->applicationDirPath() + "/python" + STRINGIFY(PYTHON_VERSION_MAJOR_MINOR) + ".zip");
if (QFile(py_file).exists() || QDir(py_patch).exists()) if (QFile(py_file).exists() || QDir(py_patch).exists())
{ {
Py_NoSiteFlag++; Py_NoSiteFlag++;
if (QFile(py_file).exists()) if (QFile(py_file).exists())
{
Py_SetPythonHome(Py_DecodeLocale(py_file.toLatin1().data(), nullptr));
Py_SetPath(Py_DecodeLocale(py_file.toLatin1().data(), nullptr)); Py_SetPath(Py_DecodeLocale(py_file.toLatin1().data(), nullptr));
}
else if (QDir(py_patch).exists()) else if (QDir(py_patch).exists())
{
Py_SetPythonHome(Py_DecodeLocale(py_file.toLatin1().data(), nullptr));
Py_SetPath(Py_DecodeLocale(py_patch.toLatin1().data(), nullptr)); Py_SetPath(Py_DecodeLocale(py_patch.toLatin1().data(), nullptr));
}
} }
// init Python // init Python

View File

@ -138,6 +138,7 @@ if(CMAKE_HOST_UNIX)
install( CODE "EXECUTE_PROCESS(COMMAND ln -sf \"../share/hyperion/bin/hyperiond\" \"${CMAKE_BINARY_DIR}/symlink_hyperiond\" )" COMPONENT "Hyperion" ) install( CODE "EXECUTE_PROCESS(COMMAND ln -sf \"../share/hyperion/bin/hyperiond\" \"${CMAKE_BINARY_DIR}/symlink_hyperiond\" )" COMPONENT "Hyperion" )
install( FILES ${CMAKE_BINARY_DIR}/symlink_hyperiond DESTINATION "bin" RENAME hyperiond COMPONENT "Hyperion" ) install( FILES ${CMAKE_BINARY_DIR}/symlink_hyperiond DESTINATION "bin" RENAME hyperiond COMPONENT "Hyperion" )
install( CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_hyperiond )" COMPONENT "Hyperion" ) install( CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_hyperiond )" COMPONENT "Hyperion" )
install ( FILES ${CMAKE_SOURCE_DIR}/bin/scripts/updateHyperionUser.sh DESTINATION "share/hyperion/scripts" COMPONENT "Hyperion" )
endif() endif()
# Deploy Qt DLLs into the binary folder. # Deploy Qt DLLs into the binary folder.