Last active
November 3, 2016 05:09
-
-
Save siyo/a9e45d892f17edf5c9bf1447e868b315 to your computer and use it in GitHub Desktop.
部屋の環境を監視しインターネットに公開するIoTソリューション (Runtime: Node.js v.4.4.x)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
// node deps | |
const path = require('path'); | |
const os = require ('os'); | |
const fs = require('fs'); | |
// npm deps | |
const noble = require('noble'); | |
const Netatmo = require('netatmo'); | |
const Twitter = require('twitter'); | |
const RaspiCam = require('raspicam'); | |
const _ = require('lodash'); | |
const log = require('npmlog'); | |
// config | |
// e.g. config.json | |
// { | |
// "twitter":{ | |
// "consumer_key": "xxxxxxxxxxxxxxx", | |
// "consumer_secret": "xxxxxxxxxxxxxxx", | |
// "access_token_key": "xxxxxxxxxxxxxxx", | |
// "access_token_secret": "xxxxxxxxxxxxxxx" | |
// }, | |
// "netatmo": { | |
// "client_id": "xxxxxxxxxxxxxxx", | |
// "client_secret": "xxxxxxxxxxxxxxx", | |
// "username": "xxxxxxxxxxxxxxx", | |
// "password": "xxxxxxxxxxxxxxx" | |
// } | |
// } | |
// | |
const config = require('./config.json'); | |
const TAG = path.basename(__filename); | |
const INTERVAL_TWEET = 15 * 60 * 1000; // msec | |
const IMG_DIR = '/tmp'; | |
const IMG_FILENAME = 'raspicam.jpg'; | |
const IMG_PATH = IMG_DIR + '/' + IMG_FILENAME; | |
//log.level = 'verbose'; | |
var luxes = []; | |
const tw = new Twitter(config.twitter); | |
const netatmo = new Netatmo(config.netatmo); | |
const cam = new RaspiCam({ | |
mode: 'photo', | |
output: IMG_PATH, | |
w: 1280, | |
h: 720, | |
q: 70, | |
e: 'jpg' | |
}); | |
function calcLux() { | |
var total = _.reduce(luxes, (memo, lux) => { | |
return memo += lux; | |
}, 0); | |
var val = Math.round(total / luxes.length); | |
var emoji = val > 2750 ? '☀️' : | |
val > 2000 ? '🌤' : | |
val > 1000 ? '⛅️' : | |
val > 500 ? '☁️' : | |
val > 100 ? '💡': | |
val > 10 ? '🕯': | |
'👻'; | |
luxes = []; | |
return emoji + ' ' + val + ' '; | |
} | |
function parseNetatmoDevice(device) { | |
var status = ''; | |
var data = device.dashboard_data; | |
if (!data) { | |
log.warn(TAG, 'data: %j', data); | |
return status; | |
} | |
_.each([ | |
['Temperature', '🌡', '℃'], | |
['Humidity','💧', '%'], | |
['Pressure', '🎈', 'hPa'], | |
['CO2','🌳', 'ppm' ], | |
['Noise', '🔊','dB '] | |
], (ary) => { | |
var key = ary[0], | |
emoji = ary[1], | |
unit = ary[2]; | |
var val = data[key]; | |
if (_.isNumber(val)) | |
status += emoji + ' ' + val + unit + ' '; | |
}); | |
return status; | |
} | |
function twpost(status, media_ids) { | |
var st = { | |
status: status, | |
media_ids: media_ids | |
}; | |
tw.post('statuses/update', st, (error) => { | |
if(error) | |
log.error(TAG, 'Error: %j', error); | |
else | |
log.info(TAG, 'POST: %j', st); | |
}); | |
} | |
const throttledCamStill = _.throttle(() => { | |
cam.start(); | |
}, INTERVAL_TWEET); | |
cam.on('start', () => { | |
log.info(TAG, 'cam start'); | |
}); | |
cam.on('read', (err, timestamp, filename) => { | |
if (err) | |
log.error(TAG, 'cam saved Error: %j', err); | |
else | |
log.info(TAG, 'cam saved: %s (%d)', filename, timestamp ); | |
if (filename !== IMG_FILENAME) { | |
return; | |
} | |
netatmo.getDevicelist((err, devices) => { | |
var data = fs.readFileSync(IMG_PATH); | |
tw.post('media/upload', {media: data}, function(error, media) { | |
var media_ids = null; | |
var status = ''; | |
if (!error) { | |
media_ids = media.media_id_string; | |
} | |
status += calcLux(); | |
if (!err) | |
status += parseNetatmoDevice(devices[0]); | |
else | |
log.error(TAG, '%j', err); | |
status += '⏰ ' + _.now() + 'UTC'; | |
twpost(status, media_ids); | |
}); | |
}); | |
}); | |
cam.on('stop', () => { | |
log.info(TAG, 'cam stop'); | |
}); | |
cam.on('exit', () => { | |
console.log('cam exit'); | |
}); | |
noble.on('stateChange', (state) => { | |
if (state === 'poweredOn') { | |
noble.startScanning([], true); | |
} else { | |
noble.stopScanning(); | |
} | |
}); | |
noble.on('discover', (peripheral) => { | |
var name = peripheral.advertisement.localName, | |
data = peripheral.advertisement.manufacturerData; | |
var val; | |
log.verbose(TAG, 'peripheral.advertisement:%j', peripheral.advertisement); | |
log.verbose(TAG, 'endian:%s', os.endianness()); | |
if (!_.isString(name) || !name.match(/^BLECAST_BL/) || !data) | |
return; | |
val = data.readUInt8(5) * 256 + data.readUInt8(4); | |
luxes.push(val); | |
log.verbose(TAG, 'MEASURE: %s %d', name, val); | |
throttledCamStill(); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "kitazawagosho", | |
"version": "1.1.0", | |
"description": "Environment of Kitazawa-gosho", | |
"main": "index.js", | |
"scripts": { | |
"test": "echo \"Error: no test specified\" && exit 1" | |
}, | |
"keywords": [ | |
"ble", | |
"bluetooth", | |
"illuminance", | |
"sensor", | |
"IoT", | |
"netatmo" | |
], | |
"author": "siyo", | |
"license": "MIT", | |
"dependencies": { | |
"async": "^2.0.0-rc.5", | |
"lodash": "^4.13.1", | |
"netatmo": "^1.5.0", | |
"noble": "1.5.0", | |
"npmlog": "^2.0.4", | |
"raspicam": "^0.2.13", | |
"twitter": "^1.3.0" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment