diff --git a/.gitignore b/.gitignore
index 4e2e516..92a5879 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,5 @@ dist/
builds/
packaged
dist
-.env
\ No newline at end of file
+.env
+.vscode
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..215db93
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,5 @@
+{
+ "tabWidth": 2,
+ "semi": false,
+ "singleQuote": true
+ }
\ No newline at end of file
diff --git a/main.js b/main.js
index 1646359..f006c84 100644
--- a/main.js
+++ b/main.js
@@ -1,56 +1,53 @@
-'use strict';
-const { autoUpdater } = require("electron-updater");
-const log = require('electron-log');
-const { app, BrowserWindow, systemPreferences, Tray, ipcMain, shell, dialog} = require('electron');
+'use strict'
+const { autoUpdater } = require('electron-updater')
+const log = require('electron-log')
+const {
+ app,
+ BrowserWindow,
+ systemPreferences,
+ Tray,
+ ipcMain,
+ shell,
+ dialog
+} = require('electron')
const path = require('path')
const url = require('url')
const Positioner = require('electron-positioner')
const tesla = require('./tesla-api')
-const Store = require('electron-store');
-const store = new Store();
-const Poller = require('./poller');
-const contextMenu = require('electron-context-menu');
+const Store = require('electron-store')
+const store = new Store()
+const Poller = require('./poller')
+const contextMenu = require('electron-context-menu')
+const isDev = require('electron-is-dev')
// Logging
-autoUpdater.logger = log;
-autoUpdater.logger.transports.file.level = 'info';
-log.info('Nikola App starting...');
+autoUpdater.logger = log
+autoUpdater.logger.transports.file.level = 'info'
+log.info('Nikola App starting...')
-
-// Keep a global reference of the window object, if you don't, the window will
-// be closed automatically when the JavaScript object is garbage collected.
-let mainWindow;
-
-// Keep a reference for dev mode
-let dev = false;
-if (process.defaultApp || /[\\/]electron-prebuilt[\\/]/.test(process.execPath) || /[\\/]electron[\\/]/.test(process.execPath)) {
- dev = true;
-}
+// Keep a global reference of the window object
+let mainWindow
function createWindow() {
-
-
// Create the browser window.
mainWindow = new BrowserWindow({
width: 300,
height: 450,
show: true,
frame: false,
- title: "Nikola",
+ title: 'Nikola',
fullscreenable: false,
resizable: false,
transparent: true,
titleBarStyle: 'customButtonsOnHover',
webPreferences: {
- // Prevents renderer process code from not running when window is
- // hidden
backgroundThrottling: false,
- nodeIntegration: true,
+ nodeIntegration: true
}
})
// tray stuff
- let tray;
+ let tray
if (process.platform === 'darwin') {
if (systemPreferences.isDarkMode()) {
@@ -62,22 +59,22 @@ function createWindow() {
}
if (process.platform !== 'darwin') {
- tray = new Tray(path.join(__dirname, 'src', 'assets', 'img', 'win_tray.png'))
+ tray = new Tray(
+ path.join(__dirname, 'src', 'assets', 'img', 'win_tray.png')
+ )
}
-
- // Don't show the app in the dock
+ // Don't show the app in the dock (OSX)
if (process.platform === 'darwin') {
app.dock.hide()
// Main window behavior
- mainWindow.on('blur', () => {
- mainWindow.hide()
- })
+ mainWindow.on('blur', () => {
+ mainWindow.hide()
+ })
}
-
// Show detached devtools (for development)
- if (dev && process.argv.indexOf('--noDevServer') === -1) {
+ if (isDev) {
mainWindow.openDevTools({
mode: 'detach'
})
@@ -87,30 +84,38 @@ function createWindow() {
let bounds = tray.getBounds()
mainWindow.webContents.on('did-finish-load', () => {
-
- if(store.get('betaReleases')){
- autoUpdater.channel = "beta"
+ // Auto update features
+ if (!isDev) {
+ store.get('betaReleases')
+ ? (autoUpdater.channel = 'beta')
+ : (autoUpdater.channel = 'latest')
+ autoUpdater.checkForUpdates()
}
- autoUpdater.checkForUpdates();
-
setInterval(() => {
- if(store.get('betaReleases')){
- autoUpdater.channel = "beta"
+ if (!isDev) {
+ store.get('betaReleases')
+ ? (autoUpdater.channel = 'beta')
+ : (autoUpdater.channel = 'latest')
+ autoUpdater.checkForUpdates()
}
- autoUpdater.checkForUpdates()
- }, 300000);
-
- const dialogOptions = {type: 'info', buttons: ['Restart and install', 'Not now'], message: 'A new Nikola version has been downloaded!'}
+ }, 300000)
- autoUpdater.on('update-downloaded', (info) => {
- dialog.showMessageBox(dialogOptions, i => i === 0 ? autoUpdater.quitAndInstall() : null)
- })
+ const dialogOptions = {
+ type: 'info',
+ buttons: ['Restart and install', 'Not now'],
+ message: 'A new Nikola version has been downloaded!'
+ }
- autoUpdater.on('error', (err) => {
- log.error(err)
- })
+ autoUpdater.on('update-downloaded', info => {
+ dialog.showMessageBox(dialogOptions, i =>
+ i === 0 ? autoUpdater.quitAndInstall() : null
+ )
+ })
+ autoUpdater.on('error', err => {
+ log.error(err)
+ })
if (process.platform === 'darwin') {
bounds = tray.getBounds()
@@ -137,13 +142,13 @@ function createWindow() {
}
// get tesla Data
- let authToken;
+ let authToken
const getTeslaData = async () => {
authToken = store.get('authToken')
if (mainWindow.isVisible() && authToken) {
try {
- let vehicle;
+ let vehicle
try {
vehicle = await tesla.vehicle(authToken)
} catch (error) {
@@ -163,7 +168,10 @@ function createWindow() {
await tesla.wakeUp(authToken, vehicle.vehicleID)
return
}
- const vehicleData = await tesla.vehicleData(authToken, vehicle.vehicleID)
+ const vehicleData = await tesla.vehicleData(
+ authToken,
+ vehicle.vehicleID
+ )
mainWindow.webContents.send('tesla-data', {
model: vehicle.model,
...vehicleData
@@ -177,18 +185,18 @@ function createWindow() {
}
// Polling function
- let poller;
+ let poller
const startPoller = () => {
// Set 10s timeout between polls
- poller = new Poller(10000);
+ poller = new Poller(10000)
// Wait till the timeout sent our event to the EventEmitter
poller.onPoll(async () => {
await getTeslaData()
- poller.poll();
- });
+ poller.poll()
+ })
// Initial start
if (store.get('authToken')) {
- poller.poll();
+ poller.poll()
}
}
@@ -206,8 +214,8 @@ function createWindow() {
// wait function
async function wait(ms) {
return new Promise(resolve => {
- setTimeout(resolve, ms);
- });
+ setTimeout(resolve, ms)
+ })
}
ipcMain.on('door', async (event, action) => {
@@ -234,10 +242,16 @@ function createWindow() {
try {
if (action === 'climate-on') {
log.info('triggered climate start')
- await tesla.climateStart(store.get('authToken'), store.get('vehicleId'))
+ await tesla.climateStart(
+ store.get('authToken'),
+ store.get('vehicleId')
+ )
} else {
log.info('triggering climate stop')
- await tesla.climateStop(store.get('authToken'), store.get('vehicleId'))
+ await tesla.climateStop(
+ store.get('authToken'),
+ store.get('vehicleId')
+ )
}
await wait(500)
await getTeslaData()
@@ -255,10 +269,18 @@ function createWindow() {
try {
if (action === 'sentry-on') {
log.info('triggering Sentry On')
- await tesla.setSentryMode(store.get('authToken'), store.get('vehicleId'), true)
+ await tesla.setSentryMode(
+ store.get('authToken'),
+ store.get('vehicleId'),
+ true
+ )
} else {
log.info('triggering Sentry Off')
- await tesla.setSentryMode(store.get('authToken'), store.get('vehicleId'), false)
+ await tesla.setSentryMode(
+ store.get('authToken'),
+ store.get('vehicleId'),
+ false
+ )
}
await wait(500)
await getTeslaData()
@@ -275,7 +297,11 @@ function createWindow() {
mainWindow.webContents.send('action-loading', 'climate-temp')
try {
log.info('Changing temperature')
- await tesla.setTemps(store.get('authToken'), store.get('vehicleId'), temp)
+ await tesla.setTemps(
+ store.get('authToken'),
+ store.get('vehicleId'),
+ temp
+ )
await wait(500)
await getTeslaData()
mainWindow.webContents.send('action-loading', null)
@@ -286,10 +312,10 @@ function createWindow() {
}
})
-
const setMenu = () => {
contextMenu({
- menu: actions => [{
+ menu: actions => [
+ {
label: `Nikola ${app.getVersion()}`,
click() {
shell.openExternal('https://github.com/geraldoramos/nikola')
@@ -299,8 +325,8 @@ function createWindow() {
label: 'Enable Beta Releases',
type: 'checkbox',
checked: store.get('betaReleases'),
- click: function (item) {
- if(store.get('betaReleases')){
+ click: function(item) {
+ if (store.get('betaReleases')) {
store.set('betaReleases', false)
return
}
@@ -326,7 +352,7 @@ function createWindow() {
}
}
]
- });
+ })
}
setMenu()
@@ -343,12 +369,11 @@ function createWindow() {
}
getTeslaData()
})
-
})
// position window to the tray area
tray.setIgnoreDoubleClickEvents(true)
- tray.on('click', (event) => {
+ tray.on('click', event => {
log.info('click tray event')
if (process.platform === 'darwin') {
bounds = tray.getBounds()
@@ -358,53 +383,41 @@ function createWindow() {
})
// and load the index.html of the app.
- let indexPath;
- if (dev && process.argv.indexOf('--noDevServer') === -1) {
+ let indexPath
+ if (isDev) {
indexPath = url.format({
protocol: 'http:',
host: 'localhost:8080',
pathname: 'index.html',
slashes: true
- });
+ })
} else {
indexPath = url.format({
protocol: 'file:',
pathname: path.join(__dirname, 'dist', 'index.html'),
slashes: true
- });
+ })
}
- mainWindow.loadURL(indexPath);
+ mainWindow.loadURL(indexPath)
- // Emitted when the window is closed.
- mainWindow.on('closed', function () {
+ mainWindow.on('closed', function() {
log.info('window closed event')
- // Dereference the window object, usually you would store windows
- // in an array if your app supports multi windows, this is the time
- // when you should delete the corresponding element.
- mainWindow = null;
- });
+ mainWindow = null
+ })
}
-// This method will be called when Electron has finished
-// initialization and is ready to create browser windows.
-// Some APIs can only be used after this event occurs.
-app.on('ready', createWindow);
+app.on('ready', createWindow)
-// Quit when all windows are closed.
app.on('window-all-closed', () => {
log.info('window-all-closed event')
- // On macOS it is common for applications and their menu bar
- // to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
- app.quit();
+ app.quit()
}
-});
+})
app.on('activate', () => {
log.info('app on activate event')
- // On macOS it's common to re-create a window in the app when the
- // dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
- createWindow();
+ createWindow()
}
})
diff --git a/package.json b/package.json
index e9c7156..de17757 100644
--- a/package.json
+++ b/package.json
@@ -89,6 +89,7 @@
"dependencies": {
"antd": "^3.19.2",
"electron-context-menu": "^0.12.1",
+ "electron-is-dev": "^1.1.0",
"electron-log": "^3.0.6",
"electron-positioner": "^4.1.0",
"electron-store": "^3.2.0",
diff --git a/poller.js b/poller.js
index bd4020c..d62ca5e 100644
--- a/poller.js
+++ b/poller.js
@@ -1,21 +1,21 @@
-const EventEmitter = require('events');
+const EventEmitter = require('events')
class Poller extends EventEmitter {
- /**
- * @param {int} timeout how long should we wait after the poll started?
- */
- constructor(timeout = 100) {
- super();
- this.timeout = timeout;
- }
+ /**
+ * @param {int} timeout how long should we wait after the poll started?
+ */
+ constructor(timeout = 100) {
+ super()
+ this.timeout = timeout
+ }
- poll() {
- setTimeout(() => this.emit('poll'), this.timeout);
- }
+ poll() {
+ setTimeout(() => this.emit('poll'), this.timeout)
+ }
- onPoll(cb) {
- this.on('poll', cb);
- }
+ onPoll(cb) {
+ this.on('poll', cb)
+ }
}
-module.exports = Poller;
\ No newline at end of file
+module.exports = Poller
diff --git a/src/components/Actions.js b/src/components/Actions.js
index 6f90f87..e3547dd 100644
--- a/src/components/Actions.js
+++ b/src/components/Actions.js
@@ -1,4 +1,4 @@
-import React, { Component } from 'react';
+import React, { Component } from 'react'
import unlock from '../assets/img/unlock.svg'
import lock from '../assets/img/lock.svg'
import nofan from '../assets/img/nofan.svg'
@@ -8,69 +8,94 @@ import sentryOff from '../assets/img/sentry-off.svg'
import temp from '../assets/img/temp.svg'
class Actions extends React.Component {
-
render() {
-
- if(this.props.type === 'door'){
- return (
-
- { this.props.loading === 'door-lock' || this.props.loading === 'door-unlock' ?
-
- :
-
- }
-
- )
- }
+ if (this.props.type === 'door') {
+ return (
+
+ {this.props.loading === 'door-lock' ||
+ this.props.loading === 'door-unlock' ? (
+
+ ) : (
+
+
+
+ )}
+
+ )
+ }
- if(this.props.type === 'climate'){
- return (
-
- { this.props.loading === 'climate-on' || this.props.loading === 'climate-off'?
-
- :
-
- }
-
- )
- }
+ if (this.props.type === 'climate') {
+ return (
+
+ {this.props.loading === 'climate-on' ||
+ this.props.loading === 'climate-off' ? (
+
+ ) : (
+
+
+
+ )}
+
+ )
+ }
- if(this.props.type === 'sentryMode'){
- return (
-
- { this.props.loading === 'sentry-on' || this.props.loading === 'sentry-off'?
-
- :
-
- }
-
- )
- }
+ if (this.props.type === 'sentryMode') {
+ return (
+
+ {this.props.loading === 'sentry-on' ||
+ this.props.loading === 'sentry-off' ? (
+
+ ) : (
+
+
+
+ )}
+
+ )
+ }
- if(this.props.type === 'climateTemp'){
- return (
-
- { this.props.loading === 'climate-temp' ?
-
- :
-
}
-
-
- )
- }
+ if (this.props.type === 'climateTemp') {
+ return (
+
+ {this.props.loading === 'climate-temp' ? (
+
+ ) : (
+
+
+
+ )}
+
+ )
+ }
}
}
-export default Actions;
+export default Actions
diff --git a/src/components/App.js b/src/components/App.js
index 4928a99..606cd24 100644
--- a/src/components/App.js
+++ b/src/components/App.js
@@ -1,9 +1,14 @@
-import modelImage from './helpers/model-image'
-import React, { Component } from 'react';
+import modelImage from './helpers/model-image'
+import React, { Component } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
-import { faTemperatureLow, faTachometerAlt, faBed, faPowerOff, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'
+import {
+ faTemperatureLow,
+ faTachometerAlt,
+ faBed,
+ faPowerOff,
+ faExternalLinkAlt
+} from '@fortawesome/free-solid-svg-icons'
import Maps from './Maps'
-const {ipcRenderer, remote} = window.require('electron')
import Actions from './Actions'
import ReactTooltip from 'react-tooltip'
import cToF from './helpers/c-to-f'
@@ -11,20 +16,20 @@ import fToC from './helpers/f-to-c'
import mToKm from './helpers/m-to-km'
import Modal from './Modal'
const { shell } = require('electron')
+const { ipcRenderer, remote } = window.require('electron')
class App extends React.Component {
-
constructor(props) {
- super(props);
+ super(props)
this.state = {
climateTempShowModal: false
- };
- this.handleLockClick = this.handleLockClick.bind(this);
- this.handleFanClick = this.handleFanClick.bind(this);
- this.handleClimateTempClick = this.handleClimateTempClick.bind(this);
- this.handleClimateTempOk = this.handleClimateTempOk.bind(this);
- this.handleClimateTempCancel = this.handleClimateTempCancel.bind(this);
- this.handleOpenMapClick = this.handleOpenMapClick.bind(this);
+ }
+ this.handleLockClick = this.handleLockClick.bind(this)
+ this.handleFanClick = this.handleFanClick.bind(this)
+ this.handleClimateTempClick = this.handleClimateTempClick.bind(this)
+ this.handleClimateTempOk = this.handleClimateTempOk.bind(this)
+ this.handleClimateTempCancel = this.handleClimateTempCancel.bind(this)
+ this.handleOpenMapClick = this.handleOpenMapClick.bind(this)
}
handleLockClick(event) {
@@ -39,124 +44,279 @@ class App extends React.Component {
ipcRenderer.send('sentryMode', event.target.name)
}
- handleClimateTempClick(){
- this.setState({climateTempShowModal:true})
+ handleClimateTempClick() {
+ this.setState({ climateTempShowModal: true })
}
- handleOpenMapClick(){
- shell.openExternal(`https://www.google.com/maps/search/?api=1&query=${this.props.status.location.lat},${this.props.status.location.lng}`)
+ handleOpenMapClick() {
+ shell.openExternal(
+ `https://www.google.com/maps/search/?api=1&query=${this.props.status.location.lat},${this.props.status.location.lng}`
+ )
}
- handleClimateTempOk(temp){
- if(temp.match(/^-{0,1}\d+$/)){
- this.setState({climateTempShowModal:false})
- const temperature = this.props.vehicle.temperatureUnits === 'F' ? fToC(temp) : temp
+ handleClimateTempOk(temp) {
+ if (temp.match(/^-{0,1}\d+$/)) {
+ this.setState({ climateTempShowModal: false })
+ const temperature =
+ this.props.vehicle.temperatureUnits === 'F' ? fToC(temp) : temp
ipcRenderer.send('climateTemp', temperature)
return
}
- this.setState({climateTempModalError:'Climate temperature needs to be a number'})
+ this.setState({
+ climateTempModalError: 'Climate temperature needs to be a number'
+ })
setTimeout(() => {
- this.setState({climateTempModalError:null})
- }, 2000);
-
+ this.setState({ climateTempModalError: null })
+ }, 2000)
}
- handleClimateTempCancel(){
- this.setState({climateTempShowModal:false})
+ handleClimateTempCancel() {
+ this.setState({ climateTempShowModal: false })
}
-
render() {
-
- if(this.props.vehicle && this.props.vehicle.state === 'asleep'){
- return (
+ if (this.props.vehicle && this.props.vehicle.state === 'asleep') {
+ return (
-
-
Trying to wake up
-
Make sure to close other Tesla Apps
-
+
+
- )
+
Trying to wake up
+
+
+ Make sure to close other Tesla Apps
+
+
+
+
+ )
}
- if(this.props.vehicle && this.props.vehicle.state === 'offline'){
+ if (this.props.vehicle && this.props.vehicle.state === 'offline') {
return (
-
-
-
Vehicle is Offline
-
Internet connection in the car is down
-
+
+
+
+
+
Vehicle is Offline
+
+
+ Internet connection in the car is down
+
+
+
)
- }
+ }
- if(this.props.loading){
+ if (this.props.loading) {
return (
-
+
)
}
return (
-
-
-
-
- Tesla {this.props.vehicle.model}
-
-
- Car version: {this.props.status.carVersion}
- Charging state: {this.props.status.chargingState}
- Battery level: {`${this.props.status.batteryLevel}%`}
- Time to full charge: {`${this.props.status.timetoFullCharge} hours`}
- Door: {this.props.status.locked ? 'Locked' : 'Unlocked'}
- Climate: {this.props.status.climate ? 'ON' : 'OFF'}
- Passenger temp. setting: {this.props.vehicle.temperatureUnits === 'F' ? Math.round(cToF(this.props.status.passengerTempSetting)) + ` ${this.props.vehicle.temperatureUnits}` : Math.round(this.props.status.passengerTempSetting) + ` ${this.props.vehicle.temperatureUnits}`}
- Driver temp. setting: {this.props.vehicle.temperatureUnits === 'F' ? Math.round(cToF(this.props.status.driverTempSetting)) + ` ${this.props.vehicle.temperatureUnits}` : Math.round(this.props.status.passengerTempSetting) + ` ${this.props.vehicle.temperatureUnits}` }
- Odometer: {this.props.vehicle.distanceUnits.split('/')[0] ==='km' ? Math.round(mToKm(this.props.status.odometer)) : Math.round(this.props.status.odometer) + ` ${this.props.vehicle.distanceUnits.split('/')[0].toUpperCase()}`}
- Sentry mode: {this.props.status.sentryMode ? 'ON' : 'OFF'}
- Valet mode: {this.props.status.valetMode ? 'ON' : 'OFF' }
-
-
-
-
-
- {!this.props.status.speed ? 'Stopped' : (this.props.vehicle.distanceUnits.split('/')[0] === 'km' ? Math.round(mToKm(this.props.status.speed)) : Math.round(this.props.status.speed))}
- {this.props.status.speed ? ` ${this.props.vehicle.distanceUnits}` : '' }
-
-
- {(this.props.vehicle.distanceUnits.split('/')[0] === 'km' ? Math.round(mToKm(this.props.status.batteryRange)) : Math.round(this.props.status.batteryRange))}
- {this.props.vehicle.distanceUnits.split('/')[0]}
-
-
- {this.props.vehicle.temperatureUnits === 'F' ? Math.round(cToF(this.props.status.temperature)) : Math.round(this.props.status.temperature) }
- {this.props.vehicle.temperatureUnits}
-
+
+
+
+
Tesla {this.props.vehicle.model}
+
+
+ Car version: {this.props.status.carVersion}
-
-
-
Controls
-
{this.props.actionError? 'Action Failed': null}
-
-
-
- {this.props.status.sentryModeAvailable ?
: null}
-
-
+
+ Charging state: {this.props.status.chargingState}
+
+ Battery level: {`${this.props.status.batteryLevel}%`}
-
-
-
-
+
+ Time to full charge:{' '}
+ {`${this.props.status.timetoFullCharge} hours`}
+
+ Door:{' '}
+ {this.props.status.locked ? 'Locked' : 'Unlocked'}
+
+
+ Climate: {this.props.status.climate ? 'ON' : 'OFF'}
+
+
+ Passenger temp. setting:{' '}
+
+ {this.props.vehicle.temperatureUnits === 'F'
+ ? Math.round(cToF(this.props.status.passengerTempSetting)) +
+ ` ${this.props.vehicle.temperatureUnits}`
+ : Math.round(this.props.status.passengerTempSetting) +
+ ` ${this.props.vehicle.temperatureUnits}`}
+
+
+
+ Driver temp. setting:{' '}
+
+ {this.props.vehicle.temperatureUnits === 'F'
+ ? Math.round(cToF(this.props.status.driverTempSetting)) +
+ ` ${this.props.vehicle.temperatureUnits}`
+ : Math.round(this.props.status.passengerTempSetting) +
+ ` ${this.props.vehicle.temperatureUnits}`}
+
+
+
+ Odometer:{' '}
+
+ {this.props.vehicle.distanceUnits.split('/')[0] === 'km'
+ ? Math.round(mToKm(this.props.status.odometer))
+ : Math.round(this.props.status.odometer) +
+ ` ${this.props.vehicle.distanceUnits
+ .split('/')[0]
+ .toUpperCase()}`}
+
+
+
+ Sentry mode:{' '}
+ {this.props.status.sentryMode ? 'ON' : 'OFF'}
+
+
+ Valet mode:{' '}
+ {this.props.status.valetMode ? 'ON' : 'OFF'}
+
+
+
+
+
+
+
+
+
+
+
+ {' '}
+ {!this.props.status.speed
+ ? 'Stopped'
+ : this.props.vehicle.distanceUnits.split('/')[0] === 'km'
+ ? Math.round(mToKm(this.props.status.speed))
+ : Math.round(this.props.status.speed)}
+
+
+ {' '}
+ {this.props.status.speed
+ ? ` ${this.props.vehicle.distanceUnits}`
+ : ''}
+
+
+
+
+
+
+
+ {' '}
+ {this.props.vehicle.distanceUnits.split('/')[0] === 'km'
+ ? Math.round(mToKm(this.props.status.batteryRange))
+ : Math.round(this.props.status.batteryRange)}
+
+
+ {' '}
+ {this.props.vehicle.distanceUnits.split('/')[0]}
+
+
+
+
+
+
+
+ {' '}
+ {this.props.vehicle.temperatureUnits === 'F'
+ ? Math.round(cToF(this.props.status.temperature))
+ : Math.round(this.props.status.temperature)}
+
+
+ {' '}
+ {this.props.vehicle.temperatureUnits}
+
+
+
+
+
+
+
Controls
+
+ {this.props.actionError ? 'Action Failed' : null}
+
+
+
+
+ {this.props.status.sentryModeAvailable ? (
+
+ ) : null}
+
+
+
+
+
- );
+ )
}
}
-export default App;
+export default App
diff --git a/src/components/Home.js b/src/components/Home.js
index 630af09..b739cf9 100644
--- a/src/components/Home.js
+++ b/src/components/Home.js
@@ -1,173 +1,221 @@
-import '../assets/css/Photon.css';
-import '../assets/css/App.css';
-import React, { Component } from 'react';
-const {ipcRenderer, remote} = window.require('electron')
+import '../assets/css/Photon.css'
+import '../assets/css/App.css'
+import React, { Component } from 'react'
+const { ipcRenderer, remote } = window.require('electron')
import batteryLevelIcon from './helpers/battery-level-icon'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faWifi } from '@fortawesome/free-solid-svg-icons'
-import App from './App';
-import Login from './Login';
+import App from './App'
+import Login from './Login'
class Home extends React.Component {
+ constructor(props) {
+ super(props)
+ this.state = {
+ firstData: false,
+ loading: true,
+ actionLoading: null,
+ error: false,
+ actionError: null,
+ auth: false,
+ online: true
+ }
+ this.alertOnlineStatus = this.alertOnlineStatus.bind(this)
+ }
- constructor(props) {
- super(props);
- this.state = {
- firstData: false,
- loading: true,
- actionLoading: null,
- error: false,
- actionError: null,
- auth:false,
- online: true,
- }
- this.alertOnlineStatus = this.alertOnlineStatus.bind(this);
- }
+ alertOnlineStatus() {
+ this.setState({ online: navigator.onLine })
+ }
+
+ componentDidMount() {
+ window.onerror = function(error, url, line) {
+ ipcRenderer.send('errorInWindow', error)
+ }
+
+ window.addEventListener('online', this.alertOnlineStatus)
+ window.addEventListener('offline', this.alertOnlineStatus)
+
+ this.alertOnlineStatus()
- alertOnlineStatus() {
- this.setState({online:navigator.onLine})
+ ipcRenderer.once('platform', function(event, platform) {
+ if (platform !== 'darwin') {
+ document.querySelector('.header-arrow').style = 'display: none'
+ document.querySelector('.toolbar').style =
+ '-webkit-app-region: drag;min-height: 10px'
}
+ })
+
+ ipcRenderer.on(
+ 'login',
+ function(event, isLogged) {
+ if (isLogged) {
+ this.setState({ auth: true })
+ return
+ }
+ this.setState({ auth: false })
+ }.bind(this)
+ )
- componentDidMount() {
-
- window.onerror = function(error, url, line) {
- ipcRenderer.send('errorInWindow', error);
- };
-
- window.addEventListener('online', this.alertOnlineStatus)
- window.addEventListener('offline', this.alertOnlineStatus)
-
- this.alertOnlineStatus()
-
- ipcRenderer.once('platform', function (event, platform) {
- if(platform!=='darwin'){
- document.querySelector('.header-arrow').style = 'display: none'
- document.querySelector('.toolbar').style = '-webkit-app-region: drag;min-height: 10px'
- }
- })
+ ipcRenderer.on(
+ 'action-loading',
+ function(event, actionLoading) {
+ this.setState({ actionLoading })
+ }.bind(this)
+ )
- ipcRenderer.on('login', function (event, isLogged) {
- if(isLogged){
- this.setState({auth:true})
- return
- }
- this.setState({auth:false})
-
- }.bind(this))
-
- ipcRenderer.on('action-loading', function (event, actionLoading) {
- this.setState({actionLoading})
- }.bind(this))
-
- ipcRenderer.on('action-error', function (event, actionError) {
- this.setState({actionError})
- setTimeout(() => {
- this.setState({actionError:null})
- }, 3000);
- }.bind(this))
-
- ipcRenderer.on('tesla-data-error', function (event,store) {
- if(!this.state.firstData){
- this.setState({
- error:store
- })
- }
-
- }.bind(this))
-
- ipcRenderer.on('tesla-data', function (event,store) {
+ ipcRenderer.on(
+ 'action-error',
+ function(event, actionError) {
+ this.setState({ actionError })
+ setTimeout(() => {
+ this.setState({ actionError: null })
+ }, 3000)
+ }.bind(this)
+ )
+
+ ipcRenderer.on(
+ 'tesla-data-error',
+ function(event, store) {
+ if (!this.state.firstData) {
this.setState({
- loading:false,
- firstData:true,
- batteryIcon: batteryLevelIcon(store.charge_state ? store.charge_state.battery_level : 'default'),
- vehicle:{
- model: store.model,
- state: store.state,
- temperatureUnits: store.gui_settings? store.gui_settings.gui_temperature_units : null,
- distanceUnits: store.gui_settings? store.gui_settings.gui_distance_units : '',
- chargeRateUnits: store.gui_settings? store.gui_settings.gui_charge_rate_units : null
+ error: store
+ })
+ }
+ }.bind(this)
+ )
+ ipcRenderer.on(
+ 'tesla-data',
+ function(event, store) {
+ this.setState({
+ loading: false,
+ firstData: true,
+ batteryIcon: batteryLevelIcon(
+ store.charge_state ? store.charge_state.battery_level : 'default'
+ ),
+ vehicle: {
+ model: store.model,
+ state: store.state,
+ temperatureUnits: store.gui_settings
+ ? store.gui_settings.gui_temperature_units
+ : null,
+ distanceUnits: store.gui_settings
+ ? store.gui_settings.gui_distance_units
+ : '',
+ chargeRateUnits: store.gui_settings
+ ? store.gui_settings.gui_charge_rate_units
+ : null
+ },
+ status: {
+ driverTempSetting: store.climate_state
+ ? store.climate_state.driver_temp_setting
+ : null,
+ passengerTempSetting: store.climate_state
+ ? store.climate_state.passenger_temp_setting
+ : null,
+ carVersion: store.vehicle_state
+ ? store.vehicle_state.car_version
+ : null,
+ batteryRange: store.charge_state
+ ? store.charge_state.battery_range
+ : null,
+ batteryLevel: store.charge_state
+ ? store.charge_state.battery_level
+ : null,
+ locked: store.vehicle_state ? store.vehicle_state.locked : null,
+ odometer: store.vehicle_state ? store.vehicle_state.odometer : null,
+ sentryMode: store.vehicle_state
+ ? store.vehicle_state.sentry_mode
+ : null,
+ sentryModeAvailable: store.vehicle_state
+ ? store.vehicle_state.sentry_mode_available
+ : null,
+ valetMode: store.vehicle_state
+ ? store.vehicle_state.valet_mode
+ : null,
+ climate: store.climate_state
+ ? store.climate_state.is_climate_on
+ : null,
+ speed: store.drive_state ? store.drive_state.speed : null,
+ chargingState: store.charge_state
+ ? store.charge_state.charging_state
+ : null,
+ timetoFullCharge: store.charge_state
+ ? store.charge_state.time_to_full_charge
+ : null,
+ temperature: store.climate_state
+ ? store.climate_state.inside_temp
+ : null,
+ location: {
+ lat: store.drive_state ? store.drive_state.latitude : null,
+ lng: store.drive_state ? store.drive_state.longitude : null
},
- status:{
- driverTempSetting: store.climate_state ? store.climate_state.driver_temp_setting: null,
- passengerTempSetting: store.climate_state ? store.climate_state.passenger_temp_setting: null,
- carVersion: store.vehicle_state? store.vehicle_state.car_version: null,
- batteryRange: store.charge_state ? store.charge_state.battery_range: null,
- batteryLevel: store.charge_state ? store.charge_state.battery_level: null,
- locked: store.vehicle_state? store.vehicle_state.locked : null,
- odometer:store.vehicle_state? store.vehicle_state.odometer : null,
- sentryMode:store.vehicle_state? store.vehicle_state.sentry_mode : null,
- sentryModeAvailable:store.vehicle_state? store.vehicle_state.sentry_mode_available : null,
- valetMode:store.vehicle_state? store.vehicle_state.valet_mode : null,
- climate: store.climate_state ? store.climate_state.is_climate_on : null,
- speed: store.drive_state? store.drive_state.speed : null,
- chargingState: store.charge_state ? store.charge_state.charging_state : null,
- timetoFullCharge:store.charge_state ? store.charge_state.time_to_full_charge : null,
- temperature: store.climate_state ? store.climate_state.inside_temp: null,
- location: {lat: store.drive_state?store.drive_state.latitude:null, lng: store.drive_state? store.drive_state.longitude : null},
- locationAsOf: store.drive_state? store.drive_state.gps_as_of : null
- }
- });
- }.bind(this));
- }
+ locationAsOf: store.drive_state ? store.drive_state.gps_as_of : null
+ }
+ })
+ }.bind(this)
+ )
+ }
- componentWillUnmount(){
- ipcRenderer.removeAllListeners()
- window.removeEventListener('online', this.alertOnlineStatus)
- window.removeEventListener('offline', this.alertOnlineStatus)
- }
+ componentWillUnmount() {
+ ipcRenderer.removeAllListeners()
+ window.removeEventListener('online', this.alertOnlineStatus)
+ window.removeEventListener('offline', this.alertOnlineStatus)
+ }
render() {
-
- if(!this.state.online){
+ if (!this.state.online) {
return (
-
-
-
-
-
-
-
-
No Internet Connection
-
+
+
+
+
+
+
+
+
+
+
No Internet Connection
+
+
+
+
-
-
-
- )}
+ )
+ }
- if(!this.state.auth){
+ if (!this.state.auth) {
return (
-
- )
- }
-
- return (
+ )
+ }
+
+ return (
+
- )
+
+
+
+ )
}
}
-export default Home;
+export default Home
diff --git a/src/components/Login.js b/src/components/Login.js
index c3f2cc9..93668ee 100644
--- a/src/components/Login.js
+++ b/src/components/Login.js
@@ -1,69 +1,101 @@
-import React, { Component } from 'react';
-const {ipcRenderer, remote} = window.require('electron')
+import React, { Component } from 'react'
+const { ipcRenderer, remote } = window.require('electron')
import logo from '../assets/img/logo.svg'
class Login extends React.Component {
-
constructor(props) {
- super(props);
- this.state = {username: '', password:'', error: null, loading:false};
- this.handleChange = this.handleChange.bind(this);
- this.handleSubmit = this.handleSubmit.bind(this);
+ super(props)
+ this.state = { username: '', password: '', error: null, loading: false }
+ this.handleChange = this.handleChange.bind(this)
+ this.handleSubmit = this.handleSubmit.bind(this)
}
- handleChange(event) {
+ handleChange(event) {
const value = event.target.value
const name = event.target.name
this.setState({
[name]: value
- });
+ })
}
handleSubmit(event) {
- event.preventDefault();
- ipcRenderer.send('login-attempt', {username:this.state.username, password:this.state.password})
- this.setState({loading:true})
- ipcRenderer.on('login-failed', function (event,store) {
- console.log(store)
- this.setState({
- error:store,
- loading:false
- })
-
- }.bind(this))
+ event.preventDefault()
+ ipcRenderer.send('login-attempt', {
+ username: this.state.username,
+ password: this.state.password
+ })
+ this.setState({ loading: true })
+ ipcRenderer.on(
+ 'login-failed',
+ function(event, store) {
+ console.log(store)
+ this.setState({
+ error: store,
+ loading: false
+ })
+ }.bind(this)
+ )
}
render() {
-
- if(this.state.loading){
+ if (this.state.loading) {
return (
-
+
)
}
- return (
-
- )
+ return (
+
+
+
+
+
+
+ )
}
}
-export default Login;
+export default Login
diff --git a/src/components/Maps.js b/src/components/Maps.js
index 0cbd623..3dce2d7 100644
--- a/src/components/Maps.js
+++ b/src/components/Maps.js
@@ -1,200 +1,207 @@
-import React, { Component } from 'react';
-import {Map, InfoWindow, Marker, GoogleApiWrapper} from 'google-maps-react';
+import React, { Component } from 'react'
+import { Map, InfoWindow, Marker, GoogleApiWrapper } from 'google-maps-react'
const mapStyles = {
- width: '100%',
- height: '220px',
- position: 'relative',
- margin: '6px auto'
- }
+ width: '100%',
+ height: '220px',
+ position: 'relative',
+ margin: '6px auto'
+}
export class MapContainer extends Component {
-
render() {
return (
-
- );
+ )
}
}
export default GoogleApiWrapper({
apiKey: process.env.GOOGLE_MAPS
-})(MapContainer)
\ No newline at end of file
+})(MapContainer)
diff --git a/src/components/Modal.js b/src/components/Modal.js
index 6e247a6..9782ce4 100644
--- a/src/components/Modal.js
+++ b/src/components/Modal.js
@@ -1,36 +1,44 @@
-import React, { Component } from 'react';
-import { Modal } from 'antd';
-
+import React, { Component } from 'react'
+import { Modal } from 'antd'
class Popup extends React.Component {
constructor(props) {
- super(props);
+ super(props)
this.state = {
input: ''
- }
- this.handleChange = this.handleChange.bind(this);
-}
- handleChange(e){
- this.setState({ input: e.target.value });
}
+ this.handleChange = this.handleChange.bind(this)
+ }
+ handleChange(e) {
+ this.setState({ input: e.target.value })
+ }
- render() {
- return (
-
this.props.onOk(this.state.input)}
- onCancel={this.props.onCancel}
- cancelButtonProps={ {type:'ghost'} }
+ render() {
+ return (
+ this.props.onOk(this.state.input)}
+ onCancel={this.props.onCancel}
+ cancelButtonProps={{ type: 'ghost' }}
+ >
+
-
{!this.props.errorMessage ? this.props.info : this.props.errorMessage}
-
-
+ {!this.props.errorMessage ? this.props.info : this.props.errorMessage}
-
+
+
+
+
)
- }
-};
-export default Popup;
\ No newline at end of file
+ }
+}
+export default Popup
diff --git a/src/components/helpers/battery-level-icon.js b/src/components/helpers/battery-level-icon.js
index 93df2cd..d1f656e 100644
--- a/src/components/helpers/battery-level-icon.js
+++ b/src/components/helpers/battery-level-icon.js
@@ -1,22 +1,28 @@
-import { faBatteryFull, faBatteryEmpty, faBatteryQuarter, faBatteryHalf, faBatteryThreeQuarters } from '@fortawesome/free-solid-svg-icons'
+import {
+ faBatteryFull,
+ faBatteryEmpty,
+ faBatteryQuarter,
+ faBatteryHalf,
+ faBatteryThreeQuarters
+} from '@fortawesome/free-solid-svg-icons'
-export default (level) =>{
- let battery;
- switch (true) {
- case (0 <= level && level <= 9):
- battery = {type: faBatteryEmpty, color:'#cc0001'}
- break
- case (10 <= level && level <= 39):
- battery = {type: faBatteryQuarter, color:'#cc0001'}
- break
- case (40 <= level && level <= 69):
- battery = {type: faBatteryHalf, color:'#1BC47D'}
- break
- case (70 <= level && level <= 90):
- battery = {type: faBatteryThreeQuarters, color:'#1BC47D'}
- break
- default:
- battery = {type: faBatteryFull, color:'#1BC47D'}
- }
- return battery
- }
\ No newline at end of file
+export default level => {
+ let battery
+ switch (true) {
+ case 0 <= level && level <= 9:
+ battery = { type: faBatteryEmpty, color: '#cc0001' }
+ break
+ case 10 <= level && level <= 39:
+ battery = { type: faBatteryQuarter, color: '#cc0001' }
+ break
+ case 40 <= level && level <= 69:
+ battery = { type: faBatteryHalf, color: '#1BC47D' }
+ break
+ case 70 <= level && level <= 90:
+ battery = { type: faBatteryThreeQuarters, color: '#1BC47D' }
+ break
+ default:
+ battery = { type: faBatteryFull, color: '#1BC47D' }
+ }
+ return battery
+}
diff --git a/src/components/helpers/c-to-f.js b/src/components/helpers/c-to-f.js
index 610f6e4..fc3c6a8 100644
--- a/src/components/helpers/c-to-f.js
+++ b/src/components/helpers/c-to-f.js
@@ -1,4 +1,4 @@
export default function cToF(celsius) {
- const fahrenheit = (celsius * (9/5)) + 32
- return fahrenheit;
- }
\ No newline at end of file
+ const fahrenheit = celsius * (9 / 5) + 32
+ return fahrenheit
+}
diff --git a/src/components/helpers/f-to-c.js b/src/components/helpers/f-to-c.js
index 2ea8069..af956ff 100644
--- a/src/components/helpers/f-to-c.js
+++ b/src/components/helpers/f-to-c.js
@@ -1,4 +1,4 @@
export default function fToC(fahrenheit) {
- const celsius = (fahrenheit - 32) * 5 / 9
- return celsius;
- }
\ No newline at end of file
+ const celsius = ((fahrenheit - 32) * 5) / 9
+ return celsius
+}
diff --git a/src/components/helpers/m-to-km.js b/src/components/helpers/m-to-km.js
index 21e09ad..57a7507 100644
--- a/src/components/helpers/m-to-km.js
+++ b/src/components/helpers/m-to-km.js
@@ -1,4 +1,4 @@
export default function mToKm(miles) {
- const km = miles * 1.60934;
- return km
- }
\ No newline at end of file
+ const km = miles * 1.60934
+ return km
+}
diff --git a/src/components/helpers/model-image.js b/src/components/helpers/model-image.js
index 78ed019..a67e20c 100644
--- a/src/components/helpers/model-image.js
+++ b/src/components/helpers/model-image.js
@@ -3,28 +3,28 @@ import models from '../../assets/img/models.png'
import modely from '../../assets/img/modely.png'
import modelx from '../../assets/img/modelx.png'
-export default (model) =>{
- let img;
- if(!model){
- img = model3
- return img
- }
- model = model.toLowerCase()
- switch (true) {
- case (model === 'model 3'):
- img = model3
- break
- case (model === 'model s'):
- img = models
- break
- case (model === 'model y'):
- img = modely
- break
- case (model === 'model x'):
- img = modelx
- break
- default:
- img = model3
- }
+export default model => {
+ let img
+ if (!model) {
+ img = model3
return img
}
+ model = model.toLowerCase()
+ switch (true) {
+ case model === 'model 3':
+ img = model3
+ break
+ case model === 'model s':
+ img = models
+ break
+ case model === 'model y':
+ img = modely
+ break
+ case model === 'model x':
+ img = modelx
+ break
+ default:
+ img = model3
+ }
+ return img
+}
diff --git a/src/index.js b/src/index.js
index 4dcf897..4aee25b 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,11 +1,11 @@
-import React from 'react';
-import { render } from 'react-dom';
-import Home from './components/Home';
+import React from 'react'
+import { render } from 'react-dom'
+import Home from './components/Home'
// Since we are using HtmlWebpackPlugin WITHOUT a template, we should create our own root node in the body element before rendering into it
-let root = document.createElement('div');
-root.id = "root";
-document.body.appendChild( root );
+let root = document.createElement('div')
+root.id = 'root'
+document.body.appendChild(root)
// Now we can render our application into it
-render(
, document.getElementById('root') );
+render(
, document.getElementById('root'))
diff --git a/yarn.lock b/yarn.lock
index a5303d1..fa0e9d3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2367,7 +2367,7 @@ electron-download@^4.1.0, electron-download@^4.1.1:
semver "^5.4.1"
sumchecker "^2.0.2"
-electron-is-dev@^1.0.1:
+electron-is-dev@^1.0.1, electron-is-dev@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-1.1.0.tgz#b15a2a600bdc48a51a857d460e05f15b19a2522c"
integrity sha512-Z1qA/1oHNowGtSBIcWk0pcLEqYT/j+13xUw/MYOrBUOL4X7VN0i0KCTf5SqyvMPmW5pSPKbo28wkxMxzZ20YnQ==
@@ -4690,11 +4690,6 @@ node-libs-browser@^2.0.0:
util "^0.11.0"
vm-browserify "0.0.4"
-node-machine-id@^1.1.12:
- version "1.1.12"
- resolved "https://registry.yarnpkg.com/node-machine-id/-/node-machine-id-1.1.12.tgz#37904eee1e59b320bb9c5d6c0a59f3b469cb6267"
- integrity sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==
-
node-pre-gyp@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149"
@@ -6166,7 +6161,7 @@ repeating@^2.0.0:
dependencies:
is-finite "^1.0.0"
-request@^2.45.0, request@^2.81.0, request@^2.88.0:
+request@^2.45.0, request@^2.81.0:
version "2.88.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
@@ -7200,15 +7195,6 @@ unique-string@^1.0.0:
dependencies:
crypto-random-string "^1.0.0"
-universal-analytics@^0.4.20:
- version "0.4.20"
- resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.20.tgz#d6b64e5312bf74f7c368e3024a922135dbf24b03"
- integrity sha512-gE91dtMvNkjO+kWsPstHRtSwHXz0l2axqptGYp5ceg4MsuurloM0PU3pdOfpb5zBXUvyjT4PwhWK2m39uczZuw==
- dependencies:
- debug "^3.0.0"
- request "^2.88.0"
- uuid "^3.0.0"
-
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
@@ -7345,7 +7331,7 @@ utils-merge@1.0.1:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
-uuid@^3.0.0, uuid@^3.0.1, uuid@^3.3.2:
+uuid@^3.0.1, uuid@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==