~aleteoryx/muditaos

2ae306bd40be7dbf5ecc3de00e84d4d0e84f59d0 — Paweł Joński 4 years ago 7424b4b
[BH-662] Handle alarm turn off and snooze

Handle alarm turn off and snooze
Music stop not included
36 files changed, 482 insertions(+), 83 deletions(-)

M image/user/db/events_001.sql
M image/user/db/settings_bell_002.sql
M module-db/Interface/EventRecord.hpp
M module-services/service-appmgr/model/ApplicationManagerCommon.cpp
M module-services/service-time/AlarmMessageHandler.cpp
M module-services/service-time/AlarmMessageHandler.hpp
M module-services/service-time/AlarmOperations.cpp
M module-services/service-time/AlarmOperations.hpp
M module-services/service-time/AlarmServiceAPI.cpp
M module-services/service-time/ServiceTime.cpp
A module-services/service-time/SnoozedAlarmEventRecord.hpp
M module-services/service-time/include/service-time/AlarmHandler.hpp
M module-services/service-time/include/service-time/AlarmMessage.hpp
M module-services/service-time/include/service-time/AlarmServiceAPI.hpp
M module-services/service-time/tests/MockAlarmHandler.hpp
M module-services/service-time/tests/tests-AlarmOperations.cpp
M products/BellHybrid/alarms/BellAlarmHandler.cpp
M products/BellHybrid/alarms/include/AbstractAlarmAction.hpp
M products/BellHybrid/alarms/include/BellAlarmHandler.hpp
M products/BellHybrid/alarms/src/actions/FrontlightAction.cpp
M products/BellHybrid/alarms/src/actions/FrontlightAction.hpp
M products/BellHybrid/alarms/src/actions/NotifyGUIAction.cpp
M products/BellHybrid/alarms/src/actions/NotifyGUIAction.hpp
M products/BellHybrid/alarms/src/actions/PlayToneAction.cpp
M products/BellHybrid/alarms/src/actions/PlayToneAction.hpp
M products/BellHybrid/apps/application-bell-main/include/application-bell-main/ApplicationBellMain.hpp
M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp
M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.hpp
M products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp
M products/BellHybrid/apps/application-bell-main/presenters/StateController.hpp
M products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.cpp
M products/BellHybrid/apps/common/CMakeLists.txt
M products/BellHybrid/apps/common/include/common/models/AbstractAlarmModel.hpp
M products/BellHybrid/apps/common/include/common/models/AlarmModel.hpp
M products/BellHybrid/apps/common/src/AlarmModel.cpp
M products/BellHybrid/services/db/include/db/SystemSettings.hpp
M image/user/db/events_001.sql => image/user/db/events_001.sql +2 -1
@@ 17,5 17,6 @@ CREATE TABLE IF NOT EXISTS alarm_events(
                  music_tone TEXT,
                  enabled BOOLEAN,
                  snooze_duration INTEGER,
                  FOREIGN KEY (event_id) REFERENCES events (_id) ON DELETE CASCADE
                  FOREIGN KEY (event_id) REFERENCES events (_id)
);


M image/user/db/settings_bell_002.sql => image/user/db/settings_bell_002.sql +6 -2
@@ 40,6 40,10 @@ INSERT OR IGNORE INTO settings_tab (path, value) VALUES
    ('\ServiceTime\\gs_automatic_date_and_time_is_on', '1'),
    ('temperature_unit', 'C'),
    ('ringing_duration', '10000'),
    ('ringing_tone', 'Nick_Lewis_-_Kristies_Elephant.mp3');

    ('ringing_tone', 'Nick_Lewis_-_Kristies_Elephant.mp3'),
    ('snooze_active', '1'),
    ('snooze_length','10'),
    ('snooze_interval','1'),
    ('snooze_tone','Meditative surprises'),
    ('snooze_volume','10');


M module-db/Interface/EventRecord.hpp => module-db/Interface/EventRecord.hpp +2 -0
@@ 59,6 59,8 @@ struct SingleEventRecord : public Record, public EventInfo
{
    std::shared_ptr<EventRecord> parent;

    virtual ~SingleEventRecord() = default;

    SingleEventRecord() = default;
    SingleEventRecord(std::shared_ptr<EventRecord> parent, TimePoint startDate, TimePoint endDate)
        : EventInfo{parent->name, startDate, endDate, parent->duration, parent->isAllDay}, parent{parent} {};

M module-services/service-appmgr/model/ApplicationManagerCommon.cpp => module-services/service-appmgr/model/ApplicationManagerCommon.cpp +2 -0
@@ 13,6 13,7 @@
#include <SystemManager/messages/SystemManagerMessage.hpp>
#include <application-onboarding/data/OnBoardingMessages.hpp>
#include <apps-common/messages/AppMessage.hpp>
#include <apps-common/actions/AlarmTriggeredAction.hpp>
#include <i18n/i18n.hpp>
#include <log/log.hpp>
#include <service-audio/AudioMessage.hpp>


@@ 270,6 271,7 @@ namespace app::manager
        auto convertibleToActionHandler = [this](sys::Message *request) { return handleMessageAsAction(request); };
        connect(typeid(sys::CriticalBatteryLevelNotification), convertibleToActionHandler);
        connect(typeid(VolumeChanged), convertibleToActionHandler);
        connect(typeid(app::actions::AlarmTriggeredAction), convertibleToActionHandler);
    }

    sys::ReturnCodes ApplicationManagerCommon::SwitchPowerModeHandler(const sys::ServicePowerMode mode)

M module-services/service-time/AlarmMessageHandler.cpp => module-services/service-time/AlarmMessageHandler.cpp +18 -0
@@ 77,6 77,24 @@ namespace alarms
            });
    }

    auto AlarmMessageHandler::handleTurnOffRingingAlarm(RingingAlarmTurnOffRequestMessage *request)
        -> std::shared_ptr<RingingAlarmTurnOffResponseMessage>
    {
        return handleWithCallback<RingingAlarmTurnOffRequestMessage, RingingAlarmTurnOffResponseMessage, bool>(
            request, [&](RingingAlarmTurnOffRequestMessage *request, IAlarmOperations::OnTurnOffRingingAlarm callback) {
                alarmOperations->turnOffRingingAlarm(request->id, callback);
            });
    }

    auto AlarmMessageHandler::handleSnoozeRingingAlarm(RingingAlarmSnoozeRequestMessage *request)
        -> std::shared_ptr<RingingAlarmSnoozeResponseMessage>
    {
        return handleWithCallback<RingingAlarmSnoozeRequestMessage, RingingAlarmSnoozeResponseMessage, bool>(
            request, [&](RingingAlarmSnoozeRequestMessage *request, IAlarmOperations::OnSnoozeRingingAlarm callback) {
                alarmOperations->snoozeRingingAlarm(request->id, request->nextAlarmTime, callback);
            });
    }

    auto AlarmMessageHandler::handleMinuteUpdated() -> void
    {
        alarmOperations->minuteUpdated(TimePointNow());

M module-services/service-time/AlarmMessageHandler.hpp => module-services/service-time/AlarmMessageHandler.hpp +4 -0
@@ 29,6 29,10 @@ namespace alarms
            -> std::shared_ptr<AlarmsGetInRangeResponseMessage>;
        auto handleGetNextSingleEvents(AlarmGetNextSingleEventsRequestMessage *request)
            -> std::shared_ptr<AlarmGetNextSingleEventsResponseMessage>;
        auto handleTurnOffRingingAlarm(RingingAlarmTurnOffRequestMessage *request)
            -> std::shared_ptr<RingingAlarmTurnOffResponseMessage>;
        auto handleSnoozeRingingAlarm(RingingAlarmSnoozeRequestMessage *request)
            -> std::shared_ptr<RingingAlarmSnoozeResponseMessage>;
        auto handleMinuteUpdated() -> void;

        auto addAlarmExecutionHandler(const alarms::AlarmType type, const std::shared_ptr<alarms::AlarmHandler> handler)

M module-services/service-time/AlarmOperations.cpp => module-services/service-time/AlarmOperations.cpp +110 -14
@@ 6,6 6,8 @@
#include <module-db/Interface/AlarmEventRecord.hpp>
#include <module-db/Interface/EventRecord.hpp>

#include <vector>

namespace alarms
{
    AlarmOperations::AlarmOperations(std::unique_ptr<AbstractAlarmEventsRepository> &&alarmEventsRepo,


@@ 15,7 17,11 @@ namespace alarms
    void AlarmOperations::updateEventsCache(TimePoint now)
    {
        OnGetNextSingleProcessed callback = [&](std::vector<SingleEventRecord> singleEvents) {
            nextSingleEvents = std::move(singleEvents);
            nextSingleEvents.clear();
            nextSingleEvents.reserve(singleEvents.size());
            for (auto &ev : singleEvents) {
                nextSingleEvents.emplace_back(std::make_unique<SingleEventRecord>(ev));
            }
        };
        getNextSingleEvents(now, callback);
    }


@@ 38,10 44,11 @@ namespace alarms
    void AlarmOperations::updateAlarm(AlarmEventRecord record, OnUpdateAlarmProcessed callback)
    {
        OnUpdateAlarmEventCallback repoCallback = [&, callback, record](bool success) mutable {
            auto found =
                std::find_if(nextSingleEvents.begin(),
                             nextSingleEvents.end(),
                             [recordId = record.ID](const SingleEventRecord &e) { return e.parent->ID == recordId; });
            auto found = std::find_if(nextSingleEvents.begin(),
                                      nextSingleEvents.end(),
                                      [recordId = record.ID](const std::unique_ptr<SingleEventRecord> &e) {
                                          return e->parent->ID == recordId;
                                      });

            if (found != std::end(nextSingleEvents)) {
                updateEventsCache(getCurrentTime());


@@ 57,9 64,10 @@ namespace alarms
    void AlarmOperations::removeAlarm(const std::uint32_t alarmId, OnRemoveAlarmProcessed callback)
    {
        OnRemoveAlarmEventCallback repoCallback = [&, callback, alarmId](bool success) {
            auto found = std::find_if(nextSingleEvents.begin(),
                                      nextSingleEvents.end(),
                                      [alarmId](const SingleEventRecord &e) { return e.parent->ID == alarmId; });
            auto found = std::find_if(
                nextSingleEvents.begin(),
                nextSingleEvents.end(),
                [alarmId](const std::unique_ptr<SingleEventRecord> &e) { return e->parent->ID == alarmId; });

            if (found != std::end(nextSingleEvents) && nextSingleEvents.size() == 1) {
                updateEventsCache(getCurrentTime());


@@ 93,6 101,57 @@ namespace alarms
        alarmEventsRepo->getNext(start, getNextSingleEventsOffset, getNextSingleEventsLimit, repoGetNextCallback);
    }

    void AlarmOperations::turnOffRingingAlarm(const std::uint32_t id, OnTurnOffRingingAlarm callback)
    {
        auto found =
            std::find_if(ongoingSingleEvents.begin(),
                         ongoingSingleEvents.end(),
                         [id](const std::unique_ptr<SingleEventRecord> &event) { return id == event->parent->ID; });
        if (found == ongoingSingleEvents.end()) {
            LOG_ERROR("Trying to turn off nonexisting event");
            callback(false);
            return;
        }
        switchAlarmExecution(*(*found), false);
        ongoingSingleEvents.erase(found);
        callback(true);
    }

    void AlarmOperations::snoozeRingingAlarm(const std::uint32_t id,
                                             const TimePoint nextAlarmTime,
                                             OnSnoozeRingingAlarm callback)
    {
        auto found =
            std::find_if(ongoingSingleEvents.begin(),
                         ongoingSingleEvents.end(),
                         [id](const std::unique_ptr<SingleEventRecord> &event) { return id == event->parent->ID; });
        if (found == ongoingSingleEvents.end()) {
            LOG_ERROR("Trying to snooze nonexisting event");
            callback(false);
            return;
        }

        auto newSnoozed = std::make_unique<SnoozedAlarmEventRecord>((*found).get());

        if (typeid(*(*found)) == typeid(SnoozedAlarmEventRecord)) {
            auto oldSnoozedPtr      = dynamic_cast<SnoozedAlarmEventRecord *>((*found).get());
            newSnoozed->snoozeCount = oldSnoozedPtr->snoozeCount + 1;
        }
        else if (typeid(*(*found)) != typeid(SingleEventRecord)) {
            LOG_ERROR("Unkown alarm type detected");
            callback(false);
            return;
        }

        newSnoozed->startDate = nextAlarmTime;
        snoozedSingleEvents.push_back(std::move(newSnoozed));

        switchAlarmExecution(*(*found), false);
        ongoingSingleEvents.erase(found);

        callback(true);
    }

    void AlarmOperations::onRepoGetNextResponse(OnGetNextSingleProcessed handledCallback,
                                                std::shared_ptr<std::vector<AlarmEventRecord>> nextEvents,
                                                TimePoint start,


@@ 148,7 207,7 @@ namespace alarms
        if (nearestNewSingleEvent.EventInfo::isValid() && nearestNewSingleEvent.startDate > getCurrentTime()) {
            auto alarmEvent = std::dynamic_pointer_cast<AlarmEventRecord>(nearestNewSingleEvent.parent);
            if (record.enabled &&
                (nextSingleEvents.empty() || nearestNewSingleEvent.startDate <= nextSingleEvents.front().startDate)) {
                (nextSingleEvents.empty() || nearestNewSingleEvent.startDate <= nextSingleEvents.front()->startDate)) {
                updateEventsCache(getCurrentTime());
            }
        }


@@ 156,11 215,19 @@ namespace alarms

    auto AlarmOperations::minuteUpdated(TimePoint now) -> void
    {
        if (nextSingleEvents.empty() || nextSingleEvents.front().startDate > now) {
            return;
        auto isHandlingInProgress = !ongoingSingleEvents.empty();

        if (!nextSingleEvents.empty()) {
            processNextEventsQueue(now);
        }

        executeAlarm(nextSingleEvents.front());
        if (!snoozedSingleEvents.empty()) {
            processSnoozedEventsQueue(now);
        }

        if (!isHandlingInProgress && !ongoingSingleEvents.empty()) {
            switchAlarmExecution(*(ongoingSingleEvents.front()), true);
        }
    }

    void AlarmOperations::addAlarmExecutionHandler(const alarms::AlarmType type,


@@ 169,7 236,7 @@ namespace alarms
        alarmHandlerFactory.addHandler(type, handler);
    }

    void AlarmOperations::executeAlarm(const SingleEventRecord &singleAlarmEvent)
    void AlarmOperations::switchAlarmExecution(const SingleEventRecord &singleAlarmEvent, bool newStateOn)
    {
        alarms::AlarmType alarmType = alarms::AlarmType::None;
        if (typeid(*(singleAlarmEvent.parent)) == typeid(AlarmEventRecord)) {


@@ 180,7 247,12 @@ namespace alarms
        if (alarmEventPtr) {
            auto handler = alarmHandlerFactory.getHandler(alarmType);
            if (handler) {
                handler->handle(*alarmEventPtr);
                if (newStateOn) {
                    handler->handle(*alarmEventPtr);
                }
                else {
                    handler->handleOff(*alarmEventPtr);
                }
            }
        }
        else {


@@ 188,6 260,29 @@ namespace alarms
        }
    }

    auto AlarmOperations::processNextEventsQueue(const TimePoint now) -> void
    {
        if (nextSingleEvents.front()->startDate <= now) {
            ongoingSingleEvents.insert(ongoingSingleEvents.end(),
                                       std::make_move_iterator(nextSingleEvents.begin()),
                                       std::make_move_iterator(nextSingleEvents.end()));
            nextSingleEvents.clear();
        }
    }

    auto AlarmOperations::processSnoozedEventsQueue(const TimePoint now) -> void
    {
        for (auto it = snoozedSingleEvents.begin(); it != snoozedSingleEvents.end();) {
            if ((*it)->startDate <= now) {
                ongoingSingleEvents.push_back(std::move(*it));
                it = snoozedSingleEvents.erase(it);
            }
            else {
                ++it;
            }
        }
    }

    TimePoint AlarmOperations::getCurrentTime()
    {
        if (!getCurrentTimeCallback) {


@@ 195,4 290,5 @@ namespace alarms
        }
        return getCurrentTimeCallback();
    }

} // namespace alarms

M module-services/service-time/AlarmOperations.hpp => module-services/service-time/AlarmOperations.hpp +18 -2
@@ 4,6 4,7 @@
#pragma once

#include "AlarmRepository.hpp"
#include "SnoozedAlarmEventRecord.hpp"

#include <service-time/AlarmHandlerFactory.hpp>



@@ 21,6 22,8 @@ namespace alarms
        using OnUpdateAlarmProcessed      = std::function<void(bool)>;
        using OnRemoveAlarmProcessed      = std::function<void(bool)>;
        using OnGetNextSingleProcessed    = std::function<void(std::vector<SingleEventRecord>)>;
        using OnSnoozeRingingAlarm        = std::function<void(bool)>;
        using OnTurnOffRingingAlarm       = std::function<void(bool)>;

        virtual ~IAlarmOperations() noexcept = default;



@@ 38,6 41,10 @@ namespace alarms
                                      std::uint32_t limit,
                                      OnGetAlarmsInRangeProcessed callback)                    = 0;
        virtual void getNextSingleEvents(TimePoint start, OnGetNextSingleProcessed callback)   = 0;
        virtual void turnOffRingingAlarm(const std::uint32_t id, OnTurnOffRingingAlarm callback)   = 0;
        virtual void snoozeRingingAlarm(const std::uint32_t id,
                                        const TimePoint nextAlarmTime,
                                        OnSnoozeRingingAlarm callback)                             = 0;
        virtual void minuteUpdated(TimePoint now)                                                  = 0;
        virtual void addAlarmExecutionHandler(const alarms::AlarmType type,
                                              const std::shared_ptr<alarms::AlarmHandler> handler) = 0;


@@ 61,6 68,10 @@ namespace alarms
                              std::uint32_t limit,
                              OnGetAlarmsInRangeProcessed callback) override;
        void getNextSingleEvents(TimePoint start, OnGetNextSingleProcessed callback) override;
        void turnOffRingingAlarm(const std::uint32_t id, OnTurnOffRingingAlarm callback) override;
        void snoozeRingingAlarm(const std::uint32_t id,
                                const TimePoint nextAlarmTime,
                                OnSnoozeRingingAlarm callback) override;
        void minuteUpdated(TimePoint now) override;
        void addAlarmExecutionHandler(const alarms::AlarmType type,
                                      const std::shared_ptr<alarms::AlarmHandler> handler) override;


@@ 70,7 81,9 @@ namespace alarms
        AlarmHandlerFactory alarmHandlerFactory;

        // Events we are waiting for (on one timepoint)
        std::vector<SingleEventRecord> nextSingleEvents;
        std::vector<std::unique_ptr<SingleEventRecord>> nextSingleEvents;
        std::vector<std::unique_ptr<SingleEventRecord>> ongoingSingleEvents;
        std::vector<std::unique_ptr<SnoozedAlarmEventRecord>> snoozedSingleEvents;

        GetCurrentTime getCurrentTimeCallback;



@@ 89,7 102,10 @@ namespace alarms
                                               std::vector<AlarmEventRecord> records);

        void checkAndUpdateCache(AlarmEventRecord record);
        void executeAlarm(const SingleEventRecord &singleAlarmEvent);
        void switchAlarmExecution(const SingleEventRecord &singleAlarmEvent, bool newStateOn);
        void processNextEventsQueue(const TimePoint now);
        void processSnoozedEventsQueue(const TimePoint now);

        TimePoint getCurrentTime();
    };
} // namespace alarms

M module-services/service-time/AlarmServiceAPI.cpp => module-services/service-time/AlarmServiceAPI.cpp +9 -0
@@ 50,6 50,15 @@ namespace alarms
            return sendRequest<AlarmGetNextSingleEventsRequestMessage>(serv);
        }

        bool requestTurnOffRingingAlarm(sys::Service *serv, const std::uint32_t id)
        {
            return sendRequest<RingingAlarmTurnOffRequestMessage>(serv, id);
        }

        bool requestSnoozeRingingAlarm(sys::Service *serv, const std::uint32_t id, const TimePoint nextAlarmTime)
        {
            return sendRequest<RingingAlarmSnoozeRequestMessage>(serv, id, nextAlarmTime);
        }
    }; // namespace AlarmServiceAPI

} // namespace alarms

M module-services/service-time/ServiceTime.cpp => module-services/service-time/ServiceTime.cpp +8 -0
@@ 158,6 158,14 @@ namespace stm
                    return alarmMessageHandler->handleGetNextSingleEvents(
                        static_cast<alarms::AlarmGetNextSingleEventsRequestMessage *>(request));
                });
        connect(typeid(alarms::RingingAlarmTurnOffRequestMessage), [&](sys::Message *request) -> sys::MessagePointer {
            return alarmMessageHandler->handleTurnOffRingingAlarm(
                static_cast<alarms::RingingAlarmTurnOffRequestMessage *>(request));
        });
        connect(typeid(alarms::RingingAlarmSnoozeRequestMessage), [&](sys::Message *request) -> sys::MessagePointer {
            return alarmMessageHandler->handleSnoozeRingingAlarm(
                static_cast<alarms::RingingAlarmSnoozeRequestMessage *>(request));
        });
    }

    auto ServiceTime::handleSetAutomaticDateAndTimeRequest(sys::Message *request)

A module-services/service-time/SnoozedAlarmEventRecord.hpp => module-services/service-time/SnoozedAlarmEventRecord.hpp +20 -0
@@ 0,0 1,20 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <module-db/Interface/AlarmEventRecord.hpp>

struct SnoozedAlarmEventRecord : public SingleEventRecord
{
  public:
    std::uint32_t snoozeCount = 1;

    explicit SnoozedAlarmEventRecord(SingleEventRecord *singleAlarm)
        : SingleEventRecord(singleAlarm->parent, singleAlarm->startDate, singleAlarm->endDate){};

    std::uint32_t snooze() noexcept
    {
        return ++snoozeCount;
    }
};

M module-services/service-time/include/service-time/AlarmHandler.hpp => module-services/service-time/include/service-time/AlarmHandler.hpp +1 -0
@@ 21,5 21,6 @@ namespace alarms
        virtual ~AlarmHandler() = default;

        virtual auto handle(const AlarmEventRecord &record) -> bool = 0;
        virtual auto handleOff(const AlarmEventRecord &record) -> bool = 0;
    };
} // namespace alarms

M module-services/service-time/include/service-time/AlarmMessage.hpp => module-services/service-time/include/service-time/AlarmMessage.hpp +30 -0
@@ 119,4 119,34 @@ namespace alarms
        std::vector<SingleEventRecord> singleEvents;
    };

    class RingingAlarmTurnOffRequestMessage : public AlarmMessage
    {
      public:
        explicit RingingAlarmTurnOffRequestMessage(const std::uint32_t id = 0) : id(id){};
        const std::uint32_t id;
    };

    class RingingAlarmTurnOffResponseMessage : public AlarmResponse
    {
      public:
        explicit RingingAlarmTurnOffResponseMessage(const bool success = false) : success(success){};
        const bool success;
    };

    class RingingAlarmSnoozeRequestMessage : public AlarmMessage
    {
      public:
        RingingAlarmSnoozeRequestMessage(const std::uint32_t id, const TimePoint nextAlarmTime = TIME_POINT_INVALID)
            : id(id), nextAlarmTime(nextAlarmTime){};
        const std::uint32_t id;
        const TimePoint nextAlarmTime;
    };

    class RingingAlarmSnoozeResponseMessage : public AlarmResponse
    {
      public:
        explicit RingingAlarmSnoozeResponseMessage(const bool success = false) : success(success){};
        const bool success;
    };

} // namespace alarms

M module-services/service-time/include/service-time/AlarmServiceAPI.hpp => module-services/service-time/include/service-time/AlarmServiceAPI.hpp +2 -0
@@ 27,6 27,8 @@ namespace alarms
        bool requestGetAlarmsInRange(
            sys::Service *serv, TimePoint start, TimePoint end, unsigned int offset, unsigned int limit);
        bool requestGetNextSingleEvents(sys::Service *serv);
        bool requestTurnOffRingingAlarm(sys::Service *serv, const std::uint32_t id);
        bool requestSnoozeRingingAlarm(sys::Service *serv, const std::uint32_t id, const TimePoint nextAlarmTime);
    }; // namespace AlarmServiceAPI

} // namespace alarms

M module-services/service-time/tests/MockAlarmHandler.hpp => module-services/service-time/tests/MockAlarmHandler.hpp +5 -0
@@ 16,5 16,10 @@ namespace alarms
            LOG_DEBUG("MockAlarmHandler");
            return true;
        }
        auto handleOff(const AlarmEventRecord &record) -> bool
        {
            LOG_DEBUG("MockAlarmHandler");
            return true;
        }
    };
} // namespace alarms

M module-services/service-time/tests/tests-AlarmOperations.cpp => module-services/service-time/tests/tests-AlarmOperations.cpp +1 -0
@@ 14,6 14,7 @@ class MockAlarmHandler : public alarms::AlarmHandler
{
  public:
    MOCK_METHOD(bool, handle, (const AlarmEventRecord &record), ());
    MOCK_METHOD(bool, handleOff, (const AlarmEventRecord &record), ());
};
class MockAlarmEventsRepository : public alarms::AbstractAlarmEventsRepository
{

M products/BellHybrid/alarms/BellAlarmHandler.cpp => products/BellHybrid/alarms/BellAlarmHandler.cpp +16 -2
@@ 30,11 30,25 @@ namespace alarms
        return result;
    }

    auto EveningReminderHandler::handle(const AlarmEventRecord &record) -> bool
    auto BellAlarmClockHandler::handleOff([[maybe_unused]] const AlarmEventRecord &record) -> bool
    {
        auto result{true};
        for (const auto &action : actions) {
            result &= action->turnOff();
        }

        return result;
    }

    auto EveningReminderHandler::handle([[maybe_unused]] const AlarmEventRecord &record) -> bool
    {
        LOG_DEBUG("EveningReminderHandler");
        // implement this alarm type handling here
        return true;
    }

    auto EveningReminderHandler::handleOff([[maybe_unused]] const AlarmEventRecord &record) -> bool
    {
        return true;
    }

} // namespace alarms

M products/BellHybrid/alarms/include/AbstractAlarmAction.hpp => products/BellHybrid/alarms/include/AbstractAlarmAction.hpp +1 -0
@@ 10,5 10,6 @@ namespace alarms
      public:
        virtual ~AbstractAlarmAction() = default;
        virtual bool execute()         = 0;
        virtual bool turnOff()         = 0;
    };
} // namespace alarms

M products/BellHybrid/alarms/include/BellAlarmHandler.hpp => products/BellHybrid/alarms/include/BellAlarmHandler.hpp +2 -0
@@ 14,6 14,7 @@ namespace alarms
      public:
        explicit BellAlarmClockHandler(sys::Service *service);
        auto handle(const AlarmEventRecord &record) -> bool;
        auto handleOff(const AlarmEventRecord &record) -> bool;

        static constexpr auto name = "BellAlarmClockHandler";



@@ 26,5 27,6 @@ namespace alarms
    {
      public:
        auto handle(const AlarmEventRecord &record) -> bool;
        auto handleOff(const AlarmEventRecord &record) -> bool;
    };
} // namespace alarms

M products/BellHybrid/alarms/src/actions/FrontlightAction.cpp => products/BellHybrid/alarms/src/actions/FrontlightAction.cpp +6 -0
@@ 10,4 10,10 @@ namespace alarms
        // turnOnFrontlight after it will be implemented [BH-756]
        return true;
    }

    bool FrontlightAction::turnOff()
    {
        // turnOffFrontlight after it will be implemented [BH-756]
        return true;
    }
} // namespace alarms

M products/BellHybrid/alarms/src/actions/FrontlightAction.hpp => products/BellHybrid/alarms/src/actions/FrontlightAction.hpp +1 -0
@@ 12,6 12,7 @@ namespace alarms
    {
      public:
        bool execute() override;
        bool turnOff() override;
    };

} // namespace alarms

M products/BellHybrid/alarms/src/actions/NotifyGUIAction.cpp => products/BellHybrid/alarms/src/actions/NotifyGUIAction.cpp +4 -0
@@ 14,4 14,8 @@ namespace alarms
    {
        return service.bus.sendUnicast(std::make_shared<app::actions::AlarmTriggeredAction>(), service::name::appmgr);
    }
    bool NotifyGUIAction::turnOff()
    {
        return true;
    }
} // namespace alarms

M products/BellHybrid/alarms/src/actions/NotifyGUIAction.hpp => products/BellHybrid/alarms/src/actions/NotifyGUIAction.hpp +1 -0
@@ 14,6 14,7 @@ namespace alarms
      public:
        explicit NotifyGUIAction(sys::Service &service);
        bool execute() override;
        bool turnOff() override;

      private:
        sys::Service &service;

M products/BellHybrid/alarms/src/actions/PlayToneAction.cpp => products/BellHybrid/alarms/src/actions/PlayToneAction.cpp +5 -0
@@ 32,6 32,11 @@ namespace alarms
        return AudioServiceAPI::PlaybackStart(&service, audio::PlaybackType::Alarm, tonePath);
    }

    bool alarms::PlayToneAction::turnOff()
    {
        return true;
    }

    void PlayToneAction::detachTimer()
    {
        if (timer.isValid()) {

M products/BellHybrid/alarms/src/actions/PlayToneAction.hpp => products/BellHybrid/alarms/src/actions/PlayToneAction.hpp +1 -0
@@ 15,6 15,7 @@ namespace alarms
      public:
        explicit PlayToneAction(sys::Service &service);
        bool execute() override;
        bool turnOff() override;

      private:
        void spawnTimer(std::chrono::seconds timeout);

M products/BellHybrid/apps/application-bell-main/include/application-bell-main/ApplicationBellMain.hpp => products/BellHybrid/apps/application-bell-main/include/application-bell-main/ApplicationBellMain.hpp +1 -1
@@ 48,7 48,7 @@ namespace app
    {
        static auto GetManifest() -> manager::ApplicationManifest
        {
            return {{manager::actions::Launch}};
            return {{manager::actions::Launch, manager::actions::ShowAlarm}};
        }
    };
} // namespace app

M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp => products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp +6 -1
@@ 42,7 42,7 @@ namespace app::home_screen
    {
        if (not timer.isValid()) {
            auto callback = [this](sys::Timer &) { stateController->handleTimerEvent(); };
            timer         = sys::TimerFactory::createSingleShotTimer(app, timerName, defaultTimeout, callback);
            timer         = sys::TimerFactory::createSingleShotTimer(app, timerName, timeout, callback);
        }
        timer.stop();
        timer.start();


@@ 84,4 84,9 @@ namespace app::home_screen
            alarmModel->update();
        }
    }
    void HomeScreenPresenter::handleAlarmModelReady()
    {
        getView()->setAlarmTime(alarmModel->getAlarmTime());
        stateController->handleAlarmModelReady();
    }
} // namespace app::home_screen

M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.hpp => products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.hpp +4 -1
@@ 14,6 14,7 @@
#include <utf8/UTF8.hpp>

#include <chrono>
#include <functional>

namespace app
{


@@ 79,6 80,7 @@ namespace app::home_screen
        virtual void spawnTimer(std::chrono::milliseconds timeout = defaultTimeout) = 0;
        virtual void detachTimer()                                                  = 0;
        virtual void handleAlarmRingingEvent()                                      = 0;
        virtual void handleAlarmModelReady()                                        = 0;

        static constexpr auto defaultTimeout = std::chrono::milliseconds{5000};
    };


@@ 104,9 106,10 @@ namespace app::home_screen
        void onDatabaseMessage(db::NotificationMessage *msg) override;
        void refreshWindow() override;

        void spawnTimer(std::chrono::milliseconds timeout) override;
        void spawnTimer(std::chrono::milliseconds timeout = defaultTimeout) override;
        void detachTimer() override;
        void handleAlarmRingingEvent() override;
        void handleAlarmModelReady() override;

      private:
        Application *app;

M products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp => products/BellHybrid/apps/application-bell-main/presenters/StateController.cpp +51 -33
@@ 37,25 37,14 @@ namespace app::home_screen
            auto updateTemperature    = [](AbstractView &view, AbstractTemperatureModel &temperatureModel) {
                view.setTemperature(temperatureModel.getTemperature());
            };

            auto setDefaultAlarmTime =
                [](AbstractView &view, AbstractAlarmModel &alarmModel, AbstractTimeModel &timeModel) {
                    constexpr auto defaultAlarmTimeHour = 7U;
                    constexpr auto defaultAlarmTimeMin  = 0U;
                    const auto now                      = timeModel.getCurrentTime();
                    const auto newTime                  = std::localtime(&now);
                    newTime->tm_hour                    = defaultAlarmTimeHour;
                    newTime->tm_min                     = defaultAlarmTimeMin;
                    auto alarmTime                      = std::mktime(newTime);

                    if (alarmTime < now) {
                        alarmTime += utils::time::secondsInDay;
                    }
                    view.setAlarmTime(alarmTime);
                    alarmModel.setAlarmTime(alarmTime);
            auto setNewAlarmTime =
                [](AbstractView &view, AbstractAlarmModel &alarmModel, AbstractPresenter &presenter) {
                    alarmModel.setAlarmTime(view.getAlarmTime());
                    detachTimer(presenter);
                };

            auto isAlarmActive = [](AbstractAlarmModel &alarmModel) -> bool { return alarmModel.isActive(); };
            auto isSnoozeAllowed = [](AbstractAlarmModel &alarmModel) -> bool { return alarmModel.isSnoozeAllowed(); };

        } // namespace Helpers



@@ 79,15 68,30 @@ namespace app::home_screen
            {};
            struct AlarmRinging
            {};
            struct ModelReady
            {};
        } // namespace Events

        namespace Init
        {
            auto entry = [](AbstractView &view,
                            AbstractTemperatureModel &temperatureModel,
                            AbstractPresenter &presenter,
                            AbstractAlarmModel &alarmModel) {
                alarmModel.update([&]() { presenter.handleAlarmModelReady(); });
                view.setAlarmEdit(false);
                view.setAlarmActive(false);
                view.setAlarmVisible(false);
                view.setTemperature(temperatureModel.getTemperature());
            };
        } // namespace Init

        namespace Deactivated
        {
            auto entry = [](AbstractView &view,
                            AbstractTemperatureModel &temperatureModel,
                            AbstractAlarmModel &alarmModel,
                            AbstractTimeModel &timeModel) {
                Helpers::setDefaultAlarmTime(view, alarmModel, timeModel);
                view.setAlarmEdit(false);
                view.setAlarmActive(false);
                view.setAlarmVisible(false);


@@ 147,7 151,6 @@ namespace app::home_screen
                            AbstractPresenter &presenter,
                            AbstractAlarmModel &alarmModel,
                            AbstractTimeModel &timeModel) {
                alarmModel.setAlarmTime(view.getAlarmTime());
                alarmModel.activate(true);
                presenter.spawnTimer();
                view.setBottomDescription(utils::time::getBottomDescription(


@@ 172,7 175,7 @@ namespace app::home_screen
        namespace AlarmRinging
        {
            auto entry = [](AbstractView &view, AbstractPresenter &presenter) {
                presenter.spawnTimer();
                presenter.spawnTimer(defaultAlarmRingingTime);
                view.setAlarmTimeVisible(false);
                view.setAlarmTriggered();
            };


@@ 181,8 184,9 @@ namespace app::home_screen

        namespace AlarmRingingDeactivatedWait
        {
            auto entry = [](AbstractView &view, AbstractPresenter &presenter) {
            auto entry = [](AbstractView &view, AbstractAlarmModel &alarmModel, AbstractPresenter &presenter) {
                presenter.spawnTimer();
                alarmModel.turnOff();
                view.setBottomDescription(utils::translate("app_bell_alarm_ringing_deactivated"));
                view.setAlarmActive(false);
            };


@@ 191,12 195,13 @@ namespace app::home_screen

        namespace AlarmSnoozedWait
        {
            auto entry = [](AbstractView &view, AbstractPresenter &presenter) {
            auto entry = [](AbstractView &view, AbstractAlarmModel &alarmModel, AbstractPresenter &presenter) {
                presenter.spawnTimer();
                alarmModel.snooze();
                view.setAlarmTimeVisible(false);
                view.setAlarmSnoozed();
                const auto bottomDescription = utils::translate("app_bellmain_home_screen_bottom_desc") +
                                               " 10 min"; // TODO: Get duration from settings
                const auto bottomDescription = utils::translate("app_bellmain_home_screen_bottom_desc") + " " +
                                               std::to_string(alarmModel.getSnoozeDuration()) + " min";
                view.setBottomDescription(bottomDescription);
            };
            auto exit = [](AbstractPresenter &presenter) { presenter.detachTimer(); };


@@ 221,7 226,10 @@ namespace app::home_screen
            {
                using namespace sml;
                // clang-format off
                return make_transition_table(*"Deactivated"_s + sml::on_entry<_> / Deactivated::entry,
                return make_transition_table(*"Init"_s + sml::on_entry<_> / Init::entry,
                                             "Init"_s + event<Events::ModelReady> = "Deactivated"_s,

                                             "Deactivated"_s + sml::on_entry<_> / Deactivated::entry,
                                             "Deactivated"_s [Helpers::isAlarmActive] = "Activated"_s,
                                             "Deactivated"_s + event<Events::LightPress>/ Helpers::switchToMenu = "Deactivated"_s,
                                             "Deactivated"_s + event<Events::RotateRightPress> / Helpers::makeAlarmEditable = "DeactivatedEdit"_s,


@@ 239,8 247,8 @@ namespace app::home_screen
                                             "DeactivatedEdit"_s + event<Events::RotateLeftPress> / AlarmEdit::processRotateLeft,
                                             "DeactivatedEdit"_s + event<Events::RotateRightPress> / AlarmEdit::processRotateRight,
                                             "DeactivatedEdit"_s + event<Events::DeepUpPress> / Helpers::detachTimer = "ActivatedWait"_s,
                                             "DeactivatedEdit"_s + event<Events::Timer>  = "WaitForConfirmation"_s,
                                             "DeactivatedEdit"_s + event<Events::LightPress> / Helpers::detachTimer = "WaitForConfirmation"_s,
                                             "DeactivatedEdit"_s + event<Events::Timer> / Helpers::setNewAlarmTime = "WaitForConfirmation"_s,
                                             "DeactivatedEdit"_s + event<Events::LightPress> / Helpers::setNewAlarmTime = "WaitForConfirmation"_s,

                                             "WaitForConfirmation"_s + sml::on_entry<_> / WaitForConfirmation::entry,
                                             "WaitForConfirmation"_s + sml::on_exit<_> / WaitForConfirmation::exit,


@@ 265,15 273,19 @@ namespace app::home_screen
                                             "ActivatedEdit"_s + event<Events::TimeUpdate> / Helpers::updateTemperature,
                                             "ActivatedEdit"_s + event<Events::RotateLeftPress> / AlarmEdit::processRotateLeft,
                                             "ActivatedEdit"_s + event<Events::RotateRightPress> / AlarmEdit::processRotateRight,
                                             "ActivatedEdit"_s + event<Events::Timer>  = "ActivatedWait"_s,
                                             "ActivatedEdit"_s + event<Events::LightPress> / Helpers::detachTimer = "ActivatedWait"_s,
                                             "ActivatedEdit"_s + event<Events::Timer> / Helpers::setNewAlarmTime = "ActivatedWait"_s,
                                             "ActivatedEdit"_s + event<Events::LightPress> / Helpers::setNewAlarmTime = "ActivatedWait"_s,

                                             "AlarmRinging"_s + sml::on_entry<_> / AlarmRinging::entry,
                                             "AlarmRinging"_s + sml::on_exit<_> / AlarmRinging::exit,
                                             "AlarmRinging"_s + event<Events::Timer> = "AlarmSnoozedWait"_s,
                                             "AlarmRinging"_s + event<Events::LightPress> = "AlarmSnoozedWait"_s,
                                             "AlarmRinging"_s + event<Events::RotateLeftPress> = "AlarmSnoozedWait"_s,
                                             "AlarmRinging"_s + event<Events::RotateRightPress> = "AlarmSnoozedWait"_s,
                                             "AlarmRinging"_s + event<Events::Timer> [Helpers::isSnoozeAllowed] = "AlarmSnoozedWait"_s,
                                             "AlarmRinging"_s + event<Events::Timer> [!Helpers::isSnoozeAllowed] = "AlarmRingingDeactivatedWait"_s,
                                             "AlarmRinging"_s + event<Events::LightPress> [Helpers::isSnoozeAllowed] = "AlarmSnoozedWait"_s,
                                             "AlarmRinging"_s + event<Events::LightPress> [!Helpers::isSnoozeAllowed] = "AlarmRingingDeactivatedWait"_s,
                                             "AlarmRinging"_s + event<Events::RotateLeftPress> [Helpers::isSnoozeAllowed] = "AlarmSnoozedWait"_s,
                                             "AlarmRinging"_s + event<Events::RotateLeftPress> [!Helpers::isSnoozeAllowed] = "AlarmRingingDeactivatedWait"_s,
                                             "AlarmRinging"_s + event<Events::RotateRightPress> [Helpers::isSnoozeAllowed] = "AlarmSnoozedWait"_s,
                                             "AlarmRinging"_s + event<Events::RotateRightPress> [!Helpers::isSnoozeAllowed] = "AlarmRingingDeactivatedWait"_s,
                                             "AlarmRinging"_s + event<Events::DeepDownPress> = "AlarmRingingDeactivatedWait"_s,

                                             "AlarmRingingDeactivatedWait"_s + sml::on_entry<_> / AlarmRingingDeactivatedWait::entry,


@@ 288,7 300,7 @@ namespace app::home_screen

                                             "AlarmSnoozed"_s + sml::on_entry<_> / AlarmSnoozed::entry,
                                             "AlarmSnoozed"_s + sml::on_entry<_> / AlarmSnoozed::exit,
                                             "AlarmSnoozed"_s + event<Events::Timer> = "AlarmRinging"_s,
                                             "AlarmSnoozed"_s + event<Events::AlarmRinging>  = "AlarmRinging"_s,
                                             "AlarmSnoozed"_s + event<Events::DeepDownPress> = "DeactivatedWait"_s
                    );
                // clang-format on


@@ 379,4 391,10 @@ namespace app::home_screen
        pimpl->sm.process_event(Events::AlarmRinging{});
        return true;
    }

    bool StateController::handleAlarmModelReady()
    {
        pimpl->sm.process_event(Events::ModelReady{});
        return true;
    }
} // namespace app::home_screen

M products/BellHybrid/apps/application-bell-main/presenters/StateController.hpp => products/BellHybrid/apps/application-bell-main/presenters/StateController.hpp +7 -0
@@ 5,10 5,15 @@

#include <common/models/AbstractAlarmModel.hpp>
#include <gui/input/InputEvent.hpp>

#include <chrono>
#include <memory>

namespace app::home_screen
{

    inline constexpr auto defaultAlarmRingingTime = std::chrono::minutes(5);

    class AbstractView;
    class AbstractPresenter;
    class AbstractTemperatureModel;


@@ 22,6 27,7 @@ namespace app::home_screen
        virtual bool handleTimerEvent()                                  = 0;
        virtual bool handleTimeUpdateEvent()                             = 0;
        virtual bool handleAlarmRingingEvent()                           = 0;
        virtual bool handleAlarmModelReady()                             = 0;
    };

    class StateController : public AbstractController


@@ 37,6 43,7 @@ namespace app::home_screen
        bool handleTimerEvent() override;
        bool handleTimeUpdateEvent() override;
        bool handleAlarmRingingEvent() override;
        bool handleAlarmModelReady() override;

      private:
        class Impl;

M products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.cpp => products/BellHybrid/apps/application-bell-main/windows/BellHomeScreenWindow.cpp +1 -1
@@ 6,7 6,7 @@

#include <application-bell-main/ApplicationBellMain.hpp>
#include <apps-common/widgets/BellBaseLayout.hpp>
#include <apps-common/actions/AlarmTriggeredAction.hpp>
#include <apps-common/actions/AlarmRingingData.hpp>
#include <gui/input/InputEvent.hpp>
#include <gui/widgets/TextFixedSize.hpp>
#include <gui/widgets/Style.hpp>

M products/BellHybrid/apps/common/CMakeLists.txt => products/BellHybrid/apps/common/CMakeLists.txt +1 -0
@@ 28,6 28,7 @@ target_link_libraries(application-bell-common
        apps-common

    PRIVATE
        bell::db
        module-gui
        service-time
)

M products/BellHybrid/apps/common/include/common/models/AbstractAlarmModel.hpp => products/BellHybrid/apps/common/include/common/models/AbstractAlarmModel.hpp +14 -5
@@ 4,20 4,29 @@
#pragma once

#include <ctime>
#include <cstdint>
#include <functional>

namespace app
{

    using AlarmModelReadyHandler = std::function<void()>;

    class AbstractAlarmModel
    {
      public:
        virtual ~AbstractAlarmModel() noexcept = default;

        virtual bool isActive() const          = 0;
        virtual void setAlarmTime(time_t time) = 0;
        virtual time_t getAlarmTime() const    = 0;
        virtual void activate(bool value)      = 0;
        virtual bool isActive() const             = 0;
        virtual void setAlarmTime(time_t time)    = 0;
        virtual time_t getAlarmTime() const       = 0;
        virtual void activate(bool value)         = 0;
        virtual std::uint32_t getSnoozeDuration() = 0;
        virtual bool isSnoozeAllowed()            = 0;
        virtual void turnOff()                    = 0;
        virtual void snooze()                     = 0;
        /// Command model to update its internal data
        virtual void update() = 0;
        virtual void update(AlarmModelReadyHandler callback = AlarmModelReadyHandler()) = 0;
    };

} // namespace app

M products/BellHybrid/apps/common/include/common/models/AlarmModel.hpp => products/BellHybrid/apps/common/include/common/models/AlarmModel.hpp +16 -3
@@ 7,6 7,7 @@

#include <apps-common/AsyncTask.hpp>
#include <module-db/Interface/AlarmEventRecord.hpp>
#include <service-db/Settings.hpp>

#include <functional>



@@ 17,6 18,8 @@ namespace app

namespace app
{
    constexpr std::uint32_t maxSnoozeCount = 3;

    class AlarmModel : public AbstractAlarmModel, public AsyncCallbackReceiver
    {
      public:


@@ 26,7 29,11 @@ namespace app
        void setAlarmTime(time_t time) override;
        time_t getAlarmTime() const override;
        void activate(bool value) override;
        void update() override;
        void update(AlarmModelReadyHandler callback) override;
        std::uint32_t getSnoozeDuration() override;
        bool isSnoozeAllowed() override;
        void turnOff() override;
        void snooze() override;

      private:
        enum class State


@@ 36,11 43,17 @@ namespace app
            Valid
        };

        void updateAlarm(const AlarmEventRecord &alarm);
        void updateAlarm(AlarmEventRecord &alarm);
        AlarmEventRecord generateDefaultAlarm() const;
        std::shared_ptr<AlarmEventRecord> getAlarmPtr() const;

        Application *app{};
        AlarmEventRecord cachedRecord;
        State state{State::Invalid};
        SingleEventRecord cachedRecord;
        std::uint32_t snoozeCount = 0;

        std::function<bool(sys::ResponseMessage *)> responseCallback;

        mutable settings::Settings settings;
    };
} // namespace app

M products/BellHybrid/apps/common/src/AlarmModel.cpp => products/BellHybrid/apps/common/src/AlarmModel.cpp +98 -17
@@ 4,8 4,10 @@
#include "models/AlarmModel.hpp"

#include <apps-common/Application.hpp>
#include <db/SystemSettings.hpp>
#include <module-db/Interface/AlarmEventRecord.hpp>
#include <time/dateCommon.hpp>
#include <service-time/AlarmServiceAPI.hpp>
#include <service-time/api/TimeSettingsApi.hpp>
#include <service-time/Constants.hpp>
#include <service-time/AlarmMessage.hpp>


@@ 14,40 16,52 @@ namespace app
{
    AlarmModel::AlarmModel(Application *app) : app::AsyncCallbackReceiver{app}, app{app}
    {
        responseCallback = [this](const auto response) -> bool {
        state = State::InitInProgress;
        settings.init(service::ServiceProxy{app->weak_from_this()});
    }

    void AlarmModel::update(AlarmModelReadyHandler callback)
    {
        responseCallback = [this, callback](const auto response) -> bool {
            const auto resp = dynamic_cast<alarms::AlarmGetResponseMessage *>(response);
            if (resp) {
                if (resp->alarm.isValid()) {
                    cachedRecord = resp->alarm;
                    auto alarm   = resp->alarm;
                    cachedRecord = alarm.getNextSingleEvent(TimePointNow());
                    state        = State::Valid;
                    snoozeCount  = 0;
                    if (callback) {
                        callback();
                    }
                }
                else if (state == State::InitInProgress) {
                    /// We received invalid response from DB during model initialization. It means alarm record does not
                    /// exist, hence we need to create one.
                    auto request = AsyncRequest::createFromMessage(
                        std::make_unique<alarms::AlarmAddRequestMessage>(cachedRecord), service::name::service_time);
                    auto defaultAlarm = generateDefaultAlarm();
                    auto request      = AsyncRequest::createFromMessage(
                        std::make_unique<alarms::AlarmAddRequestMessage>(defaultAlarm), service::name::service_time);
                    request->execute(this->app, this, responseCallback);

                    state = State::Valid;
                    this->update();
                    this->update(callback);
                }
            }
            return true;
        };
        state = State::InitInProgress;
        update();
    }
    void AlarmModel::update()
    {

        auto request = AsyncRequest::createFromMessage(std::make_unique<alarms::AlarmGetRequestMessage>(),
                                                       service::name::service_time);
        request->execute(app, this, responseCallback);
    }
    void AlarmModel::setAlarmTime(time_t time)
    {
        cachedRecord.startDate = Clock::from_time_t(time);
        auto alarmEventPtr = getAlarmPtr();
        if (!alarmEventPtr) {
            return;
        }
        alarmEventPtr->startDate = Clock::from_time_t(time);

        updateAlarm(cachedRecord);
        updateAlarm(*alarmEventPtr);
    }
    time_t AlarmModel::getAlarmTime() const
    {


@@ 55,19 69,86 @@ namespace app
    }
    void AlarmModel::activate(bool value)
    {
        cachedRecord.enabled = value;
        updateAlarm(cachedRecord);
        auto alarmEventPtr = getAlarmPtr();
        if (!alarmEventPtr) {
            return;
        }
        alarmEventPtr->enabled = value;
        updateAlarm(*alarmEventPtr);
    }
    void AlarmModel::updateAlarm(const AlarmEventRecord &alarm)
    void AlarmModel::updateAlarm(AlarmEventRecord &alarm)
    {
        auto request = AsyncRequest::createFromMessage(std::make_unique<alarms::AlarmUpdateRequestMessage>(alarm),
                                                       service::name::service_time);
        request->execute(app, this, responseCallback);

        cachedRecord = alarm;
        cachedRecord = alarm.getNextSingleEvent(TimePointNow());
    }
    bool AlarmModel::isActive() const
    {
        return cachedRecord.enabled;
        if (!cachedRecord.parent) {
            return false;
        }

        auto alarmEventPtr = getAlarmPtr();
        if (!alarmEventPtr) {
            return false;
        }
        return alarmEventPtr->enabled;
    }
    std::uint32_t AlarmModel::getSnoozeDuration()
    {
        const auto snoozeDurationStr =
            settings.getValue(bell::settings::Snooze::length, settings::SettingsScope::Global);
        const auto snoozeDuration = utils::getNumericValue<std::uint32_t>(snoozeDurationStr);

        return snoozeDuration;
    }
    bool AlarmModel::isSnoozeAllowed()
    {
        const auto snoozeActiveStr = settings.getValue(bell::settings::Snooze::active, settings::SettingsScope::Global);
        const auto snoozeActive    = utils::getNumericValue<bool>(snoozeActiveStr);

        return (snoozeActive && snoozeCount < maxSnoozeCount);
    }
    void AlarmModel::turnOff()
    {
        snoozeCount = 0;
        alarms::AlarmServiceAPI::requestTurnOffRingingAlarm(app, cachedRecord.parent->ID);
    }

    void AlarmModel::snooze()
    {
        const auto snoozeDurationStr =
            settings.getValue(bell::settings::Snooze::length, settings::SettingsScope::Global);
        const auto snoozeDuration = utils::getNumericValue<std::uint32_t>(snoozeDurationStr);

        snoozeCount++;
        auto newAlarmTime =
            std::chrono::floor<std::chrono::minutes>(TimePointNow()) + std::chrono::minutes(snoozeDuration);
        alarms::AlarmServiceAPI::requestSnoozeRingingAlarm(app, cachedRecord.parent->ID, newAlarmTime);
    }

    AlarmEventRecord AlarmModel::generateDefaultAlarm() const
    {
        auto defaultAlarm      = AlarmEventRecord{};
        defaultAlarm.startDate = TimePointFromString("2021-01-01 07:00:00");
        defaultAlarm.rruleText = "FREQ=DAILY";
        defaultAlarm.endDate   = TIME_POINT_MAX;
        return defaultAlarm;
    }

    std::shared_ptr<AlarmEventRecord> AlarmModel::getAlarmPtr() const
    {
        if (!cachedRecord.parent) {
            LOG_ERROR("Cached event has no parent");
            return nullptr;
        }
        auto alarmEventPtr = std::dynamic_pointer_cast<AlarmEventRecord>(cachedRecord.parent);
        if (!alarmEventPtr) {
            LOG_ERROR("Processed event is not an alarm");
            return nullptr;
        }
        return alarmEventPtr;
    }
} // namespace app

M products/BellHybrid/services/db/include/db/SystemSettings.hpp => products/BellHybrid/services/db/include/db/SystemSettings.hpp +8 -0
@@ 14,4 14,12 @@ namespace bell::settings
        constexpr inline auto duration = "ringing_duration";
        constexpr inline auto tone     = "ringing_tone";
    } // namespace Ringing
    namespace Snooze
    {
        constexpr inline auto active   = "snooze_active";
        constexpr inline auto length   = "snooze_length";
        constexpr inline auto interval = "snooze_interval";
        constexpr inline auto tone     = "snooze_tone";
        constexpr inline auto volume   = "snooze_volume";
    } // namespace Snooze
};    // namespace bell::settings