~aleteoryx/muditaos

71c88bacb5dcca077690381f89c86042019d428e — Maciej Gibowicz 9 months ago 790c746
[BH-2097] Adding and deleting a single custom quote

Adding and removing custom quotes in the database sent by Center
M module-services/service-db/DBServiceAPI.cpp => module-services/service-db/DBServiceAPI.cpp +33 -0
@@ 317,6 317,39 @@ void DBServiceAPI::InformDateChanged(sys::Service *serv)
    DBServiceAPI::GetQuery(serv, db::Interface::Name::Quotes, std::move(query));
}

bool DBServiceAPI::QuotesAddNewEntry(sys::Service *serv,
                                     const std::string &quote,
                                     const std::string &author,
                                     std::unique_ptr<db::QueryListener> &&listener)
{
    auto query = std::make_unique<Quotes::Messages::AddNewEntry>(quote, author);
    query->setQueryListener(std::move(listener));
    const auto [result, _] = DBServiceAPI::GetQuery(serv, db::Interface::Name::Quotes, std::move(query));
    return result;
}

bool DBServiceAPI::QuotesEditEntry(sys::Service *serv,
                                   std::uint32_t id,
                                   const std::string &quote,
                                   const std::string &author,
                                   std::unique_ptr<db::QueryListener> &&listener)
{
    auto query = std::make_unique<Quotes::Messages::EditEntry>(id, quote, author);
    query->setQueryListener(std::move(listener));
    const auto [result, _] = DBServiceAPI::GetQuery(serv, db::Interface::Name::Quotes, std::move(query));
    return result;
}

bool DBServiceAPI::QuotesDeleteEntry(sys::Service *serv,
                                     std::uint32_t id,
                                     std::unique_ptr<db::QueryListener> &&listener)
{
    auto query = std::make_unique<Quotes::Messages::DeleteQuoteRequest>(id);
    query->setQueryListener(std::move(listener));
    const auto [result, _] = DBServiceAPI::GetQuery(serv, db::Interface::Name::Quotes, std::move(query));
    return result;
}

void DBServiceAPI::QuotesGroupChanged(sys::Service *serv, const std::string &group)
{
    auto query = std::make_unique<Quotes::Messages::InformGroupChanged>(group);

M module-services/service-db/include/service-db/DBServiceAPI.hpp => module-services/service-db/include/service-db/DBServiceAPI.hpp +10 -0
@@ 128,5 128,15 @@ class DBServiceAPI

    static void InformLanguageChanged(sys::Service *serv);
    static void InformDateChanged(sys::Service *serv);
    static bool QuotesAddNewEntry(sys::Service *serv,
                                  const std::string &quote,
                                  const std::string &author,
                                  std::unique_ptr<db::QueryListener> &&listener);
    static bool QuotesEditEntry(sys::Service *serv,
                                std::uint32_t id,
                                const std::string &quote,
                                const std::string &author,
                                std::unique_ptr<db::QueryListener> &&listener);
    static bool QuotesDeleteEntry(sys::Service *serv, std::uint32_t id, std::unique_ptr<db::QueryListener> &&listener);
    static void QuotesGroupChanged(sys::Service *serv, const std::string &group);
};

M module-services/service-db/include/service-db/QuotesMessages.hpp => module-services/service-db/include/service-db/QuotesMessages.hpp +47 -2
@@ 298,7 298,8 @@ namespace Quotes
        class AddQuoteResponse : public db::QueryResult
        {
          public:
            explicit AddQuoteResponse(bool success, unsigned int quoteId) : success(success), quoteId(quoteId)
            explicit AddQuoteResponse(bool success = false, unsigned int quoteId = 0)
                : success(success), quoteId(quoteId)
            {}
            const bool success;
            const unsigned int quoteId;


@@ 374,6 375,50 @@ namespace Quotes
            }
        };

        class AddNewEntry : public db::Query
        {
          public:
            explicit AddNewEntry(const std::string &quote, const std::string &author)
                : Query(Query::Type::Create), quote(quote), author(author)
            {}
            const std::string quote;
            const std::string author;

            auto debugInfo() const -> std::string
            {
                return "AddNewEntry";
            }
        };

        class EditEntry : public db::Query
        {
          public:
            explicit EditEntry(std::uint32_t id, const std::string &quote, const std::string &author)
                : Query(Query::Type::Update), id(id), quote(quote), author(author)
            {}
            const std::uint32_t id;
            const std::string quote;
            const std::string author;

            auto debugInfo() const -> std::string
            {
                return "EditEntry";
            }
        };

        class EditEntryResponse : public db::QueryResult
        {
          public:
            explicit EditEntryResponse(bool success = false) : success(success)
            {}
            const bool success;

            auto debugInfo() const -> std::string
            {
                return "EditEntryResponse";
            }
        };

        class InformGroupChanged : public db::Query
        {
          public:


@@ 434,7 479,7 @@ namespace Quotes
        class DeleteQuoteResponse : public db::QueryResult
        {
          public:
            explicit DeleteQuoteResponse(bool success) : success(success)
            explicit DeleteQuoteResponse(bool success = false) : success(success)
            {}
            const bool success;


M module-services/service-desktop/endpoints/include/endpoints/JsonKeyNames.hpp => module-services/service-desktop/endpoints/include/endpoints/JsonKeyNames.hpp +6 -13
@@ 126,18 126,11 @@ namespace sdesktop::endpoints::json

    namespace quotes
    {
        namespace settings
        {
            inline constexpr auto group    = "group";
            inline constexpr auto interval = "interval";
        } // namespace settings

        inline constexpr auto quotation = "quotation";
        namespace quotations
        {
            inline constexpr auto text   = "text";
            inline constexpr auto author = "author";
        } // namespace quotations
    }     // namespace quotes
        inline constexpr auto quote    = "quote";
        inline constexpr auto author   = "author";
        inline constexpr auto group    = "group";
        inline constexpr auto interval = "interval";
        inline constexpr auto quoteID = "quoteID";
    } // namespace quotes

} // namespace sdesktop::endpoints::json

M products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp => products/BellHybrid/apps/application-bell-main/presenters/HomeScreenPresenter.cpp +2 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2025, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md

#include "application-bell-main/presenters/HomeScreenPresenter.hpp"


@@ 288,6 288,7 @@ namespace app::home_screen
        const auto batteryState   = batteryModel.getLevelState().state;
        const bool isUsbConnected = usbStatusModel.isUsbConnected(batteryState);
        getView()->updateUsbStatus(isUsbConnected);
        requestQuote();
    }

    bool HomeScreenPresenter::isLowBatteryWarningNeeded()

M products/BellHybrid/services/db/agents/QuotesAgent.cpp => products/BellHybrid/services/db/agents/QuotesAgent.cpp +75 -0
@@ 24,6 24,15 @@ namespace Quotes
            shuffleQuoteModel.updateList(ListUpdateMode::Forced);
            return std::make_unique<Messages::NotificationResult>(true);
        }
        else if (typeid(*query) == typeid(Messages::AddNewEntry)) {
            return handleAddNewEntry(query);
        }
        else if (typeid(*query) == typeid(Messages::EditEntry)) {
            return handleEditEntry(query);
        }
        else if (typeid(*query) == typeid(Messages::DeleteQuoteRequest)) {
            return handleDeleteEntry(query);
        }
        else if (typeid(*query) == typeid(Messages::InformGroupChanged)) {
            return handleGroupChanged(query);
        }


@@ 51,6 60,72 @@ namespace Quotes
        return nullptr;
    }

    auto QuotesAgent::handleAddNewEntry(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
    {
        const auto request = std::dynamic_pointer_cast<Messages::AddNewEntry>(query);
        if (request == nullptr) {
            auto response = std::make_unique<Messages::AddQuoteResponse>();
            response->setRequestQuery(query);
            return response;
        }

        const auto quotesGroup = settings->getValue(settings::Quotes::selectedGroup, settings::SettingsScope::Global);
        const auto executeResult =
            quotesDB->execute(Queries::addCustomQuote, request->quote.c_str(), request->author.c_str());

        if (executeResult && quotesGroup == customGroup) {
            shuffleQuoteModel.updateList(ListUpdateMode::Forced);
        }

        const auto quoteId = quotesDB->getLastInsertRowId();
        auto response      = std::make_unique<Messages::AddQuoteResponse>(executeResult, quoteId);
        response->setRequestQuery(query);
        return response;
    }

    auto QuotesAgent::handleEditEntry(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
    {
        const auto request = std::dynamic_pointer_cast<Messages::EditEntry>(query);
        if (request == nullptr) {
            auto response = std::make_unique<Messages::EditEntryResponse>();
            response->setRequestQuery(query);
            return response;
        }

        const auto quotesGroup = settings->getValue(settings::Quotes::selectedGroup, settings::SettingsScope::Global);
        const auto executeResult =
            quotesDB->execute(Queries::editCustomQuote, request->quote.c_str(), request->author.c_str(), request->id);

        if (executeResult && quotesGroup == customGroup) {
            shuffleQuoteModel.updateList(ListUpdateMode::Forced);
        }

        auto response = std::make_unique<Messages::EditEntryResponse>(executeResult);
        response->setRequestQuery(query);
        return response;
    }

    auto QuotesAgent::handleDeleteEntry(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
    {
        const auto request = std::dynamic_pointer_cast<Messages::DeleteQuoteRequest>(query);
        if (request == nullptr) {
            auto response = std::make_unique<Messages::DeleteQuoteResponse>();
            response->setRequestQuery(query);
            return response;
        }

        const auto quotesGroup   = settings->getValue(settings::Quotes::selectedGroup, settings::SettingsScope::Global);
        const auto executeResult = quotesDB->execute(Queries::deleteCustomQuote, request->quoteId);

        if (executeResult && quotesGroup == customGroup) {
            shuffleQuoteModel.updateList(ListUpdateMode::Forced);
        }

        auto response = std::make_unique<Messages::DeleteQuoteResponse>(executeResult);
        response->setRequestQuery(query);
        return response;
    }

    auto QuotesAgent::handleGroupChanged(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>
    {
        const auto request = std::dynamic_pointer_cast<Messages::InformGroupChanged>(query);

M products/BellHybrid/services/db/agents/QuotesAgent.hpp => products/BellHybrid/services/db/agents/QuotesAgent.hpp +3 -0
@@ 30,6 30,9 @@ namespace Quotes
        Database *quotesDB;
        ShuffleQuoteModel shuffleQuoteModel;

        auto handleAddNewEntry(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
        auto handleEditEntry(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
        auto handleDeleteEntry(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
        auto handleGroupChanged(std::shared_ptr<db::Query> query) -> std::unique_ptr<db::QueryResult>;
    };
} // namespace Quotes

M products/BellHybrid/services/db/agents/QuotesQueries.hpp => products/BellHybrid/services/db/agents/QuotesQueries.hpp +7 -0
@@ 20,4 20,11 @@ namespace Quotes::Queries
        "SELECT quote_id, quote, author FROM custom_quote_table WHERE quote_id=" u32_ ";";

    inline constexpr auto getCustomQuotesCount = "SELECT COUNT(quote_id) FROM custom_quote_table;";

    inline constexpr auto addCustomQuote = "INSERT INTO custom_quote_table (quote, author) VALUES (" str_c str_ ");";

    inline constexpr auto deleteCustomQuote = "DELETE FROM custom_quote_table WHERE quote_id=" u32_ ";";

    inline constexpr auto editCustomQuote =
        "UPDATE custom_quote_table SET quote=" str_c "author=" str_ " WHERE quote_id=" u32_ ";";
} // namespace Quotes::Queries

M products/BellHybrid/services/desktop/endpoints/quotes/QuotesHelper.cpp => products/BellHybrid/services/desktop/endpoints/quotes/QuotesHelper.cpp +80 -6
@@ 4,8 4,10 @@
#include <endpoints/quotes/QuotesHelper.hpp>
#include <endpoints/Context.hpp>
#include <endpoints/JsonKeyNames.hpp>
#include <endpoints/message/Sender.hpp>
#include <Service/Service.hpp>
#include <service-db/DBServiceAPI.hpp>
#include <service-db/QuotesMessages.hpp>
#include <log/log.hpp>

namespace sdesktop::endpoints


@@ 18,17 20,68 @@ namespace sdesktop::endpoints

    auto QuotesHelper::processPost(Context &context) -> ProcessResult
    {
        // TODO: https://appnroll.atlassian.net/browse/BH-2097
        return {Sent::No, ResponseContext{.status = http::Code::InternalServerError}};
        const auto &body = context.getBody();
        if (const auto &quote = body[json::quotes::quote].string_value(); !quote.empty()) {
            const auto &author = body[json::quotes::author].string_value();

            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context &context) {
                    const auto addQuoteResult = dynamic_cast<Quotes::Messages::AddQuoteResponse *>(result);
                    if (addQuoteResult == nullptr || !addQuoteResult->success) {
                        context.setResponseStatus(http::Code::InternalServerError);
                        sender::putToSendQueue(context.createSimpleResponse());
                        return false;
                    }

                    context.setResponseBody(
                        json11::Json::object{{json::quotes::quoteID, static_cast<int>(addQuoteResult->quoteId)}});
                    context.setResponseStatus(http::Code::OK);
                    sender::putToSendQueue(context.createSimpleResponse());
                    return true;
                },
                context);

            DBServiceAPI::QuotesAddNewEntry(owner, quote.c_str(), author.c_str(), std::move(listener));
            return {Sent::Yes, std::nullopt};
        }

        LOG_ERROR("Bad request! New quote is incorrect or missing!");
        return {Sent::No, ResponseContext{.status = http::Code::BadRequest}};
    }

    auto QuotesHelper::processPut(Context &context) -> ProcessResult
    {
        const auto &body = context.getBody();
        if (const auto group = body[json::quotes::settings::group].string_value(); !group.empty()) {
        if (const auto &quote = body[json::quotes::quote].string_value(); !quote.empty()) {
            const auto &author = body[json::quotes::author].string_value();
            const auto id     = body[json::quotes::quoteID];

            if (!id.is_number()) {
                return {Sent::No, ResponseContext{.status = http::Code::BadRequest}};
            }

            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context &context) {
                    const auto editQuoteResult = dynamic_cast<Quotes::Messages::EditEntryResponse *>(result);
                    if (editQuoteResult == nullptr || !editQuoteResult->success) {
                        context.setResponseStatus(http::Code::InternalServerError);
                        sender::putToSendQueue(context.createSimpleResponse());
                        return false;
                    }

                    context.setResponseStatus(http::Code::OK);
                    sender::putToSendQueue(context.createSimpleResponse());
                    return true;
                },
                context);

            DBServiceAPI::QuotesEditEntry(owner, id.int_value(), quote.c_str(), author.c_str(), std::move(listener));
            return {Sent::Yes, std::nullopt};
        }
        else if (const auto group = body[json::quotes::group].string_value(); !group.empty()) {
            DBServiceAPI::QuotesGroupChanged(owner, group.c_str());
        }
        else if (const auto interval = body[json::quotes::settings::interval].string_value(); !interval.empty()) {
        else if (const auto &interval = body[json::quotes::interval].string_value(); !interval.empty()) {
            // TODO: https://appnroll.atlassian.net/browse/BH-2095
        }
        else {


@@ 40,8 93,29 @@ namespace sdesktop::endpoints

    auto QuotesHelper::processDelete(Context &context) -> ProcessResult
    {
        // TODO: https://appnroll.atlassian.net/browse/BH-2097
        return {Sent::No, ResponseContext{.status = http::Code::InternalServerError}};
        const auto &body = context.getBody();
        if (const auto quoteID = body[json::quotes::quoteID]; quoteID.is_number()) {
            auto listener = std::make_unique<db::EndpointListener>(
                [=](db::QueryResult *result, Context context) {
                    const auto deleteResult = dynamic_cast<Quotes::Messages::DeleteQuoteResponse *>(result);
                    if (deleteResult == nullptr || !deleteResult->success) {
                        context.setResponseStatus(http::Code::InternalServerError);
                        sender::putToSendQueue(context.createSimpleResponse());
                        return false;
                    }

                    context.setResponseStatus(http::Code::NoContent);
                    sender::putToSendQueue(context.createSimpleResponse());
                    return true;
                },
                context);

            DBServiceAPI::QuotesDeleteEntry(owner, quoteID.int_value(), std::move(listener));
            return {Sent::Yes, std::nullopt};
        }

        LOG_ERROR("Bad request! Quote ID is incorrect or missing!");
        return {Sent::No, ResponseContext{.status = http::Code::BadRequest}};
    }

} // namespace sdesktop::endpoints

A test/custom_quotes.py => test/custom_quotes.py +255 -0
@@ 0,0 1,255 @@
import base64
import os.path
import serial
import random
import json
import zlib
import argparse
import time
from tqdm import tqdm

http_methods = {
    'GET': 1,
    'POST': 2,
    'PUT': 3,
    'DELETE': 4
}

endpoint_types = {
    'Invalid': 0,
    'DeviceInfo': 1,
    'Update': 2,
    'FilesystemUpload': 3,
    'Backup': 4,
    'Restore': 5,
    'Factory': 6,
    'Contacts': 7,
    'Messages': 8,
    'Calllog': 9,
    'CalendarEventsPlaceholder': 10,
    'DeveloperMode': 11,
    'Bluetooth': 12,
    'UsbSecurity': 13,
    'Outbox': 14,
    'Reboot': 15,
    'TimeSync': 16,
    'Quotes': 17
}

payload_marker = '#'
payload_size_len = 9
port_timeout_short_s = 1
port_timeout_long_s = 5
port_baudrate = 115200


def get_new_uuid() -> int:
    return random.randint(1, 10000)

def add_quote(port_path: str, quote_str: str, author_str: str) -> bool:
    uuid = get_new_uuid()

    payload = {
        'endpoint': endpoint_types['Quotes'],
        'method': http_methods['POST'],
        'uuid': uuid,
        'body': {
            'quote': quote_str,
            'author': author_str
        }
    }

    payload_str = json.dumps(payload)
    payload_len_str = str(len(payload_str)).rjust(payload_size_len, '0')
    request_str = payload_marker + payload_len_str + payload_str

    with serial.Serial(port_path, port_baudrate, timeout=port_timeout_short_s) as port:
        port.write(request_str.encode('ascii'))
        response = port.read(2048)
        resp_json = json.loads(response[10:])
        status = resp_json['status']
        if status == 200:
            print(f'Request success {resp_json["body"]}')
            return True
        print(f'Request failed, status {status}')
        return False
    
def edit_quote(port_path: str, quote_id: int,  quote_str: str, author_str: str) -> bool:
    uuid = get_new_uuid()

    payload = {
        'endpoint': endpoint_types['Quotes'],
        'method': http_methods['PUT'],
        'uuid': uuid,
        'body': {
            'quoteID': quote_id,
            'quote': quote_str,
            'author': author_str
        }
    }

    payload_str = json.dumps(payload)
    payload_len_str = str(len(payload_str)).rjust(payload_size_len, '0')
    request_str = payload_marker + payload_len_str + payload_str

    with serial.Serial(port_path, port_baudrate, timeout=port_timeout_short_s) as port:
        port.write(request_str.encode('ascii'))
        response = port.read(2048)
        resp_json = json.loads(response[10:])
        status = resp_json['status']
        if status == 200:
            print(f'Request success')
            return True
        print(f'Request failed, status {status}')
        return False

def delete_quote(port_path: str, quote_id: int) -> bool:
    uuid = get_new_uuid()

    payload = {
        'endpoint': endpoint_types['Quotes'],
        'method': http_methods['DELETE'],
        'uuid': uuid,
        'body': {
            'quoteID': quote_id
        }
    }

    payload_str = json.dumps(payload)
    payload_len_str = str(len(payload_str)).rjust(payload_size_len, '0')
    request_str = payload_marker + payload_len_str + payload_str

    with serial.Serial(port_path, port_baudrate, timeout=port_timeout_short_s) as port:
        port.write(request_str.encode('ascii'))
        response = port.read(2048)
        resp_json = json.loads(response[10:])
        status = resp_json['status']
        if status == 204:
            print(f'Request success')
            return True
        print(f'Request failed, status {status}')
        return False

def change_group(port_path: str, quote_group: str) -> bool:
    uuid = get_new_uuid()

    payload = {
        'endpoint': endpoint_types['Quotes'],
        'method': http_methods['PUT'],
        'uuid': uuid,
        'body': {
            'group': quote_group
        }
    }

    payload_str = json.dumps(payload)
    payload_len_str = str(len(payload_str)).rjust(payload_size_len, '0')
    request_str = payload_marker + payload_len_str + payload_str

    with serial.Serial(port_path, port_baudrate, timeout=port_timeout_short_s) as port:
        port.write(request_str.encode('ascii'))
        response = port.read(2048)
        resp_json = json.loads(response[10:])
        status = resp_json['status']
        if status == 200:
            print(f'Request success')
            return True
        print(f'Request failed, status {status}')
        return False

def change_interval(port_path: str, quote_interval: str) -> bool:
    uuid = get_new_uuid()

    payload = {
        'endpoint': endpoint_types['Quotes'],
        'method': http_methods['PUT'],
        'uuid': uuid,
        'body': {
            'interval': quote_interval
        }
    }

    payload_str = json.dumps(payload)
    payload_len_str = str(len(payload_str)).rjust(payload_size_len, '0')
    request_str = payload_marker + payload_len_str + payload_str

    with serial.Serial(port_path, port_baudrate, timeout=port_timeout_short_s) as port:
        port.write(request_str.encode('ascii'))
        response = port.read(2048)
        resp_json = json.loads(response[10:])
        status = resp_json['status']
        if status == 200:
            print(f'Request success')
            return True
        print(f'Request failed, status {status}')
        return False

def main():
    parser = argparse.ArgumentParser(
        prog='custom_quotes',
        description='Script used to test functionality of custom quotes endpoint in Harmony project'
    )
    parser.add_argument('-p', '--port',
                        metavar='path_to_com_port',
                        help='path to COM port of the device (e.g. /dev/ttyACM0)')
    parser.add_argument('-a', '--add',
                        action='store_true',
                        help='add new custom quote')
    parser.add_argument('-d', '--delete',
                        metavar='quote_id_to_delete',
                        type=int,
                        help='delete custom quote')
    parser.add_argument('-e', '--edit',
                        metavar='quote_id_to_edit',
                        type=int,
                        help='edit custom quote')
    parser.add_argument('-q', '--quote',
                        metavar='string_with_quote',
                        help='string with new quote')
    parser.add_argument('-t', '--author',
                        metavar='string_with_author',
                        help='string with author')
    parser.add_argument('-g', '--group',
                        metavar='string_with_group_type',
                        help='string with type of group of displayed quotes [Predefined or Custom]')
    parser.add_argument('-i', '--interval',
                        metavar='quotes_display_interval',
                        help='quotes display interval [\'x\' minutes or \'at midnight\']')

    args = parser.parse_args()
    if not args.port:
        print('Invalid usage: please specify device port')
        print('Run with -h to see help')
        return
    if not args.add and not args.delete and not args.edit and not args.group and not args.interval:
        print('Invalid usage: please specify add, delete, edit, group or interval argument')
        print('Run with -h to see help')
        return
    if args.add:
        if not args.quote:
            print('Invalid usage: please add quote')
            print('Run with -h to see help')
            return
        else:
            print("adding new quotes: " + args.quote)
            status = add_quote(args.port, args.quote, args.author)
    elif args.delete:
        print("deleting quotes nr: " + str(args.delete))
        status = delete_quote(args.port, args.delete)
    elif args.edit:
        if not args.quote:
            print('Invalid usage: please add quote')
            print('Run with -h to see help')
            return
        else:
            print("editing quotes nr: " + str(args.edit))
            status = edit_quote(args.port, args.edit, args.quote, args.author)
    elif args.group:
        print("quotes group: " + args.group)
        status = change_group(args.port, args.group)
    elif args.interval:
        print("quotes interval: " + args.interval)
        status = change_interval(args.port, args.interval)

if __name__ == '__main__':
    main()