diff --git a/config.xml b/config.xml index 24de9b1..d046dc4 100644 --- a/config.xml +++ b/config.xml @@ -83,6 +83,7 @@ + diff --git a/package-lock.json b/package-lock.json index a9cc126..29be2df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,6 +96,11 @@ "resolved": "https://registry.npmjs.org/@ionic-native/core/-/core-4.15.0.tgz", "integrity": "sha512-WlFcBktHnufiu+SB1u2GemEkDrXE94+LUkg59BQL1Tr0j+7EXcNY20JUUFCnQZP+uTJbUxaYZEa36B8UKBBpbA==" }, + "@ionic-native/network": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@ionic-native/network/-/network-4.17.0.tgz", + "integrity": "sha512-isAZx0CWIGHxl4u7GYkwkertNrSarNXhQQ4iQgqOefDJUtOj78zJsYNVxUWk1MiqTyQrnV9H7+WHWf62JAR82w==" + }, "@ionic-native/splash-screen": { "version": "4.15.0", "resolved": "https://registry.npmjs.org/@ionic-native/splash-screen/-/splash-screen-4.15.0.tgz", @@ -1959,6 +1964,11 @@ "resolved": "https://registry.npmjs.org/cordova-plugin-ionic-webview/-/cordova-plugin-ionic-webview-2.2.0.tgz", "integrity": "sha512-mNTJaIRsz83Vntk2d3jrPCnlqEPQsfOJW6U2AzS7WV1T15Jj/STdXI/Uv1vIyvPVd9h1OLF+yu64ZsmQH2VRMQ==" }, + "cordova-plugin-network-information": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cordova-plugin-network-information/-/cordova-plugin-network-information-2.0.1.tgz", + "integrity": "sha1-6QQh9DDGq3bUCSI/Jfzvu7zhdpA=" + }, "cordova-plugin-splashscreen": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/cordova-plugin-splashscreen/-/cordova-plugin-splashscreen-5.0.2.tgz", diff --git a/package.json b/package.json index 8ab7c83..a34738e 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@angular/platform-browser": "5.2.11", "@angular/platform-browser-dynamic": "5.2.11", "@ionic-native/core": "~4.15.0", + "@ionic-native/network": "^4.17.0", "@ionic-native/splash-screen": "~4.15.0", "@ionic-native/status-bar": "~4.15.0", "@ionic/storage": "2.2.0", @@ -31,6 +32,7 @@ "cordova-plugin-device": "^2.0.2", "cordova-plugin-ionic-keyboard": "^2.1.3", "cordova-plugin-ionic-webview": "^2.2.0", + "cordova-plugin-network-information": "2.0.1", "cordova-plugin-splashscreen": "^5.0.2", "cordova-plugin-statusbar": "^2.4.2", "cordova-plugin-whitelist": "^1.3.3", @@ -56,11 +58,12 @@ "ANDROID_SUPPORT_ANNOTATIONS_VERSION": "27.+" }, "cordova-plugin-ionic-keyboard": {}, - "wifiwizard2": {} + "wifiwizard2": {}, + "cordova-plugin-network-information": {} }, "platforms": [ "android", "browser" ] } -} +} \ No newline at end of file diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 69d51d9..0f6bf88 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -8,7 +8,6 @@ import { openSenseApp } from './app.component'; import { HomePage } from '../pages/home/home'; import { ApiProvider } from '../providers/api/api'; import { HttpClientModule } from '@angular/common/http'; -import { OtaWifiProvider } from '../providers/ota-wifi/ota-wifi'; import { OtaWizardPage } from '../pages/ota-wizard/ota-wizard'; import { OtaWizardPageModule } from '../pages/ota-wizard/ota-wizard.module'; @@ -34,7 +33,6 @@ import { OtaWizardPageModule } from '../pages/ota-wizard/ota-wizard.module'; SplashScreen, {provide: ErrorHandler, useClass: IonicErrorHandler}, ApiProvider, - OtaWifiProvider ] }) export class AppModule {} diff --git a/src/pages/Blockly/blockly.ts b/src/pages/Blockly/blockly.ts index 86ef125..d6b2e77 100644 --- a/src/pages/Blockly/blockly.ts +++ b/src/pages/Blockly/blockly.ts @@ -1,6 +1,5 @@ import { Component } from '@angular/core'; import { IonicPage, NavController, NavParams } from 'ionic-angular'; -import { OtaWifiProvider } from '../../providers/ota-wifi/ota-wifi'; import { OtaWizardPage } from '../ota-wizard/ota-wizard'; /** @@ -19,13 +18,7 @@ export class BlocklyPage { constructor( public navCtrl: NavController, - public navParams: NavParams, - private otaWifi: OtaWifiProvider) { - // otaWifi is here only for testing, should later be encapsulated by OtaWizardComponent - console.log('wifi strategy:', otaWifi.strategy) - otaWifi.findSenseboxes() - .then(console.log) - .catch(console.error) + public navParams: NavParams) { } ionViewDidLoad() { diff --git a/src/pages/ota-wizard/ota-wizard.html b/src/pages/ota-wizard/ota-wizard.html index d8275ec..f7a8052 100644 --- a/src/pages/ota-wizard/ota-wizard.html +++ b/src/pages/ota-wizard/ota-wizard.html @@ -38,12 +38,33 @@ + -

Check internet connection for compilation

-
-        - if connected: background: compile code
-        - if not: enable wifi (manually on iOS)
-      
+ +

Compiling your sketch...

+ +
+ + +

You are offline.

+

+ For compilation, you need to connect to the internet. + Please enable a connection. +

+
+ + +

Sketch successfully compiled.

+ +
+ + +

Error compiling your sketch.

+ +
diff --git a/src/pages/ota-wizard/ota-wizard.module.ts b/src/pages/ota-wizard/ota-wizard.module.ts index 3e8650c..2b902cd 100644 --- a/src/pages/ota-wizard/ota-wizard.module.ts +++ b/src/pages/ota-wizard/ota-wizard.module.ts @@ -1,6 +1,8 @@ import { NgModule } from '@angular/core'; import { IonicPageModule } from 'ionic-angular'; import { OtaWizardPage } from './ota-wizard'; +import { Network } from '@ionic-native/network'; +import { OtaWifiProvider } from '../../providers/ota-wifi/ota-wifi'; @NgModule({ declarations: [ @@ -9,5 +11,9 @@ import { OtaWizardPage } from './ota-wizard'; imports: [ IonicPageModule.forChild(OtaWizardPage), ], + providers: [ + Network, + OtaWifiProvider, + ] }) export class OtaWizardPageModule {} diff --git a/src/pages/ota-wizard/ota-wizard.ts b/src/pages/ota-wizard/ota-wizard.ts index 63a3fc3..8f5f9cb 100644 --- a/src/pages/ota-wizard/ota-wizard.ts +++ b/src/pages/ota-wizard/ota-wizard.ts @@ -1,25 +1,132 @@ -import { Component } from '@angular/core'; -import { IonicPage, NavController, NavParams } from 'ionic-angular'; +import { + Component, + OnDestroy, + OnInit, + ViewChild, +} from '@angular/core' +import { + IonicPage, + Slides, +} from 'ionic-angular' +import { Network } from '@ionic-native/network' +import { Subscription } from 'rxjs/Subscription'; -/** - * Generated class for the OtaWizardPage page. - * - * See https://ionicframework.com/docs/components/#navigation for more info on - * Ionic pages and navigation. - */ +import { OtaWifiProvider, WifiStrategy } from '../../providers/ota-wifi/ota-wifi'; @IonicPage() @Component({ selector: 'page-ota-wizard', templateUrl: 'ota-wizard.html', }) -export class OtaWizardPage { +export class OtaWizardPage implements OnInit, OnDestroy { + @ViewChild(Slides) slides: Slides + onlineSub: Subscription + offlineSub: Subscription - constructor(public navCtrl: NavController, public navParams: NavParams) { + compilationFailed: boolean + + state: OtaState = { + isOnline: false, + compiledSketch: undefined, + compilation: 'compiling', } - ionViewDidLoad() { - console.log('ionViewDidLoad OtaWizardPage'); + constructor( + private network: Network, + private otaWifi: OtaWifiProvider, + ) { } + ngOnInit() { + // try to start compilation already in the background + this.compileSketch() + + this.state.isOnline = this.network.type !== 'none' + + this.onlineSub = this.network.onConnect().subscribe(() => { + this.state.isOnline = true + // trigger compilation, only when needed + if (this.state.compilation == 'go-online') this.compileSketch() + }) + + this.offlineSub = this.network.onDisconnect().subscribe(() => { + this.state.isOnline = false + }) + } + + ngOnDestroy () { + this.onlineSub.unsubscribe() + this.offlineSub.unsubscribe() + } + + // call logic for each slide + onSlideChange () { + switch (this.slides.getActiveIndex()) { + case OtaSlides.Intro: + this.slides.lockSwipeToNext(false) + break + + case OtaSlides.Compilation: + this.handleCompilation() + break + + case OtaSlides.WifiSelection: + this.handleWifiSelection() + break + + case OtaSlides.Status: + break + + default: + console.warn('unknown slide, please define its logic') + } + } + + private handleCompilation () { + this.slides.lockSwipeToNext(!this.state.compiledSketch) + + // need to go online for compilation. compilation is retriggered via this.onlineSub + if (!this.state.compiledSketch && !this.state.isOnline) { + switch (this.otaWifi.strategy) { + case WifiStrategy.Automatic: + // TODO: auto connect to previous network, if available + default: + this.state.compilation = 'go-online' + break + } + } + } + + private handleWifiSelection () { + this.slides.lockSwipeToNext(true) + + console.log('wifi strategy:', this.otaWifi.strategy) + this.otaWifi.findSenseboxes() + .then(console.log) + .catch(console.error) + } + + private compileSketch () { + // TODO: mock + this.state.compilation = 'compiling' + setTimeout(() => { + this.state.compiledSketch = 'firmware binary here..' + this.state.compilation = 'done' + this.slides.lockSwipeToNext(false) + }, 4000) + } +} + +type OtaState = { + isOnline: boolean, + compiledSketch: any, + compilation: 'compiling' | 'go-online' | 'done' | 'error', +} + +// names for the slide indices for easier access +enum OtaSlides { + Intro = 0, + Compilation = 1, + WifiSelection = 2, + Status = 3, } diff --git a/src/providers/ota-wifi/ota-wifi.ts b/src/providers/ota-wifi/ota-wifi.ts index a3f46a5..5b03c2d 100644 --- a/src/providers/ota-wifi/ota-wifi.ts +++ b/src/providers/ota-wifi/ota-wifi.ts @@ -17,32 +17,30 @@ export class OtaWifiProvider { public strategy: WifiStrategy constructor(private platform: Platform, private http: HttpClient) { - this.selectStrategy() + this.strategy = this.selectStrategy() } private selectStrategy (): WifiStrategy { try { // check if plugin is available (e.g. not in browser builds) WifiWizard2 - - if ( - this.platform.is('android') || - this.platform.is('ios') && this.platform.version().major >= 11 - ) { - this.strategy = WifiStrategy.Automatic - } else { - this.strategy = WifiStrategy.Manual - } } catch (err) { - this.strategy = WifiStrategy.Unavailable + return WifiStrategy.Unavailable + } + + if ( + this.platform.is('android') || + this.platform.is('ios') && this.platform.version().major >= 11 + ) { + return WifiStrategy.Automatic } - return this.strategy; + return WifiStrategy.Manual } async findSenseboxes (): Promise { if (this.strategy != WifiStrategy.Automatic) - throw Error('can not search for WiFi networks on this platform') + throw new Error('can not search for WiFi networks on this platform') return WifiWizard2.scan() .then(n => n.filter(n.SSID.includes(SSID_PREFIX))) @@ -51,7 +49,7 @@ export class OtaWifiProvider { async connectToSensebox (ssid: string): Promise { if (this.strategy != WifiStrategy.Automatic) - throw Error('can not connect to WiFi network on this platform') + throw new Error('can not connect to WiFi network on this platform') return this.platform.is('ios') ? WifiWizard2.iOSConnectNetwork(ssid) @@ -66,7 +64,7 @@ export class OtaWifiProvider { // TODO: replace with "WifiCapabilities". // makes it easier to check in each functions if required functionality is available -enum WifiStrategy { +export enum WifiStrategy { Automatic = 'Automatic', // android, iOS 11+ Manual = 'Manual', // older iOS Unavailable = 'Unavailable', // browser