Initial InfiniSim project

This commit is contained in:
Reinhold Gschweicher
2022-02-16 21:42:29 +01:00
parent cea006b049
commit f19355949b
78 changed files with 9978 additions and 0 deletions

View File

@@ -0,0 +1,125 @@
#include "components/ble/AlertNotificationService.h"
#include <hal/nrf_rtc.h>
#include <cstring>
#include <algorithm>
#include "components/ble/NotificationManager.h"
#include "systemtask/SystemTask.h"
using namespace Pinetime::Controllers;
//constexpr ble_uuid16_t AlertNotificationService::ansUuid;
//constexpr ble_uuid16_t AlertNotificationService::ansCharUuid;
//constexpr ble_uuid128_t AlertNotificationService::notificationEventUuid;
int AlertNotificationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
auto anService = static_cast<AlertNotificationService*>(arg);
return anService->OnAlert(conn_handle, attr_handle, ctxt);
}
void AlertNotificationService::Init() {
// int res;
// res = ble_gatts_count_cfg(serviceDefinition);
// ASSERT(res == 0);
//
// res = ble_gatts_add_svcs(serviceDefinition);
// ASSERT(res == 0);
}
AlertNotificationService::AlertNotificationService(System::SystemTask& systemTask, NotificationManager& notificationManager)
:
// : characteristicDefinition {{.uuid = &ansCharUuid.u, .access_cb = AlertNotificationCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE},
// {.uuid = &notificationEventUuid.u,
// .access_cb = AlertNotificationCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_NOTIFY,
// .val_handle = &eventHandle},
// {0}},
// serviceDefinition {
// {/* Device Information Service */
// .type = BLE_GATT_SVC_TYPE_PRIMARY,
// .uuid = &ansUuid.u,
// .characteristics = characteristicDefinition},
// {0},
// },
systemTask {systemTask},
notificationManager {notificationManager} {
}
int AlertNotificationService::OnAlert(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
// if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
// constexpr size_t stringTerminatorSize = 1; // end of string '\0'
// constexpr size_t headerSize = 3;
// const auto maxMessageSize {NotificationManager::MaximumMessageSize()};
// const auto maxBufferSize {maxMessageSize + headerSize};
//
// // Ignore notifications with empty message
// const auto packetLen = OS_MBUF_PKTLEN(ctxt->om);
// if (packetLen <= headerSize) {
// return 0;
// }
//
// size_t bufferSize = std::min(packetLen + stringTerminatorSize, maxBufferSize);
// auto messageSize = std::min(maxMessageSize, (bufferSize - headerSize));
// Categories category;
//
// NotificationManager::Notification notif;
// os_mbuf_copydata(ctxt->om, headerSize, messageSize - 1, notif.message.data());
// os_mbuf_copydata(ctxt->om, 0, 1, &category);
// notif.message[messageSize - 1] = '\0';
// notif.size = messageSize;
//
// // TODO convert all ANS categories to NotificationController categories
// switch (category) {
// case Categories::Call:
// notif.category = Pinetime::Controllers::NotificationManager::Categories::IncomingCall;
// break;
// default:
// notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
// break;
// }
//
// auto event = Pinetime::System::Messages::OnNewNotification;
// notificationManager.Push(std::move(notif));
// systemTask.PushMessage(event);
// }
return 0;
}
void AlertNotificationService::AcceptIncomingCall() {
// auto response = IncomingCallResponses::Answer;
// auto* om = ble_hs_mbuf_from_flat(&response, 1);
//
// uint16_t connectionHandle = systemTask.nimble().connHandle();
//
// if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
// return;
// }
//
// ble_gattc_notify_custom(connectionHandle, eventHandle, om);
}
void AlertNotificationService::RejectIncomingCall() {
// auto response = IncomingCallResponses::Reject;
// auto* om = ble_hs_mbuf_from_flat(&response, 1);
//
// uint16_t connectionHandle = systemTask.nimble().connHandle();
//
// if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
// return;
// }
//
// ble_gattc_notify_custom(connectionHandle, eventHandle, om);
}
void AlertNotificationService::MuteIncomingCall() {
// auto response = IncomingCallResponses::Mute;
// auto* om = ble_hs_mbuf_from_flat(&response, 1);
//
// uint16_t connectionHandle = systemTask.nimble().connHandle();
//
// if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
// return;
// }
//
// ble_gattc_notify_custom(connectionHandle, eventHandle, om);
}

View File

@@ -0,0 +1,68 @@
#pragma once
#include <cstdint>
#include <array>
//#define min // workaround: nimble's min/max macros conflict with libstdc++
//#define max
//#include <host/ble_gap.h>
//#undef max
//#undef min
// 00020001-78fc-48fe-8e23-433b3a1942d0
//#define NOTIFICATION_EVENT_SERVICE_UUID_BASE \
// { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, 0x01, 0x00, 0x02, 0x00 }
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class NotificationManager;
class AlertNotificationService {
public:
AlertNotificationService(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::NotificationManager& notificationManager);
void Init();
int OnAlert(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
void AcceptIncomingCall();
void RejectIncomingCall();
void MuteIncomingCall();
enum class IncomingCallResponses : uint8_t { Reject = 0x00, Answer = 0x01, Mute = 0x02 };
private:
enum class Categories : uint8_t {
SimpleAlert = 0x00,
Email = 0x01,
News = 0x02,
Call = 0x03,
MissedCall = 0x04,
MmsSms = 0x05,
VoiceMail = 0x06,
Schedule = 0x07,
HighPrioritizedAlert = 0x08,
InstantMessage = 0x09,
All = 0xff
};
static constexpr uint16_t ansId {0x1811};
static constexpr uint16_t ansCharId {0x2a46};
// static constexpr ble_uuid16_t ansUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansId};
// static constexpr ble_uuid16_t ansCharUuid {.u {.type = BLE_UUID_TYPE_16}, .value = ansCharId};
// static constexpr ble_uuid128_t notificationEventUuid {.u {.type = BLE_UUID_TYPE_128}, .value = NOTIFICATION_EVENT_SERVICE_UUID_BASE};
// struct ble_gatt_chr_def characteristicDefinition[3];
// struct ble_gatt_svc_def serviceDefinition[2];
Pinetime::System::SystemTask& systemTask;
NotificationManager& notificationManager;
uint16_t eventHandle;
};
}
}

View File

@@ -0,0 +1,198 @@
/* Copyright (C) 2020-2021 JF, Adam Pigg, Avamander
This file is part of InfiniTime.
InfiniTime is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
InfiniTime is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "components/ble/MusicService.h"
#include "systemtask/SystemTask.h"
namespace {
// 0000yyxx-78fc-48fe-8e23-433b3a1942d0
//constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) {
// return ble_uuid128_t{
// .u = {.type = BLE_UUID_TYPE_128},
// .value = { 0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, x, y, 0x00, 0x00 }
// };
//}
// 00000000-78fc-48fe-8e23-433b3a1942d0
//constexpr ble_uuid128_t BaseUuid() {
// return CharUuid(0x00, 0x00);
//}
//constexpr ble_uuid128_t msUuid {BaseUuid()};
//constexpr ble_uuid128_t msEventCharUuid {CharUuid(0x01, 0x00)};
//constexpr ble_uuid128_t msStatusCharUuid {CharUuid(0x02, 0x00)};
//constexpr ble_uuid128_t msArtistCharUuid {CharUuid(0x03, 0x00)};
//constexpr ble_uuid128_t msTrackCharUuid {CharUuid(0x04, 0x00)};
//constexpr ble_uuid128_t msAlbumCharUuid {CharUuid(0x05, 0x00)};
//constexpr ble_uuid128_t msPositionCharUuid {CharUuid(0x06, 0x00)};
//constexpr ble_uuid128_t msTotalLengthCharUuid {CharUuid(0x07, 0x00)};
//constexpr ble_uuid128_t msTrackNumberCharUuid {CharUuid(0x08, 0x00)};
//constexpr ble_uuid128_t msTrackTotalCharUuid {CharUuid(0x09, 0x00)};
//constexpr ble_uuid128_t msPlaybackSpeedCharUuid {CharUuid(0x0a, 0x00)};
//constexpr ble_uuid128_t msRepeatCharUuid {CharUuid(0x0b, 0x00)};
//constexpr ble_uuid128_t msShuffleCharUuid {CharUuid(0x0c, 0x00)};
//int MusicCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
// return static_cast<Pinetime::Controllers::MusicService*>(arg)->OnCommand(conn_handle, attr_handle, ctxt);
//}
}
Pinetime::Controllers::MusicService::MusicService(Pinetime::System::SystemTask& system) : m_system(system) {
// characteristicDefinition[0] = {.uuid = &msEventCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_NOTIFY,
// .val_handle = &eventHandle};
// characteristicDefinition[1] = {.uuid = &msStatusCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[2] = {.uuid = &msTrackCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[3] = {.uuid = &msArtistCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[4] = {.uuid = &msAlbumCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[5] = {.uuid = &msPositionCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[6] = {.uuid = &msTotalLengthCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[7] = {.uuid = &msTotalLengthCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[8] = {.uuid = &msTrackNumberCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[9] = {.uuid = &msTrackTotalCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[10] = {.uuid = &msPlaybackSpeedCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[11] = {.uuid = &msRepeatCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[12] = {.uuid = &msShuffleCharUuid.u,
// .access_cb = MusicCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[13] = {0};
//
// serviceDefinition[0] = {
// .type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &msUuid.u, .characteristics = characteristicDefinition};
// serviceDefinition[1] = {0};
}
void Pinetime::Controllers::MusicService::Init() {
//uint8_t res = 0;
//res = ble_gatts_count_cfg(serviceDefinition);
//ASSERT(res == 0);
//res = ble_gatts_add_svcs(serviceDefinition);
//ASSERT(res == 0);
}
//int Pinetime::Controllers::MusicService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
// if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
// size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
// char data[notifSize + 1];
// data[notifSize] = '\0';
// os_mbuf_copydata(ctxt->om, 0, notifSize, data);
// char* s = &data[0];
// if (ble_uuid_cmp(ctxt->chr->uuid, &msArtistCharUuid.u) == 0) {
// artistName = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackCharUuid.u) == 0) {
// trackName = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msAlbumCharUuid.u) == 0) {
// albumName = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msStatusCharUuid.u) == 0) {
// playing = s[0];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msRepeatCharUuid.u) == 0) {
// repeat = s[0];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msShuffleCharUuid.u) == 0) {
// shuffle = s[0];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msPositionCharUuid.u) == 0) {
// trackProgress = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTotalLengthCharUuid.u) == 0) {
// trackLength = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackNumberCharUuid.u) == 0) {
// trackNumber = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msTrackTotalCharUuid.u) == 0) {
// tracksTotal = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &msPlaybackSpeedCharUuid.u) == 0) {
// playbackSpeed = static_cast<float>(((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3])) / 100.0f;
// }
// }
// return 0;
//}
std::string Pinetime::Controllers::MusicService::getAlbum() const {
return albumName;
}
std::string Pinetime::Controllers::MusicService::getArtist() const {
return artistName;
}
std::string Pinetime::Controllers::MusicService::getTrack() const {
return trackName;
}
bool Pinetime::Controllers::MusicService::isPlaying() const {
return playing;
}
float Pinetime::Controllers::MusicService::getPlaybackSpeed() const {
return playbackSpeed;
}
int Pinetime::Controllers::MusicService::getProgress() const {
return trackProgress;
}
int Pinetime::Controllers::MusicService::getTrackLength() const {
return trackLength;
}
void Pinetime::Controllers::MusicService::event(char event) {
std::ignore = event;
//auto* om = ble_hs_mbuf_from_flat(&event, 1);
//uint16_t connectionHandle = m_system.nimble().connHandle();
//if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
// return;
//}
//ble_gattc_notify_custom(connectionHandle, eventHandle, om);
}

View File

@@ -0,0 +1,87 @@
/* Copyright (C) 2020-2021 JF, Adam Pigg, Avamander
This file is part of InfiniTime.
InfiniTime is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
InfiniTime is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include <string>
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class MusicService {
public:
explicit MusicService(Pinetime::System::SystemTask& system);
void Init();
//int OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
void event(char event);
std::string getArtist() const;
std::string getTrack() const;
std::string getAlbum() const;
int getProgress() const;
int getTrackLength() const;
float getPlaybackSpeed() const;
bool isPlaying() const;
static const char EVENT_MUSIC_OPEN = 0xe0;
static const char EVENT_MUSIC_PLAY = 0x00;
static const char EVENT_MUSIC_PAUSE = 0x01;
static const char EVENT_MUSIC_NEXT = 0x03;
static const char EVENT_MUSIC_PREV = 0x04;
static const char EVENT_MUSIC_VOLUP = 0x05;
static const char EVENT_MUSIC_VOLDOWN = 0x06;
enum MusicStatus { NotPlaying = 0x00, Playing = 0x01 };
private:
//struct ble_gatt_chr_def characteristicDefinition[14];
//struct ble_gatt_svc_def serviceDefinition[2];
uint16_t eventHandle {};
std::string artistName {"Waiting for"};
std::string albumName {};
std::string trackName {"track information.."};
bool playing {false};
int trackProgress {0};
int trackLength {0};
int trackNumber {};
int tracksTotal {};
float playbackSpeed {1.0f};
bool repeat {false};
bool shuffle {false};
Pinetime::System::SystemTask& m_system;
};
}
}

View File

@@ -0,0 +1,111 @@
/* Copyright (C) 2021 Adam Pigg
This file is part of InfiniTime.
InfiniTime is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
InfiniTime is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "components/ble/NavigationService.h"
#include "systemtask/SystemTask.h"
namespace {
// // 0001yyxx-78fc-48fe-8e23-433b3a1942d0
// constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) {
// return ble_uuid128_t {.u = {.type = BLE_UUID_TYPE_128},
// .value = {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, x, y, 0x01, 0x00}};
// }
//
// // 00010000-78fc-48fe-8e23-433b3a1942d0
// constexpr ble_uuid128_t BaseUuid() {
// return CharUuid(0x00, 0x00);
// }
//
// constexpr ble_uuid128_t navUuid {BaseUuid()};
//
// constexpr ble_uuid128_t navFlagCharUuid {CharUuid(0x01, 0x00)};
// constexpr ble_uuid128_t navNarrativeCharUuid {CharUuid(0x02, 0x00)};
// constexpr ble_uuid128_t navManDistCharUuid {CharUuid(0x03, 0x00)};
// constexpr ble_uuid128_t navProgressCharUuid {CharUuid(0x04, 0x00)};
//
// int NAVCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
// auto navService = static_cast<Pinetime::Controllers::NavigationService*>(arg);
// return navService->OnCommand(conn_handle, attr_handle, ctxt);
// }
} // namespace
Pinetime::Controllers::NavigationService::NavigationService(Pinetime::System::SystemTask& system) : m_system(system) {
// characteristicDefinition[0] = {
// .uuid = &navFlagCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
//
// characteristicDefinition[1] = {
// .uuid = &navNarrativeCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[2] = {
// .uuid = &navManDistCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
// characteristicDefinition[3] = {
// .uuid = &navProgressCharUuid.u, .access_cb = NAVCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ};
//
// characteristicDefinition[4] = {0};
//
// serviceDefinition[0] = {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &navUuid.u, .characteristics = characteristicDefinition};
// serviceDefinition[1] = {0};
m_progress = 0;
}
void Pinetime::Controllers::NavigationService::Init() {
// int res = 0;
// res = ble_gatts_count_cfg(serviceDefinition);
// ASSERT(res == 0);
//
// res = ble_gatts_add_svcs(serviceDefinition);
// ASSERT(res == 0);
}
//int Pinetime::Controllers::NavigationService::OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
//
// if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
// size_t notifSize = OS_MBUF_PKTLEN(ctxt->om);
// uint8_t data[notifSize + 1];
// data[notifSize] = '\0';
// os_mbuf_copydata(ctxt->om, 0, notifSize, data);
// char* s = (char*) &data[0];
// if (ble_uuid_cmp(ctxt->chr->uuid, &navFlagCharUuid.u) == 0) {
// m_flag = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &navNarrativeCharUuid.u) == 0) {
// m_narrative = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &navManDistCharUuid.u) == 0) {
// m_manDist = s;
// } else if (ble_uuid_cmp(ctxt->chr->uuid, &navProgressCharUuid.u) == 0) {
// m_progress = data[0];
// }
// }
// return 0;
//}
std::string Pinetime::Controllers::NavigationService::getFlag() {
return m_flag;
}
std::string Pinetime::Controllers::NavigationService::getNarrative() {
return m_narrative;
}
std::string Pinetime::Controllers::NavigationService::getManDist() {
return m_manDist;
}
int Pinetime::Controllers::NavigationService::getProgress() {
return m_progress;
}

View File

@@ -0,0 +1,63 @@
/* Copyright (C) 2021 Adam Pigg
This file is part of InfiniTime.
InfiniTime is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
InfiniTime is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include <string>
//#define min // workaround: nimble's min/max macros conflict with libstdc++
//#define max
//#include <host/ble_gap.h>
//#include <host/ble_uuid.h>
//#undef max
//#undef min
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class NavigationService {
public:
explicit NavigationService(Pinetime::System::SystemTask& system);
void Init();
// int OnCommand(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt);
std::string getFlag();
std::string getNarrative();
std::string getManDist();
int getProgress();
private:
// struct ble_gatt_chr_def characteristicDefinition[5];
// struct ble_gatt_svc_def serviceDefinition[2];
std::string m_flag;
std::string m_narrative;
std::string m_manDist;
int m_progress = 0;
Pinetime::System::SystemTask& m_system;
};
}
}

View File

@@ -0,0 +1,456 @@
#include "components/ble/NimbleController.h"
#include <cstring>
//#include <hal/nrf_rtc.h>
//#define min // workaround: nimble's min/max macros conflict with libstdc++
//#define max
//#include <host/ble_gap.h>
//#include <host/ble_hs.h>
//#include <host/ble_hs_id.h>
//#include <host/util/util.h>
//#include <controller/ble_ll.h>
//#include <controller/ble_hw.h>
//#include <services/gap/ble_svc_gap.h>
//#include <services/gatt/ble_svc_gatt.h>
//#undef max
//#undef min
#include "components/ble/BleController.h"
#include "components/ble/NotificationManager.h"
#include "components/datetime/DateTimeController.h"
#include "components/fs/FS.h"
#include "systemtask/SystemTask.h"
using namespace Pinetime::Controllers;
NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
Pinetime::Controllers::Ble& bleController,
DateTime& dateTimeController,
Pinetime::Controllers::NotificationManager& notificationManager,
Controllers::Battery& batteryController,
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController,
Controllers::FS& fs)
: systemTask {systemTask},
bleController {bleController},
dateTimeController {dateTimeController},
notificationManager {notificationManager},
spiNorFlash {spiNorFlash},
fs {fs},
// dfuService {systemTask, bleController, spiNorFlash},
// currentTimeClient {dateTimeController},
anService {systemTask, notificationManager},
// alertNotificationClient {systemTask, notificationManager},
// currentTimeService {dateTimeController},
musicService {systemTask},
weatherService {systemTask, dateTimeController},
navService {systemTask} {
// batteryInformationService {batteryController},
// immediateAlertService {systemTask, notificationManager},
// heartRateService {systemTask, heartRateController},
// motionService {systemTask, motionController},
// fsService {systemTask, fs},
// serviceDiscovery({&currentTimeClient, &alertNotificationClient}) {
}
//void nimble_on_reset(int reason) {
// NRF_LOG_INFO("Nimble lost sync, resetting state; reason=%d", reason);
//}
//
//void nimble_on_sync(void) {
// int rc;
//
// NRF_LOG_INFO("Nimble is synced");
//
// rc = ble_hs_util_ensure_addr(0);
// ASSERT(rc == 0);
//
// nptr->StartAdvertising();
//}
//
//int GAPEventCallback(struct ble_gap_event* event, void* arg) {
// auto nimbleController = static_cast<NimbleController*>(arg);
// return nimbleController->OnGAPEvent(event);
//}
void NimbleController::Init() {
// while (!ble_hs_synced()) {
// }
//
// nptr = this;
// ble_hs_cfg.reset_cb = nimble_on_reset;
// ble_hs_cfg.sync_cb = nimble_on_sync;
// ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
//
// ble_svc_gap_init();
// ble_svc_gatt_init();
//
// deviceInformationService.Init();
// currentTimeClient.Init();
// currentTimeService.Init();
musicService.Init();
weatherService.Init();
navService.Init();
// anService.Init();
// dfuService.Init();
// batteryInformationService.Init();
// immediateAlertService.Init();
// heartRateService.Init();
// motionService.Init();
// fsService.Init();
//
// int rc;
// rc = ble_hs_util_ensure_addr(0);
// ASSERT(rc == 0);
// rc = ble_hs_id_infer_auto(0, &addrType);
// ASSERT(rc == 0);
// rc = ble_svc_gap_device_name_set(deviceName);
// ASSERT(rc == 0);
// rc = ble_svc_gap_device_appearance_set(0xC2);
// ASSERT(rc == 0);
// Pinetime::Controllers::Ble::BleAddress address;
// rc = ble_hs_id_copy_addr(addrType, address.data(), nullptr);
// ASSERT(rc == 0);
//
// bleController.Address(std::move(address));
// switch (addrType) {
// case BLE_OWN_ADDR_PUBLIC:
// bleController.AddressType(Ble::AddressTypes::Public);
// break;
// case BLE_OWN_ADDR_RANDOM:
// bleController.AddressType(Ble::AddressTypes::Random);
// break;
// case BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT:
// bleController.AddressType(Ble::AddressTypes::RPA_Public);
// break;
// case BLE_OWN_ADDR_RPA_RANDOM_DEFAULT:
// bleController.AddressType(Ble::AddressTypes::RPA_Random);
// break;
// }
//
// rc = ble_gatts_start();
// ASSERT(rc == 0);
//
// RestoreBond();
//
// StartAdvertising();
}
//void NimbleController::StartAdvertising() {
// struct ble_gap_adv_params adv_params;
// struct ble_hs_adv_fields fields;
// struct ble_hs_adv_fields rsp_fields;
//
// memset(&adv_params, 0, sizeof(adv_params));
// memset(&fields, 0, sizeof(fields));
// memset(&rsp_fields, 0, sizeof(rsp_fields));
//
// adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
// adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
// /* fast advertise for 30 sec */
// if (fastAdvCount < 15) {
// adv_params.itvl_min = 32;
// adv_params.itvl_max = 47;
// fastAdvCount++;
// } else {
// adv_params.itvl_min = 1636;
// adv_params.itvl_max = 1651;
// }
//
// fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
// fields.uuids128 = &dfuServiceUuid;
// fields.num_uuids128 = 1;
// fields.uuids128_is_complete = 1;
// fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
//
// rsp_fields.name = reinterpret_cast<const uint8_t*>(deviceName);
// rsp_fields.name_len = strlen(deviceName);
// rsp_fields.name_is_complete = 1;
//
// int rc;
// rc = ble_gap_adv_set_fields(&fields);
// ASSERT(rc == 0);
//
// rc = ble_gap_adv_rsp_set_fields(&rsp_fields);
// ASSERT(rc == 0);
//
// rc = ble_gap_adv_start(addrType, NULL, 2000, &adv_params, GAPEventCallback, this);
// ASSERT(rc == 0);
//}
//
//int NimbleController::OnGAPEvent(ble_gap_event* event) {
// switch (event->type) {
// case BLE_GAP_EVENT_ADV_COMPLETE:
// NRF_LOG_INFO("Advertising event : BLE_GAP_EVENT_ADV_COMPLETE");
// NRF_LOG_INFO("reason=%d; status=%0X", event->adv_complete.reason, event->connect.status);
// StartAdvertising();
// break;
//
// case BLE_GAP_EVENT_CONNECT:
// /* A new connection was established or a connection attempt failed. */
// NRF_LOG_INFO("Connect event : BLE_GAP_EVENT_CONNECT");
// NRF_LOG_INFO("connection %s; status=%0X ", event->connect.status == 0 ? "established" : "failed", event->connect.status);
//
// if (event->connect.status != 0) {
// /* Connection failed; resume advertising. */
// currentTimeClient.Reset();
// alertNotificationClient.Reset();
// connectionHandle = BLE_HS_CONN_HANDLE_NONE;
// bleController.Disconnect();
// fastAdvCount = 0;
// StartAdvertising();
// } else {
// connectionHandle = event->connect.conn_handle;
// bleController.Connect();
// systemTask.PushMessage(Pinetime::System::Messages::BleConnected);
// // Service discovery is deferred via systemtask
// }
// break;
//
// case BLE_GAP_EVENT_DISCONNECT:
// /* Connection terminated; resume advertising. */
// NRF_LOG_INFO("Disconnect event : BLE_GAP_EVENT_DISCONNECT");
// NRF_LOG_INFO("disconnect reason=%d", event->disconnect.reason);
//
// if (event->disconnect.conn.sec_state.bonded) {
// PersistBond(event->disconnect.conn);
// }
//
// currentTimeClient.Reset();
// alertNotificationClient.Reset();
// connectionHandle = BLE_HS_CONN_HANDLE_NONE;
// bleController.Disconnect();
// fastAdvCount = 0;
// StartAdvertising();
// break;
//
// case BLE_GAP_EVENT_CONN_UPDATE:
// /* The central has updated the connection parameters. */
// NRF_LOG_INFO("Update event : BLE_GAP_EVENT_CONN_UPDATE");
// NRF_LOG_INFO("update status=%0X ", event->conn_update.status);
// break;
//
// case BLE_GAP_EVENT_CONN_UPDATE_REQ:
// /* The central has requested updated connection parameters */
// NRF_LOG_INFO("Update event : BLE_GAP_EVENT_CONN_UPDATE_REQ");
// NRF_LOG_INFO("update request : itvl_min=%d itvl_max=%d latency=%d supervision=%d",
// event->conn_update_req.peer_params->itvl_min,
// event->conn_update_req.peer_params->itvl_max,
// event->conn_update_req.peer_params->latency,
// event->conn_update_req.peer_params->supervision_timeout);
// break;
//
// case BLE_GAP_EVENT_ENC_CHANGE:
// /* Encryption has been enabled or disabled for this connection. */
// NRF_LOG_INFO("Security event : BLE_GAP_EVENT_ENC_CHANGE");
// NRF_LOG_INFO("encryption change event; status=%0X ", event->enc_change.status);
//
// if (event->enc_change.status == 0) {
// struct ble_gap_conn_desc desc;
// ble_gap_conn_find(event->enc_change.conn_handle, &desc);
// if (desc.sec_state.bonded) {
// PersistBond(desc);
// }
//
// NRF_LOG_INFO("new state: encrypted=%d authenticated=%d bonded=%d key_size=%d",
// desc.sec_state.encrypted,
// desc.sec_state.authenticated,
// desc.sec_state.bonded,
// desc.sec_state.key_size);
// }
// break;
//
// case BLE_GAP_EVENT_PASSKEY_ACTION:
// /* Authentication has been requested for this connection.
// *
// * BLE authentication is determined by the combination of I/O capabilities
// * on the central and peripheral. When the peripheral is display only and
// * the central has a keyboard and display then passkey auth is selected.
// * When both the central and peripheral have displays and support yes/no
// * buttons then numeric comparison is selected. We currently advertise
// * display capability only so we only handle the "display" action here.
// *
// * Standards insist that the rand() PRNG be deterministic.
// * Use the tinycrypt prng here since rand() is predictable.
// */
// NRF_LOG_INFO("Security event : BLE_GAP_EVENT_PASSKEY_ACTION");
// if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
// struct ble_sm_io pkey = {0};
// pkey.action = event->passkey.params.action;
// pkey.passkey = ble_ll_rand() % 1000000;
// bleController.SetPairingKey(pkey.passkey);
// systemTask.PushMessage(Pinetime::System::Messages::OnPairing);
// ble_sm_inject_io(event->passkey.conn_handle, &pkey);
// }
// break;
//
// case BLE_GAP_EVENT_SUBSCRIBE:
// NRF_LOG_INFO("Subscribe event; conn_handle=%d attr_handle=%d "
// "reason=%d prevn=%d curn=%d previ=%d curi=???\n",
// event->subscribe.conn_handle,
// event->subscribe.attr_handle,
// event->subscribe.reason,
// event->subscribe.prev_notify,
// event->subscribe.cur_notify,
// event->subscribe.prev_indicate);
//
// if (event->subscribe.reason == BLE_GAP_SUBSCRIBE_REASON_TERM) {
// heartRateService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// motionService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// } else if (event->subscribe.prev_notify == 0 && event->subscribe.cur_notify == 1) {
// heartRateService.SubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// motionService.SubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// } else if (event->subscribe.prev_notify == 1 && event->subscribe.cur_notify == 0) {
// heartRateService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// motionService.UnsubscribeNotification(event->subscribe.conn_handle, event->subscribe.attr_handle);
// }
// break;
//
// case BLE_GAP_EVENT_MTU:
// NRF_LOG_INFO("MTU Update event; conn_handle=%d cid=%d mtu=%d", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value);
// break;
//
// case BLE_GAP_EVENT_REPEAT_PAIRING: {
// NRF_LOG_INFO("Pairing event : BLE_GAP_EVENT_REPEAT_PAIRING");
// /* We already have a bond with the peer, but it is attempting to
// * establish a new secure link. This app sacrifices security for
// * convenience: just throw away the old bond and accept the new link.
// */
//
// /* Delete the old bond. */
// struct ble_gap_conn_desc desc;
// ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
// ble_store_util_delete_peer(&desc.peer_id_addr);
//
// /* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
// * continue with the pairing operation.
// */
// }
// return BLE_GAP_REPEAT_PAIRING_RETRY;
//
// case BLE_GAP_EVENT_NOTIFY_RX: {
// /* Peer sent us a notification or indication. */
// /* Attribute data is contained in event->notify_rx.attr_data. */
// NRF_LOG_INFO("Notify event : BLE_GAP_EVENT_NOTIFY_RX");
// size_t notifSize = OS_MBUF_PKTLEN(event->notify_rx.om);
//
// NRF_LOG_INFO("received %s; conn_handle=%d attr_handle=%d "
// "attr_len=%d",
// event->notify_rx.indication ? "indication" : "notification",
// event->notify_rx.conn_handle,
// event->notify_rx.attr_handle,
// notifSize);
//
// alertNotificationClient.OnNotification(event);
// } break;
//
// case BLE_GAP_EVENT_NOTIFY_TX:
// NRF_LOG_INFO("Notify event : BLE_GAP_EVENT_NOTIFY_TX");
// break;
//
// case BLE_GAP_EVENT_IDENTITY_RESOLVED:
// NRF_LOG_INFO("Identity event : BLE_GAP_EVENT_IDENTITY_RESOLVED");
// break;
//
// default:
// NRF_LOG_INFO("UNHANDLED GAP event : %d", event->type);
// break;
// }
// return 0;
//}
void NimbleController::StartDiscovery() {
// if (connectionHandle != BLE_HS_CONN_HANDLE_NONE) {
// serviceDiscovery.StartDiscovery(connectionHandle);
// }
}
//uint16_t NimbleController::connHandle() {
// return connectionHandle;
//}
void NimbleController::NotifyBatteryLevel(uint8_t level) {
// if (connectionHandle != BLE_HS_CONN_HANDLE_NONE) {
// batteryInformationService.NotifyBatteryLevel(connectionHandle, level);
// }
}
//void NimbleController::PersistBond(struct ble_gap_conn_desc& desc) {
// union ble_store_key key;
// union ble_store_value our_sec, peer_sec, peer_cccd_set[MYNEWT_VAL(BLE_STORE_MAX_CCCDS)] = {0};
// int rc;
//
// memset(&key, 0, sizeof key);
// memset(&our_sec, 0, sizeof our_sec);
// key.sec.peer_addr = desc.peer_id_addr;
// rc = ble_store_read_our_sec(&key.sec, &our_sec.sec);
//
// if (memcmp(&our_sec.sec, &bondId, sizeof bondId) == 0) {
// return;
// }
//
// memcpy(&bondId, &our_sec.sec, sizeof bondId);
//
// memset(&key, 0, sizeof key);
// memset(&peer_sec, 0, sizeof peer_sec);
// key.sec.peer_addr = desc.peer_id_addr;
// rc += ble_store_read_peer_sec(&key.sec, &peer_sec.sec);
//
// if (rc == 0) {
// memset(&key, 0, sizeof key);
// key.cccd.peer_addr = desc.peer_id_addr;
// int peer_count = 0;
// ble_store_util_count(BLE_STORE_OBJ_TYPE_CCCD, &peer_count);
// for (int i = 0; i < peer_count; i++) {
// key.cccd.idx = peer_count;
// ble_store_read_cccd(&key.cccd, &peer_cccd_set[i].cccd);
// }
//
// /* Wakeup Spi and SpiNorFlash before accessing the file system
// * This should be fixed in the FS driver
// */
// systemTask.PushMessage(Pinetime::System::Messages::GoToRunning);
// systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
// vTaskDelay(10);
//
// lfs_file_t file_p;
//
// rc = fs.FileOpen(&file_p, "/bond.dat", LFS_O_WRONLY | LFS_O_CREAT);
// if (rc == 0) {
// fs.FileWrite(&file_p, reinterpret_cast<uint8_t*>(&our_sec.sec), sizeof our_sec);
// fs.FileWrite(&file_p, reinterpret_cast<uint8_t*>(&peer_sec.sec), sizeof peer_sec);
// fs.FileWrite(&file_p, reinterpret_cast<const uint8_t*>(&peer_count), 1);
// for (int i = 0; i < peer_count; i++) {
// fs.FileWrite(&file_p, reinterpret_cast<uint8_t*>(&peer_cccd_set[i].cccd), sizeof(struct ble_store_value_cccd));
// }
// fs.FileClose(&file_p);
// }
// systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
// }
//}
//void NimbleController::RestoreBond() {
// lfs_file_t file_p;
// union ble_store_value sec, cccd;
// uint8_t peer_count = 0;
//
// if (fs.FileOpen(&file_p, "/bond.dat", LFS_O_RDONLY) == 0) {
// memset(&sec, 0, sizeof sec);
// fs.FileRead(&file_p, reinterpret_cast<uint8_t*>(&sec.sec), sizeof sec);
// ble_store_write_our_sec(&sec.sec);
//
// memset(&sec, 0, sizeof sec);
// fs.FileRead(&file_p, reinterpret_cast<uint8_t*>(&sec.sec), sizeof sec);
// ble_store_write_peer_sec(&sec.sec);
//
// fs.FileRead(&file_p, &peer_count, 1);
// for (int i = 0; i < peer_count; i++) {
// fs.FileRead(&file_p, reinterpret_cast<uint8_t*>(&cccd.cccd), sizeof(struct ble_store_value_cccd));
// ble_store_write_cccd(&cccd.cccd);
// }
//
// fs.FileClose(&file_p);
// fs.FileDelete("/bond.dat");
// }
//}

View File

@@ -0,0 +1,132 @@
#pragma once
#include <cstdint>
//#define min // workaround: nimble's min/max macros conflict with libstdc++
//#define max
//#include <host/ble_gap.h>
//#undef max
//#undef min
//#include "components/ble/AlertNotificationClient.h"
#include "components/ble/AlertNotificationService.h"
//#include "components/ble/BatteryInformationService.h"
//#include "components/ble/CurrentTimeClient.h"
//#include "components/ble/CurrentTimeService.h"
//#include "components/ble/DeviceInformationService.h"
//#include "components/ble/DfuService.h"
//#include "components/ble/HeartRateService.h"
//#include "components/ble/ImmediateAlertService.h"
#include "components/ble/MusicService.h"
#include "components/ble/NavigationService.h"
//#include "components/ble/ServiceDiscovery.h"
//#include "components/ble/MotionService.h"
#include "components/ble/weather/WeatherService.h"
#include "components/fs/FS.h"
//#include "components/ble/FSService.h"
namespace Pinetime {
namespace Drivers {
class SpiNorFlash;
}
namespace System {
class SystemTask;
}
namespace Controllers {
class Battery;
class Ble;
class DateTime;
class FS;
class HeartRateController;
class MotionController;
class NotificationManager;
class NimbleController {
public:
NimbleController(Pinetime::System::SystemTask& systemTask,
Pinetime::Controllers::Ble& bleController,
DateTime& dateTimeController,
Pinetime::Controllers::NotificationManager& notificationManager,
Controllers::Battery& batteryController,
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
Controllers::HeartRateController& heartRateController,
Controllers::MotionController& motionController,
Pinetime::Controllers::FS& fs);
void Init();
void StartAdvertising();
// int OnGAPEvent(ble_gap_event* event);
// int OnDiscoveryEvent(uint16_t i, const ble_gatt_error* pError, const ble_gatt_svc* pSvc);
// int OnCTSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
// int OnANSCharacteristicDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
// int OnCurrentTimeReadResult(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute);
// int OnANSDescriptorDiscoveryEventCallback(uint16_t connectionHandle,
// const ble_gatt_error* error,
// uint16_t characteristicValueHandle,
// const ble_gatt_dsc* descriptor);
void StartDiscovery();
Pinetime::Controllers::MusicService& music() {
return musicService;
};
Pinetime::Controllers::NavigationService& navigation() {
return navService;
};
Pinetime::Controllers::AlertNotificationService& alertService() {
return anService;
};
Pinetime::Controllers::WeatherService& weather() {
return weatherService;
};
uint16_t connHandle();
void NotifyBatteryLevel(uint8_t level);
void RestartFastAdv() {
fastAdvCount = 0;
}
private:
// void PersistBond(struct ble_gap_conn_desc& desc);
// void RestoreBond();
static constexpr const char* deviceName = "InfiniTime";
Pinetime::System::SystemTask& systemTask;
Pinetime::Controllers::Ble& bleController;
DateTime& dateTimeController;
Pinetime::Controllers::NotificationManager& notificationManager;
Pinetime::Drivers::SpiNorFlash& spiNorFlash;
Pinetime::Controllers::FS& fs;
// Pinetime::Controllers::DfuService dfuService;
// DeviceInformationService deviceInformationService;
// CurrentTimeClient currentTimeClient;
AlertNotificationService anService;
// AlertNotificationClient alertNotificationClient;
// CurrentTimeService currentTimeService;
MusicService musicService;
WeatherService weatherService;
NavigationService navService;
// BatteryInformationService batteryInformationService;
// ImmediateAlertService immediateAlertService;
// HeartRateService heartRateService;
// MotionService motionService;
// FSService fsService;
// ServiceDiscovery serviceDiscovery;
uint8_t addrType;
// uint16_t connectionHandle = BLE_HS_CONN_HANDLE_NONE;
uint8_t fastAdvCount = 0;
uint8_t bondId[16] = {0};
// ble_uuid128_t dfuServiceUuid {
// .u {.type = BLE_UUID_TYPE_128},
// .value = {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x30, 0x15, 0x00, 0x00}};
};
// static NimbleController* nptr;
}
}

View File

@@ -0,0 +1,602 @@
/* Copyright (C) 2021 Avamander
This file is part of InfiniTime.
InfiniTime is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
InfiniTime is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//#include <qcbor/qcbor_spiffy_decode.h>
#include "components/ble/weather/WeatherService.h"
//#include "libs/QCBOR/inc/qcbor/qcbor.h"
#include "systemtask/SystemTask.h"
using namespace Pinetime::Controllers;
int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
return static_cast<Pinetime::Controllers::WeatherService*>(arg)->OnCommand(connHandle, attrHandle, ctxt);
}
WeatherService::WeatherService(System::SystemTask& system, DateTime& dateTimeController)
: system(system), dateTimeController(dateTimeController) {
nullHeader = &nullTimelineheader;
nullTimelineheader->timestamp = 0;
}
void WeatherService::Init() {
// uint8_t res = 0;
// res = ble_gatts_count_cfg(serviceDefinition);
// ASSERT(res == 0);
//
// res = ble_gatts_add_svcs(serviceDefinition);
// ASSERT(res == 0);
}
int WeatherService::OnCommand(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt) {
// if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
// const uint8_t packetLen = OS_MBUF_PKTLEN(ctxt->om); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
// if (packetLen <= 0) {
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// // Decode
// QCBORDecodeContext decodeContext;
// UsefulBufC encodedCbor = {ctxt->om->om_data, OS_MBUF_PKTLEN(ctxt->om)}; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
// QCBORDecode_Init(&decodeContext, encodedCbor, QCBOR_DECODE_MODE_NORMAL);
// // KINDLY provide us a fixed-length map
// QCBORDecode_EnterMap(&decodeContext, nullptr);
// // Always encodes to the smallest number of bytes based on the value
// int64_t tmpTimestamp = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Timestamp", &tmpTimestamp);
// if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// int64_t tmpExpires = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Expires", &tmpExpires);
// if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS || tmpExpires < 0 || tmpExpires > 4294967295) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// int64_t tmpEventType = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "EventType", &tmpEventType);
// if (QCBORDecode_GetError(&decodeContext) != QCBOR_SUCCESS || tmpEventType < 0 ||
// tmpEventType >= static_cast<int64_t>(WeatherData::eventtype::Length)) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// switch (static_cast<WeatherData::eventtype>(tmpEventType)) {
// case WeatherData::eventtype::AirQuality: {
// std::unique_ptr<WeatherData::AirQuality> airquality = std::make_unique<WeatherData::AirQuality>();
// airquality->timestamp = tmpTimestamp;
// airquality->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// airquality->expires = tmpExpires;
// UsefulBufC stringBuf; // TODO: Everything ok with lifecycle here?
// QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Polluter", &stringBuf);
// if (UsefulBuf_IsNULLOrEmptyC(stringBuf) != 0) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// airquality->polluter = std::string(static_cast<const char*>(stringBuf.ptr), stringBuf.len);
// int64_t tmpAmount = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount);
// if (tmpAmount < 0 || tmpAmount > 4294967295) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// airquality->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(airquality))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Obscuration: {
// std::unique_ptr<WeatherData::Obscuration> obscuration = std::make_unique<WeatherData::Obscuration>();
// obscuration->timestamp = tmpTimestamp;
// obscuration->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// obscuration->expires = tmpExpires;
// int64_t tmpType = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType);
// if (tmpType < 0 || tmpType >= static_cast<int64_t>(WeatherData::obscurationtype::Length)) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// obscuration->type = static_cast<WeatherData::obscurationtype>(tmpType);
// int64_t tmpAmount = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount);
// if (tmpAmount < 0 || tmpAmount > 65535) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// obscuration->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(obscuration))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Precipitation: {
// std::unique_ptr<WeatherData::Precipitation> precipitation = std::make_unique<WeatherData::Precipitation>();
// precipitation->timestamp = tmpTimestamp;
// precipitation->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// precipitation->expires = tmpExpires;
// int64_t tmpType = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType);
// if (tmpType < 0 || tmpType >= static_cast<int64_t>(WeatherData::precipitationtype::Length)) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// precipitation->type = static_cast<WeatherData::precipitationtype>(tmpType);
// int64_t tmpAmount = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount);
// if (tmpAmount < 0 || tmpAmount > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// precipitation->amount = tmpAmount; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(precipitation))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Wind: {
// std::unique_ptr<WeatherData::Wind> wind = std::make_unique<WeatherData::Wind>();
// wind->timestamp = tmpTimestamp;
// wind->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// wind->expires = tmpExpires;
// int64_t tmpMin = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "SpeedMin", &tmpMin);
// if (tmpMin < 0 || tmpMin > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// wind->speedMin = tmpMin; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// int64_t tmpMax = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "SpeedMin", &tmpMax);
// if (tmpMax < 0 || tmpMax > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// wind->speedMax = tmpMax; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// int64_t tmpDMin = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "DirectionMin", &tmpDMin);
// if (tmpDMin < 0 || tmpDMin > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// wind->directionMin = tmpDMin; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// int64_t tmpDMax = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "DirectionMax", &tmpDMax);
// if (tmpDMax < 0 || tmpDMax > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// wind->directionMax = tmpDMax; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(wind))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Temperature: {
// std::unique_ptr<WeatherData::Temperature> temperature = std::make_unique<WeatherData::Temperature>();
// temperature->timestamp = tmpTimestamp;
// temperature->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// temperature->expires = tmpExpires;
// int64_t tmpTemperature = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Temperature", &tmpTemperature);
// if (tmpTemperature < -32768 || tmpTemperature > 32767) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// temperature->temperature =
// static_cast<int16_t>(tmpTemperature); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// int64_t tmpDewPoint = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "DewPoint", &tmpDewPoint);
// if (tmpDewPoint < -32768 || tmpDewPoint > 32767) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// temperature->dewPoint =
// static_cast<int16_t>(tmpDewPoint); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(temperature))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Special: {
// std::unique_ptr<WeatherData::Special> special = std::make_unique<WeatherData::Special>();
// special->timestamp = tmpTimestamp;
// special->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// special->expires = tmpExpires;
// int64_t tmpType = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Type", &tmpType);
// if (tmpType < 0 || tmpType >= static_cast<int64_t>(WeatherData::specialtype::Length)) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// special->type = static_cast<WeatherData::specialtype>(tmpType);
// if (!AddEventToTimeline(std::move(special))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Pressure: {
// std::unique_ptr<WeatherData::Pressure> pressure = std::make_unique<WeatherData::Pressure>();
// pressure->timestamp = tmpTimestamp;
// pressure->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// pressure->expires = tmpExpires;
// int64_t tmpPressure = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Pressure", &tmpPressure);
// if (tmpPressure < 0 || tmpPressure >= 65535) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// pressure->pressure = tmpPressure; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
// if (!AddEventToTimeline(std::move(pressure))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Location: {
// std::unique_ptr<WeatherData::Location> location = std::make_unique<WeatherData::Location>();
// location->timestamp = tmpTimestamp;
// location->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// location->expires = tmpExpires;
// UsefulBufC stringBuf; // TODO: Everything ok with lifecycle here?
// QCBORDecode_GetTextStringInMapSZ(&decodeContext, "Location", &stringBuf);
// if (UsefulBuf_IsNULLOrEmptyC(stringBuf) != 0) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// location->location = std::string(static_cast<const char*>(stringBuf.ptr), stringBuf.len);
// int64_t tmpAltitude = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Altitude", &tmpAltitude);
// if (tmpAltitude < -32768 || tmpAltitude >= 32767) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// location->altitude = static_cast<int16_t>(tmpAltitude);
// int64_t tmpLatitude = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Latitude", &tmpLatitude);
// if (tmpLatitude < -2147483648 || tmpLatitude >= 2147483647) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// location->latitude = static_cast<int32_t>(tmpLatitude);
// int64_t tmpLongitude = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Longitude", &tmpLongitude);
// if (tmpLongitude < -2147483648 || tmpLongitude >= 2147483647) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// location->latitude = static_cast<int32_t>(tmpLongitude);
// if (!AddEventToTimeline(std::move(location))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Clouds: {
// std::unique_ptr<WeatherData::Clouds> clouds = std::make_unique<WeatherData::Clouds>();
// clouds->timestamp = tmpTimestamp;
// clouds->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// clouds->expires = tmpExpires;
// int64_t tmpAmount = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Amount", &tmpAmount);
// if (tmpAmount < 0 || tmpAmount > 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// clouds->amount = static_cast<uint8_t>(tmpAmount);
// if (!AddEventToTimeline(std::move(clouds))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// case WeatherData::eventtype::Humidity: {
// std::unique_ptr<WeatherData::Humidity> humidity = std::make_unique<WeatherData::Humidity>();
// humidity->timestamp = tmpTimestamp;
// humidity->eventType = static_cast<WeatherData::eventtype>(tmpEventType);
// humidity->expires = tmpExpires;
// int64_t tmpType = 0;
// QCBORDecode_GetInt64InMapSZ(&decodeContext, "Humidity", &tmpType);
// if (tmpType < 0 || tmpType >= 255) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// humidity->humidity = static_cast<uint8_t>(tmpType);
// if (!AddEventToTimeline(std::move(humidity))) {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// break;
// }
// default: {
// CleanUpQcbor(&decodeContext);
// return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
// }
// }
// QCBORDecode_ExitMap(&decodeContext);
// GetTimelineLength();
// TidyTimeline();
// if (QCBORDecode_Finish(&decodeContext) != QCBOR_SUCCESS) {
// return BLE_ATT_ERR_INSUFFICIENT_RES;
// }
// } else if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
// // Encode
// uint8_t buffer[64];
// QCBOREncodeContext encodeContext;
// /* TODO: This is very much still a test endpoint
// * it needs a characteristic UUID check
// * and actual implementations that show
// * what actually has to be read.
// * WARN: Consider commands not part of the API for now!
// */
// QCBOREncode_Init(&encodeContext, UsefulBuf_FROM_BYTE_ARRAY(buffer));
// QCBOREncode_OpenMap(&encodeContext);
// QCBOREncode_AddTextToMap(&encodeContext, "test", UsefulBuf_FROM_SZ_LITERAL("test"));
// QCBOREncode_AddInt64ToMap(&encodeContext, "test", 1ul);
// QCBOREncode_CloseMap(&encodeContext);
// UsefulBufC encodedEvent;
// auto uErr = QCBOREncode_Finish(&encodeContext, &encodedEvent);
// if (uErr != 0) {
// return BLE_ATT_ERR_INSUFFICIENT_RES;
// }
// auto res = os_mbuf_append(ctxt->om, &buffer, sizeof(buffer));
// if (res == 0) {
// return BLE_ATT_ERR_INSUFFICIENT_RES;
// }
// return 0;
// }
return 0;
}
std::unique_ptr<WeatherData::Clouds>& WeatherService::GetCurrentClouds() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Clouds && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Clouds>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Clouds>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Obscuration>& WeatherService::GetCurrentObscuration() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Obscuration && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Obscuration>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Obscuration>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Precipitation>& WeatherService::GetCurrentPrecipitation() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Precipitation && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Precipitation>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Precipitation>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Wind>& WeatherService::GetCurrentWind() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Wind && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Wind>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Wind>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Temperature>& WeatherService::GetCurrentTemperature() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Temperature>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Temperature>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Humidity>& WeatherService::GetCurrentHumidity() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Humidity && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Humidity>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Humidity>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Pressure>& WeatherService::GetCurrentPressure() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Pressure && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Pressure>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Pressure>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::Location>& WeatherService::GetCurrentLocation() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Location && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::Location>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::Location>&>(*this->nullHeader);
}
std::unique_ptr<WeatherData::AirQuality>& WeatherService::GetCurrentQuality() {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::AirQuality && IsEventStillValid(header, currentTimestamp)) {
return reinterpret_cast<std::unique_ptr<WeatherData::AirQuality>&>(header);
}
}
return reinterpret_cast<std::unique_ptr<WeatherData::AirQuality>&>(*this->nullHeader);
}
size_t WeatherService::GetTimelineLength() const {
return timeline.size();
}
bool WeatherService::AddEventToTimeline(std::unique_ptr<WeatherData::TimelineHeader> event) {
if (timeline.size() == timeline.max_size()) {
return false;
}
timeline.push_back(std::move(event));
return true;
}
bool WeatherService::HasTimelineEventOfType(const WeatherData::eventtype type) const {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
for (auto&& header : timeline) {
if (header->eventType == type && IsEventStillValid(header, currentTimestamp)) {
return true;
}
}
return false;
}
void WeatherService::TidyTimeline() {
uint64_t timeCurrent = GetCurrentUnixTimestamp();
timeline.erase(std::remove_if(std::begin(timeline),
std::end(timeline),
[&](std::unique_ptr<WeatherData::TimelineHeader> const& header) {
return !IsEventStillValid(header, timeCurrent);
}),
std::end(timeline));
std::sort(std::begin(timeline), std::end(timeline), CompareTimelineEvents);
}
bool WeatherService::CompareTimelineEvents(const std::unique_ptr<WeatherData::TimelineHeader>& first,
const std::unique_ptr<WeatherData::TimelineHeader>& second) {
return first->timestamp > second->timestamp;
}
bool WeatherService::IsEventStillValid(const std::unique_ptr<WeatherData::TimelineHeader>& uniquePtr, const uint64_t timestamp) {
// Not getting timestamp in isEventStillValid for more speed
return uniquePtr->timestamp + uniquePtr->expires >= timestamp;
}
uint64_t WeatherService::GetCurrentUnixTimestamp() const {
return std::chrono::duration_cast<std::chrono::seconds>(dateTimeController.CurrentDateTime().time_since_epoch()).count();
}
int16_t WeatherService::GetTodayMinTemp() const {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
uint64_t currentDayEnd = currentTimestamp - ((24 - dateTimeController.Hours()) * 60 * 60) -
((60 - dateTimeController.Minutes()) * 60) - (60 - dateTimeController.Seconds());
int16_t result = -32768;
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp) &&
header->timestamp < currentDayEnd &&
reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature != -32768) {
int16_t temperature = reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature;
if (result == -32768) {
result = temperature;
} else if (result > temperature) {
result = temperature;
} else {
// The temperature in this item is higher than the lowest we've found
}
}
}
return result;
}
int16_t WeatherService::GetTodayMaxTemp() const {
uint64_t currentTimestamp = GetCurrentUnixTimestamp();
uint64_t currentDayEnd = currentTimestamp - ((24 - dateTimeController.Hours()) * 60 * 60) -
((60 - dateTimeController.Minutes()) * 60) - (60 - dateTimeController.Seconds());
int16_t result = -32768;
for (auto&& header : this->timeline) {
if (header->eventType == WeatherData::eventtype::Temperature && IsEventStillValid(header, currentTimestamp) &&
header->timestamp < currentDayEnd &&
reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature != -32768) {
int16_t temperature = reinterpret_cast<const std::unique_ptr<WeatherData::Temperature>&>(header)->temperature;
if (result == -32768) {
result = temperature;
} else if (result < temperature) {
result = temperature;
} else {
// The temperature in this item is lower than the highest we've found
}
}
}
return result;
}
// void WeatherService::CleanUpQcbor(QCBORDecodeContext* decodeContext) {
// QCBORDecode_ExitMap(decodeContext);
// QCBORDecode_Finish(decodeContext);
// }

View File

@@ -0,0 +1,172 @@
/* Copyright (C) 2021 Avamander
This file is part of InfiniTime.
InfiniTime is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
InfiniTime is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include <string>
#include <vector>
#include <memory>
//#define min // workaround: nimble's min/max macros conflict with libstdc++
//#define max
//#include <host/ble_gap.h>
//#include <host/ble_uuid.h>
//#undef max
//#undef min
#include "components/ble/weather/WeatherData.h"
//#include "libs/QCBOR/inc/qcbor/qcbor.h"
#include "components/datetime/DateTimeController.h"
//int WeatherCallback(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt, void* arg);
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class WeatherService {
public:
explicit WeatherService(System::SystemTask& system, DateTime& dateTimeController);
void Init();
int OnCommand(uint16_t connHandle, uint16_t attrHandle, struct ble_gatt_access_ctxt* ctxt);
/*
* Helper functions for quick access to currently valid data
*/
std::unique_ptr<WeatherData::Location>& GetCurrentLocation();
std::unique_ptr<WeatherData::Clouds>& GetCurrentClouds();
std::unique_ptr<WeatherData::Obscuration>& GetCurrentObscuration();
std::unique_ptr<WeatherData::Precipitation>& GetCurrentPrecipitation();
std::unique_ptr<WeatherData::Wind>& GetCurrentWind();
std::unique_ptr<WeatherData::Temperature>& GetCurrentTemperature();
std::unique_ptr<WeatherData::Humidity>& GetCurrentHumidity();
std::unique_ptr<WeatherData::Pressure>& GetCurrentPressure();
std::unique_ptr<WeatherData::AirQuality>& GetCurrentQuality();
/**
* Searches for the current day's maximum temperature
* @return -32768 if there's no data, degrees Celsius times 100 otherwise
*/
int16_t GetTodayMaxTemp() const;
/**
* Searches for the current day's minimum temperature
* @return -32768 if there's no data, degrees Celsius times 100 otherwise
*/
int16_t GetTodayMinTemp() const;
/*
* Management functions
*/
/**
* Adds an event to the timeline
* @return
*/
bool AddEventToTimeline(std::unique_ptr<WeatherData::TimelineHeader> event);
/**
* Gets the current timeline length
*/
size_t GetTimelineLength() const;
/**
* Checks if an event of a certain type exists in the timeline
*/
bool HasTimelineEventOfType(WeatherData::eventtype type) const;
private:
// 00040000-78fc-48fe-8e23-433b3a1942d0
// static constexpr ble_uuid128_t BaseUuid() {
// return CharUuid(0x00, 0x00);
// }
// // 0004yyxx-78fc-48fe-8e23-433b3a1942d0
// static constexpr ble_uuid128_t CharUuid(uint8_t x, uint8_t y) {
// return ble_uuid128_t {.u = {.type = BLE_UUID_TYPE_128},
// .value = {0xd0, 0x42, 0x19, 0x3a, 0x3b, 0x43, 0x23, 0x8e, 0xfe, 0x48, 0xfc, 0x78, y, x, 0x04, 0x00}};
// }
// ble_uuid128_t weatherUuid {BaseUuid()};
/**
* Just write timeline data here.
*
* See {@link WeatherData.h} for more information.
*/
// ble_uuid128_t weatherDataCharUuid {CharUuid(0x00, 0x01)};
/**
* This doesn't take timeline data, provides some control over it.
*
* NOTE: Currently not supported. Companion app implementer feedback required.
* There's very little point in solidifying an API before we know the needs.
*/
// ble_uuid128_t weatherControlCharUuid {CharUuid(0x00, 0x02)};
// const struct ble_gatt_chr_def characteristicDefinition[3] = {
// {.uuid = &weatherDataCharUuid.u,
// .access_cb = WeatherCallback,
// .arg = this,
// .flags = BLE_GATT_CHR_F_WRITE,
// .val_handle = &eventHandle},
// {.uuid = &weatherControlCharUuid.u, .access_cb = WeatherCallback, .arg = this, .flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ},
// {nullptr}};
// const struct ble_gatt_svc_def serviceDefinition[2] = {
// {.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &weatherUuid.u, .characteristics = characteristicDefinition}, {0}};
uint16_t eventHandle {};
Pinetime::System::SystemTask& system;
Pinetime::Controllers::DateTime& dateTimeController;
std::vector<std::unique_ptr<WeatherData::TimelineHeader>> timeline;
std::unique_ptr<WeatherData::TimelineHeader> nullTimelineheader = std::make_unique<WeatherData::TimelineHeader>();
std::unique_ptr<WeatherData::TimelineHeader>* nullHeader;
/**
* Cleans up the timeline of expired events
*/
void TidyTimeline();
/**
* Compares two timeline events
*/
static bool CompareTimelineEvents(const std::unique_ptr<WeatherData::TimelineHeader>& first,
const std::unique_ptr<WeatherData::TimelineHeader>& second);
/**
* Returns current UNIX timestamp
*/
uint64_t GetCurrentUnixTimestamp() const;
/**
* Checks if the event hasn't gone past and expired
*
* @param header timeline event to check
* @param currentTimestamp what's the time right now
* @return if the event is valid
*/
static bool IsEventStillValid(const std::unique_ptr<WeatherData::TimelineHeader>& uniquePtr, const uint64_t timestamp);
/**
* This is a helper function that closes a QCBOR map and decoding context cleanly
*/
// void CleanUpQcbor(QCBORDecodeContext* decodeContext);
};
}
}