mainIkea.js

//////////////////////////////////////////////////////////////////////
//	Copyright (C) Hiroshi SUGIMURA 2020.10.30
//////////////////////////////////////////////////////////////////////
/**
 * @module mainIkea
 */
'use strict'

//////////////////////////////////////////////////////////////////////
// 基本ライブラリ
const Store = require('electron-store');
const TF = require('tradfri-handler');
const cron = require('node-cron');
const { Sequelize, sqlite3 } = require('./models/localDBModels');   // DBデータと連携
const { objectSort, getNow, getToday, isObjEmpty, mergeDeeply } = require('./mainSubmodule');

let sendIPCMessage = null;

const store = new Store();

let config = {
	enabled: false,
	securityCode: "",
	identity: "",
	psk: "",
	debug: false
};

let persist = {};


//////////////////////////////////////////////////////////////////////
// config
let mainIkea = {
	callback: null,
	observationJob: null,
	isRun: false,

	//////////////////////////////////////////////////////////////////////
	/**
	 * @func start
	 * @desc start
	 * @async
	 * @param {void} 
	 * @return void
	 * @throw error
	 */
	// interfaces
	start: async function (_sendIPCMessage) {
		sendIPCMessage = _sendIPCMessage;

		if (mainIkea.isRun) { // 重複起動対策
			if (!isObjEmpty(persist)) {
				sendIPCMessage("renewIkeaConfigView", config);
				sendIPCMessage("fclIkea", persist);
			}
			return;
		}

		config.enabled = store.get('config.Ikea.enabled', false);
		config.securityCode = store.get('config.Ikea.securityCode', '');
		config.identity = store.get('config.Ikea.identity', '');
		config.psk = store.get('config.Ikea.psk', '');
		config.debug = store.get('config.Ikea.debug', false);
		persist = store.get('persist.Ikea', {});
		sendIPCMessage("renewIkeaConfigView", config);

		if (config.enabled == false) {
			config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainIkea.start() disabled.') : 0;
			mainIkea.isRun = false;
			return;
		}
		mainIkea.isRun = true;

		config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainIkea.start(), config:\x1b[32m', config, '\x1b[0m') : 0;

		try {
			let co = await mainIkea.startCore((facilities) => {
				persist = facilities;
				if (!isObjEmpty(persist)) {
					sendIPCMessage("fclIkea", persist);
				}
			});

			config.identity = co.identity;
			config.psk = co.psk;
			config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mianIkea.start() is connected. config:\x1b[32m', config, '\x1b[0m') : 0;
			await store.set('config.Ikea', config);

		} catch (error) {
			console.error(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainIkea.start() error:\x1b[32m', error, '\x1b[0m');
			config.enabled = false;
			mainIkea.isRun = false;
			return;
		}

		if (!isObjEmpty(persist)) {
			sendIPCMessage("fclIkea", persist);
		}
	},

	/**
	 * @func stop
	 * @desc stop
	 * @async
	 * @param {void} 
	 * @return void
	 * @throw error
	 */
	stop: async function () {
		mainIkea.isRun = false;

		config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainIkea.stop()') : 0;

		await mainIkea.stop();

		await mainIkea.setConfig();
		await store.set('persist.Ikea', persist);
	},

	/**
	 * @func stopWithoutSave
	 * @desc stopWithoutSave
	 * @async
	 * @param {void} 
	 * @return void
	 * @throw error
	 */
	stopWithoutSave: async function () {
		mainIkea.isRun = false;

		config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainIkea.stopWithoutSave()') : 0;

		await mainIkea.stop();
	},



	/**
	 * @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.Ikea', config);

		sendIPCMessage("renewIkeaConfigView", config);  // 保存したので画面に通知
		sendIPCMessage("configSaved", 'Ikea');  // 保存したので画面に通知
	},

	/**
	 * @func getConfig
	 * @desc getConfig
	 * @async
	 * @param {void} 
	 * @return void
	 * @throw error
	 */
	getConfig: function () {
		return config;
	},

	/**
	 * @func getPersist
	 * @desc getPersist
	 * @async
	 * @param {void} 
	 * @return void
	 * @throw error
	 */
	getPersist: function () {
		return persist;
	},


	//////////////////////////////////////////////////////////////////////
	/**
	 * @func startCore
	 * @desc inner functions
	 * @async
	 * @param {void} 
	 * @return void
	 * @throw error
	 */
	startCore: async function (callback) {
		if (!config.securityCode || config.securityCode == "") {
			console.error('mainIkea.startCore() config.key is not valid.');
		}

		mainIkea.callback = callback;

		config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainIkea.startCore() config::\x1b[32m', config, '\x1b[0m') : 0;

		let ret;
		try {
			ret = await TF.initialize(config.securityCode, mainIkea.received, { identity: config.identity, psk: config.psk, debugMode: config.debug });
			mainIkea.observe();
			return ret;
		} catch (error) {
			console.error('mainIkea.startCore() error:\x1b[32m', error, '\x1b[0m');
			throw error;
		}
	},


	/**
	 * @func received
	 * @desc 受信データ処理
	 * @async
	 * @param {void} 
	 * @return void
	 * @throw error
	 */
	received: function (rIP, device, error) {
		if (error) {
			console.log('-- received error');
			console.error(error);
			return;
		}

		// if( device.type === AccessoryTypes.lightbulb ) {
		// console.log( device );
		// }
		// console.log('-- received, IP:', rIP, ', device:', device);
	},


	/**
	 * @func observe
	 * @desc Ikeaを監視する
	 * @async
	 * @param {void} 
	 * @return void
	 * @throw error
	 */
	observe: async function (interval) {
		config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainIkea.observe() start.') : 0;

		if (mainIkea.observationJob) {
			config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainIkea.observe() already started.') : 0;
		}

		// facilitiesの定期的監視
		let oldVal = JSON.stringify(TF.objectSort(TF.facilities));
		mainIkea.observationJob = cron.schedule('0 * * * * *', () => {  // 1分毎にautoget、変化があればログ表示
			const newVal = JSON.stringify(TF.objectSort(TF.facilities));
			if (oldVal == newVal) return; // 変化した
			oldVal = newVal;
			mainIkea.callback(TF.facilities);
			// console.log('TF changed, new TF.facilities:', newVal);
		});
		mainIkea.observationJob.start();
	},


	/**
	 * @func stop
	 * @desc 監視をやめる、リリースする
	 * @async
	 * @param {void} 
	 * @return void
	 * @throw error
	 */
	stop: function () {
		config.debug ? console.log(new Date().toFormat("YYYY-MM-DDTHH24:MI:SS"), '| mainIkea.stop().') : 0;

		if (mainIkea.observationJob) {
			mainIkea.observationJob.stop();
			mainIkea.observationJob = null;
		}
		TF.release();
	}
};



module.exports = mainIkea;
//////////////////////////////////////////////////////////////////////
// EOF
//////////////////////////////////////////////////////////////////////