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
shell: bash
run: |
tr -d '\n' < version > temp && mv temp version
tr -d '\n' < .version > temp && mv temp .version
mkdir -p "${GITHUB_WORKSPACE}/deploy"
docker run --rm \
-v "${GITHUB_WORKSPACE}/deploy:/deploy" \
@ -43,7 +43,7 @@ jobs:
ghcr.io/hyperion-project/${{ matrix.architecture }}:$(echo ${{ matrix.distribution }} | tr '[:upper:]' '[:lower:]') \
/bin/bash -c "cd /source && \
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 && \
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 . && \

View File

@ -36,12 +36,12 @@ jobs:
with:
submodules: true
# Append PR number to version
# Append PR number to .version
- name: Append PR number to version
shell: bash
run: |
tr -d '\n' < version > temp && mv temp version
echo -n "+PR${{ github.event.pull_request.number }}" >> version
tr -d '\n' < .version > temp && mv temp .version
echo -n "+PR${{ github.event.pull_request.number }}" >> .version
# Build packages
- name: Build packages
@ -82,12 +82,12 @@ jobs:
with:
submodules: true
# Append PR number to version
# Append PR number to .version
- name: Append PR number to version
shell: bash
run: |
tr -d '\n' < version > temp && mv temp version
echo -n "+PR${{ github.event.pull_request.number }}" >> version
tr -d '\n' < .version > temp && mv temp .version
echo -n "+PR${{ github.event.pull_request.number }}" >> .version
# Install dependencies
- name: Install dependencies
@ -131,12 +131,12 @@ jobs:
with:
submodules: true
# Append PR number to version
# Append PR number to .version
- name: Append PR number to version
shell: bash
run: |
tr -d '\n' < version > temp && mv temp version
echo -n "+PR${{ github.event.pull_request.number }}" >> version
tr -d '\n' < .version > temp && mv temp .version
echo -n "+PR${{ github.event.pull_request.number }}" >> .version
- name: Cache Qt
uses: actions/cache@v2
@ -199,27 +199,3 @@ jobs:
with:
name: 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:
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 ########
#######################################
@ -195,10 +170,10 @@ jobs:
uses: actions/checkout@v2
# generate environment variables
- name: Generate environment variables from version and tag
- name: Generate environment variables from .version and tag
run: |
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
# If version contains alpha or beta, mark draft release as pre-release
@ -223,35 +198,3 @@ jobs:
prerelease: ${{ env.preRelease }}
env:
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
include (${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.cmake)
file (STRINGS "version" HYPERION_VERSION)
file (STRINGS ".version" HYPERION_VERSION)
SetVersionNumber(HYPERION ${HYPERION_VERSION})
set(DEFAULT_JSON_CONFIG_FILE ${CMAKE_CURRENT_SOURCE_DIR}/config/hyperion.config.json.default)
file(READ ${DEFAULT_JSON_CONFIG_FILE} DEFAULT_JSON_CONFIG_VAR)

View File

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

View File

@ -764,6 +764,88 @@ li a:active:after {
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*/
.checkbox {

View File

@ -636,12 +636,17 @@
"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_fps": "Bilder pro Sekunde",
"edt_eff_grayscale": "Graustufen",
"edt_eff_frequency": "Frequenz",
"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_height": "Höhe",
"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_url": "Bildadresse",
"edt_eff_initial_blink": "Blinken beim Start",
"edt_eff_interval": "Intervall",
"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_title": "Yeelight Einrichtungsassistent",
"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_fps": "Frames per seconds",
"edt_eff_frequency": "Frequency",
"edt_eff_grayscale": "Grayscale",
"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_height": "Height",
"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_url": "Image adress",
"edt_eff_initial_blink" : "Flash for attention",
"edt_eff_interval": "Interval",
"edt_eff_knightrider_header": "Knight Rider",

View File

@ -55,7 +55,11 @@ $(document).ready(function () {
testrun = true;
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
@ -133,7 +137,12 @@ $(document).ready(function () {
// Save Effect
$('#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) {
if (event.response.success)
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.iconlib = this.jsoneditor.iconlib;
this.access = this.jsoneditor.access;
this.translate = this.jsoneditor.translate || JSONEditor.defaults.translate;
this.original_schema = options.schema;
@ -1447,7 +1447,7 @@ JSONEditor.AbstractEditor = Class.extend({
if (!deps) {
return;
}
var self = this;
Object.keys(deps).forEach(function(dependency) {
var path = self.path.split('.');
@ -1464,13 +1464,13 @@ JSONEditor.AbstractEditor = Class.extend({
if (this.path === path || !wrapper) {
return;
}
var self = this;
var editor = this.jsoneditor.getEditor(path);
var value = editor ? editor.getValue() : undefined;
var previousStatus = this.dependenciesFulfilled;
this.dependenciesFulfilled = false;
if (!editor || !editor.dependenciesFulfilled) {
this.dependenciesFulfilled = false;
} else if (Array.isArray(choices)) {
@ -1504,7 +1504,7 @@ JSONEditor.AbstractEditor = Class.extend({
this.dependenciesFulfilled = !value;
}
}
if (this.dependenciesFulfilled !== previousStatus) {
this.notify();
}
@ -1535,7 +1535,7 @@ JSONEditor.AbstractEditor = Class.extend({
this.updateHeaderText();
this.register();
this.onWatchedFieldChange();
//hide input fields, if they didn't match the current access level
var storedAccess = this.access
if(this.schema.access){
@ -1544,12 +1544,12 @@ JSONEditor.AbstractEditor = Class.extend({
else if(this.schema.access == 'advanced' && storedAccess == 'default')
{
this.container.style.display = "none";
}
}
else if(this.schema.access == 'expert' && storedAccess != 'expert')
{
this.container.style.display = "none";
//this.disable();
}
}
}
},
@ -2046,7 +2046,7 @@ JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({
if(this.schema.append) this.append = this.theme.getFormInputAppend(this.getAppend());
this.placeholder = this.schema.default;
this.format = this.schema.format;
if(!this.format && this.schema.media && this.schema.media.type) {
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) {
this.format = this.options.format;
}
// Specific format
if(this.format) {
// Text Area
@ -2146,7 +2146,7 @@ JSONEditor.defaults.editors.string = JSONEditor.AbstractEditor.extend({
}
// Number or integer adds html5 tag 'number'
else if (this.schema.type == "number" || this.schema.type == "integer"){
var min = this.schema.minimum
var max = this.schema.maximum
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.defaultValue) this.input.setAttribute('data-schemaformat',this.format);
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.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.schema.description) this.description = this.theme.getFormInputDescription(this.schema.description);
if(this.schema.append) this.append = this.theme.getFormInputAppend(this.getAppend());
if(this.options.compact) this.container.className += ' compact';
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);
this.control = this.theme.getFormControl(this.label, this.input, this.description);
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
JSONEditor.defaults.editors.colorPicker = JSONEditor.defaults.editors.string.extend({
getValue: function() {
@ -6253,9 +6368,9 @@ JSONEditor.defaults.editors.colorPicker = JSONEditor.defaults.editors.string.ext
$(this.input).colorpicker('updatePicker', rgb2hex(val));
$(this.input).colorpicker('updateComponent', 'rgb('+val+')');
},
build: function() {
this._super();
var myinput = this;
@ -6277,12 +6392,12 @@ JSONEditor.defaults.editors.colorPicker = JSONEditor.defaults.editors.string.ext
$("#event_catcher").detach().insertAfter(myinput.input);
$("#event_catcher").attr("id", "selector");
$(this.input).colorpicker().on('changeColor', function(e) {
$(myinput).val(e.color.toRGB()).change();
});
});
},
destroy: function() {
$(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('updateComponent', 'rgba('+val+')');
},
build: function() {
this._super();
var myinput = this;
@ -6333,12 +6448,12 @@ JSONEditor.defaults.editors.colorPickerRGBA = JSONEditor.defaults.editors.string
$("#event_catcher").detach().insertAfter(myinput.input);
$("#event_catcher").attr("id", "selector");
$(this.input).colorpicker().on('changeColor', function(e) {
$(myinput).val(e.color.toRGB()).change();
});
});
},
destroy: function() {
$(this.input).colorpicker('destroy');
}
@ -6508,7 +6623,7 @@ JSONEditor.AbstractTheme = Class.extend({
},
getRangeInput: function(min,max,step) {
if (typeof step == "undefined") step = 1;
var el = this.getFormInputField('number');
if (typeof min != "undefined") el.setAttribute('min',min);
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) {
var group = document.createElement('div');
var subgroup = document.createElement('div');
if(placeholder)
input.setAttribute('placeholder',placeholder);
if (input.type === 'checkbox'){
var helplabel = document.createElement("label")
group.className += ' form-group';
group.style.minHeight = "30px";
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.appendChild(input);
}
if(description) group.appendChild(description);
@ -7096,10 +7211,10 @@ JSONEditor.defaults.template = 'default';
JSONEditor.defaults.options = {};
// String translate function
JSONEditor.defaults.translate = function(key, variables) {
JSONEditor.defaults.translate = function(key, variables) {
return $.i18n(key, variables);
};
// Miscellaneous Plugin Settings
@ -7174,7 +7289,12 @@ JSONEditor.defaults.resolvers.unshift(function(schema) {
});
// Use the `select` editor for dynamic enumSource enums
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
JSONEditor.defaults.resolvers.unshift(function(schema) {
@ -7183,6 +7303,11 @@ JSONEditor.defaults.resolvers.unshift(function(schema) {
return "enum";
}
else if(schema.type === "number" || schema.type === "integer" || schema.type === "string") {
if(schema.format === "radio") {
return "radio";
}
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]
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]
ExecStart=/usr/bin/hyperiond

View File

@ -158,12 +158,12 @@ macro(DeployUnix TARGET)
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)
install(
DIRECTORY ${PYTHON_MODULES_DIR}/
DESTINATION "share/hyperion/lib/python"
DESTINATION "share/hyperion/lib/python${PYTHON_VERSION_MAJOR_MINOR}"
COMPONENT "Hyperion"
PATTERN "*.pyc" EXCLUDE # compiled bytecodes
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
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()
{
src="$1"
@ -15,13 +24,11 @@ install_file()
fi
}
echo "--- Hyperion ambient light postinstall ---"
#check system
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`
OS_HYPERBIAN=`grep ID /etc/os-release | grep -m1 -c HyperBian`
#Check for a bootloader as Berryboot
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
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
if [ $OS_HYPERBIAN -eq 1 ]; then
# start service only
echo "--> Service file already exists, skip creation"
start_msg="--> systemctl start hyperion"
systemctl start hyperion
else
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}"
# service registration if no gui is present (only on initial installation and not upgrade)
if [ "$IS_UPGRADE" = false ]; then
if [ -z "${DISPLAY}" ] && [ -z "${WAYLAND_DISPLAY}" ] && [ -z "${XDG_CURRENT_DESKTOP}" ]; then
STARTUP_MSG="echo ---> Hyperion has been installed as service, it will start on each system startup"
if grep -m1 systemd /proc/1/comm > /dev/null
then
# systemd
echo "---> init deamon: systemd"
install_file /usr/share/hyperion/service/hyperion.systemd /etc/systemd/system/hyperion@.service
systemctl enable hyperion"@${FOUND_USR}".service
start_msg="--> systemctl start hyperion for user ${FOUND_USR}"
systemctl start hyperion"@${FOUND_USR}"
elif [ -e /sbin/initctl ]
then
# 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
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
else
STARTUP_MSG="echo ---> You can start Hyperion from your menu now"
fi
fi
@ -87,25 +78,30 @@ fi
rm -r /usr/share/hyperion/service
#link binarys and set exec bit
BINSP=/usr/share/hyperion/bin
BINSP=/usr/share/hyperion
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
chmod +x -R $BINSP/bin
ln -fs $BINSP/bin/hyperiond $BINTP/hyperiond
ln -fs $BINSP/bin/hyperion-remote $BINTP/hyperion-remote
ln -fs $BINSP/bin/hyperion-v4l2 $BINTP/hyperion-v4l2
ln -fs $BINSP/bin/hyperion-framebuffer $BINTP/hyperion-framebuffer 2>/dev/null
ln -fs $BINSP/bin/hyperion-dispmanx $BINTP/hyperion-dispmanx 2>/dev/null
ln -fs $BINSP/bin/hyperion-x11 $BINTP/hyperion-x11 2>/dev/null
ln -fs $BINSP/bin/hyperion-xcb $BINTP/hyperion-xcb 2>/dev/null
ln -fs $BINSP/bin/hyperion-aml $BINTP/hyperion-aml 2>/dev/null
ln -fs $BINSP/bin/hyperion-qt $BINTP/hyperion-qt 2>/dev/null
# install desktop icons / not on HyperBian
if [ $OS_HYPERBIAN -ne 1 ]; 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
#create symlink for updateHyperionUser.sh script
ln -fs $BINSP/scripts/updateHyperionUser.sh $BINTP/updateHyperionUser 2>/dev/null
# install desktop icons (only on initial installation and not upgrade)
if [ "$IS_UPGRADE" = false ]; then
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
# cleanup desktop icons
@ -133,7 +129,11 @@ fi
echo ${start_msg}
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 "---> "
$STARTUP_MSG
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 ---"
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
FOUND_USR=`who | grep -o -m1 '^\w*\b'` || "root"
@ -13,17 +24,18 @@ if grep -m1 systemd /proc/1/comm > /dev/null
then
echo "---> stop init deamon: systemd"
# systemd
$HYPERION_RUNNING && systemctl stop hyperion hyperiond"@${FOUND_USR}" 2> /dev/null
# disable user specific symlink / not on HyperBian
$HYPERION_RUNNING && systemctl stop hyperion hyperiond"@${FOUND_USR}" hyperion"@${FOUND_USR}" 2> /dev/null
# disable user specific symlink
echo "---> Disable service and remove entry"
systemctl -q disable hyperiond"@${FOUND_USR}" 2> /dev/null
rm -v /etc/systemd/system/hyperiond@.service 2>/dev/null
systemctl -q disable hyperion hyperiond"@${FOUND_USR}" hyperion"@${FOUND_USR}" 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 ]
then
echo "---> stop init deamon: upstart"
# upstart
$HYPERION_RUNNING && initctl stop hyperiond
$HYPERION_RUNNING && initctl stop hyperion
echo "---> Remove upstart service"
rm -v /etc/init/hyperion* 2>/dev/null
initctl reload-configuration
@ -32,6 +44,7 @@ else
echo "---> stop init deamon: sysV"
# sysV
$HYPERION_RUNNING && service hyperiond stop 2>/dev/null
$HYPERION_RUNNING && service hyperion stop 2>/dev/null
echo "---> Remove sysV service"
update-rc.d -f hyperion remove
rm /etc/init.d/hyperion* 2>/dev/null

View File

@ -65,7 +65,7 @@ ENDIF()
# Specific CPack Package Generators
# https://cmake.org/Wiki/CMake:CPackPackageGenerators
# .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_SECTION "Miscellaneous" )
@ -75,9 +75,9 @@ SET ( CPACK_RPM_PACKAGE_RELEASE 1 )
SET ( CPACK_RPM_PACKAGE_LICENSE "MIT" )
SET ( CPACK_RPM_PACKAGE_GROUP "Applications" )
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_POST_INSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/rpm/postinst" )
SET ( CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/rpm/prerm" )
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/package-scripts/postinst" )
SET ( CPACK_RPM_PRE_UNINSTALL_SCRIPT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/package-scripts/prerm" )
# OSX "Bundle" generator TODO Add more osx generators
# 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
build:
build:
mkdir $(BUILDDIR);
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)
@ -15,11 +15,11 @@ binary-indep:
binary-arch:
cd $(BUILDDIR); cmake -P cmake_install.cmake
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
cp cmake/debian/preinst debian/tmp/DEBIAN
cp cmake/package-scripts/preinst debian/tmp/DEBIAN
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
dpkg-gencontrol -phyperion
dpkg --build debian/tmp ..

View File

@ -14,7 +14,7 @@ include(ExternalProject)
ExternalProject_Add(
mbedtls
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
DOWNLOAD_DIR "${DOWNLOAD_DIR}"
SOURCE_DIR "${SOURCE_DIR}"

View File

@ -233,7 +233,7 @@ if (NOT USE_SYSTEM_MBEDTLS_LIBS)
FetchContent_Declare(
mbedtls
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
GIT_PROGRESS 1
DOWNLOAD_DIR "${MBEDTLS_DOWNLOAD_DIR}"

View File

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

View File

@ -1,18 +1,27 @@
import hyperion, time
# 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))
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
imageList = []
imageFrameList = []
if imageFile:
imageList = [reversed(hyperion.getImage(imageFile))] if reverse else hyperion.getImage(imageFile)
if imageData:
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
while not hyperion.abort() and imageList:
for image in imageList:
hyperion.setImage(image["imageWidth"], image["imageHeight"], image["imageData"])
time.sleep(sleepTime)
while not hyperion.abort() and imageFrameList:
for image in imageFrameList:
if not hyperion.abort():
hyperion.setImage(image["imageWidth"], image["imageHeight"], image["imageData"])
time.sleep(sleepTime)

View File

@ -3,8 +3,14 @@
"script" : "gif.py",
"args" :
{
"image" : ":lights.gif",
"cropBottom": 0,
"cropLeft": 0,
"cropRight": 0,
"cropTop": 0,
"file" : ":lights.gif",
"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",
"required":true,
"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",
"title":"edt_eff_image",
"format" : "url",
"options" :
{
"upload" : true,
"auto_upload" : true
"auto_upload" : true,
"dependencies": {
"imageSource": "file"
}
},
"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": {
"type": "integer",
@ -23,13 +85,13 @@
"minimum" : 1,
"maximum" : 100,
"step" : 1,
"propertyOrder" : 2
"propertyOrder" : 9
},
"reverse": {
"type": "boolean",
"title":"edt_eff_reversedirection",
"default": false,
"propertyOrder" : 3
"propertyOrder" : 10
}
},
"additionalProperties": false

View File

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

View File

@ -79,6 +79,15 @@ public:
///
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:
///

View File

@ -70,9 +70,9 @@ QString EffectFileHandler::deleteEffect(const QString& effectName)
{
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())
{
QFile::remove(effectImageFile.absoluteFilePath());
@ -159,19 +159,26 @@ QString EffectFileHandler::saveEffect(const QJsonObject& message)
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();
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);
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
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;
}

View File

@ -12,6 +12,10 @@
#include <QDateTime>
#include <QImageReader>
#include <QBuffer>
#include <QUrl>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QEventLoop>
// Get the effect from the capsule
#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)
{
QString file;
QBuffer buffer;
QImageReader reader;
char *source;
int cropLeft = 0, cropTop = 0, cropRight = 0, cropBottom = 0;
bool grayscale = false;
if (getEffect()->_imageData.isEmpty())
{
Q_INIT_RESOURCE(EffectEngine);
char *source;
if(!PyArg_ParseTuple(args, "s", &source))
if(!PyArg_ParseTuple(args, "s|iiiii", &source, &cropLeft, &cropTop, &cropRight, &cropBottom, &grayscale))
{
PyErr_SetString(PyExc_TypeError, "String required");
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) == ":")
file = ":/effects/"+file.mid(1);
QEventLoop eventLoop;
connect(networkReply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
reader.setDecideFormatFromContent(true);
reader.setFileName(file);
if (networkReply->error() == QNetworkReply::NoError)
{
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
{
PyArg_ParseTuple(args, "|siiiii", &source, &cropLeft, &cropTop, &cropRight, &cropBottom, &grayscale);
buffer.setData(QByteArray::fromBase64(getEffect()->_imageData.toUtf8()));
buffer.open(QBuffer::ReadOnly);
reader.setDecideFormatFromContent(true);
@ -260,19 +290,33 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
if (reader.canRead())
{
QImage qimage = reader.read();
int width = qimage.width();
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;
for (int i = 0; i<height; ++i)
for (int i = 0; i<height; 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((char) qGreen(scanline[j]));
binaryImage.append((char) qBlue(scanline[j]));
binaryImage.append(!grayscale ? (char) qRed(scanline[0]) : (char) qGray(scanline[0]));
binaryImage.append(!grayscale ? (char) qGreen(scanline[1]) : (char) qGray(scanline[1]));
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())));
@ -283,6 +327,7 @@ PyObject* EffectModule::wrapGetImage(PyObject *self, PyObject *args)
return nullptr;
}
}
return result;
}
else

View File

@ -11,6 +11,10 @@
#include <QJsonArray>
#include <QJsonDocument>
#ifdef _WIN32
#include <Windows.h>
#endif
// Constants
namespace {
const bool verbose = false;
@ -156,6 +160,58 @@ void QtGrabber::geometryChanged(const QRect &geo)
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 rc = 0;
@ -170,7 +226,11 @@ int QtGrabber::grabFrame(Image<ColorRgb> & image)
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);
#endif
if (originalPixmap.isNull())
{
rc = -1;

View File

@ -23,17 +23,26 @@ PythonInit::PythonInit()
// register modules
EffectModule::registerHyperionExtensionModule();
// Set Program name
Py_SetProgramName(L"Hyperion");
// 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");
if (QFile(py_file).exists() || QDir(py_patch).exists())
{
Py_NoSiteFlag++;
if (QFile(py_file).exists())
{
Py_SetPythonHome(Py_DecodeLocale(py_file.toLatin1().data(), nullptr));
Py_SetPath(Py_DecodeLocale(py_file.toLatin1().data(), nullptr));
}
else if (QDir(py_patch).exists())
{
Py_SetPythonHome(Py_DecodeLocale(py_file.toLatin1().data(), nullptr));
Py_SetPath(Py_DecodeLocale(py_patch.toLatin1().data(), nullptr));
}
}
// 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( FILES ${CMAKE_BINARY_DIR}/symlink_hyperiond DESTINATION "bin" RENAME 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()
# Deploy Qt DLLs into the binary folder.