diff --git a/CHANGELOG.md b/CHANGELOG.md index e4024253..9edd7c24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New language: Hebrew +**Event Services +Newly introduced Event Service configuration and consistent handling across all components +- Suspend/Resume & Screen Locking support for MaxOS +- Allow to enable/disable suspend & lock on operating system events (#1633, #1632) +- Scheduled events allowing to suspend,resume, etc. (#1088) +- Configurable CEC event handling + ##### LED-Devices **Philips Hue** diff --git a/assets/webconfig/i18n/en.json b/assets/webconfig/i18n/en.json index 24a42b56..4e8742a9 100644 --- a/assets/webconfig/i18n/en.json +++ b/assets/webconfig/i18n/en.json @@ -196,6 +196,8 @@ "conf_cec_events_intro": "Settings related to diffent CEC (Consumer Electronics Control) protocol events Hyperion can handle", "conf_os_events_heading_title": "Operating System Events", "conf_os_events_intro": "Settings related to diffent Operating System events Hyperion can handle", + "conf_sched_events_heading_title": "Scheduled Events", + "conf_sched_events_intro": "Settings related to scheduled, i.e. time based events, which Hyperion will handle", "conf_webconfig_label_intro": "Webconfiguration settings. Edit wisely.", "dashboard_active_instance": "Selected instance", "dashboard_alert_message_confedit": "Your Hyperion configuration has been modified. To apply it, restart Hyperion.", @@ -249,6 +251,28 @@ "edt_append_sdegree": "s/degree", "edt_conf_action_title": "Action", "edt_conf_action_expl": "Action to be applied", + "edt_conf_action_record_validation_error": "The same event can trigger only one action. Clean up Actions $1", + "edt_conf_audio_device_expl": "Selected audio input device", + "edt_conf_audio_device_title": "Audio Device", + "edt_conf_audio_effects_expl": "Select an effect on how the audio signal is transformed to", + "edt_conf_audio_effects_title": "Audio Effects", + "edt_conf_audio_effect_enum_vumeter": "VU-Meter", + "edt_conf_audio_effect_hotcolor_expl": "Hot Color", + "edt_conf_audio_effect_hotcolor_title": "Hot Color", + "edt_conf_audio_effect_multiplier_expl": "Audio Signal Value multiplier", + "edt_conf_audio_effect_multiplier_title": "Multiplier", + "edt_conf_audio_effect_safecolor_expl": "Safe Color", + "edt_conf_audio_effect_safecolor_title": "Safe Color", + "edt_conf_audio_effect_safevalue_expl": "Safe Threshold", + "edt_conf_audio_effect_safevalue_title": "Safe Threshold", + "edt_conf_audio_effect_set_defaults": "Reset to default values", + "edt_conf_audio_effect_tolerance_expl": "Tolerance used when auto calculating a signal multipler from 0-100", + "edt_conf_audio_effect_tolerance_title": "Tolerance", + "edt_conf_audio_effect_warncolor_expl": "Warning Color", + "edt_conf_audio_effect_warncolor_title": "Warning Color", + "edt_conf_audio_effect_warnvalue_expl": "Warning Threshold", + "edt_conf_audio_effect_warnvalue_title": "Warning Threshold", + "edt_conf_audio_heading_title": "Audio Capture", "edt_conf_bb_blurRemoveCnt_expl": "Number of pixels that get removed from the detected border to cut away blur.", "edt_conf_bb_blurRemoveCnt_title": "Blur pixel", "edt_conf_bb_borderFrameCnt_expl": "Number of frames before a consistent detected border is set.", @@ -267,7 +291,6 @@ "edt_conf_cec_actions_header_title": "Actions", "edt_conf_cec_actions_header_expl": "Define which action should take place on a recognised CEC event", "edt_conf_cec_actions_header_item_title": "Action", - "edt_conf_cec_action_record_validation_error": "One CEC Event can trigger only one action. Clean up Actions $1", "edt_conf_cec_button_release_delay_ms_title": "Button release time", "edt_conf_cec_button_release_delay_ms_expl": "Remote button press release time", "edt_conf_cec_button_repeat_rate_ms_title": "Button repeat rate", @@ -276,8 +299,6 @@ "edt_conf_cec_double_tap_timeout_ms_expl": "Remote button press delay before repeating", "edt_conf_cec_event_title": "CEC Event", "edt_conf_cec_event_expl": "CEC event that will trigger an action", - "edt_conf_cec_events_heading_title": "Events", - "edt_conf_cec_events_heading_expl": "Events explain", "edt_conf_color_accuracyLevel_expl": "Level how accurate dominat colors are evaluated. A higher level creates more accurate results, but also requries more processing power. Should to be combined with reduced pixel processing.", "edt_conf_color_accuracyLevel_title": "Accuracy level", "edt_conf_color_backlightColored_expl": "Add some color to your backlight.", @@ -497,6 +518,9 @@ "edt_conf_pbs_heading_title": "Protocol Buffers Server", "edt_conf_pbs_timeout_expl": "If no data are received for the given period, the component will be (soft) disabled.", "edt_conf_pbs_timeout_title": "Timeout", + "edt_conf_sched_actions_header_title": "Actions", + "edt_conf_sched_actions_header_expl": "Define which action should take place on a point in time. The action will be scheduled daily.", + "edt_conf_sched_actions_header_item_title": "Action", "edt_conf_smooth_continuousOutput_expl": "Update the LEDs even there is no changed picture.", "edt_conf_smooth_continuousOutput_title": "Continuous output", "edt_conf_smooth_decay_expl": "The speed of decay. 1 is linear, greater values are have stronger effect.", @@ -514,6 +538,8 @@ "edt_conf_smooth_updateDelay_title": "Output delay", "edt_conf_smooth_updateFrequency_expl": "The output speed to your LED controller.", "edt_conf_smooth_updateFrequency_title": "Update frequency", + "edt_conf_time_event_title": "Time", + "edt_conf_time_event_expl": "Point in time that will trigger an action", "edt_conf_v4l2_blueSignalThreshold_expl": "Darkens low blue values (recognized as black)", "edt_conf_v4l2_blueSignalThreshold_title": "Blue signal threshold", "edt_conf_v4l2_cecDetection_expl": "If enabled, USB capture will be temporarily disabled when CEC standby event received from HDMI bus.", @@ -573,27 +599,6 @@ "edt_conf_v4l2_hardware_set_defaults_tip": "Set device's default values for brightness, contrast, hue and saturation", "edt_conf_v4l2_noSignalCounterThreshold_title": "Signal Counter Threshold", "edt_conf_v4l2_noSignalCounterThreshold_expl": "Count of frames (check that with grabber's current FPS mode) after which the no signal is triggered", - "edt_conf_audio_device_expl": "Selected audio input device", - "edt_conf_audio_device_title": "Audio Device", - "edt_conf_audio_effects_expl": "Select an effect on how the audio signal is transformed to", - "edt_conf_audio_effects_title": "Audio Effects", - "edt_conf_audio_effect_enum_vumeter": "VU-Meter", - "edt_conf_audio_effect_hotcolor_expl": "Hot Color", - "edt_conf_audio_effect_hotcolor_title": "Hot Color", - "edt_conf_audio_effect_multiplier_expl": "Audio Signal Value multiplier", - "edt_conf_audio_effect_multiplier_title": "Multiplier", - "edt_conf_audio_effect_safecolor_expl": "Safe Color", - "edt_conf_audio_effect_safecolor_title": "Safe Color", - "edt_conf_audio_effect_safevalue_expl": "Safe Threshold", - "edt_conf_audio_effect_safevalue_title": "Safe Threshold", - "edt_conf_audio_effect_set_defaults": "Reset to default values", - "edt_conf_audio_effect_tolerance_expl": "Tolerance used when auto calculating a signal multipler from 0-100", - "edt_conf_audio_effect_tolerance_title": "Tolerance", - "edt_conf_audio_effect_warncolor_expl": "Warning Color", - "edt_conf_audio_effect_warncolor_title": "Warning Color", - "edt_conf_audio_effect_warnvalue_expl": "Warning Threshold", - "edt_conf_audio_effect_warnvalue_title": "Warning Threshold", - "edt_conf_audio_heading_title": "Audio Capture", "edt_conf_webc_crtPath_expl": "Path to the certification file (format should be PEM)", "edt_conf_webc_crtPath_title": "Certificate path", "edt_conf_webc_docroot_expl": "Local webinterface root path (just for webui developer)", diff --git a/assets/webconfig/js/content_events.js b/assets/webconfig/js/content_events.js index 6aab086e..7f7201e2 100644 --- a/assets/webconfig/js/content_events.js +++ b/assets/webconfig/js/content_events.js @@ -5,6 +5,7 @@ $(document).ready(function () { let conf_editor_osEvents = null; let conf_editor_cecEvents = null; + let conf_editor_schedEvents = null; if (window.showOptHelp) { //Operating System Events @@ -12,6 +13,12 @@ $(document).ready(function () { $('#conf_cont_os_events').append(createOptPanel('fa-laptop', $.i18n("conf_os_events_heading_title"), 'editor_container_os_events', 'btn_submit_os_events', 'panel-system')); $('#conf_cont_os_events').append(createHelpTable(window.schema.osEvents.properties, $.i18n("conf_os_events_heading_title"))); + //Scheduled Events + $('#conf_cont').append(createRow('conf_cont_sched_events')); + $('#conf_cont_sched_events').append(createOptPanel('fa-laptop', $.i18n("conf_sched_events_heading_title"), 'editor_container_sched_events', 'btn_submit_sched_events', 'panel-system')); + $('#conf_cont_sched_events').append(createHelpTable(window.schema.schedEvents.properties, $.i18n("conf_sched_events_heading_title"))); + + //CEC Events if (CEC_ENABLED) { $('#conf_cont').append(createRow('conf_cont_event_cec')); @@ -22,41 +29,42 @@ $(document).ready(function () { else { $('#conf_cont').addClass('row'); $('#conf_cont').append(createOptPanel('fa-laptop', $.i18n("conf_os_events_heading_title"), 'editor_container_os_events', 'btn_submit_os_events')); + $('#conf_cont').append(createOptPanel('fa-laptop', $.i18n("conf_sched_events_heading_title"), 'editor_container_sched_events', 'btn_submit_sched_events')); if (CEC_ENABLED) { $('#conf_cont').append(createOptPanel('fa-tv', $.i18n("conf_cec_events_heading_title"), 'editor_container_cec_events', 'btn_submit_cec_events')); } } - function findDuplicateCecEventsIndices(data) { - const cecEventIndices = {}; + function findDuplicateEventsIndices(data) { + const eventIndices = {}; data.forEach((item, index) => { - const cecEvent = item.cec_event; - if (!cecEventIndices[cecEvent]) { - cecEventIndices[cecEvent] = [index]; + const event = item.event; + if (!eventIndices[event]) { + eventIndices[event] = [index]; } else { - cecEventIndices[cecEvent].push(index); + eventIndices[event].push(index); } }); - return Object.values(cecEventIndices).filter(indices => indices.length > 1); + return Object.values(eventIndices).filter(indices => indices.length > 1); } JSONEditor.defaults.custom_validators.push(function (schema, value, path) { let errors = []; if (schema.type === 'array' && Array.isArray(value)) { - const duplicateCecEventIndices = findDuplicateCecEventsIndices(value); + const duplicateEventIndices = findDuplicateEventsIndices(value); - if (duplicateCecEventIndices.length > 0) { + if (duplicateEventIndices.length > 0) { let recs; - duplicateCecEventIndices.forEach(indices => { + duplicateEventIndices.forEach(indices => { const displayIndices = indices.map(index => index + 1); recs = displayIndices.join(', '); }); errors.push({ path: path, - message: $.i18n('edt_conf_cec_action_record_validation_error', recs) + message: $.i18n('edt_conf_action_record_validation_error', recs) }); } } @@ -76,6 +84,35 @@ $(document).ready(function () { requestWriteConfig(conf_editor_osEvents.getValue()); }); + //Scheduled Events + conf_editor_schedEvents = createJsonEditor('editor_container_sched_events', { + schedEvents: window.schema.schedEvents + }, true, true); + + conf_editor_schedEvents.on('change', function () { + + const schedEventsEnable = conf_editor_schedEvents.getEditor("root.schedEvents.enable").getValue(); + + if (schedEventsEnable) { + showInputOptionsForKey(conf_editor_schedEvents, "schedEvents", "enable", true); + $('#schedEventsHelpPanelId').show(); + } else { + showInputOptionsForKey(conf_editor_schedEvents, "schedEvents", "enable", false); + $('#schedEventsHelpPanelId').hide(); + } + + conf_editor_schedEvents.validate().length || window.readOnlyMode ? $('#btn_submit_sched_events').prop('disabled', true) : $('#btn_submit_sched_events').prop('disabled', false); + }); + + $('#btn_submit_sched_events').off().on('click', function () { + + const saveOptions = conf_editor_schedEvents.getValue(); + // Workaround, as otherwise values are not reflected correctly + saveOptions.schedEvents.enable = conf_editor_schedEvents.getEditor("root.schedEvents.enable").getValue(); + saveOptions.schedEvents.actions = conf_editor_schedEvents.getEditor("root.schedEvents.actions").getValue(); + requestWriteConfig(saveOptions); + }); + //CEC Events if (CEC_ENABLED) { conf_editor_cecEvents = createJsonEditor('editor_container_cec_events', { diff --git a/config/hyperion.config.json.default b/config/hyperion.config.json.default index 16beefb3..572667fb 100644 --- a/config/hyperion.config.json.default +++ b/config/hyperion.config.json.default @@ -21,16 +21,20 @@ "enableAttempts": 6, "enableAttemptsInterval": 15 }, + + "schedEvents": { + "enable": false + }, "cecEvents": { "actions": [ { "action": "Suspend", - "cec_event": "standby" + "event": "standby" }, { "action": "Resume", - "cec_event": "set stream path" + "event": "set stream path" } ], "enable": false diff --git a/include/db/SettingsTable.h b/include/db/SettingsTable.h index 0c7c790e..d1b31c21 100644 --- a/include/db/SettingsTable.h +++ b/include/db/SettingsTable.h @@ -113,7 +113,7 @@ public: // capture << "framegrabber" << "grabberV4L2" << "grabberAudio" //Events - << "osEvents" << "cecEvents" + << "osEvents" << "cecEvents" << "schedEvents" // other << "logger" << "general"; diff --git a/include/events/EventScheduler.h b/include/events/EventScheduler.h new file mode 100644 index 00000000..d350a55f --- /dev/null +++ b/include/events/EventScheduler.h @@ -0,0 +1,55 @@ +#ifndef EVENTSCHEDULER_H +#define EVENTSCHEDULER_H +#include +#include +#include +#include + +#include +#include + +class Logger; + +class EventScheduler : public QObject +{ + Q_OBJECT + +public: + EventScheduler(); + ~EventScheduler() override; + + virtual void handleSettingsUpdate(settings::type type, const QJsonDocument& config); + +signals: + void signalEvent(Event event); + +private slots: + + void handleEvent(int timerIndex); + +private: + + struct timeEvent + { + QTime time; + Event action; + }; + + bool enable(); + void disable(); + + int getMillisecondsToNextScheduledTime(const QTime& time); + + void clearTimers(); + + QJsonDocument _config; + + bool _isEnabled; + + QList _scheduledEvents; + QList _timers; + + Logger * _log {}; +}; + +#endif // EVENTSCHEDULER_H diff --git a/include/events/OsEventHandler.h b/include/events/OsEventHandler.h index 0dffea38..fef22af0 100644 --- a/include/events/OsEventHandler.h +++ b/include/events/OsEventHandler.h @@ -1,5 +1,5 @@ -#ifndef OsEventHandler_H -#define OsEventHandler_H +#ifndef OSEVENTHANDLER_H +#define OSEVENTHANDLER_H #include #include @@ -129,4 +129,4 @@ using OsEventHandler = OsEventHandlerMacOS; using OsEventHandler = OsEventHandlerBase; #endif -#endif // OsEventHandler_H +#endif // OSEVENTHANDLER_H diff --git a/include/utils/settings.h b/include/utils/settings.h index f1d36b5c..10cd879b 100644 --- a/include/utils/settings.h +++ b/include/utils/settings.h @@ -32,6 +32,7 @@ namespace settings { PROTOSERVER, OSEVENTS, CECEVENTS, + SCHEDEVENTS, INVALID }; @@ -68,6 +69,7 @@ namespace settings { case PROTOSERVER: return "protoServer"; case OSEVENTS: return "osEvents"; case CECEVENTS: return "cecEvents"; + case SCHEDEVENTS: return "schedEvents"; default: return "invalid"; } } @@ -103,6 +105,7 @@ namespace settings { else if (type == "protoServer") return PROTOSERVER; else if (type == "osEvents") return OSEVENTS; else if (type == "cecEvents") return CECEVENTS; + else if (type == "schedEvents") return SCHEDEVENTS; else return INVALID; } } diff --git a/libsrc/cec/CECHandler.cpp b/libsrc/cec/CECHandler.cpp index 3b6a687c..ddceb9ec 100644 --- a/libsrc/cec/CECHandler.cpp +++ b/libsrc/cec/CECHandler.cpp @@ -27,7 +27,7 @@ CECHandler::CECHandler(const QJsonDocument& config, QObject * parent) { qRegisterMetaType("Event"); - _logger = Logger::getInstance("CEC"); + _logger = Logger::getInstance("EVENTS-CEC"); _cecCallbacks = getCallbacks(); _cecConfig = getConfig(); @@ -43,8 +43,6 @@ void CECHandler::handleSettingsUpdate(settings::type type, const QJsonDocument& { if(type == settings::CECEVENTS) { - Debug( _logger, "config: [%s]", QString(QJsonDocument(config).toJson(QJsonDocument::Compact)).toUtf8().constData()); - if (_isInitialised) { const QJsonObject& obj = config.object(); @@ -72,7 +70,7 @@ void CECHandler::handleSettingsUpdate(settings::type type, const QJsonDocument& { for (const QJsonValue &item : actionItems) { - QString cecEvent = item.toObject().value("cec_event").toString(); + QString cecEvent = item.toObject().value("event").toString(); QString action = item.toObject().value("action").toString(); _cecEventActionMap.insert(cecEvent, stringToEvent(action)); Debug(_logger, "CEC-Event : \"%s\" linked to action \"%s\"", QSTRING_CSTR(cecEvent), QSTRING_CSTR(action)); diff --git a/libsrc/events/CMakeLists.txt b/libsrc/events/CMakeLists.txt index ec3a023d..40b43a54 100644 --- a/libsrc/events/CMakeLists.txt +++ b/libsrc/events/CMakeLists.txt @@ -2,8 +2,10 @@ add_library(events ${CMAKE_SOURCE_DIR}/include/events/EventEnum.h ${CMAKE_SOURCE_DIR}/include/events/EventHandler.h ${CMAKE_SOURCE_DIR}/include/events/OsEventHandler.h + ${CMAKE_SOURCE_DIR}/include/events/EventScheduler.h ${CMAKE_SOURCE_DIR}/libsrc/events/EventHandler.cpp ${CMAKE_SOURCE_DIR}/libsrc/events/OsEventHandler.cpp + ${CMAKE_SOURCE_DIR}/libsrc/events/EventScheduler.cpp ) if(UNIX AND NOT APPLE) diff --git a/libsrc/events/EventScheduler.cpp b/libsrc/events/EventScheduler.cpp new file mode 100644 index 00000000..82d5866c --- /dev/null +++ b/libsrc/events/EventScheduler.cpp @@ -0,0 +1,151 @@ +#include "events/EventScheduler.h" + +#include +#include +#include +#include +#include + +#include +#include + +EventScheduler::EventScheduler() + : _isEnabled(false) +{ + qRegisterMetaType("Event"); + _log = Logger::getInstance("EVENTS-SCHED"); + + QObject::connect(this, &EventScheduler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent); +} + +EventScheduler::~EventScheduler() +{ + QObject::disconnect(this, &EventScheduler::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent); +} + +void EventScheduler::handleSettingsUpdate(settings::type type, const QJsonDocument& config) +{ + if(type == settings::SCHEDEVENTS) + { + const QJsonObject& obj = config.object(); + + _isEnabled = obj["enable"].toBool(false); + Debug(_log, "Event scheduler is %s", _isEnabled? "enabled" : "disabled"); + + if (_isEnabled) + { + _scheduledEvents.clear(); + + const QJsonArray actionItems = obj["actions"].toArray(); + if (!actionItems.isEmpty()) + { + timeEvent timeEvent; + for (const QJsonValue &item : actionItems) + { + QString action = item.toObject().value("action").toString(); + timeEvent.action = stringToEvent(action); + + QString event = item.toObject().value("event").toString(); + timeEvent.time = QTime::fromString(event,"hh:mm"); + if (timeEvent.time.isValid()) + { + _scheduledEvents.append(timeEvent); + Debug(_log, "Time-Event : \"%s\" linked to action \"%s\"", QSTRING_CSTR(event), QSTRING_CSTR(action)); + } + else + { + Error(_log, "Error in configured time : \"%s\" linked to action \"%s\"", QSTRING_CSTR(event), QSTRING_CSTR(action)); + } + } + } + + if (!_scheduledEvents.isEmpty()) + { + enable(); + } + else + { + Warning(_log, "No scheduled events to listen to are configured currently."); + } + } + else + { + disable(); + } + + } +} + +bool EventScheduler::enable() +{ + bool enabled {false}; + + clearTimers(); + for (int i = 0; i < _scheduledEvents.size(); ++i) + { + QTimer* timer = new QTimer(this); + timer->setTimerType(Qt::PreciseTimer); + _timers.append(timer); + + // Calculate the milliseconds until the next occurrence of the scheduled time + int milliseconds = getMillisecondsToNextScheduledTime(_scheduledEvents.at(i).time); + timer->start(milliseconds); + + QObject::connect(timer, &QTimer::timeout, this, [this, i]() { handleEvent(i); }); + } + + enabled = true; + Info(_log, "Event scheduler started"); + + return enabled; +} + +void EventScheduler::disable() +{ + Info(_log, "Disabling event scheduler"); + clearTimers(); +} + +void EventScheduler::clearTimers() +{ + for (QTimer *timer : std::as_const(_timers)) { + QObject::disconnect(timer, &QTimer::timeout, nullptr, nullptr); + timer->stop(); // Stop the timer before deleting + delete timer; + } + _timers.clear(); +} + +void EventScheduler::handleEvent(int timerIndex) +{ + QTime time = _scheduledEvents.at(timerIndex).time; + Event action = _scheduledEvents.at(timerIndex).action; + Debug(_log, "Event : \"%s\" triggers action \"%s\"", QSTRING_CSTR(time.toString()), eventToString(action) ); + if ( action != Event::Unknown ) + { + emit signalEvent(action); + } + + // Retrigger the timer for the next occurrence + QTimer* timer = _timers.at(timerIndex); + int milliseconds = getMillisecondsToNextScheduledTime(time); + timer->start(milliseconds); +} + +int EventScheduler::getMillisecondsToNextScheduledTime(const QTime& scheduledTime) +{ + QDateTime currentDateTime = QDateTime::currentDateTime(); + QTime currentTime = currentDateTime.time(); + + QDateTime nextOccurrence = currentDateTime; + nextOccurrence.setTime(scheduledTime); + + // If the scheduled time has already passed for today, schedule it for tomorrow + if (currentTime > scheduledTime) + { + nextOccurrence = nextOccurrence.addDays(1); + } + + int milliseconds = currentDateTime.msecsTo(nextOccurrence); + return (milliseconds > 0) ? milliseconds : 0; +} diff --git a/libsrc/events/OsEventHandler.cpp b/libsrc/events/OsEventHandler.cpp index 59370dfe..88627cd9 100644 --- a/libsrc/events/OsEventHandler.cpp +++ b/libsrc/events/OsEventHandler.cpp @@ -28,7 +28,7 @@ OsEventHandlerBase::OsEventHandlerBase() , _isLockRegistered(false) { qRegisterMetaType("Event"); - _log = Logger::getInstance("EVENTS"); + _log = Logger::getInstance("EVENTS-OS"); QObject::connect(this, &OsEventHandlerBase::signalEvent, EventHandler::getInstance(), &EventHandler::handleEvent); } diff --git a/libsrc/hyperion/SettingsManager.cpp b/libsrc/hyperion/SettingsManager.cpp index 1fcd8c6c..2c1201b1 100644 --- a/libsrc/hyperion/SettingsManager.cpp +++ b/libsrc/hyperion/SettingsManager.cpp @@ -914,12 +914,12 @@ bool SettingsManager::handleConfigUpgrade(QJsonObject& config) QJsonObject action1 { {"action", "Suspend"}, - {"cec_event", "standby"} + {"event", "standby"} }; QJsonObject action2 { {"action", "Resume"}, - {"cec_event", "set stream path"} + {"event", "set stream path"} }; QJsonArray actions { action1, action2 }; diff --git a/libsrc/hyperion/hyperion.schema.json b/libsrc/hyperion/hyperion.schema.json index e874190a..ae22cfe2 100644 --- a/libsrc/hyperion/hyperion.schema.json +++ b/libsrc/hyperion/hyperion.schema.json @@ -98,7 +98,11 @@ "cecEvents": { "$ref": "schema-cecEvents.json" - } + }, + "schedEvents": + { + "$ref": "schema-schedEvents.json" + } }, "additionalProperties" : false } diff --git a/libsrc/hyperion/resource.qrc b/libsrc/hyperion/resource.qrc index 9627856a..b1c52ea1 100644 --- a/libsrc/hyperion/resource.qrc +++ b/libsrc/hyperion/resource.qrc @@ -23,6 +23,8 @@ schema/schema-leds.json schema/schema-instCapture.json schema/schema-network.json + schema/schema-eventActions.json + schema/schema-schedEvents.json schema/schema-osEvents.json schema/schema-cecEvents.json diff --git a/libsrc/hyperion/schema/schema-cecEvents.json b/libsrc/hyperion/schema/schema-cecEvents.json index 3deaa8da..1ccc1aab 100644 --- a/libsrc/hyperion/schema/schema-cecEvents.json +++ b/libsrc/hyperion/schema/schema-cecEvents.json @@ -1,7 +1,6 @@ { "type": "object", "required": true, - "title": "edt_conf_cec_events_heading_title", "properties": { "enable": { "type": "boolean", @@ -53,7 +52,6 @@ "type": "array", "title": "edt_conf_cec_actions_header_title", "minItems": 0, - "uniqueItems": true, "required": false, "propertyOrder": 5, "items": { @@ -61,7 +59,7 @@ "required": true, "title": "edt_conf_cec_actions_header_item_title", "properties": { - "cec_event": { + "event": { "type": "string", "title": "edt_conf_cec_event_title", "enum": [ @@ -85,29 +83,7 @@ "propertyOrder": 1 }, "action": { - "type": "string", - "title": "edt_conf_action_title", - "enum": [ - "Suspend", - "Resume", - "ToggleSuspend", - "Idle", - "ResumeIdle", - "ToggleIdle", - "Restart" - ], - "options": { - "enum_titles": [ - "edt_conf_enum_action_suspend", - "edt_conf_enum_action_resume", - "edt_conf_enum_action_toggleSuspend", - "edt_conf_enum_action_idle", - "edt_conf_enum_action_resumeIdle", - "edt_conf_enum_action_toggleIdle", - "edt_conf_enum_action_restart" - ] - }, - "propertyOrder": 2 + "$ref": "schema-eventActions.json" } }, "additionalProperties": false diff --git a/libsrc/hyperion/schema/schema-eventActions.json b/libsrc/hyperion/schema/schema-eventActions.json new file mode 100644 index 00000000..db3af57e --- /dev/null +++ b/libsrc/hyperion/schema/schema-eventActions.json @@ -0,0 +1,25 @@ +{ + "type": "string", + "title": "edt_conf_action_title", + "enum": [ + "Suspend", + "Resume", + "ToggleSuspend", + "Idle", + "ResumeIdle", + "ToggleIdle", + "Restart" + ], + "options": { + "enum_titles": [ + "edt_conf_enum_action_suspend", + "edt_conf_enum_action_resume", + "edt_conf_enum_action_toggleSuspend", + "edt_conf_enum_action_idle", + "edt_conf_enum_action_resumeIdle", + "edt_conf_enum_action_toggleIdle", + "edt_conf_enum_action_restart" + ] + } +} + diff --git a/libsrc/hyperion/schema/schema-schedEvents.json b/libsrc/hyperion/schema/schema-schedEvents.json new file mode 100644 index 00000000..52fb8b96 --- /dev/null +++ b/libsrc/hyperion/schema/schema-schedEvents.json @@ -0,0 +1,40 @@ +{ + "type": "object", + "required": true, + "properties": { + "enable": { + "type": "boolean", + "required": true, + "title": "edt_conf_general_enable_title", + "default": false, + "propertyOrder": 1 + }, + "actions": { + "type": "array", + "title": "edt_conf_sched_actions_header_title", + "minItems": 0, + "required": false, + "propertyOrder": 2, + "items": { + "type": "object", + "required": true, + "title": "edt_conf_sched_actions_header_item_title", + "properties": { + "event": { + "type": "string", + "format": "time", + "default": "23:00", + "title": "edt_conf_time_event_title", + "propertyOrder": 1 + }, + "action": { + "$ref": "schema-eventActions.json" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false +} + diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index fa0a7849..7ff6fe24 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -177,8 +177,7 @@ HyperionDaemon::HyperionDaemon(const QString& rootPath, QObject* parent, bool lo startNetworkServices(); // init events services - handleSettingsUpdate(settings::OSEVENTS, getSetting(settings::OSEVENTS)); - handleSettingsUpdate(settings::CECEVENTS, getSetting(settings::CECEVENTS)); + startEventServices(); } HyperionDaemon::~HyperionDaemon() @@ -336,6 +335,7 @@ void HyperionDaemon::freeObjects() _sslWebserver = nullptr; } + delete _eventScheduler; delete _osEventHandler; // stop Hyperions (non blocking) @@ -424,6 +424,33 @@ void HyperionDaemon::startNetworkServices() ssdpThread->start(); } +void HyperionDaemon::startEventServices() +{ + if (_eventHandler == nullptr) + { + _eventHandler = EventHandler::getInstance(); + Debug(_log, "Hyperion event handler created"); + } + + if (_eventScheduler == nullptr) + { + _eventScheduler = new EventScheduler(); + _eventScheduler->handleSettingsUpdate(settings::SCHEDEVENTS, getSetting(settings::SCHEDEVENTS)); + connect(this, &HyperionDaemon::settingsChanged, _eventScheduler, &EventScheduler::handleSettingsUpdate); + Debug(_log, "Hyperion event scheduler created"); + } + + if (_osEventHandler == nullptr) + { + _osEventHandler = new OsEventHandler(); + _osEventHandler->handleSettingsUpdate(settings::OSEVENTS, getSetting(settings::OSEVENTS)); + connect(this, &HyperionDaemon::settingsChanged, _osEventHandler, &OsEventHandler::handleSettingsUpdate); + Debug(_log, "Operating System event handler created"); + } + + startCecHandler(); +} + void HyperionDaemon::handleSettingsUpdate(settings::type settingsType, const QJsonDocument& config) { if (settingsType == settings::LOGGER) @@ -721,30 +748,6 @@ void HyperionDaemon::handleSettingsUpdate(settings::type settingsType, const QJs Debug(_log, "Audio capture not supported on this platform"); #endif } - else if (settingsType == settings::OSEVENTS) - { - if (_eventHandler == nullptr) - { - _eventHandler = EventHandler::getInstance(); - Debug(_log, "Hyperion event handler created"); - } - - // Create suspend handler - if (_osEventHandler == nullptr) - { - _osEventHandler = new OsEventHandler(); - _osEventHandler->handleSettingsUpdate(settings::OSEVENTS, getSetting(settings::OSEVENTS)); - - connect(this, &HyperionDaemon::settingsChanged, _osEventHandler, &OsEventHandler::handleSettingsUpdate); - - Debug(_log, "Operating System event handler created"); - } - } - else if (settingsType == settings::CECEVENTS) - { - Debug(_log, "startCecHandler"); - startCecHandler(); - } } void HyperionDaemon::createGrabberDispmanx(const QJsonObject& /*grabberConfig*/) diff --git a/src/hyperiond/hyperiond.h b/src/hyperiond/hyperiond.h index b82a0b6c..7f22b67f 100644 --- a/src/hyperiond/hyperiond.h +++ b/src/hyperiond/hyperiond.h @@ -77,6 +77,7 @@ #include #include +#include class HyperionIManager; class SysTray; @@ -122,6 +123,7 @@ public: QJsonDocument getSetting(settings::type type) const; void startNetworkServices(); + void startEventServices(); static HyperionDaemon* getInstance() { return daemon; } static HyperionDaemon* daemon; @@ -216,6 +218,7 @@ private: #endif EventHandler* _eventHandler; OsEventHandler* _osEventHandler; + EventScheduler* _eventScheduler; #if defined(ENABLE_FLATBUF_SERVER) FlatBufferServer* _flatBufferServer;