//////////////////////////////////////////////////////////////////////
// Copyright (C) Hiroshi SUGIMURA 2021.11.09
//////////////////////////////////////////////////////////////////////
/**
* @module mainHALsync
*/
'use strict'
//////////////////////////////////////////////////////////////////////
// 基本ライブラリ
const { Op, eldataModel, IOT_MajorResultsModel, IOT_MinorResultsModel, IOT_GarminDailiesModel, IOT_GarminStressDetailsModel, IOT_GarminEpochsModel, IOT_GarminSleepsModel, IOT_GarminUserMetricsModel, IOT_GarminActivitiesModel, IOT_GarminActivityDetailsModel, IOT_GarminMoveIQActivitiesModel, IOT_GarminAllDayRespirationModel, IOT_GarminPulseoxModel, IOT_GarminBodyCompsModel } = require('./models/localDBModels'); // DBデータと連携
const https = require('https');
const Store = require('electron-store');
const store = new Store();
const { getToday, mergeDeeply } = require('./mainSubmodule');
const HAL_API_BASE_URL = 'https://hal.sugi-lab.net/api';
let config = { // = config.HAL
halApiToken: '', // HALと連携するユーザ別のトークン
// startUploadEldataTime: 300000,
resultExpireDays: 365,
ellogExpireDays: 30,
UPLOAD_UNIT_NUM: 100, // 分割アップロードのログの数
UPLOAD_UNIT_INTERVAL: 1000, // 分割アップロードの間隔 (ミリ秒)
UPLOAD_START_INTERVAL: 300000, // アップロード処理の起動間隔 (ミリ秒)
lastUploadedTime: 0, // 最終アップロード時間
lastUploadedId: 0, // 最終アップロードID
debug: false
};
let persist = { // = persist.HAL
name: 'No Profile', // HALからのprofile
UID: 'No Data',
sex: 'No Data',
age: 'No Data',
garmin: {}
};
let sendIPCMessage = null;
//////////////////////////////////////////////////////////////////////
// HAL, Home-life Assessment Listの処理
let mainHALsync = {
isRun: false,
observationJob: null,
//----------------------------------
/**
* @func start
* @desc 初期化
* @async
* @param {void}
* @return void
* @throw error
*/
start: async function (_sendIPCMessage) {
sendIPCMessage = _sendIPCMessage;
if (mainHALsync.isRun) { // 重複起動対策
sendIPCMessage("renewHALConfigView", config); // configを送る、そうするとViewがkeyチェックのためにprofile取りに来る
sendIPCMessage("showGarmin", persist.garmin); // 保持しているGarminデータを表示する
return;
}
mainHALsync.isRun = true;
config = await store.get('config.HAL', config);
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainHALsync.start()') : 0;
persist = store.get('persist.HAL', persist);
// mainHALsync.startUploadEldata(); // 家電操作ログのアップロードを開始、HALのDBがきついのでとりあえずやらない
sendIPCMessage("renewHALConfigView", config); // configを送る、そうするとViewがkeyチェックのためにprofile取りに来る
sendIPCMessage("showGarmin", persist.garmin); // 保持しているGarminデータを表示する
},
//----------------------------------------------------------------------------------------------
/**
* @func startSync
* @desc 同期処理, トリガー:APIKey設定時、同期ボタン押下、定時処理
* @async
* @param {void}
* @return void
* @throw error
*/
startSync: async function () {
mainHALsync.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainHALsync.startSync().') : 0;
// HAL API トークンが登録されていなければ終了
if (!config.halApiToken) {
return;
}
// 今日の日付を取得 (YYYY-MM-DD)
let today = getToday();
try {
let updata = {
MajorResults: null,
MinorResults: null
};
// ローカルの MajorResults から今日のレコードを取得
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Getting the latest record in the local MajorResults table.') : 0;
let major_data = await IOT_MajorResultsModel.findOne({
where: {
date: today
}
});
if (major_data) {
updata.MajorResults = major_data.dataValues;
// config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- major_data:', JSON.stringify(major_data.dataValues, null, ' ')) : 0;
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- major_data: found in DB') : 0;
} else {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- major_data: null') : 0;
}
// ローカルの MinorResults から最新のレコードを取得
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Getting the latest record in the local MinorResults table.') : 0;
let minor_data = await IOT_MinorResultsModel.findOne({
where: {
date: today
}
});
if (minor_data) {
updata.MinorResults = minor_data.dataValues;
// config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- minor_data:', JSON.stringify(minor_data.dataValues, null, ' ')) : 0;
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- minor_data: found in DB') : 0;
} else {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- minor_data: null') : 0;
}
// 成績データを HAL にアップロード
const hal_results_url = HAL_API_BASE_URL + '/results';
if (updata.MajorResults || updata.MinorResults) {
// config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Uploading to HAL, updata:', JSON.stringify(updata, null, ' ')) : 0;
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Uploading to HAL') : 0;
await mainHALsync.httpPostRequest(hal_results_url, updata);
}
// HAL から成績データをダウンロード
let dndata = await mainHALsync.httpGetRequest(hal_results_url);
// config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Downloading from HAL, dndata:', JSON.stringify(dndata, null, ' ')) : 0;
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Downloading from HAL') : 0;
// HAL からダウンロードした成績データをローカルに保存
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Saving.') : 0;
// 今の日時 ("YYYY-MM-DD hh:mm:ss")
//let now = getNow();
let now = new Date();
// MajorResults テーブルのレコードを保存
if (dndata.MajorResults) {
let rec = {};
if (updata.MajorResults) {
// ローカルに今日のレコードがあれば、それをダウンロードデータで UPDATE する。
// ただし、ローカルのレコードとダウンロードしたレコードの assessmentSource
// の値がともに "questionnaire" なら、すべてのカラムの値を上書きし、そうで
// なければ、ローカル側が null のカラムのみを更新する
if (updata.MajorResults.assessmentSource === 'questionnaire' && dndata.MajorResults.assessmentSource === 'questionnaire') {
for (let [k] of Object.entries(updata.MajorResults)) {
rec[k] = dndata.MajorResults[k];
}
} else {
for (let [k, v] of Object.entries(updata.MajorResults)) {
if (v === null && dndata.MajorResults[k] !== null) {
rec[k] = dndata.MajorResults[k];
}
}
}
let id = updata.MajorResults.idIOT_MajorResults;
rec.updatedAt = now;
await IOT_MajorResultsModel.update(rec, {
where: { idIOT_MajorResults: id }
});
let updated_res = await IOT_MajorResultsModel.findOne({
where: { idIOT_MajorResults: id }
});
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Updated the latest record in the IOT_MajorResults table:') : 0;
// config.debug ? console.log(JSON.stringify(updated_res.dataValues, null, ' ')) : 0;
} else {
// 今日のレコードがなければ、それを INSERT
rec = dndata.MajorResults;
delete rec.idIOT_MajorResults;
delete rec.UID;
// rec.createdAt = now;
// rec.updatedAt = now;
let ins_res = await IOT_MajorResultsModel.create(rec);
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Inserted a new record in the IOT_MajorResults table:') : 0;
// config.debug ? console.log(JSON.stringify(ins_res.dataValues, null, ' ')) : 0;
}
}
// MinorResults テーブルのレコードを保存
if (dndata.MinorResults) {
let rec = {};
if (updata.MinorResults) {
// ローカルに今日のレコードがあれば、それをダウンロードデータで UPDATE する。
// ただし、ローカルのレコードとダウンロードしたレコードの assessmentSource
// の値がともに "questionnaire" なら、すべてのカラムの値を上書きし、そうで
// なければ、ローカル側が null のカラムのみを更新する
if (updata.MinorResults.assessmentSource === 'questionnaire' && dndata.MinorResults.assessmentSource === 'questionnaire') {
for (let [k] of Object.entries(updata.MinorResults)) {
rec[k] = dndata.MinorResults[k];
}
} else {
for (let [k, v] of Object.entries(rec)) {
if (v === null && dndata.MinorResults[k] !== null) {
rec[k] = dndata.MinorResults[k];
}
}
}
let id = updata.MinorResults.idIOT_MinorResults;
rec.updatedAt = now;
await IOT_MinorResultsModel.update(rec, {
where: { idIOT_MinorResults: id }
});
let updated_res = await IOT_MinorResultsModel.findOne({
where: { idIOT_MinorResults: id }
});
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Updated the latest record in the IOT_MinorResults table:') : 0;
// config.debug ? console.log(JSON.stringify(updated_res.dataValues, null, ' ')) : 0;
} else {
// 今日のレコードがなければ、それを INSERT
rec = dndata.MinorResults;
delete rec.idIOT_MinorResults;
delete rec.UID;
// rec.createdAt = now;
// rec.updatedAt = now;
let ins_res = await IOT_MinorResultsModel.create(rec);
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Inserted a new record in the IOT_MinorResults table:') : 0;
// config.debug ? console.log(JSON.stringify(ins_res.dataValues, null, ' ')) : 0;
}
}
// MinorkeyMeans テーブルのレコードを保存
// MinorkeyMeansに関してはローカルで生成することにした
/*
if (dndata.MinorkeyMeans) {
let mdata = dndata.MinorkeyMeans;
// 同じバージョンのレコードをテーブルから削除
let deleted_num = await IOT_MinorkeyMeansModel.destroy({
where: {
version: mdata.version
}
});
console.log('Deleted ' + deleted_num + ' records in the MinorkeyMeans teble.');
// ダウンロードしたデータをテーブルに追加
for (let [name, val] of Object.entries(mdata.data)) {
let parts = name.split('_');
let major_key = parseInt(parts[1], 10);
let minor_key = parseInt(parts[2], 10);
await IOT_MinorkeyMeansModel.create({
version: mdata.version,
majorKey: major_key,
minorKey: minor_key,
means: val
});
}
console.log('Inserted ' + Object.keys(mdata.data).length + ' records in the MinorkeyMeans teble.');
}
*/
mainHALsync.garminDownload();
// メインプロセスに同期完了のイベントを送信
sendIPCMessage("HALSyncResponse", {});
} catch (error) {
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainHALsync.startSync() ', error);
let arg = {
error: error.message
};
sendIPCMessage('Error', {
datetime: new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"),
moduleName: 'mainHALsync.startSync()',
stackLog: `Detail: ${error}`
});
// mainWindow.webContents.send('to-renderer', JSON.stringify({ cmd: "Synced", arg: arg }));
}
},
//----------------------------------------------------------------------------------------------
/**
* @func garminDownload
* @desc garminDownload
* @async
* @param {void}
* @return void
* @throw error
*/
garminDownload: async function () {
mainHALsync.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainHALsync.garminDownload().') : 0;
// HAL API トークンが登録されていなければ終了
if (!config.halApiToken) {
return;
}
const hal_garmin_download_url = HAL_API_BASE_URL + '/garminDownload';
try {
// HAL からGarminデータをダウンロード
let dndata = await mainHALsync.httpGetRequest(hal_garmin_download_url);
// config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Downloading garmin data from HAL', JSON.stringify(dndata, null, ' ')) : 0;
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Downloading garmin data from HAL') : 0;
// HAL からダウンロードしたGarminデータをローカルに保存
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Saving.') : 0;
// Activities
if (dndata.Activities) {
// ダウンロードしたデータをテーブルに追加
await IOT_GarminActivitiesModel.findOrCreate({
where: {
idIOT_GarminActivities: dndata.Activities.idIOT_GarminActivities
},
defaults: {
idIOT_GarminActivities: dndata.Activities.idIOT_GarminActivities,
garminId: dndata.Activities.garminId,
garminAccessToken: dndata.Activities.garminAccessToken,
summaryId: dndata.Activities.summaryId,
activityId: dndata.Activities.activityId,
durationInSeconds: dndata.Activities.durationInSeconds,
startTimeInSeconds: dndata.Activities.startTimeInSeconds,
startTimeOffsetInSeconds: dndata.Activities.startTimeOffsetInSeconds,
activityType: dndata.Activities.activityType,
averageHeartRateInBeatsPerMinute: dndata.Activities.averageHeartRateInBeatsPerMinute,
averageRunCadenceInStepsPerMinute: dndata.Activities.averageRunCadenceInStepsPerMinute,
averageSpeedInMetersPerSecond: dndata.Activities.averageSpeedInMetersPerSecond,
averagePaceInMinutesPerKilometer: dndata.Activities.averagePaceInMinutesPerKilometer,
activeKilocalories: dndata.Activities.activeKilocalories,
deviceName: dndata.Activities.deviceName,
distanceInMeters: dndata.Activities.distanceInMeters,
maxHeartRateInBeatsPerMinute: dndata.Activities.maxHeartRateInBeatsPerMinute,
maxPaceInMinutesPerKilometer: dndata.Activities.maxPaceInMinutesPerKilometer,
maxRunCadenceInStepsPerMinute: dndata.Activities.maxRunCadenceInStepsPerMinute,
maxSpeedInMetersPerSecond: dndata.Activities.maxSpeedInMetersPerSecond,
steps: dndata.Activities.steps,
createdAt: dndata.Activities.createdAt,
updatedAt: dndata.Activities.updatedAt
}
});
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Inserted a record in the IOT_GarminActivitiesModel teble.') : 0;
} else {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- No record in the IOT_GarminActivitiesModel teble.') : 0;
}
// ActivityDetails
if (dndata.ActivityDetails) {
// ダウンロードしたデータをテーブルに追加
await IOT_GarminActivityDetailsModel.findOrCreate({
where: {
idIOT_GarminActivityDetails: dndata.ActivityDetails.idIOT_GarminActivityDetails
},
defaults: {
idIOT_GarminActivityDetails: dndata.ActivityDetails.idIOT_GarminActivityDetails,
garminId: dndata.ActivityDetails.garminId,
garminAccessToken: dndata.ActivityDetails.garminAccessToken,
summaryId: dndata.ActivityDetails.summaryId,
activityId: dndata.ActivityDetails.activityId,
summary: dndata.ActivityDetails.summary,
samples: dndata.ActivityDetails.samples,
laps: dndata.ActivityDetails.laps,
createdAt: dndata.ActivityDetails.createdAt,
updatedAt: dndata.ActivityDetails.updatedAt
}
});
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Inserted a record in the IOT_GarminActivityDetailsModel teble.') : 0;
} else {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- No record in the IOT_GarminActivityDetailsModel teble.') : 0;
}
// BodyComps
if (dndata.BodyComps) {
// ダウンロードしたデータをテーブルに追加
await IOT_GarminBodyCompsModel.findOrCreate({
where: {
idIOT_GarminBodyComps: dndata.BodyComps.idIOT_GarminBodyComps
},
defaults: {
idIOT_GarminBodyComps: dndata.BodyComps.idIOT_GarminBodyComps,
garminId: dndata.BodyComps.garminId,
garminAccessToken: dndata.BodyComps.garminAccessToken,
summaryId: dndata.BodyComps.summaryId,
muscleMassInGrams: dndata.BodyComps.muscleMassInGrams,
boneMassInGrams: dndata.BodyComps.boneMassInGrams,
bodyWaterInPercent: dndata.BodyComps.bodyWaterInPercent,
bodyFatInPercent: dndata.BodyComps.bodyFatInPercent,
bodyMassIndex: dndata.BodyComps.bodyMassIndex,
weightInGrams: dndata.BodyComps.weightInGrams,
measurementTimeInSeconds: dndata.BodyComps.measurementTimeInSeconds,
measurementTimeOffsetInSeconds: dndata.BodyComps.measurementTimeOffsetInSeconds,
createdAt: dndata.BodyComps.createdAt,
updatedAt: dndata.BodyComps.updatedAt
}
});
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Inserted a record in the IOT_GarminPulseoxModel teble.') : 0;
} else {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- No record in the IOT_GarminPulseoxModel teble.') : 0;
}
// Dailies
if (dndata.Dailies) {
// ダウンロードしたデータをテーブルに追加
await IOT_GarminDailiesModel.findOrCreate({
where: {
idIOT_GarminDailies: dndata.Dailies.idIOT_GarminDailies
},
defaults: {
idIOT_GarminDailies: dndata.Dailies.idIOT_GarminDailies,
garminId: dndata.Dailies.garminId,
garminAccessToken: dndata.Dailies.garminAccessToken,
summaryId: dndata.Dailies.summaryId,
calendarDate: dndata.Dailies.calendarDate,
startTimeInSeconds: dndata.Dailies.startTimeInSeconds,
startTimeOffsetInSeconds: dndata.Dailies.startTimeOffsetInSeconds,
activityType: dndata.Dailies.activityType,
durationInSeconds: dndata.Dailies.durationInSeconds,
steps: dndata.Dailies.steps,
distanceInMeters: dndata.Dailies.distanceInMeters,
activeTimeInSeconds: dndata.Dailies.activeTimeInSeconds,
activeKilocalories: dndata.Dailies.activeKilocalories,
bmrKilocalories: dndata.Dailies.bmrKilocalories,
cunsumedCalories: dndata.Dailies.cunsumedCalories,
moderateIntensityDurationInSeconds: dndata.Dailies.moderateIntensityDurationInSeconds,
vigorousIntensityDurationInSeconds: dndata.Dailies.vigorousIntensityDurationInSeconds,
floorsClimbed: dndata.Dailies.floorsClimbed,
minHeartRateInBeatsPerMinute: dndata.Dailies.minHeartRateInBeatsPerMinute,
averageHeartRateInBeatsPerMinute: dndata.Dailies.averageHeartRateInBeatsPerMinute,
maxHeartRateInBeatsPerMinute: dndata.Dailies.maxHeartRateInBeatsPerMinute,
restStressDurationInSeconds: dndata.Dailies.restStressDurationInSeconds,
timeOffsetHeartRateSamples: dndata.Dailies.timeOffsetHeartRateSamples,
averageStressLevel: dndata.Dailies.averageStressLevel,
maxStressLevel: dndata.Dailies.maxStressLevel,
stressDurationInSeconds: dndata.Dailies.stressDurationInSeconds,
activityStressDurationInSeconds: dndata.Dailies.activityStressDurationInSeconds,
lowStressDurationInSeconds: dndata.Dailies.lowStressDurationInSeconds,
mediumStressDurationInSeconds: dndata.Dailies.mediumStressDurationInSeconds,
highStressDurationInSeconds: dndata.Dailies.highStressDurationInSeconds,
stressQualifier: dndata.Dailies.stressQualifier,
stepsGoal: dndata.Dailies.stepsGoal,
netKilocaloriesGoal: dndata.Dailies.netKilocaloriesGoal,
intensityDurationGoalInSeconds: dndata.Dailies.intensityDurationGoalInSeconds,
floorsClimbedGoal: dndata.Dailies.floorsClimbedGoal,
createdAt: dndata.Dailies.createdAt,
updatedAt: dndata.Dailies.updatedAt
}
});
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Inserted a record in the IOT_GarminDailiesModel teble.') : 0;
} else {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- No record in the IOT_GarminDailiesModel teble.') : 0;
}
// Epochs
if (dndata.Epochs) {
// ダウンロードしたデータをテーブルに追加
await IOT_GarminEpochsModel.findOrCreate({
where: {
idIOT_GarminEpochs: dndata.Epochs.idIOT_GarminEpochs
},
defaults: {
idIOT_GarminEpochs: dndata.Epochs.idIOT_GarminEpochs,
garminId: dndata.Epochs.garminId,
garminAccessToken: dndata.Epochs.garminAccessToken,
summaryId: dndata.Epochs.summaryId,
startTimeInSeconds: dndata.Epochs.startTimeInSeconds,
startTimeOffsetInSeconds: dndata.Epochs.startTimeOffsetInSeconds,
activityType: dndata.Epochs.activityType,
durationInSeconds: dndata.Epochs.durationInSeconds,
activeTimeInSeconds: dndata.Epochs.activeTimeInSeconds,
steps: dndata.Epochs.steps,
distanceInMeters: dndata.Epochs.distanceInMeters,
activeKilocalories: dndata.Epochs.activeKilocalories,
met: dndata.Epochs.met,
intensity: dndata.Epochs.intensity,
meanMotionIntensity: dndata.Epochs.meanMotionIntensity,
maxMotionIntensity: dndata.Epochs.maxMotionIntensity,
createdAt: dndata.Epochs.createdAt,
updatedAt: dndata.Epochs.updatedAt
}
});
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Inserted a record in the IOT_GarminEpochsModel teble.') : 0;
} else {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- No record in the IOT_GarminEpochsModel teble.') : 0;
}
// MoveIQActivities
if (dndata.MoveIQActivities) {
// ダウンロードしたデータをテーブルに追加
await IOT_GarminMoveIQActivitiesModel.findOrCreate({
where: {
idIOT_GarminMoveIQActivities: dndata.MoveIQActivities.idIOT_GarminMoveIQActivities
},
defaults: {
idIOT_GarminMoveIQActivities: dndata.MoveIQActivities.idIOT_GarminMoveIQActivities,
garminId: dndata.MoveIQActivities.garminId,
garminAccessToken: dndata.MoveIQActivities.garminAccessToken,
summaryId: dndata.MoveIQActivities.summaryId,
calendarDate: dndata.MoveIQActivities.calendarDate,
startTimeInSeconds: dndata.MoveIQActivities.startTimeInSeconds,
offsetInSeconds: dndata.MoveIQActivities.offsetInSeconds,
durationInSeconds: dndata.MoveIQActivities.durationInSeconds,
activityType: dndata.MoveIQActivities.activityType,
activitySubType: dndata.MoveIQActivities.activitySubType,
createdAt: dndata.MoveIQActivities.createdAt,
updatedAt: dndata.MoveIQActivities.updatedAt
}
});
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Inserted a record in the IOT_GarminMoveIQActivitiesModel teble.') : 0;
} else {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- No record in the IOT_GarminMoveIQActivitiesModel teble.') : 0;
}
// Pulseox
if (dndata.Pulseox) {
// ダウンロードしたデータをテーブルに追加
await IOT_GarminPulseoxModel.findOrCreate({
where: {
idIOT_GarminPulseox: dndata.Pulseox.idIOT_GarminPulseox
},
defaults: {
idIOT_GarminPulseox: dndata.Pulseox.idIOT_GarminPulseox,
garminId: dndata.Pulseox.garminId,
garminAccessToken: dndata.Pulseox.garminAccessToken,
summaryId: dndata.Pulseox.summaryId,
calendarDate: dndata.Pulseox.calendarDate,
startTimeInSeconds: dndata.Pulseox.startTimeInSeconds,
durationInSeconds: dndata.Pulseox.durationInSeconds,
startTimeOffsetInSeconds: dndata.Pulseox.startTimeOffsetInSeconds,
timeOffsetSpo2Values: dndata.Pulseox.timeOffsetSpo2Values,
onDemand: dndata.Pulseox.onDemand,
createdAt: dndata.Pulseox.createdAt,
updatedAt: dndata.Pulseox.updatedAt
}
});
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Inserted a record in the IOT_GarminPulseoxModel teble.') : 0;
} else {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- No record in the IOT_GarminPulseoxModel teble.') : 0;
}
// Sleeps
if (dndata.Sleeps) {
// ダウンロードしたデータをテーブルに追加
await IOT_GarminSleepsModel.findOrCreate({
where: {
idIOT_GarminSleeps: dndata.Sleeps.idIOT_GarminSleeps
},
defaults: {
idIOT_GarminSleeps: dndata.Sleeps.idIOT_GarminSleeps,
garminId: dndata.Sleeps.garminId,
garminAccessToken: dndata.Sleeps.garminAccessToken,
summaryId: dndata.Sleeps.summaryId,
calendarDate: dndata.Sleeps.calendarDate,
startTimeInSeconds: dndata.Sleeps.startTimeInSeconds,
startTimeOffsetInSeconds: dndata.Sleeps.startTimeOffsetInSeconds,
durationInSeconds: dndata.Sleeps.durationInSeconds,
unmeasurableSleepInSeconds: dndata.Sleeps.unmeasurableSleepInSeconds,
deepSleepDurationInSeconds: dndata.Sleeps.deepSleepDurationInSeconds,
lightSleepDurationInSeconds: dndata.Sleeps.lightSleepDurationInSeconds,
remSleepInSeconds: dndata.Sleeps.remSleepInSeconds,
awakeDurationInSeconds: dndata.Sleeps.awakeDurationInSeconds,
sleepLevelsMap: dndata.Sleeps.sleepLevelsMap,
validation: dndata.Sleeps.validation,
timeOffsetSleepRespiration: dndata.Sleeps.timeOffsetSleepRespiration,
timeOffsetSleepSpo2: dndata.Sleeps.timeOffsetSleepSpo2,
timeOffsetSleepSpo2: dndata.Sleeps.timeOffsetSleepSpo2,
overallSleepScore: dndata.Sleeps.overallSleepScore,
createdAt: dndata.Sleeps.createdAt,
updatedAt: dndata.Sleeps.updatedAt
}
});
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Inserted a record in the IOT_GarminSleepsModel teble.') : 0;
} else {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- No record in the IOT_GarminSleepsModel teble.') : 0;
}
// StressDetails
if (dndata.StressDetails) {
// ダウンロードしたデータをテーブルに追加
await IOT_GarminStressDetailsModel.findOrCreate({
where: {
idIOT_GarminStressDetails: dndata.StressDetails.idIOT_GarminStressDetails
},
defaults: {
idIOT_GarminStressDetails: dndata.StressDetails.idIOT_GarminStressDetails,
garminId: dndata.StressDetails.garminId,
garminAccessToken: dndata.StressDetails.garminAccessToken,
summaryId: dndata.StressDetails.summaryId,
startTimeInSeconds: dndata.StressDetails.startTimeInSeconds,
startTimeOffsetInSeconds: dndata.StressDetails.startTimeOffsetInSeconds,
durationInSeconds: dndata.StressDetails.durationInSeconds,
calendarDate: dndata.StressDetails.calendarDate,
timeOffsetStressLevelValues: dndata.StressDetails.timeOffsetStressLevelValues,
timeOffsetBodyBatteryValues: dndata.StressDetails.timeOffsetBodyBatteryValues,
createdAt: dndata.StressDetails.createdAt,
updatedAt: dndata.StressDetails.updatedAt
}
});
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Inserted a record in the IOT_GarminStressDetailsModel teble.') : 0;
} else {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- No record in the IOT_GarminStressDetailsModel teble.') : 0;
}
// UserMetrics
if (dndata.UserMetrics) {
// ダウンロードしたデータをテーブルに追加
await IOT_GarminUserMetricsModel.findOrCreate({
where: {
idIOT_GarminUserMetrics: dndata.UserMetrics.idIOT_GarminUserMetrics
},
defaults: {
idIOT_GarminUserMetrics: dndata.UserMetrics.idIOT_GarminUserMetrics,
garminId: dndata.UserMetrics.garminId,
garminAccessToken: dndata.UserMetrics.garminAccessToken,
summaryId: dndata.UserMetrics.summaryId,
calendarDate: dndata.UserMetrics.calendarDate,
vo2Max: dndata.UserMetrics.vo2Max,
fitnessAge: dndata.UserMetrics.fitnessAge,
createdAt: dndata.UserMetrics.createdAt,
updatedAt: dndata.UserMetrics.updatedAt
}
});
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- Inserted a record in the IOT_GarminUserMetrics teble.') : 0;
} else {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '|- No record in the IOT_GarminUserMetrics teble.') : 0;
}
// 画面表示
if (dndata) {
persist.garmin = dndata;
sendIPCMessage("showGarmin", persist.garmin);
}
} catch (error) {
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainHALsync.garminDownload() ', error);
let arg = {
error: error.message
};
sendIPCMessage('Error', {
datetime: new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"),
moduleName: 'mainHALsync.garminDownload()',
stackLog: `Detail: ${error}`
});
// mainWindow.webContents.send('to-renderer', JSON.stringify({ cmd: "Synced", arg: arg }));
}
},
//----------------------------------------------------------------------------------------------
/**
* @func httpGetRequest
* @desc httpGetRequest
* @async
* @param {void}
* @return void
* @throw error
*/
httpGetRequest: function (url, token) {
return new Promise((resolve, reject) => {
if (!token) {
token = config.halApiToken;
}
const options = {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + token
},
rejectUnauthorized: false,
requestCert: true,
agent: false
};
let res_str = '';
const req = https.request(url, options, (res) => {
res.setEncoding('utf8');
res.on('data', (chunk) => {
res_str += chunk;
});
res.on('end', () => {
if (res.statusCode === 200) {
let res_data = null;
try {
res_data = JSON.parse(res_str);
} catch (error) {
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainHALsync.httpGetRequest', error);
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| error res_str:', res_str);
}
resolve(res_data);
} else {
let message = 'method=get, url=' + url + ', code=' + res.statusCode + ', message=';
try {
let res_data = JSON.parse(res_str);
message += res_data.error;
} catch (error) {
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainHALsync.httpGetRequest', error);
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| error res_str:', res_str);
}
reject(new Error('Received an error response from HAL: ' + message));
}
});
});
req.on('error', (error) => {
reject(new Error('Failed to send a http get request: ' + error.message));
});
req.end();
});
},
//----------------------------------------------------------------------------------------------
/**
* @func httpPostRequest
* @desc httpPostRequest
* @async
* @param {void}
* @return void
* @throw error
*/
httpPostRequest: function (url, data, token) {
if (!token) {
token = config.halApiToken;
}
return new Promise((resolve, reject) => {
const req_body_str = JSON.stringify(data);
const options = {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(req_body_str)
},
rejectUnauthorized: false,
requestCert: true,
agent: false
};
let res_str = '';
const req = https.request(url, options, (res) => {
res.setEncoding('utf8');
res.on('data', (chunk) => {
res_str += chunk;
});
res.on('end', () => {
if (res.statusCode === 200) {
let res_data = null;
try {
res_data = JSON.parse(res_str);
} catch (error) {
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainHALsync.httpPostRequest', error);
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| error res_str:', res_str);
}
resolve(res_data);
} else {
let message = 'method=post, url=' + url + ', code=' + res.statusCode + ', message=';
try {
let res_data = JSON.parse(res_str);
message += res_data.error;
} catch (error) {
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainHALsync.httpPostRequest', error);
}
reject(new Error('Received an error response from HAL: ' + message));
}
});
});
req.on('error', (error) => {
reject(new Error('Failed to send a http post request: ' + error.message));
});
req.write(req_body_str);
req.end();
});
},
//----------------------------------
/**
* @func setHalApiTokenRequest
* @desc HAL API トークン設定
* APIトークンをセットして、実際にプロファイルを受信できたら設定値として保存
* @async
* @param {void}
* @return void
* @throw error
*/
setHalApiTokenRequest: async function (_token) {
let arg = {};
try {
let profile = await mainHALsync.httpGetRequest(HAL_API_BASE_URL + '/profile', _token);
await store.set('config.HAL.halApiToken', _token);
config.halApiToken = _token;
arg = { profile: profile };
} catch (error) {
arg.error = error.message;
}
sendIPCMessage("HALsetApiTokenResponse", arg);
},
//----------------------------------
/**
* @func deleteHalApiToken
* @desc HAL API トークン設定削除
* @async
* @param {void}
* @return void
* @throw error
*/
deleteHalApiToken: async function () {
try {
await store.delete('config.HAL.halApiToken');
config.halApiToken = null;
persist.profile = { name: 'No Profile', UID: 'No Data', sex: 'No Data', age: 'No Data' };
} catch (error) {
arg.error = error.message;
}
sendIPCMessage("HALdeleteApiTokenResponse", null);
},
//----------------------------------
/**
* @func getHalUserProfileRequest
* @desc HAL ユーザープロファイル取得
* @async
* @param {void}
* @return void
* @throw error
*/
getHalUserProfileRequest: async function () {
let arg = {};
try {
let profile = await mainHALsync.httpGetRequest(HAL_API_BASE_URL + '/profile');
arg.profile = profile;
persist.profile = profile;
} catch (error) {
arg.error = error.message;
}
sendIPCMessage("HALgetUserProfileResponse", arg);
},
//----------------------------------------------------------------------------------------------
/**
* @func startUploadEldata
* @desc 家電操作ログのアップロードを開始、定期的実行
* @async
* @param {void}
* @return void
* @throw error
*/
startUploadEldata: async function () {
// HAL API トークンが登録されていなければ次回起動のタイマーをセットして終了
if (!config.halApiToken) {
setTimeout(mainHALsync.startUploadEldata, config.UPLOAD_START_INTERVAL, config);
return;
}
// 最後にアップロードした日時と Log ID をストレージから取得したので、エラーチェック
if (!config.lastUploadedTime) {
config.lastUploadedTime = 0;
}
if (!config.lastUploadedId) {
config.lastUploadedId = 0;
}
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainHALsync.startUploadEldata Started to upload ELData:') : 0;
// console.log('- Last Uploaded time: ' + (new Date(config.lastUploadedTime)).toLocaleString());
// console.log('- Last Uploaded Log ID: ' + config.lastUploadedId);
// 新たな家電操作ログ件数を取得
let cnt = 0;
try {
cnt = await eldataModel.count({
where: {
id: { [Op.gt]: config.lastUploadedId }
}
});
// console.log('- Number of new logs: ' + cnt);
} catch (error) {
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainHALsync.startUploadEldata', error);
setTimeout(mainHALsync.startUploadEldata, config.UPLOAD_START_INTERVAL, config);
return;
}
// 新たな家電操作ログがなければ次回起動のタイマーをセットして終了
if (!cnt) {
setTimeout(mainHALsync.startUploadEldata, config.UPLOAD_START_INTERVAL, config);
return;
}
// 新たな家電操作ログを取得 (最大 100 件)
let dlist_orig = await eldataModel.findAll({
where: { id: { [Op.gt]: config.lastUploadedId } },
order: [['id', 'ASC']], // id の値が小さい順
offset: 0,
limit: config.UPLOAD_UNIT_NUM
});
// 純粋な Object オブジェクトじゃないので、純粋な Object オブジェクトに変換
let dlist = JSON.parse(JSON.stringify(dlist_orig));
// createdAt の値が Date オブジェクトから文字列に変換されてしまったが、
// そのフォーマットが 2021-08-22T07:32:00.353Z になっているので、
// SQL の datetime のフォーマット (2021-08-22 07:32:00) に変換
for (let d of dlist) {
let t = d.createdAt;
d.createdAt = t.substr(0, 10) + ' ' + t.substr(11, 8);
}
// HAL に家電操作ログをアップロード
try {
await mainHALsync.httpPostRequest(HAL_API_BASE_URL + '/eldata', dlist);
// console.log('- Number of uploaded logs: ' + dlist.length);
} catch (error) {
console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| ', error);
sendIPCMessage('Error', {
datetime: new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"),
moduleName: 'mainHALsync.startUploadEldata()',
stackLog: `Detail: ${error}`
});
setTimeout(mainHALsync.startUploadEldata, config.UPLOAD_START_INTERVAL, config);
return;
}
// ログの id の最大値
let max_id = 0;
for (let d of dlist) {
if (d.id > max_id) {
max_id = d.id;
}
}
// 最後にアップロードした日時と Log ID をストレージに保存
await store.set('config.HAL.lastUploadedTime', Date.now());
await store.set('config.HAL.lastUploadedId', max_id);
// 次回起動のタイマーをセット
let interval = config.UPLOAD_START_INTERVAL;
if (dlist.length === config.UPLOAD_UNIT_NUM) {
interval = config.UPLOAD_UNIT_INTERVAL;
}
setTimeout(mainHALsync.startUploadEldata, interval, config);
},
//----------------------------------------------------------------------------------------------
/**
* @func ConfigSave
* @desc ConfigSave
* @async
* @param {void}
* @return void
* @throw error
*/
ConfigSave: async function () {
await store.set('config.HAL', config);
},
/**
* @func setConfig
* @desc setConfig
* @async
* @param {void}
* @return void
* @throw error
*/
setConfig: async function (_config) {
if (_config) {
config = mergeDeeply(config, _config);
}
await store.set('config.HAL', config);
sendIPCMessage("renewHALConfigView", config);
sendIPCMessage("configSaved", 'HAL'); // 保存したので画面に通知
},
/**
* @func getConfig
* @desc getConfig
* @async
* @param {void}
* @return void
* @throw error
*/
getConfig: function () {
return config;
},
/**
* @func getPersist
* @desc 現在のデータを取得する
* @async
* @param {void}
* @return persist persist
*/
getPersist: function () {
return persist;
},
/**
* @func stop
* @desc 開放して連携終了、設定や現在の数値を永続化する
* @async
* @param {void}
*/
stop: async function () {
config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainHALsync.stop()') : 0;
// startUploadEldataで仕掛けたintervalを潰さないとダメなんだけど、いまやってない
// node-cron化するべき
await mainHAL.setConfig();
await store.set('persist.HAL', persist);
},
//----------------------------------------------------------------------------------------------
/**
* @func renewConfigView
* @desc renewConfigView
* @async
* @throw error
*/
renewConfigView: async function () {
sendIPCMessage("renewHALConfigView", config); // 現在の設定値を表示
}
};
module.exports = mainHALsync;
//////////////////////////////////////////////////////////////////////
// EOF
//////////////////////////////////////////////////////////////////////