├── www ├── templates │ ├── Blank.html │ ├── More-Menu.html │ ├── Dialogs │ │ ├── Reorder-Categories.html │ │ ├── Passphrase-Entry.html │ │ └── Set-Multiple-Smart-Plugs-State.html │ ├── Settings │ │ ├── Configure-Pin.html │ │ ├── Logs-List.html │ │ ├── Log-Entry.html │ │ ├── About.html │ │ ├── Cameras-List.html │ │ ├── Configure-Passphrase.html │ │ ├── Settings-List.html │ │ ├── Hub.html │ │ ├── Log-Filter-Menu.html │ │ ├── Dashboard-Config.html │ │ └── Camera-Edit.html │ ├── Dashboard.html │ ├── Root.html │ ├── Camera-View.html │ ├── Cameras.html │ └── Devices │ │ └── Devices-List.html ├── images │ ├── finger.png │ └── about-icon.png ├── css │ ├── nprogress-overrides.css │ └── ionic-overrides.css ├── js │ ├── chrome-ext-background.js │ ├── Main.js │ └── RippleMockApi.js └── index.html ├── ionic.project ├── .readme ├── banner.jpg ├── dashboard-banner.jpg ├── screenshot-devices.jpg ├── screenshot-hub-status.jpg ├── screenshot-thermostat.jpg ├── screenshot-alarms-locks.jpg └── screenshot-lighting-outlets.jpg ├── Resources ├── icon.png ├── splash.png ├── ios │ ├── icon │ │ ├── icon.png │ │ ├── icon-40.png │ │ ├── icon-50.png │ │ ├── icon-60.png │ │ ├── icon-72.png │ │ ├── icon-76.png │ │ ├── icon@2x.png │ │ ├── icon-40@2x.png │ │ ├── icon-50@2x.png │ │ ├── icon-60@2x.png │ │ ├── icon-60@3x.png │ │ ├── icon-72@2x.png │ │ ├── icon-76@2x.png │ │ ├── icon-small.png │ │ ├── icon-small@2x.png │ │ └── icon-small@3x.png │ └── splash │ │ ├── Default-667h.png │ │ ├── Default-736h.png │ │ ├── Default~iphone.png │ │ ├── Default@2x~iphone.png │ │ ├── Default-568h@2x~iphone.png │ │ ├── Default-Landscape-736h.png │ │ ├── Default-Landscape~ipad.png │ │ ├── Default-Portrait~ipad.png │ │ ├── Default-Portrait@2x~ipad.png │ │ └── Default-Landscape@2x~ipad.png ├── misc │ ├── Icon-Mac.icns │ ├── Icon-Windows.ico │ ├── Splash Screen.psd │ ├── Icon-Sizes │ │ ├── 128x128.png │ │ ├── 16x16.png │ │ ├── 24x24.png │ │ ├── 256x256.png │ │ ├── 32x32.png │ │ ├── 400x400.png │ │ ├── 48x48.png │ │ ├── 64x64.png │ │ └── 72x72.png │ ├── touch-cursor-hover.pdn │ ├── Icon-Source.url │ └── IrisApi-v5 │ │ └── docs │ │ ├── images │ │ ├── info.png │ │ ├── header.png │ │ └── warning.png │ │ ├── calls │ │ ├── channels.html │ │ └── questions.html │ │ └── css.css ├── windows │ ├── StoreLogo.scale-100.png │ ├── StoreLogo.scale-240.png │ ├── SplashScreen.scale-100.png │ ├── Square30x30Logo.scale-100.png │ ├── Square44x44Logo.scale-100.png │ ├── Square44x44Logo.scale-240.png │ ├── Square70x70Logo.scale-100.png │ ├── Square71x71Logo.scale-100.png │ ├── Square71x71Logo.scale-240.png │ ├── Wide310x150Logo.scale-100.png │ ├── Wide310x150Logo.scale-240.png │ ├── SplashScreenPhone.scale-240.png │ ├── Square150x150Logo.scale-100.png │ ├── Square150x150Logo.scale-240.png │ └── Square310x310Logo.scale-100.png └── android │ ├── icon │ ├── drawable-hdpi-icon.png │ ├── drawable-ldpi-icon.png │ ├── drawable-mdpi-icon.png │ ├── drawable-xhdpi-icon.png │ ├── drawable-xxhdpi-icon.png │ └── drawable-xxxhdpi-icon.png │ └── splash │ ├── drawable-land-hdpi-screen.png │ ├── drawable-land-ldpi-screen.png │ ├── drawable-land-mdpi-screen.png │ ├── drawable-port-hdpi-screen.png │ ├── drawable-port-ldpi-screen.png │ ├── drawable-port-mdpi-screen.png │ ├── drawable-land-xhdpi-screen.png │ ├── drawable-land-xxhdpi-screen.png │ ├── drawable-land-xxxhdpi-screen.png │ ├── drawable-port-xhdpi-screen.png │ ├── drawable-port-xxhdpi-screen.png │ └── drawable-port-xxxhdpi-screen.png ├── .travis.yml ├── src ├── tsconfig.json ├── ViewModels │ ├── CamerasViewModel.ts │ ├── Dialogs │ │ ├── PassphraseEntryViewModel.ts │ │ ├── ReorderCategoriesViewModel.ts │ │ ├── SetMultipleSmartPlugsStateViewModel.ts │ │ └── PinEntryViewModel.ts │ ├── Settings │ │ ├── ConfigurePinViewModel.ts │ │ ├── CamerasListViewModel.ts │ │ ├── SettingsListViewModel.ts │ │ ├── AboutViewModel.ts │ │ ├── CameraEditViewModel.ts │ │ ├── HubViewModel.ts │ │ ├── LogEntryViewModel.ts │ │ ├── DeveloperViewModel.ts │ │ ├── LogsListViewModel.ts │ │ ├── DashboardConfigViewModel.ts │ │ └── ConfigurePassphraseViewModel.ts │ ├── RootViewModel.ts │ ├── Devices │ │ ├── DevicesHubInfoViewModel.ts │ │ ├── DevicesInfoViewModel.ts │ │ └── DevicesListViewModel.ts │ ├── CameraViewViewModel.ts │ ├── EmptyViewModel.ts │ ├── DashboardViewModel.ts │ ├── ThermostatViewModel.ts │ ├── CategoryItemViewModel.ts │ ├── SmartPlugsViewModel.ts │ └── SecurityViewModel.ts ├── Models │ ├── LogLevel.ts │ ├── Settings │ │ └── Camera.ts │ ├── LogEntry.ts │ ├── DashboardItem.ts │ ├── DashboardDevices.ts │ ├── Dialogs │ │ ├── PinEntryDialogResultModel.ts │ │ ├── PinEntryDialogModel.ts │ │ └── DialogOptions.ts │ └── Misc.ts ├── _references.ts ├── Filters │ ├── ObjectToArrayFilter.ts │ ├── ThousandsFilter.ts │ └── TitleCaseFilter.ts ├── Directives │ ├── ModelOnBlurDirective.ts │ ├── BaseElementDirective.ts │ ├── OnLoadDirective.ts │ └── PositionableElementDirective.ts ├── Controllers │ ├── Settings │ │ ├── SettingsListController.ts │ │ ├── AboutController.ts │ │ ├── LogEntryController.ts │ │ ├── CamerasListController.ts │ │ └── HubController.ts │ ├── Dialogs │ │ ├── PassphraseEntryController.ts │ │ ├── ReorderCategoriesController.ts │ │ ├── SetMultipleSmartPlugsStateController.ts │ │ ├── PinEntryController.ts │ │ └── BaseDialogController.ts │ ├── CamerasController.ts │ └── Devices │ │ ├── DevicesListController.ts │ │ ├── DevicesHubInfoController.ts │ │ └── DevicesInfoController.ts ├── Constants.ts └── Services │ └── Configuration.ts ├── .gitignore ├── .vscode ├── settings.json └── tasks.json ├── chrome-manifest.json ├── bower.json ├── LICENSE ├── tslint.json ├── chrome.pem ├── package.json ├── typings └── custom │ ├── Interfaces.d.ts │ ├── Extensions.d.ts │ └── ionic.d.ts ├── README.md ├── hooks ├── after_prepare │ └── 010_add_platform_class.js └── README.md ├── tsd.json └── config.xml /www/templates/Blank.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /ionic.project: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Smart Home Mobile", 3 | "app_id": "" 4 | } -------------------------------------------------------------------------------- /.readme/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/.readme/banner.jpg -------------------------------------------------------------------------------- /Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/icon.png -------------------------------------------------------------------------------- /Resources/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/splash.png -------------------------------------------------------------------------------- /www/images/finger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/www/images/finger.png -------------------------------------------------------------------------------- /Resources/ios/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon.png -------------------------------------------------------------------------------- /www/images/about-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/www/images/about-icon.png -------------------------------------------------------------------------------- /.readme/dashboard-banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/.readme/dashboard-banner.jpg -------------------------------------------------------------------------------- /Resources/misc/Icon-Mac.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/Icon-Mac.icns -------------------------------------------------------------------------------- /.readme/screenshot-devices.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/.readme/screenshot-devices.jpg -------------------------------------------------------------------------------- /Resources/ios/icon/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon-40.png -------------------------------------------------------------------------------- /Resources/ios/icon/icon-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon-50.png -------------------------------------------------------------------------------- /Resources/ios/icon/icon-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon-60.png -------------------------------------------------------------------------------- /Resources/ios/icon/icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon-72.png -------------------------------------------------------------------------------- /Resources/ios/icon/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon-76.png -------------------------------------------------------------------------------- /Resources/ios/icon/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon@2x.png -------------------------------------------------------------------------------- /Resources/misc/Icon-Windows.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/Icon-Windows.ico -------------------------------------------------------------------------------- /Resources/misc/Splash Screen.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/Splash Screen.psd -------------------------------------------------------------------------------- /.readme/screenshot-hub-status.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/.readme/screenshot-hub-status.jpg -------------------------------------------------------------------------------- /.readme/screenshot-thermostat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/.readme/screenshot-thermostat.jpg -------------------------------------------------------------------------------- /Resources/ios/icon/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon-40@2x.png -------------------------------------------------------------------------------- /Resources/ios/icon/icon-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon-50@2x.png -------------------------------------------------------------------------------- /Resources/ios/icon/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon-60@2x.png -------------------------------------------------------------------------------- /Resources/ios/icon/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon-60@3x.png -------------------------------------------------------------------------------- /Resources/ios/icon/icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon-72@2x.png -------------------------------------------------------------------------------- /Resources/ios/icon/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon-76@2x.png -------------------------------------------------------------------------------- /Resources/ios/icon/icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon-small.png -------------------------------------------------------------------------------- /.readme/screenshot-alarms-locks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/.readme/screenshot-alarms-locks.jpg -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | before_script: 5 | - export PATH=$PATH:$PWD/node_modules/.bin 6 | - gulp -------------------------------------------------------------------------------- /Resources/ios/icon/icon-small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon-small@2x.png -------------------------------------------------------------------------------- /Resources/ios/icon/icon-small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/icon/icon-small@3x.png -------------------------------------------------------------------------------- /Resources/ios/splash/Default-667h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/splash/Default-667h.png -------------------------------------------------------------------------------- /Resources/ios/splash/Default-736h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/splash/Default-736h.png -------------------------------------------------------------------------------- /Resources/misc/Icon-Sizes/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/Icon-Sizes/128x128.png -------------------------------------------------------------------------------- /Resources/misc/Icon-Sizes/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/Icon-Sizes/16x16.png -------------------------------------------------------------------------------- /Resources/misc/Icon-Sizes/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/Icon-Sizes/24x24.png -------------------------------------------------------------------------------- /Resources/misc/Icon-Sizes/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/Icon-Sizes/256x256.png -------------------------------------------------------------------------------- /Resources/misc/Icon-Sizes/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/Icon-Sizes/32x32.png -------------------------------------------------------------------------------- /Resources/misc/Icon-Sizes/400x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/Icon-Sizes/400x400.png -------------------------------------------------------------------------------- /Resources/misc/Icon-Sizes/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/Icon-Sizes/48x48.png -------------------------------------------------------------------------------- /Resources/misc/Icon-Sizes/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/Icon-Sizes/64x64.png -------------------------------------------------------------------------------- /Resources/misc/Icon-Sizes/72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/Icon-Sizes/72x72.png -------------------------------------------------------------------------------- /Resources/misc/touch-cursor-hover.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/touch-cursor-hover.pdn -------------------------------------------------------------------------------- /.readme/screenshot-lighting-outlets.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/.readme/screenshot-lighting-outlets.jpg -------------------------------------------------------------------------------- /Resources/ios/splash/Default~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/splash/Default~iphone.png -------------------------------------------------------------------------------- /Resources/ios/splash/Default@2x~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/splash/Default@2x~iphone.png -------------------------------------------------------------------------------- /Resources/misc/Icon-Source.url: -------------------------------------------------------------------------------- 1 | [InternetShortcut] 2 | URL=https://www.iconfinder.com/icons/45416/connected_home_house_local_network_icon#size=128 3 | -------------------------------------------------------------------------------- /Resources/windows/StoreLogo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/windows/StoreLogo.scale-100.png -------------------------------------------------------------------------------- /Resources/windows/StoreLogo.scale-240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/windows/StoreLogo.scale-240.png -------------------------------------------------------------------------------- /Resources/windows/SplashScreen.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/windows/SplashScreen.scale-100.png -------------------------------------------------------------------------------- /Resources/android/icon/drawable-hdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/icon/drawable-hdpi-icon.png -------------------------------------------------------------------------------- /Resources/android/icon/drawable-ldpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/icon/drawable-ldpi-icon.png -------------------------------------------------------------------------------- /Resources/android/icon/drawable-mdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/icon/drawable-mdpi-icon.png -------------------------------------------------------------------------------- /Resources/android/icon/drawable-xhdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/icon/drawable-xhdpi-icon.png -------------------------------------------------------------------------------- /Resources/android/icon/drawable-xxhdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/icon/drawable-xxhdpi-icon.png -------------------------------------------------------------------------------- /Resources/ios/splash/Default-568h@2x~iphone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/splash/Default-568h@2x~iphone.png -------------------------------------------------------------------------------- /Resources/ios/splash/Default-Landscape-736h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/splash/Default-Landscape-736h.png -------------------------------------------------------------------------------- /Resources/ios/splash/Default-Landscape~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/splash/Default-Landscape~ipad.png -------------------------------------------------------------------------------- /Resources/ios/splash/Default-Portrait~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/splash/Default-Portrait~ipad.png -------------------------------------------------------------------------------- /Resources/misc/IrisApi-v5/docs/images/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/IrisApi-v5/docs/images/info.png -------------------------------------------------------------------------------- /Resources/windows/Square30x30Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/windows/Square30x30Logo.scale-100.png -------------------------------------------------------------------------------- /Resources/windows/Square44x44Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/windows/Square44x44Logo.scale-100.png -------------------------------------------------------------------------------- /Resources/windows/Square44x44Logo.scale-240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/windows/Square44x44Logo.scale-240.png -------------------------------------------------------------------------------- /Resources/windows/Square70x70Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/windows/Square70x70Logo.scale-100.png -------------------------------------------------------------------------------- /Resources/windows/Square71x71Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/windows/Square71x71Logo.scale-100.png -------------------------------------------------------------------------------- /Resources/windows/Square71x71Logo.scale-240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/windows/Square71x71Logo.scale-240.png -------------------------------------------------------------------------------- /Resources/windows/Wide310x150Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/windows/Wide310x150Logo.scale-100.png -------------------------------------------------------------------------------- /Resources/windows/Wide310x150Logo.scale-240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/windows/Wide310x150Logo.scale-240.png -------------------------------------------------------------------------------- /Resources/android/icon/drawable-xxxhdpi-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/icon/drawable-xxxhdpi-icon.png -------------------------------------------------------------------------------- /Resources/ios/splash/Default-Portrait@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/splash/Default-Portrait@2x~ipad.png -------------------------------------------------------------------------------- /Resources/misc/IrisApi-v5/docs/images/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/IrisApi-v5/docs/images/header.png -------------------------------------------------------------------------------- /Resources/misc/IrisApi-v5/docs/images/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/misc/IrisApi-v5/docs/images/warning.png -------------------------------------------------------------------------------- /Resources/windows/SplashScreenPhone.scale-240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/windows/SplashScreenPhone.scale-240.png -------------------------------------------------------------------------------- /Resources/windows/Square150x150Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/windows/Square150x150Logo.scale-100.png -------------------------------------------------------------------------------- /Resources/windows/Square150x150Logo.scale-240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/windows/Square150x150Logo.scale-240.png -------------------------------------------------------------------------------- /Resources/windows/Square310x310Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/windows/Square310x310Logo.scale-100.png -------------------------------------------------------------------------------- /Resources/ios/splash/Default-Landscape@2x~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/ios/splash/Default-Landscape@2x~ipad.png -------------------------------------------------------------------------------- /Resources/android/splash/drawable-land-hdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/splash/drawable-land-hdpi-screen.png -------------------------------------------------------------------------------- /Resources/android/splash/drawable-land-ldpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/splash/drawable-land-ldpi-screen.png -------------------------------------------------------------------------------- /Resources/android/splash/drawable-land-mdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/splash/drawable-land-mdpi-screen.png -------------------------------------------------------------------------------- /Resources/android/splash/drawable-port-hdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/splash/drawable-port-hdpi-screen.png -------------------------------------------------------------------------------- /Resources/android/splash/drawable-port-ldpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/splash/drawable-port-ldpi-screen.png -------------------------------------------------------------------------------- /Resources/android/splash/drawable-port-mdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/splash/drawable-port-mdpi-screen.png -------------------------------------------------------------------------------- /Resources/android/splash/drawable-land-xhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/splash/drawable-land-xhdpi-screen.png -------------------------------------------------------------------------------- /Resources/android/splash/drawable-land-xxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/splash/drawable-land-xxhdpi-screen.png -------------------------------------------------------------------------------- /Resources/android/splash/drawable-land-xxxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/splash/drawable-land-xxxhdpi-screen.png -------------------------------------------------------------------------------- /Resources/android/splash/drawable-port-xhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/splash/drawable-port-xhdpi-screen.png -------------------------------------------------------------------------------- /Resources/android/splash/drawable-port-xxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/splash/drawable-port-xxhdpi-screen.png -------------------------------------------------------------------------------- /Resources/android/splash/drawable-port-xxxhdpi-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Justin-Credible/Smart-Home-Mobile/HEAD/Resources/android/splash/drawable-port-xxxhdpi-screen.png -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "out": "www/js/bundle.js", 5 | "sourceMap": true, 6 | "removeComments": true, 7 | "sourceRoot": "" 8 | } 9 | } -------------------------------------------------------------------------------- /src/ViewModels/CamerasViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class CamerasViewModel { 4 | public cameras: Models.Camera[]; 5 | } 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/ViewModels/Dialogs/PassphraseEntryViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class PassphraseEntryViewModel { 4 | public passphrase: string; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/ViewModels/Settings/ConfigurePinViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class ConfigurePinViewModel { 4 | public isPinSet: boolean; 5 | } 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/ViewModels/Settings/CamerasListViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class CamerasListViewModel { 4 | public cameras: Models.Camera[]; 5 | } 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/ViewModels/Dialogs/ReorderCategoriesViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class ReorderCategoriesViewModel { 4 | public categories: ViewModels.CategoryItemViewModel []; 5 | } 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/ViewModels/RootViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class RootViewModel { 4 | public showDashboardTab: boolean; 5 | public categories: ViewModels.CategoryItemViewModel[]; 6 | } 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/Models/LogLevel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Models { 2 | 3 | export enum LogLevel { 4 | TRACE = 0, 5 | DEBUG = 1, 6 | INFO = 2, 7 | WARN = 3, 8 | ERROR = 4, 9 | FATAL = 5 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/ViewModels/Dialogs/SetMultipleSmartPlugsStateViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class SetMultipleSmartPlugsStateViewModel { 4 | public stateChanged: boolean; 5 | public smartPlugs: AlertMeApiTypes.SmartPlugDevice[]; 6 | } 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/ViewModels/Devices/DevicesHubInfoViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class DevicesHubInfoViewModel { 4 | public isRefreshing: boolean; 5 | public lastUpdated: Date; 6 | 7 | public hub: AlertMeApiTypes.HomeStatusHub; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/ViewModels/Devices/DevicesInfoViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class DevicesInfoViewModel { 4 | public isRefreshing: boolean; 5 | public lastUpdated: Date; 6 | 7 | public device: AlertMeApiTypes.HomeStatusDevice; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/ViewModels/Dialogs/PinEntryViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class PinEntryViewModel { 4 | public pin: string; 5 | public pinToMatch: string; 6 | public showBackButton: boolean; 7 | public promptText: string; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/Models/Settings/Camera.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Models { 2 | 3 | export class Camera { 4 | public id: string; 5 | public name: string; 6 | public url: string; 7 | public type: string; 8 | public userName: string; 9 | public password: string; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Models/LogEntry.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Models { 2 | 3 | export class LogEntry { 4 | public id: string; 5 | public timestamp: Date; 6 | public level: LogLevel; 7 | public tag: string; 8 | public message: string; 9 | public metadata: any; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/ViewModels/Settings/SettingsListViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class SettingsListViewModel { 4 | public isDebugMode: boolean; 5 | public isDeveloperMode: boolean; 6 | public showPin: boolean; 7 | public showPassphrase: boolean; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /www/css/nprogress-overrides.css: -------------------------------------------------------------------------------- 1 | 2 | /* This file contains overrides for the NProgress library's default stylings. */ 3 | 4 | #nprogress .bar { 5 | background: #fff; 6 | height: 1px; 7 | } 8 | 9 | #nprogress .peg { 10 | display: none; 11 | } 12 | 13 | #nprogress .spinner-icon { 14 | border-top-color: #fff; 15 | border-left-color: #fff; 16 | } -------------------------------------------------------------------------------- /src/ViewModels/CameraViewViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class CameraViewViewModel { 4 | public camera: Models.Camera; 5 | public url: string; 6 | public iframeUrl: string; 7 | public showLoadingPanel: boolean; 8 | public lastUpdated: Date; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/ViewModels/Devices/DevicesListViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class DevicesListViewModel { 4 | public isRefreshing: boolean; 5 | public lastUpdated: Date; 6 | 7 | public homeStatus: AlertMeApiTypes.HomeStatusGetResult; 8 | public deviceCount: number; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/ViewModels/Settings/AboutViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class AboutViewModel { 4 | public logoClickCount: number; 5 | public applicationName: string; 6 | public versionString: string; 7 | public timestamp: string; 8 | public commitShortSha: string; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/ViewModels/EmptyViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | /** 4 | * A ViewModel that has no properties. Useful for controllers that 5 | * do not have any view model properties, but need to pass something 6 | * to the BaseController constructor. 7 | */ 8 | export class EmptyViewModel { 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/ViewModels/Settings/CameraEditViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class CameraEditViewModel { 4 | public title: string; 5 | public showSaveButton: boolean; 6 | public showConfirmPassword: boolean; 7 | public confirmPassword: string; 8 | public camera: Models.Camera; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Specifies intentionally untracked files to ignore when using Git 2 | # http://git-scm.com/docs/gitignore 3 | 4 | # Toolchain 5 | /node_modules 6 | /bower_components 7 | /platforms 8 | /plugins 9 | /typings/* 10 | !/typings/custom/ 11 | 12 | # Development Artifacts 13 | /www/lib 14 | /www/js/bundle.* 15 | /www/js/BuildVars.js 16 | /www/js/src/ 17 | /src/tsd.d.ts 18 | /chrome -------------------------------------------------------------------------------- /src/ViewModels/Settings/HubViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class HubViewModel { 4 | public showSaveButton: boolean; 5 | public showConfirmPassword: boolean; 6 | public apiUrl: string; 7 | public userName: string; 8 | public password: string; 9 | public confirmPassword: string; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/Models/DashboardItem.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Models { 2 | 3 | export class DashboardItem { 4 | public deviceId: string; 5 | public type: string; 6 | public missing: boolean; 7 | public visible: boolean; 8 | public isBusy: boolean; 9 | public name: string; 10 | public x: number; 11 | public y: number; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/ViewModels/DashboardViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class DashboardViewModel { 4 | public isRefreshing: boolean; 5 | public lastUpdated: Date; 6 | 7 | public floorplanImageUrl: string; 8 | public items: Models.DashboardItem[]; 9 | public devices: Models.Dictionary; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Models/DashboardDevices.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Models { 2 | 3 | export class DashboardDevices { 4 | 5 | public lockData: AlertMeApiTypes.LocksGetResult; 6 | public alarmData: AlertMeApiTypes.AlarmGetResult; 7 | public alarmOverviewData: AlertMeApiTypes.AlarmOverviewGetResult; 8 | 9 | public smartPlugs: AlertMeApiTypes.SmartPlugDevice[]; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/ViewModels/Settings/LogEntryViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class LogEntryViewModel { 4 | public logEntry: Models.LogEntry; 5 | public icon: string; 6 | public date: string; 7 | public time: string; 8 | public levelDisplay: string; 9 | public color: string; 10 | public formattedMetadata: string; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Models/Dialogs/PinEntryDialogResultModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Models { 2 | 3 | export class PinEntryDialogResultModel { 4 | matches: boolean; 5 | cancelled: boolean; 6 | pin: string; 7 | 8 | constructor(matches: boolean, cancelled: boolean, pin: string) { 9 | this.matches = matches; 10 | this.cancelled = cancelled; 11 | this.pin = pin; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/ViewModels/Settings/DeveloperViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class DeveloperViewModel { 4 | mockApiRequests: boolean; 5 | 6 | devicePlatform: string; 7 | deviceModel: string; 8 | deviceOsVersion: string; 9 | deviceUuid: string; 10 | deviceCordovaVersion: string; 11 | 12 | defaultStoragePathId: string; 13 | defaultStoragePath: string; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Models/Dialogs/PinEntryDialogModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Models { 2 | 3 | export class PinEntryDialogModel { 4 | pinToMatch: string; 5 | promptText: string; 6 | showBackButton: boolean; 7 | 8 | constructor(promptText: string, pinToMatch: string, showBackButton: boolean) { 9 | this.promptText = promptText; 10 | this.pinToMatch = pinToMatch; 11 | this.showBackButton = showBackButton; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/ViewModels/ThermostatViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class ThermostatViewModel { 4 | public isRefreshing: boolean; 5 | public lastUpdated: Date; 6 | public climate: AlertMeApiTypes.ClimateGetResult; 7 | 8 | public newTargetTemperature: number; 9 | public temperatureSliderValue: string; 10 | public newOnOffState: string; 11 | public newMode: string; 12 | public changesMade: boolean; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /www/js/chrome-ext-background.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Global application state for the Chrome Extension. 4 | * 5 | * This is a transient, in-memory only variable that 6 | * does not persist between sessions. 7 | */ 8 | var state = { 9 | 10 | /** 11 | * The user's security passphrase. 12 | * 13 | * Used to encrypt the AlertMe API password so it isn't stored in cleartext 14 | * in local storage. The user must enter their passphrase at least once so 15 | * it will be available. 16 | */ 17 | passphrase: null 18 | }; -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "search.exclude": { 3 | ".git": true, 4 | "node_modules": true, 5 | "bower_components": true, 6 | "platforms": true, 7 | "plugins": true, 8 | "www/js/bundle.js": true, 9 | "www/js/bundle.js.map": true, 10 | "www/js/bundle.d.ts": true, 11 | "www/js/src": true, 12 | "www/lib": true, 13 | "chrome": true, 14 | "Resources/misc": true 15 | }, 16 | 17 | "editor.tabSize": 4, 18 | "editor.insertSpaces": true 19 | } -------------------------------------------------------------------------------- /src/ViewModels/CategoryItemViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class CategoryItemViewModel { 4 | public name: string; 5 | public href: string; 6 | public icon: string; 7 | public order: number; 8 | 9 | constructor(name: string, href: string, icon: string, order: number) { 10 | this.name = name; 11 | this.href = href; 12 | this.icon = icon; 13 | this.order = order; 14 | } 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/ViewModels/Settings/LogsListViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class LogsListViewModel { 4 | 5 | public logs: { [day: string]: ViewModels.LogEntryViewModel[] }; 6 | 7 | public showTrace: boolean; 8 | public showDebug: boolean; 9 | public showInfo: boolean; 10 | public showWarn: boolean; 11 | public showError: boolean; 12 | public showFatal: boolean; 13 | 14 | constructor() { 15 | this.logs = {}; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/ViewModels/Settings/DashboardConfigViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class DashboardConfigViewModel { 4 | public isRefreshing: boolean; 5 | public lastUpdated: Date; 6 | 7 | public floorplanImageUrl: string; 8 | public items: Models.DashboardItem[]; 9 | public devices: Models.Dictionary; 10 | public hasChanges: boolean; 11 | public showToolbar: boolean; 12 | public selectedItem: Models.DashboardItem; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /chrome-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | 4 | "name": "Smart Home Mobile", 5 | "description": "Used to control home automation devices.", 6 | "version": "1.2.0", 7 | 8 | "browser_action": { 9 | "default_icon": "icon.png", 10 | "default_popup": "index.html", 11 | "default_title": "Smart Home Mobile" 12 | }, 13 | 14 | "background": { 15 | "scripts": ["js/chrome-ext-background.js"] 16 | }, 17 | 18 | "permissions": [ 19 | "https://*/", 20 | "http://*/", 21 | "clipboardWrite" 22 | ] 23 | } -------------------------------------------------------------------------------- /src/_references.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This file exists to control the order in which compiled TypeScript files are concatenated 4 | * into the resulting appBundle.js file. While all *.ts files could be listed here, we don't 5 | * need to list them all since the tsc compiler will automatically traverse the directory tree. 6 | * Here we can list base components that are needed by other components (eg base classes) that 7 | * must be parsed before the dependent class. 8 | */ 9 | 10 | /// 11 | /// 12 | /// 13 | -------------------------------------------------------------------------------- /src/ViewModels/Settings/ConfigurePassphraseViewModel.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.ViewModels { 2 | 3 | export class ConfigurePassphraseViewModel { 4 | public showField1: boolean; 5 | public showField2: boolean; 6 | public showField3: boolean; 7 | 8 | public showSetButton: boolean; 9 | public showChangeButton: boolean; 10 | public showRemoveButton: boolean; 11 | 12 | public passphrase1: string; 13 | public passphrase2: string; 14 | public passphrase3: string; 15 | 16 | public isSettingPassphrase: boolean; 17 | public isChangingPassphrase: boolean; 18 | public isRemovingPassphrase: boolean; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /www/templates/More-Menu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | Device List 10 | 11 | 12 | 15 | 16 | Settings 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /www/js/Main.js: -------------------------------------------------------------------------------- 1 | 2 | /*globals JustinCredible, chrome*/ 3 | 4 | // Here any special logic can be executed before boostrapping the application. 5 | 6 | // If we are running as a Chrome extension, then we'll inject some CSS to set the pop-up width. 7 | if (typeof (chrome) !== "undefined" && typeof (chrome.runtime) !== "undefined" && typeof (chrome.runtime.id) !== "undefined") { 8 | var style, css; 9 | style = document.createElement("style"); 10 | style.type = "text/css"; 11 | css = "body { width: 400px; height: 600px; }"; 12 | style.appendChild(document.createTextNode(css)); 13 | document.head.appendChild(style); 14 | } 15 | 16 | // Start the application by invoking the main entry point method defined in Application.ts. 17 | JustinCredible.SmartHomeMobile.Application.main(); -------------------------------------------------------------------------------- /src/Filters/ObjectToArrayFilter.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Filters { 2 | 3 | /** 4 | * Used to convert an object into an array; useful for using with ng-repeat 5 | * directive and a view model object that is an object instead of an array. 6 | * 7 | * 23 | 24 | 25 | 26 |
27 | 28 |
29 | 31 |
32 | 33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /www/templates/Settings/Logs-List.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | {{logGroup}} - {{logEntries.length}} Logs 15 |
16 | 17 | 18 | 19 | {{logEntryViewModel.time}} - {{logEntryViewModel.levelDisplay}} 20 |

{{logEntryViewModel.logEntry.message}}

21 | 22 |
23 | 24 |
25 | 26 |
27 |
28 | 29 |
30 | -------------------------------------------------------------------------------- /www/templates/Dashboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 |
14 | 15 | 29 | 30 |
31 | 32 |
33 | 34 |
-------------------------------------------------------------------------------- /www/templates/Settings/Log-Entry.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 |
10 | 11 |

{{viewModel.levelDisplay}}

12 |

{{viewModel.date}}   {{viewModel.time}}

13 |
14 | 15 |
16 |

{{viewModel.logEntry.message}}

17 |
18 | 19 |
20 |

{{viewModel.formattedMetadata}}

21 |
22 | 23 | 33 | 34 |
35 |
36 | 37 |
38 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "gulp", 4 | "isShellCommand": true, 5 | "args": [ 6 | "--no-color" 7 | ], 8 | "tasks": [ 9 | { 10 | "taskName": "ts", 11 | "isBuildCommand": true, 12 | "showOutput": "silent", 13 | "problemMatcher": "$tsc" 14 | }, 15 | { 16 | "taskName": "lint", 17 | "showOutput": "silent", 18 | "problemMatcher": [ 19 | { 20 | "owner": "gulp", 21 | "fileLocation": ["absolute"], 22 | "pattern": { 23 | "regexp": "^\\[\\d\\d:\\d\\d:\\d\\d\\] \\[gulp-tslint\\] (.*) \\(.*\\) (.*)\\[(\\d*), (\\d*)\\]: (.*)$", 24 | "file": 2, 25 | "line": 3, 26 | "column": 4, 27 | "message": 5, 28 | "severity": 1 29 | } 30 | } 31 | ] 32 | }, 33 | { 34 | "taskName": "emulate-ios", 35 | "showOutput": "never" 36 | }, 37 | { 38 | "taskName": "emulate-android", 39 | "showOutput": "never" 40 | }, 41 | { 42 | "taskName": "prepare-windows", 43 | "showOutput": "never" 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "curly": true, 5 | "eofline": true, 6 | "forin": true, 7 | "indent": [true, "spaces"], 8 | "label-position": true, 9 | "label-undefined": true, 10 | "max-line-length": [false, 140], 11 | "no-arg": true, 12 | "no-bitwise": true, 13 | "no-console": [true, 14 | "debug", 15 | "info", 16 | "time", 17 | "timeEnd", 18 | "trace" 19 | ], 20 | "no-construct": true, 21 | "no-debugger": true, 22 | "no-duplicate-key": true, 23 | "no-duplicate-variable": true, 24 | "no-empty": true, 25 | "no-eval": true, 26 | "no-string-literal": true, 27 | "no-switch-case-fall-through": true, 28 | "no-trailing-comma": true, 29 | "no-trailing-whitespace": true, 30 | "no-unused-expression": true, 31 | "no-unused-variable": false, 32 | "no-unreachable": true, 33 | "no-use-before-declare": true, 34 | "one-line": [true, 35 | "check-open-brace", 36 | "check-whitespace" 37 | ], 38 | "quotemark": [true, "double"], 39 | "radix": true, 40 | "semicolon": true, 41 | "triple-equals": [true, "allow-null-check"], 42 | "variable-name": false, 43 | "whitespace": [true, 44 | "check-branch", 45 | "check-decl", 46 | "check-operator", 47 | "check-separator", 48 | "check-type" 49 | ] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Models/Dialogs/DialogOptions.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Models { 2 | 3 | /** 4 | * Used to specify options for a dialog. 5 | * For use with UiHelper.openDialog(). 6 | */ 7 | export class DialogOptions { 8 | 9 | /** 10 | * The data object for the dialog. 11 | * 12 | * This will be available from BaseDialogController.getData(). 13 | */ 14 | dialogData: any; 15 | 16 | /** 17 | * Specifies if the dialog should be able to be closed by clicking/touching 18 | * the backdrop area. Default is true. 19 | */ 20 | backdropClickToClose: boolean; 21 | 22 | /** 23 | * Specifies if the dialog should be able to be closed by pressing the device's 24 | * hardware back button. Default is true. 25 | */ 26 | hardwareBackButtonClose: boolean; 27 | 28 | /** 29 | * Specifies if the contents behind the background should be visible (eg displayed 30 | * underneath an overlay element). Default is true, if set to false the background 31 | * will be completely black. 32 | */ 33 | showBackground: boolean; 34 | 35 | constructor(dialogData?: any) { 36 | this.dialogData = dialogData; 37 | 38 | this.backdropClickToClose = true; 39 | this.hardwareBackButtonClose = true; 40 | this.showBackground = true; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Filters/ThousandsFilter.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Filters { 2 | 3 | /** 4 | * Formats numbers greater than one thousand to include the K suffix. 5 | * 6 | * Numbers greater than 10,000 will not show decimal places, while numbers 7 | * between 1,000 and 9,999 will show decimal places unless the number is 8 | * a multiple of one thousand. 9 | * 10 | * For example: 11 | * 200 -> 200 12 | * 2000 -> 2K 13 | * 1321 -> 1.3K 14 | * 10700 -> 10K 15 | */ 16 | export class ThousandsFilter { 17 | 18 | public static ID = "Thousands"; 19 | 20 | public static filter(input: number): string { 21 | 22 | if (input == null) { 23 | return ""; 24 | } 25 | 26 | if (input > 9999) { 27 | if (input % 10 === 0) { 28 | return (input / 1000) + "K"; 29 | } 30 | else { 31 | return (input / 1000).toFixed(0) + "K"; 32 | } 33 | } 34 | else if (input > 999) { 35 | if (input % 10 === 0) { 36 | return (input / 1000) + "K"; 37 | } 38 | else { 39 | return (input / 1000).toFixed(1) + "K"; 40 | } 41 | } 42 | else { 43 | return input + ""; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Controllers/Settings/SettingsListController.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Controllers { 2 | 3 | export class SettingsListController extends BaseController { 4 | 5 | //#region Injection 6 | 7 | public static ID = "SettingsListController"; 8 | 9 | public static get $inject(): string[] { 10 | return [ 11 | "$scope", 12 | Services.Utilities.ID, 13 | Services.Configuration.ID 14 | ]; 15 | } 16 | 17 | constructor( 18 | $scope: ng.IScope, 19 | private Utilities: Services.Utilities, 20 | private Configuration: Services.Configuration) { 21 | super($scope, ViewModels.SettingsListViewModel); 22 | } 23 | 24 | //#endregion 25 | 26 | //#region BaseController Overrides 27 | 28 | protected view_beforeEnter(event?: ng.IAngularEvent, eventArgs?: Ionic.IViewEventArguments): void { 29 | super.view_beforeEnter(event, eventArgs); 30 | 31 | this.viewModel.isDebugMode = this.Utilities.isDebugMode; 32 | this.viewModel.isDeveloperMode = this.Configuration.enableDeveloperTools; 33 | this.viewModel.showPin = !this.Utilities.isChromeExtension || this.Utilities.isWindows; 34 | this.viewModel.showPassphrase = this.Utilities.isChromeExtension || this.Utilities.isWindows; 35 | } 36 | 37 | //#endregion 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /www/templates/Settings/About.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 |
8 | 9 |

{{viewModel.applicationName}}

10 |

Version: {{viewModel.versionString}}

11 |

Commit: {{viewModel.commitShortSha}}

12 |

Built: {{viewModel.timestamp}}

13 |
14 | 15 |
16 |

Created by Justin Unterreiner.

17 |

Copyright 2015. All rights reserved.

18 |
19 | 20 | 34 | 35 |
36 | 37 |
38 | 39 |
-------------------------------------------------------------------------------- /www/templates/Root.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /www/templates/Camera-View.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
18 | Last updated at {{viewModel.lastUpdated | date: 'MM/dd/yyyy HH:mm:ss Z'}} 19 |
20 | 21 | 22 | 23 | 26 | 27 | 32 | 33 | 34 |
35 | 36 |
-------------------------------------------------------------------------------- /src/Filters/TitleCaseFilter.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Filters { 2 | 3 | /** 4 | * Used to convert a string to "title" case where-in each letter 5 | * that is proceeded by a space character or underscore (as well 6 | * as the first character) will be capitilized. If the fromCamelCase 7 | * flag is set to true the filter will assume the incoming string 8 | * does not have spaces, but uses capital letters as seperators 9 | * instead of spaces. 10 | * 11 | * http://ng.malsup.com/#!/titlecase-filter 12 | */ 13 | export class TitleCaseFilter { 14 | 15 | public static ID = "TitleCase"; 16 | 17 | public static filter(s: string, fromCamelCase: boolean): string { 18 | s = (typeof(s) === "undefined" || s === null) ? "" : s; 19 | fromCamelCase = (typeof (fromCamelCase) === "undefined" || fromCamelCase === null) ? false : fromCamelCase; 20 | 21 | if (fromCamelCase) { 22 | // http://stackoverflow.com/questions/7225407/convert-camelcasetext-to-camel-case-text 23 | // http://jsfiddle.net/PeYYQ/ 24 | return s.replace(/([A-Z]+)/g, " $1").replace(/([A-Z][a-z])/g, " $1"); 25 | } 26 | else { 27 | // First, convert an underscores to spaces (useful for constant or 28 | // enum-like strings where underscores are used instead of spaces. 29 | s = s.replace(/_/g, " "); 30 | 31 | return s.toString().toLowerCase().replace(/\b([a-z])/g, function (ch: string) { 32 | return ch.toUpperCase(); 33 | }); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /chrome.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCRfxQt3VhoQlpI 3 | EtOdsFH5mKxA8Zb7oyX8ZT6x2IcCd8S6AAEn+9IF5cStlnk5WBt3qidPSy8LZAAp 4 | /Qrhxno8BtfSZW11WT/e0RkbeClxOOtNjVXSqOhlJIPEXcGITpipz4tQ5X16QY9S 5 | AkQhinVi7oL40ALtQYJmpgMkGz0CwyehyU/KPlV5kPF9ivvfdfz7GDSEviWUO8Tu 6 | lo50g42KsDvDrC03pV1PdjpE7BW/uU72u+4IqpYDruZUkwGf0zEgg480WMZxcH+E 7 | ji/fz8NGxekhmix3AmTN5hZm/Iz8OelPvdQFDVYpFQtJ7NLt64Cu6TyIUAC2f6+Y 8 | HGIqO48fAgMBAAECggEAKxQBGYNLxbK/oE9SsIGgA9KcTa46Ec9AU/Z2FZoSfzyO 9 | 1rNhpED+8hgRn4PJ6JyZpViPwv5zs1+sXMI7EpX9YwBzriuxuOjv7DzkvilZu9gq 10 | cw/BtVmlVSz2hpFr17vNn5l/mjgYarY2hQV+vp+MGfqcdKbB9xVKUrIcg/g8nDBR 11 | psusLrJtugmvCEx3oICeVfKWUz1bJ7nwejwRO0m7WI0G5YzUBInFvubkYO2ISVC+ 12 | ZN2RsalJI/ba4D2O0k8rbznb9PIIbq4wNEwuRAN9li/cSHSpABhahGwNsHuc+DEA 13 | um5BRBKh8Jil6jbToVnaB6AlJGuF0SrApNcPLmN/QQKBgQDBQQQ2kc0S0PC462KS 14 | KowHdmusKloga2gY/ku6Awi1zzAa2U8X6NuHJ8MaUq4U0LEAkIPz7RQI8rZwPsAA 15 | hUK/DdK2ka2RTBd4q+GPIgxn5yamIVq17WBpq7rKwwRZGEc6BmXgLrsJY74LOyqD 16 | YrI3nqYAtjOqzLIi7tEVVNhMBwKBgQDAvIYnfZfHYC1EoTRkrjVi71C0ZOWLnB7e 17 | tB/TxAeoeIbxi2BjIR3SB6iG6HQ85gwpmHVsXuL7SrQwVO9AAq9LaW6TGMzpP9uZ 18 | 1o7vlLxRKpEIhlSojl1KEjKn4UYudRl14JpzMxjbUeRYTvk5AfFPzIbTRU33EmCn 19 | 2DaB51kOKQKBgDhiMMl/Fpcj0OgS/SpqMXrH6ThNDwigbbnFD/EQi40ICx/6QSJX 20 | tR0k076tWs8F6G6iu20CwbKupd3v4PMF7mhxrwXHUR2wVrTFaUTqiTYTcepMlvu3 21 | a0bJbPFVnsIj3Dwou7aBy948dKkwNQIqXJbvub1Ba6esm1in1ubjX0wnAoGASkuh 22 | 7zEjw/BYIt99Yd4hqDyWQKOwNKv+3lxS9H/Zb+DeK9hNVIkchKz9EtwiiqMnRxWF 23 | FjxRhQrcANGM1ID0b755qGxj+PnrvhaOTN5p5WPiOoYFtmACqB3xtvNI6XmsUds9 24 | znCBAXJE8qGmwon0Ko2t310U28wSxEC9aj/y/VkCgYBb1oHrcYQ2rYY8ZUmEXQqk 25 | 2NOzq3bpLZUGMHGqjCLP6mvtkUN6c38Hzzazu0wLwTQ0Gv0a+19/Kcctv+bRnQ1H 26 | vvgOVMWcXttzN2fMrZrQtCNSVl9QYXIEN+HyC1jlKl0i3q8cRyag5WqgY6NCj01h 27 | WLMGUoawW4G0v6OAgjocCw== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /src/Controllers/Dialogs/PassphraseEntryController.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Controllers { 2 | 3 | export class PassphraseEntryController extends BaseDialogController { 4 | 5 | //#region Injection 6 | 7 | public static ID = "PassphraseEntryController"; 8 | public static TemplatePath = "templates/Dialogs/Passphrase-Entry.html"; 9 | 10 | public static get $inject(): string[] { 11 | return [ 12 | "$scope", 13 | Services.Utilities.ID, 14 | Services.Preferences.ID, 15 | Services.UiHelper.ID 16 | ]; 17 | } 18 | 19 | constructor( 20 | $scope: ng.IScope, 21 | private Utilities: Services.Utilities, 22 | private Preferences: Services.Preferences, 23 | private UiHelper: Services.UiHelper) { 24 | super($scope, ViewModels.PassphraseEntryViewModel, PassphraseEntryController.ID); 25 | } 26 | 27 | //#endregion 28 | 29 | //#region BaseController Overrides 30 | 31 | protected dialog_shown() { 32 | super.dialog_shown(); 33 | 34 | this.viewModel.passphrase = ""; 35 | } 36 | 37 | //#endregion 38 | 39 | //#region Controller Methods 40 | 41 | protected ok_click() { 42 | if (this.Preferences.isPassphraseValid(this.viewModel.passphrase)) { 43 | this.Preferences.setPassphraseForSession(this.viewModel.passphrase); 44 | this.close(); 45 | } 46 | else { 47 | this.UiHelper.alert("Invalid passphrase; please try again."); 48 | } 49 | } 50 | 51 | //#endregion 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "smart-home-mobile", 3 | "version": "1.2.0", 4 | "description": "Used to control home automation devices.", 5 | "author": "Justin Unterreiner", 6 | "license": "MIT", 7 | "homepage": "http://www.justin-credible.net/Projects/Smart-Home-Mobile", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/Justin-Credible/Smart-Home-Mobile.git" 11 | }, 12 | "dependencies": { 13 | "async": "1.4.2", 14 | "bower": "1.6.3", 15 | "bower-installer": "1.2.0", 16 | "cordova": "5.3.3", 17 | "del": "2.0.2", 18 | "gulp": "3.9.0", 19 | "gulp-eol": "0.1.1", 20 | "gulp-gzip": "1.2.0", 21 | "gulp-if": "2.0.0", 22 | "gulp-rename": "1.2.2", 23 | "gulp-tar": "1.5.0", 24 | "gulp-tslint": "3.3.1", 25 | "gulp-typescript": "2.9.2", 26 | "gulp-util": "3.0.6", 27 | "ionic": "1.7.6", 28 | "request": "2.65.0", 29 | "run-sequence": "1.1.4", 30 | "shelljs": "0.5.3", 31 | "tsd": "0.6.5", 32 | "tslint": "2.5.1", 33 | "typescript": "1.6.2", 34 | "xmldom": "0.1.19", 35 | "xpath": "0.0.9" 36 | }, 37 | "scripts": { 38 | "test": "gulp lint" 39 | }, 40 | "cordovaPlugins": [ 41 | "cordova-plugin-device@1.0.1", 42 | "cordova-plugin-console@1.0.1", 43 | "cordova-plugin-whitelist@1.0.0", 44 | "cordova-plugin-splashscreen@2.1.0", 45 | "cordova-plugin-dialogs@1.1.1", 46 | "cordova-plugin-inappbrowser@1.0.1", 47 | "cordova-plugin-statusbar@1.0.1", 48 | "ionic-plugin-keyboard@1.0.7", 49 | "cordova-plugin-x-toast@2.2.1", 50 | "com.verso.cordova.clipboard@0.1.0", 51 | { 52 | "locator": "https://github.com/Justin-Credible/cordova-progressIndicator.git" 53 | } 54 | ], 55 | "cordovaPlatforms": [ 56 | "ios", 57 | "android" 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /src/Directives/BaseElementDirective.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Directives { 2 | 3 | /** 4 | * Describes what our element directive objects look like. 5 | */ 6 | export interface IElementDirective { 7 | initialize(); 8 | render(); 9 | } 10 | 11 | /** 12 | * Describes the constructor for the an class implementing IElementDirective. 13 | */ 14 | export interface IElementDirectiveClass { 15 | new (scope: ng.IScope, instanceElement: ng.IAugmentedJQuery, instanceAttributes: ng.IAttributes, controller: any, transclude: ng.ITranscludeFunction); 16 | } 17 | 18 | /** 19 | * This is the base directive that all other directives for elements should utilize. 20 | * 21 | * It handles saving references to the various objects in its constructor. 22 | * 23 | * T - The parameter type for the scope. 24 | */ 25 | export class BaseElementDirective implements IElementDirective { 26 | 27 | /** 28 | * A flag that can be used to identify element directives that use this 29 | * class as their base class. 30 | */ 31 | public static __BaseElementDirective = true; 32 | 33 | protected scope: T; 34 | protected element: ng.IAugmentedJQuery; 35 | protected attributes: ng.IAttributes; 36 | protected controller: any; 37 | protected transclude: ng.ITranscludeFunction; 38 | 39 | public initialize() { 40 | throw new Error("Directives that extend BaseElementDirective should implement their own initialize method."); 41 | } 42 | 43 | public render() { 44 | throw new Error("Directives that extend BaseElementDirective should implement their own render method."); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Directives/OnLoadDirective.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Directives { 2 | 3 | /** 4 | * A directive for handling an element's onload event (eg an image tag). 5 | * 6 | * http://stackoverflow.com/questions/11868393/angularjs-inputtext-ngchange-fires-while-the-value-is-changing 7 | */ 8 | export class OnLoadDirective implements ng.IDirective { 9 | 10 | //#region Injection 11 | 12 | public static ID = "onLoad"; 13 | 14 | public static get $inject(): string[] { 15 | return ["$parse"]; 16 | } 17 | 18 | constructor( 19 | private $parse: ng.IParseService) { 20 | 21 | // Ensure that the link function is bound to this instance so we can 22 | // access instance variables like $parse. AngularJs normally executes 23 | // the link function in the context of the global scope. 24 | this.link = _.bind(this.link, this); 25 | } 26 | 27 | //#endregion 28 | 29 | public restrict = "A"; 30 | 31 | public link(scope: ng.IScope, element: ng.IAugmentedJQuery, attributes: ng.IAttributes, controller: any, transclude: ng.ITranscludeFunction): void { 32 | 33 | // Parse the value of the on-load property; this will be a function 34 | // that the user has set on the element for example: 35 | /* tslint:disable:no-string-literal */ 36 | var fn = this.$parse(attributes["onLoad"]); 37 | /* tslint:enable:no-string-literal */ 38 | 39 | // Subscribe to the load event of the image element. 40 | element.on("load", (event) => { 41 | // When the load event occurs, execute the user defined load function. 42 | scope.$apply(() => { 43 | fn(scope, { $event: event }); 44 | }); 45 | }); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /www/templates/Cameras.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 |
10 | Snapshot Cameras 11 |
12 | 13 | 17 | 18 | 19 | 20 | {{camera.name}} 21 | 22 | 23 |
24 | Streaming Cameras 25 |
26 | 27 | 31 | 32 | 33 | 34 | {{camera.name}} 35 | 36 | 37 |
38 | 39 |
40 | 41 |
42 | 43 | 47 | 48 |
49 | 50 |
-------------------------------------------------------------------------------- /www/js/RippleMockApi.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | /** 4 | * This script is loaded very early, before all other frameworks. It allows us to perform 5 | * special customization for the Apache Ripple in-browser emulator. 6 | */ 7 | (function() { 8 | 9 | var apisToIgnore, 10 | originalBridgeExecFn, 11 | isRunningInRipple; 12 | 13 | isRunningInRipple = window.top && window.top.ripple; 14 | 15 | /** 16 | * These are the APIs for the services that we want to ignore (eg suppress the "cheeseburger" 17 | * dialog) when they are invoked. These APIs don't exist when running in a real browser. 18 | */ 19 | apisToIgnore = { 20 | "File": ["requestAllPaths"], 21 | "StatusBar": ["_ready"] 22 | }; 23 | 24 | 25 | if (isRunningInRipple) { 26 | 27 | // Save off a reference to the Ripple's original bridge function. 28 | originalBridgeExecFn = window.top.ripple("platform/cordova/2.0.0/bridge").exec; 29 | 30 | // Override the bridge module (which is for emulating the script-to-native API bridge). 31 | // The default implementation shows an annoying "cheeseburger" dialog which is supposed 32 | // to allow a developer to specify the JSON to return to bridge calls that do not have 33 | // a native API available when running in the browser. We just want to suppress the dialog. 34 | window.top.ripple("platform/cordova/2.0.0/bridge").exec = function (success, fail, service, action, args) { 35 | 36 | if (apisToIgnore[service] && apisToIgnore[service].indexOf(action) > -1) { 37 | // If this service/action combination was in our ignore list, then 38 | // we'll do nothing for now. 39 | } 40 | else { 41 | // Otherwise, we'll delegate to the original bridge function. 42 | originalBridgeExecFn(success, fail, service, action, args); 43 | } 44 | }; 45 | } 46 | 47 | })(); -------------------------------------------------------------------------------- /src/Constants.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * A common location for application-wide constant values. 4 | */ 5 | module JustinCredible.SmartHomeMobile.Constants { 6 | 7 | /** 8 | * Value for rejection of a promise when opening a dialog using the showDialog 9 | * helper method. This value will be used when showDialog was called with a dialog 10 | * ID of a dialog that is already open. 11 | */ 12 | export const DIALOG_ALREADY_OPEN = "DIALOG_ALREADY_OPEN"; 13 | 14 | /** 15 | * Value for rejection of a promise when opening a dialog using the showDialog 16 | * helper method. This value will be used when showDialog was called with a dialog 17 | * ID who is not registered in the dialogTemplateMap map. 18 | */ 19 | export const DIALOG_ID_NOT_REGISTERED = "DIALOG_ID_NOT_REGISTERED"; 20 | } 21 | 22 | /** 23 | * A collection of titles for buttons commonly used with dialogs. 24 | */ 25 | module JustinCredible.SmartHomeMobile.Constants.Buttons { 26 | export const Yes = "Yes"; 27 | export const No = "No"; 28 | export const OK = "OK"; 29 | export const Cancel = "Cancel"; 30 | } 31 | 32 | /** 33 | * A collection of names of events used within the application. 34 | */ 35 | module JustinCredible.SmartHomeMobile.Constants.Events { 36 | export const HTTP_ERROR = "http.error"; 37 | export const HTTP_BLOCKING_REQUEST_STARTED = "http.blocking-request-started"; 38 | export const HTTP_BLOCKING_REQUESTS_COMPLETED = "http.blocking-requests-completed"; 39 | export const HTTP_NON_BLOCKING_REQUEST_STARTED = "http.non-blocking-request-started"; 40 | export const HTTP_NON_BLOCKING_REQUESTS_COMPLETED = "http.non-blocking-requests-completed"; 41 | export const APP_MENU_BUTTON = "app.menuButton"; 42 | export const SCROLL_REFRESH_COMPLETE = "scroll.refreshComplete"; 43 | } 44 | 45 | module JustinCredible.SmartHomeMobile.Constants.Dashboard { 46 | export const ITEM_WIDTH = 40; 47 | export const ITEM_DEFAULT_HORIZONTAL_SPACING = 55; 48 | export const ITEM_DEFAULT_VERTICAL_SPACING = 60; 49 | export const ITEM_DEFAULT_HORIZONTAL_START = 10; 50 | export const ITEM_DEFAULT_VERTICAL_START = 65; 51 | } 52 | -------------------------------------------------------------------------------- /src/Controllers/Dialogs/ReorderCategoriesController.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Controllers { 2 | 3 | export class ReorderCategoriesController extends BaseDialogController { 4 | 5 | //#region Injection 6 | 7 | public static ID = "ReorderCategoriesController"; 8 | public static TemplatePath = "templates/Dialogs/Reorder-Categories.html"; 9 | 10 | public static get $inject(): string[] { 11 | return [ 12 | "$scope", 13 | Services.Utilities.ID, 14 | Services.Preferences.ID, 15 | Services.UiHelper.ID 16 | ]; 17 | } 18 | 19 | constructor( 20 | $scope: ng.IScope, 21 | private Utilities: Services.Utilities, 22 | private Preferences: Services.Preferences, 23 | private UiHelper: Services.UiHelper) { 24 | super($scope, ViewModels.ReorderCategoriesViewModel, ReorderCategoriesController.ID); 25 | } 26 | 27 | //#endregion 28 | 29 | //#region BaseDialogController Overrides 30 | 31 | protected dialog_shown(): void { 32 | super.dialog_shown(); 33 | 34 | // Grab the available categories. 35 | this.viewModel.categories = this.Preferences.categories; 36 | } 37 | 38 | //#endregion 39 | 40 | //#region Controller Methods 41 | 42 | protected item_reorder(item: ViewModels.CategoryItemViewModel, fromIndex: number, toIndex: number) { 43 | this.viewModel.categories.splice(fromIndex, 1); 44 | this.viewModel.categories.splice(toIndex, 0, item); 45 | } 46 | 47 | protected done_click() { 48 | var categoryOrder: string[] = []; 49 | 50 | this.viewModel.categories.forEach((categoryItem: ViewModels.CategoryItemViewModel) => { 51 | categoryOrder.push(categoryItem.name); 52 | }); 53 | 54 | this.Preferences.categoryOrder = categoryOrder; 55 | 56 | this.close(); 57 | } 58 | 59 | //#endregion 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /www/templates/Settings/Cameras-List.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 | Snapshot Cameras 14 |
15 | 16 | 20 | 21 | 22 | 23 | {{camera.name}} 24 | 25 | 26 |
27 | Streaming Cameras 28 |
29 | 30 | 34 | 35 | 36 | 37 | {{camera.name}} 38 | 39 | 40 |
41 | 42 | 46 | 47 | 54 | 55 |
56 | 57 |
58 | -------------------------------------------------------------------------------- /www/templates/Settings/Configure-Passphrase.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

6 | 7 |

8 | 9 |

A security passphrase is used to encrypt the access credentials to your smart home devices and platforms. You will be prompted for the security passphrase once per Chrome session.

10 | 11 |

The security passphrase will remain in effect until it is removed or the application is re-installed.

12 | 13 |
14 | 15 | 20 | 21 | 26 | 27 | 32 | 33 |
34 | 35 |

36 | 39 | 40 | 43 | 44 | 47 |

48 | 49 |
50 | 51 |
52 | -------------------------------------------------------------------------------- /www/templates/Settings/Settings-List.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | AlertMe / Iris Hub 10 | 11 | 12 | 13 | 14 | 15 | Dashboard 16 | 17 | 18 | 19 | 20 | 21 | IP Cameras 22 | 23 | 24 | 31 | 32 | 34 | 35 | 36 | Security PIN 37 | 38 | 39 | 41 | 42 | 43 | Security Passphrase 44 | 45 | 46 | 47 | 48 | 49 | Development Tools 50 | 51 | 52 | 53 | 54 | 55 | About 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /typings/custom/Interfaces.d.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This module is used to house custom interfaces that aren't data types. 4 | */ 5 | declare module JustinCredible.SmartHomeMobile.Interfaces { 6 | 7 | /** 8 | * An extension of the Angular RequestConfig interface which allows us to pass along a 9 | * few extra flags to control some featuers as defined in our HttpInterceptor. 10 | */ 11 | interface RequestConfig extends ng.IRequestConfig { 12 | 13 | /** 14 | * Indicates that the user should be blocked during this request. 15 | * HttpInterceptor defaults this to true. 16 | */ 17 | blocking?: boolean; 18 | 19 | /** 20 | * If blocking is true, specifies the text to use in the blocking dialog. 21 | * HttpIntercetpor defaults this to empty string. 22 | */ 23 | blockingText?: string; 24 | 25 | /** 26 | * Indicates that the non-blocking activity spinner should be shown during this request. 27 | * HttpInterceptor defaults this to true. 28 | */ 29 | showSpinner?: boolean; 30 | 31 | /** 32 | * Used to control if the HttpInterceptor will log the body of the request before it goes 33 | * out. Useful for skipping logs that contain sensitive data (eg passwords). If not provided, 34 | * the default value is false. 35 | */ 36 | logRequestBody?: boolean; 37 | } 38 | 39 | /** 40 | * Variables emitted at build time which contain useful application information. 41 | */ 42 | interface BuildVars { 43 | /** 44 | * True if the application was build in debug configuration, false if it was 45 | * build a release or distribution configuration. 46 | */ 47 | debug: boolean; 48 | 49 | /** 50 | * The time at which the application was built. 51 | */ 52 | buildTimestamp: string; 53 | 54 | majorVersion: number; 55 | minorVersion: number; 56 | buildVersion: number; 57 | 58 | /** 59 | * The short SHA for the git commit that this build was created from. 60 | * 61 | * Will be 'unknown' if the commit couldn't be determined or the machine 62 | * that made the build did not have git installed. 63 | */ 64 | commitShortSha: string; 65 | } 66 | 67 | interface VersionInfo { 68 | majorVersion: number; 69 | minorVersion: number; 70 | buildVersion: number; 71 | versionString: string; 72 | buildTimestamp: string; 73 | applicationName: string; 74 | copyrightInfoUrl: string; 75 | websiteUrl: string; 76 | githubUrl: string; 77 | email: string; 78 | commitShortSha: string; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /www/templates/Settings/Hub.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 |
12 | Hub API URL 13 | 15 |
16 | 17 | 27 | 28 |
29 | Credentials 30 |
31 | 32 | 42 | 43 | 49 | 50 | 56 | 57 |
58 | 59 |
60 | 65 |
66 | 67 |
68 | 69 |
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Smart Home Mobile App 2 | ============================= 3 | 4 | ![banner](.readme/banner.jpg) 5 | ![dashboard banner](.readme/dashboard-banner.jpg) 6 | 7 | [![Build Status](https://travis-ci.org/Justin-Credible/Smart-Home-Mobile.svg?branch=master)](https://travis-ci.org/Justin-Credible/Smart-Home-Mobile) 8 | [![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) 9 | 10 | A cross-platform mobile application used to control several smart home devices. 11 | 12 | * [Iris Smart Home](http://www.lowes.com/cd_Iris_239939199_) (by Lowes) 13 | * Lighting / Smart Plugs 14 | * Locks 15 | * Contact Sensors 16 | * Thermostat 17 | * Alarms (intrusion, fire, water leak, etc) 18 | * [IrrigationCaddy](http://irrigationcaddy.com) (*work in progress*) 19 | * IP Video Cameras 20 | 21 | ## Why? ## 22 | 23 | I wanted to have a single mobile app to control all of my smart home devices. While Iris has a mobile app, it lacks a few features that I wanted (direct streaming of IP cameras) and only works with Iris devices. 24 | 25 | I also wanted to have an app that would be easily extensible so I can add support for non-Iris devices as I add them to my home (eg IrrigationCaddy, Philips Hue bulbs, etc). 26 | 27 | ## Supported Platforms ## 28 | 29 | The mobile app is cross-platform and supports the following platforms: 30 | 31 | * Android 32 | * iOS 33 | * Windows 10 34 | * Windows 10 IoT on Raspberry Pi 2 35 | 36 | It is built with Ionic, TypeScript, and AngularJS (among others) using the [Ionic-TypeScript-Starter](https://github.com/Justin-Credible/Ionic-TypeScript-Starter) starter project. 37 | 38 | See [`getting-started.md`](https://github.com/Justin-Credible/Smart-Home-Mobile/blob/master/getting-started.md) for information on how to build and run the application. 39 | 40 | ## Integration APIs ## 41 | 42 | ### Iris ### 43 | 44 | I'm using the AlertMe v5 RESTful API for interacting with Iris. Lowe's does not provide public documentation on their API (which is based on the AlertMe API), but I've found that it is pretty close so far. I've included the API docs that I've found on the web in `resources/misc/IrisApi-v5`. 45 | 46 | ### IrrigationCaddy ### 47 | 48 | IrrigationCaddy also does not provide a public API, but using an HTTP debugging proxy I've been able to sniff the traffic and document it enough to use some basic features. Once complete, this will be located in `resources/IrrigationCaddyApi`. 49 | 50 | ### IP Video Cameras ### 51 | 52 | While each IP camera can be quite different, they usually all have at least one thing in common: an HTTP daemon serving up static images and/or mjpeg content. The mobile app allows the user to enter these URLs (static or streaming) for each camera. 53 | 54 | ## License ## 55 | 56 | Copyright © 2015 Justin Unterreiner. 57 | 58 | Released under an MIT license; see [LICENSE](https://github.com/Justin-Credible/Ionic-TypeScript-MDHA-Starter/blob/master/LICENSE) for more information. 59 | -------------------------------------------------------------------------------- /src/Controllers/Settings/AboutController.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Controllers { 2 | 3 | export class AboutController extends BaseController { 4 | 5 | //#region Injection 6 | 7 | public static ID = "AboutController"; 8 | 9 | public static get $inject(): string[] { 10 | return [ 11 | "$scope", 12 | "$ionicHistory", 13 | Services.Utilities.ID, 14 | Services.Configuration.ID, 15 | Services.Plugins.ID, 16 | "versionInfo" 17 | ]; 18 | } 19 | 20 | constructor( 21 | $scope: ng.IScope, 22 | private $ionicHistory: any, 23 | private Utilities: Services.Utilities, 24 | private Configuration: Services.Configuration, 25 | private Plugins: Services.Plugins, 26 | private versionInfo: Interfaces.VersionInfo) { 27 | super($scope, ViewModels.AboutViewModel); 28 | } 29 | 30 | //#endregion 31 | 32 | //#region BaseController Overrides 33 | 34 | protected view_beforeEnter(event?: ng.IAngularEvent, eventArgs?: Ionic.IViewEventArguments): void { 35 | super.view_beforeEnter(event, eventArgs); 36 | 37 | this.viewModel.logoClickCount = 0; 38 | 39 | this.viewModel.applicationName = this.versionInfo.applicationName; 40 | this.viewModel.versionString = this.Utilities.format("{0}.{1}.{2}", this.versionInfo.majorVersion, this.versionInfo.minorVersion, this.versionInfo.buildVersion); 41 | this.viewModel.timestamp = this.versionInfo.buildTimestamp; 42 | this.viewModel.commitShortSha = this.versionInfo.commitShortSha; 43 | } 44 | 45 | //#endregion 46 | 47 | //#region Controller Methods 48 | 49 | protected logo_click() { 50 | 51 | if (this.Configuration.enableDeveloperTools) { 52 | return; 53 | } 54 | 55 | this.viewModel.logoClickCount += 1; 56 | 57 | // If they've clicked the logo 10 times, then enable development tools 58 | // and push them back to the settings page. 59 | if (this.viewModel.logoClickCount > 9) { 60 | this.Configuration.enableDeveloperTools = true; 61 | this.Plugins.toast.showShortBottom("Development Tools Enabled!"); 62 | this.$ionicHistory.goBack(); 63 | } 64 | } 65 | 66 | protected copyrightInfo_click(): void { 67 | window.open(this.versionInfo.copyrightInfoUrl, "_system"); 68 | } 69 | 70 | protected website_click(): void { 71 | window.open(this.versionInfo.websiteUrl, "_system"); 72 | } 73 | 74 | protected gitHubRepo_click(): void { 75 | window.open(this.versionInfo.githubUrl, "_system"); 76 | } 77 | 78 | //#endregion 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/Controllers/Settings/LogEntryController.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Controllers { 2 | 3 | export interface ILogEntryStateParams { 4 | id: string; 5 | } 6 | 7 | export class LogEntryController extends BaseController { 8 | 9 | //#region Injection 10 | 11 | public static ID = "LogEntryController"; 12 | 13 | public static get $inject(): string[] { 14 | return [ 15 | "$scope", 16 | "$stateParams", 17 | Services.Logger.ID, 18 | Services.Plugins.ID, 19 | Services.Utilities.ID, 20 | "versionInfo"]; 21 | } 22 | 23 | constructor( 24 | $scope: ng.IScope, 25 | private $stateParams: ILogEntryStateParams, 26 | private Logger: Services.Logger, 27 | private Plugins: Services.Plugins, 28 | private Utilities: Services.Utilities, 29 | private versionInfo: Interfaces.VersionInfo) { 30 | super($scope, ViewModels.LogEntryViewModel); 31 | } 32 | 33 | //#endregion 34 | 35 | //#region BaseController Overrides 36 | 37 | protected view_beforeEnter(event?: ng.IAngularEvent, eventArgs?: Ionic.IViewEventArguments): void { 38 | super.view_beforeEnter(event, eventArgs); 39 | 40 | this.viewModel.logEntry = this.Logger.getLog(this.$stateParams.id); 41 | 42 | this.viewModel.date = moment(this.viewModel.logEntry.timestamp).format("MMMM Do YYYY"); 43 | this.viewModel.time = moment(this.viewModel.logEntry.timestamp).format("h:mm:ss a"); 44 | 45 | try { 46 | this.viewModel.formattedMetadata = JSON.stringify(this.viewModel.logEntry.metadata, null, 4); 47 | } 48 | catch (exception) { 49 | this.viewModel.formattedMetadata = "Unable to stringify metadata: " + exception; 50 | } 51 | 52 | this.viewModel.icon = this.Logger.getIconForLevel(this.viewModel.logEntry.level); 53 | this.viewModel.color = this.Logger.getColorForLevel(this.viewModel.logEntry.level); 54 | this.viewModel.levelDisplay = this.Logger.getDisplayLevelForLevel(this.viewModel.logEntry.level); 55 | } 56 | 57 | //#endregion 58 | 59 | //#region Controller Methods 60 | 61 | protected copy_click(): void { 62 | this.Plugins.clipboard.copy(JSON.stringify(this.viewModel.logEntry), () => { 63 | this.Plugins.toast.showShortBottom("Log copied to clipboard!"); 64 | }, null); 65 | } 66 | 67 | protected email_click(): void { 68 | var uri = this.Utilities.format("mailto:{0}?subject={1} Error Log&body={2}", this.versionInfo.email, this.versionInfo.applicationName, JSON.stringify(this.viewModel.logEntry)); 69 | uri = encodeURI(uri); 70 | window.open(uri, "_system"); 71 | } 72 | 73 | //#endregion 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Controllers/CamerasController.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Controllers { 2 | 3 | export class CamerasController extends BaseController { 4 | 5 | //#region Injection 6 | 7 | public static ID = "CamerasController"; 8 | 9 | public static get $inject(): string[] { 10 | return [ 11 | "$scope", 12 | "$location", 13 | Services.Utilities.ID, 14 | Services.Preferences.ID, 15 | Services.UiHelper.ID 16 | ]; 17 | } 18 | 19 | constructor( 20 | $scope: ng.IScope, 21 | private $location: ng.ILocationService, 22 | private Utilities: Services.Utilities, 23 | private Preferences: Services.Preferences, 24 | private UiHelper: Services.UiHelper) { 25 | super($scope, ViewModels.CamerasViewModel); 26 | } 27 | 28 | //#endregion 29 | 30 | //#region BaseController Overrides 31 | 32 | protected view_beforeEnter(event?: ng.IAngularEvent, eventArgs?: Ionic.IViewEventArguments): void { 33 | super.view_beforeEnter(event, eventArgs); 34 | 35 | this.viewModel.cameras = this.Preferences.cameras; 36 | 37 | if (!this.viewModel.cameras) { 38 | this.viewModel.cameras = []; 39 | } 40 | } 41 | 42 | //#endregion 43 | 44 | //#region Attribute/Expression Properties 45 | 46 | protected get snapshots_show(): boolean { 47 | var snapshotCameras: Models.Camera[]; 48 | 49 | // If there is no view model data, then the section shouldn't be visible. 50 | if (this.viewModel == null || this.viewModel.cameras == null) { 51 | return false; 52 | } 53 | 54 | // We want to show the snapshots section if we have snapshot cameras. 55 | snapshotCameras = _.filter(this.viewModel.cameras, (camera: Models.Camera) => { 56 | return camera.type === "SNAPSHOT"; 57 | }); 58 | 59 | // We need at least one to show this section. 60 | return snapshotCameras.length > 0; 61 | } 62 | 63 | protected get streaming_show(): boolean { 64 | var streamingCameras: Models.Camera[]; 65 | 66 | // If there is no view model data, then the section shouldn't be visible. 67 | if (this.viewModel == null || this.viewModel.cameras == null) { 68 | return false; 69 | } 70 | 71 | // We want to show the streaming section if we have streaming cameras. 72 | streamingCameras = _.filter(this.viewModel.cameras, (camera: Models.Camera) => { 73 | return camera.type === "STREAMING"; 74 | }); 75 | 76 | // We need at least one to show this section. 77 | return streamingCameras.length > 0; 78 | } 79 | 80 | //#endregion 81 | 82 | //#region Controller Methods 83 | 84 | //#endregion 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /www/templates/Settings/Log-Filter-Menu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 10 | 12 | Trace 13 | 14 | 15 | 18 | 20 | 22 | Debug 23 | 24 | 25 | 28 | 30 | 32 | Info 33 | 34 | 35 | 38 | 40 | 42 | Warning 43 | 44 | 45 | 48 | 50 | 52 | Error 53 | 54 | 55 | 58 | 60 | 62 | Fatal 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/Controllers/Settings/CamerasListController.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Controllers { 2 | 3 | export class CamerasListController extends BaseController { 4 | 5 | //#region Injection 6 | 7 | public static ID = "CamerasListController"; 8 | 9 | public static get $inject(): string[] { 10 | return [ 11 | "$scope", 12 | "$location", 13 | Services.Utilities.ID, 14 | Services.Preferences.ID, 15 | Services.UiHelper.ID 16 | ]; 17 | } 18 | 19 | constructor( 20 | $scope: ng.IScope, 21 | private $location: ng.ILocationService, 22 | private Utilities: Services.Utilities, 23 | private Preferences: Services.Preferences, 24 | private UiHelper: Services.UiHelper) { 25 | super($scope, ViewModels.CamerasListViewModel); 26 | } 27 | 28 | //#endregion 29 | 30 | //#region BaseController Overrides 31 | 32 | protected view_beforeEnter(event?: ng.IAngularEvent, eventArgs?: Ionic.IViewEventArguments): void { 33 | super.view_beforeEnter(event, eventArgs); 34 | 35 | this.viewModel.cameras = this.Preferences.cameras; 36 | 37 | if (!this.viewModel.cameras) { 38 | this.viewModel.cameras = []; 39 | } 40 | } 41 | 42 | //#endregion 43 | 44 | //#region Attribute/Expression Properties 45 | 46 | protected get snapshots_show(): boolean { 47 | var snapshotCameras: Models.Camera[]; 48 | 49 | // If there is no view model data, then the section shouldn't be visible. 50 | if (this.viewModel == null || this.viewModel.cameras == null) { 51 | return false; 52 | } 53 | 54 | // We want to show the snapshots section if we have snapshot cameras. 55 | snapshotCameras = _.filter(this.viewModel.cameras, (camera: Models.Camera) => { 56 | return camera.type === "SNAPSHOT"; 57 | }); 58 | 59 | // We need at least one to show this section. 60 | return snapshotCameras.length > 0; 61 | } 62 | 63 | protected get streaming_show(): boolean { 64 | var streamingCameras: Models.Camera[]; 65 | 66 | // If there is no view model data, then the section shouldn't be visible. 67 | if (this.viewModel == null || this.viewModel.cameras == null) { 68 | return false; 69 | } 70 | 71 | // We want to show the streaming section if we have streaming cameras. 72 | streamingCameras = _.filter(this.viewModel.cameras, (camera: Models.Camera) => { 73 | return camera.type === "STREAMING"; 74 | }); 75 | 76 | // We need at least one to show this section. 77 | return streamingCameras.length > 0; 78 | } 79 | 80 | //#endregion 81 | 82 | //#region Controller Methods 83 | 84 | //#endregion 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /hooks/after_prepare/010_add_platform_class.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Add Platform Class 4 | // v1.0 5 | // Automatically adds the platform class to the body tag 6 | // after the `prepare` command. By placing the platform CSS classes 7 | // directly in the HTML built for the platform, it speeds up 8 | // rendering the correct layout/style for the specific platform 9 | // instead of waiting for the JS to figure out the correct classes. 10 | 11 | var fs = require('fs'); 12 | var path = require('path'); 13 | 14 | var rootdir = process.argv[2]; 15 | 16 | function addPlatformBodyTag(indexPath, platform) { 17 | // add the platform class to the body tag 18 | try { 19 | var platformClass = 'platform-' + platform; 20 | var cordovaClass = 'platform-cordova platform-webview'; 21 | 22 | var html = fs.readFileSync(indexPath, 'utf8'); 23 | 24 | var bodyTag = findBodyTag(html); 25 | if(!bodyTag) return; // no opening body tag, something's wrong 26 | 27 | if(bodyTag.indexOf(platformClass) > -1) return; // already added 28 | 29 | var newBodyTag = bodyTag; 30 | 31 | var classAttr = findClassAttr(bodyTag); 32 | if(classAttr) { 33 | // body tag has existing class attribute, add the classname 34 | var endingQuote = classAttr.substring(classAttr.length-1); 35 | var newClassAttr = classAttr.substring(0, classAttr.length-1); 36 | newClassAttr += ' ' + platformClass + ' ' + cordovaClass + endingQuote; 37 | newBodyTag = bodyTag.replace(classAttr, newClassAttr); 38 | 39 | } else { 40 | // add class attribute to the body tag 41 | newBodyTag = bodyTag.replace('>', ' class="' + platformClass + ' ' + cordovaClass + '">'); 42 | } 43 | 44 | html = html.replace(bodyTag, newBodyTag); 45 | 46 | fs.writeFileSync(indexPath, html, 'utf8'); 47 | 48 | process.stdout.write('add to body class: ' + platformClass + '\n'); 49 | } catch(e) { 50 | process.stdout.write(e); 51 | } 52 | } 53 | 54 | function findBodyTag(html) { 55 | // get the body tag 56 | try{ 57 | return html.match(/])(.*?)>/gi)[0]; 58 | }catch(e){} 59 | } 60 | 61 | function findClassAttr(bodyTag) { 62 | // get the body tag's class attribute 63 | try{ 64 | return bodyTag.match(/ class=["|'](.*?)["|']/gi)[0]; 65 | }catch(e){} 66 | } 67 | 68 | if (rootdir) { 69 | 70 | // go through each of the platform directories that have been prepared 71 | var platforms = (process.env.CORDOVA_PLATFORMS ? process.env.CORDOVA_PLATFORMS.split(',') : []); 72 | 73 | for(var x=0; x 21 | # Cordova Hooks 22 | 23 | This directory may contain scripts used to customize cordova commands. This 24 | directory used to exist at `.cordova/hooks`, but has now been moved to the 25 | project root. Any scripts you add to these directories will be executed before 26 | and after the commands corresponding to the directory name. Useful for 27 | integrating your own build systems or integrating with version control systems. 28 | 29 | __Remember__: Make your scripts executable. 30 | 31 | ## Hook Directories 32 | The following subdirectories will be used for hooks: 33 | 34 | after_build/ 35 | after_compile/ 36 | after_docs/ 37 | after_emulate/ 38 | after_platform_add/ 39 | after_platform_rm/ 40 | after_platform_ls/ 41 | after_plugin_add/ 42 | after_plugin_ls/ 43 | after_plugin_rm/ 44 | after_plugin_search/ 45 | after_prepare/ 46 | after_run/ 47 | after_serve/ 48 | before_build/ 49 | before_compile/ 50 | before_docs/ 51 | before_emulate/ 52 | before_platform_add/ 53 | before_platform_rm/ 54 | before_platform_ls/ 55 | before_plugin_add/ 56 | before_plugin_ls/ 57 | before_plugin_rm/ 58 | before_plugin_search/ 59 | before_prepare/ 60 | before_run/ 61 | before_serve/ 62 | pre_package/ <-- Windows 8 and Windows Phone only. 63 | 64 | ## Script Interface 65 | 66 | All scripts are run from the project's root directory and have the root directory passes as the first argument. All other options are passed to the script using environment variables: 67 | 68 | * CORDOVA_VERSION - The version of the Cordova-CLI. 69 | * CORDOVA_PLATFORMS - Comma separated list of platforms that the command applies to (e.g.: android, ios). 70 | * CORDOVA_PLUGINS - Comma separated list of plugin IDs that the command applies to (e.g.: org.apache.cordova.file, org.apache.cordova.file-transfer) 71 | * CORDOVA_HOOK - Path to the hook that is being executed. 72 | * CORDOVA_CMDLINE - The exact command-line arguments passed to cordova (e.g.: cordova run ios --emulate) 73 | 74 | If a script returns a non-zero exit code, then the parent cordova command will be aborted. 75 | 76 | 77 | ## Writing hooks 78 | 79 | We highly recommend writting your hooks using Node.js so that they are 80 | cross-platform. Some good examples are shown here: 81 | 82 | [http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/](http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/) 83 | 84 | -------------------------------------------------------------------------------- /www/templates/Dialogs/Set-Multiple-Smart-Plugs-State.html: -------------------------------------------------------------------------------- 1 | 80 | -------------------------------------------------------------------------------- /src/Controllers/Dialogs/SetMultipleSmartPlugsStateController.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Controllers { 2 | 3 | export class SetMultipleSmartPlugsStateController extends BaseDialogController { 4 | 5 | //#region Injection 6 | 7 | public static ID = "SetMultipleSmartPlugsStateController"; 8 | public static TemplatePath = "templates/Dialogs/Set-Multiple-Smart-Plugs-State.html"; 9 | 10 | public static get $inject(): string[] { 11 | return [ 12 | "$scope", 13 | Services.Utilities.ID, 14 | Services.Preferences.ID, 15 | Services.UiHelper.ID 16 | ]; 17 | } 18 | 19 | constructor( 20 | $scope: ng.IScope, 21 | private Utilities: Services.Utilities, 22 | private Preferences: Services.Preferences, 23 | private UiHelper: Services.UiHelper) { 24 | super($scope, ViewModels.SetMultipleSmartPlugsStateViewModel, SetMultipleSmartPlugsStateController.ID); 25 | } 26 | 27 | //#endregion 28 | 29 | //#Region BaseDialogController Overrides 30 | 31 | protected dialog_shown(): void { 32 | super.dialog_shown(); 33 | 34 | this.viewModel.stateChanged = false; 35 | this.viewModel.smartPlugs = this.getData(); 36 | } 37 | 38 | //#endregion 39 | 40 | //#region Attribute/Expression Properties 41 | 42 | protected get lighting_show(): boolean { 43 | var outlets: AlertMeApiTypes.SmartPlugDevice[]; 44 | 45 | // If there is no view model data, then the section shouldn't be visible. 46 | if (this.viewModel == null || this.viewModel.smartPlugs == null) { 47 | return false; 48 | } 49 | 50 | // We want to show the outlets section if we have applicances of type lighting. 51 | outlets = _.filter(this.viewModel.smartPlugs, (smartPlug: AlertMeApiTypes.SmartPlugDevice) => { 52 | return smartPlug.applianceType === "LIGHTS"; 53 | }); 54 | 55 | // We need at least one to show this section. 56 | return outlets.length > 0; 57 | } 58 | 59 | protected get outlets_show(): boolean { 60 | var outlets: AlertMeApiTypes.SmartPlugDevice[]; 61 | 62 | // If there is no view model data, then the section shouldn't be visible. 63 | if (this.viewModel == null || this.viewModel.smartPlugs == null) { 64 | return false; 65 | } 66 | 67 | // We want to show the outlets section if we have applicances of type smart plug. 68 | outlets = _.filter(this.viewModel.smartPlugs, (smartPlug: AlertMeApiTypes.SmartPlugDevice) => { 69 | return smartPlug.applianceType === "SMARTPLUG"; 70 | }); 71 | 72 | // We need at least one to show this section. 73 | return outlets.length > 0; 74 | } 75 | 76 | //#endregion 77 | 78 | //#region Controller Methods 79 | 80 | protected cancel_click(): void { 81 | this.close(); 82 | } 83 | 84 | protected done_click(): void { 85 | this.close(this.viewModel.smartPlugs); 86 | } 87 | 88 | protected smartPlugToggle_click(smartPlug: AlertMeApiTypes.SmartPlugDevice): void { 89 | 90 | // If this device was reported as unavailable, then there is nothing to do. 91 | if (smartPlug.onOffState === Services.AlertMeApi.SmartPlugOnOffState.Unavailable) { 92 | return; 93 | } 94 | 95 | this.viewModel.stateChanged = true; 96 | } 97 | 98 | //#endregion 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Controllers/Dialogs/PinEntryController.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Controllers { 2 | 3 | export class PinEntryController extends BaseDialogController { 4 | 5 | //#region Injection 6 | 7 | public static ID = "PinEntryController"; 8 | public static TemplatePath = "templates/Dialogs/Pin-Entry.html"; 9 | 10 | public static get $inject(): string[] { 11 | return [ 12 | "$scope", 13 | Services.Utilities.ID, 14 | Services.Preferences.ID, 15 | Services.Plugins.ID 16 | ]; 17 | } 18 | 19 | constructor( 20 | $scope: ng.IScope, 21 | private Utilities: Services.Utilities, 22 | private Preferences: Services.Preferences, 23 | private Plugins: Services.Plugins) { 24 | super($scope, ViewModels.PinEntryViewModel, PinEntryController.ID); 25 | } 26 | 27 | //#endregion 28 | 29 | //#region BaseDialogController Overrides 30 | 31 | protected dialog_shown() { 32 | super.dialog_shown(); 33 | 34 | this.viewModel.pin = ""; 35 | this.viewModel.showBackButton = !!this.getData().showBackButton; 36 | this.viewModel.promptText = this.getData().promptText; 37 | this.viewModel.pinToMatch = this.getData().pinToMatch; 38 | } 39 | 40 | //#endregion 41 | 42 | //#region Private Methods 43 | 44 | private validatePin() { 45 | 46 | if (this.viewModel.pinToMatch) { 47 | 48 | // If there is a PIN to match, then we'll see if it matches. This is 49 | // for the case when we are validating a user entered PIN against one 50 | // that is already configured. 51 | 52 | if (this.viewModel.pin === this.viewModel.pinToMatch) { 53 | // If the PIN values match, then close this dialog instance. 54 | this.close(new Models.PinEntryDialogResultModel(true, false, this.viewModel.pin)); 55 | } 56 | else { 57 | // If the PIN values do not match, then clear the fields and remain 58 | // open so the user can try again. 59 | this.viewModel.pin = ""; 60 | this.Plugins.toast.showShortTop("Invalid pin; please try again."); 61 | this.scope.$apply(); 62 | } 63 | } 64 | else { 65 | // If we aren't attempting to match a PIN, then this must be a prompt 66 | // for a new PIN value. In this case we can just set the result and 67 | // close this modal instance. 68 | this.close(new Models.PinEntryDialogResultModel(null, false, this.viewModel.pin)); 69 | } 70 | } 71 | 72 | //#endregion 73 | 74 | //#region Controller Methods 75 | 76 | protected number_click(value: number) { 77 | 78 | if (this.viewModel.pin.length < 4) { 79 | this.viewModel.pin += value; 80 | 81 | // If all four digits have been entered then we need to take action. 82 | // We wait a fraction of a second so that the user can see the last 83 | // digit in the PIN appear in the UI. 84 | if (this.viewModel.pin.length === 4) { 85 | _.delay(_.bind(this.validatePin, this), 700); 86 | } 87 | } 88 | } 89 | 90 | protected clear_click() { 91 | this.viewModel.pin = ""; 92 | } 93 | 94 | protected back_click() { 95 | this.close(new Models.PinEntryDialogResultModel(null, true, null)); 96 | } 97 | 98 | //#endregion 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /typings/custom/Extensions.d.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This file contains definition extensions to base browser interfaces which are 4 | * available via third party plug-ins as well as extensions for existing interfaces. 5 | * 6 | */ 7 | 8 | interface Window { 9 | 10 | /** 11 | * The ProgressIndicator provides some full screen user blocking spinners etc. 12 | */ 13 | ProgressIndicator: ICordovaProgressIndicator; 14 | 15 | /** 16 | * Used to obtain a directory entry on the local file system given a URI. 17 | * 18 | * Describes the resolveLocalFileSystemURL function that is exposed via the 19 | * org.apache.cordova.file@1.2.0 plugin in resolveLocalFileSystemURI.js file. 20 | * 21 | * https://github.com/apache/cordova-plugin-file/blob/master/doc/index.md 22 | * 23 | * @param uri The URI of a path on the local file system to use to obtain a directory entry. 24 | * @param successCallback Executed when the API call succeeds. 25 | * @param errorCallback Executed when the API call fails. 26 | */ 27 | resolveLocalFileSystemURL(uri: string, successCallback: (directoryEntry: DirectoryEntry) => void, errorCallback: (error: FileError) => void): void; 28 | 29 | /** 30 | * The available Cordova plug-ins. 31 | * 32 | * If your plug-in isn't available here, check Cordova.plugins. 33 | */ 34 | //plugins: ICordovaWindowPlugins; 35 | 36 | /** 37 | * The Ripple API for the Apache Ripple Emulator. 38 | */ 39 | ripple: Object; 40 | 41 | /** 42 | * Variables emitted at build time which contain useful application information. 43 | */ 44 | buildVars: JustinCredible.SmartHomeMobile.Interfaces.BuildVars; 45 | 46 | /** 47 | * Prints a stack trace at the given location. 48 | * 49 | * Uses the stacktrace.js library: 50 | * https://github.com/stacktracejs/stacktrace.js 51 | */ 52 | printStackTrace(): string[]; 53 | 54 | /** 55 | * Prints a stack trace for the given error object. 56 | * 57 | * Uses the stacktrace.js library: 58 | * https://github.com/stacktracejs/stacktrace.js 59 | * 60 | * @param data An object containing the error on a property named "e". 61 | */ 62 | printStackTrace(data: { e: Error }): string[]; 63 | } 64 | 65 | /** 66 | * These are the Cordova plug-ins that are available via the global Cordova.plugins object. 67 | */ 68 | interface CordovaPlugins { 69 | 70 | /** 71 | * This plugin allows access to the user's clipboard. 72 | */ 73 | clipboard: ICordovaClipboardPlugin; 74 | } 75 | 76 | /** 77 | * These are the Cordova plug-ins that are available via the global window.plugins object. 78 | */ 79 | interface Plugins { 80 | /** 81 | * This plugin allows showing toast messages cross platform for Android, iOS, and WP8. 82 | */ 83 | toast: ICordovaToastPlugin; 84 | } 85 | 86 | /** 87 | * The window object for the background page when running as a Chrome Extension. 88 | */ 89 | interface ChromeExtensionBackgroundWindow extends Window { 90 | 91 | /** 92 | * Global extension state 93 | */ 94 | state: { 95 | 96 | /** 97 | * The user's security passphrase. 98 | * 99 | * When running as a Chrome extension the user can enter a passphrase that will encrypt 100 | * the AlertMe API password so it isn't stored in cleartext in local storage. The user 101 | * must enter their passphrase at least once so it will be available. 102 | * 103 | * It is a transient, in-memory only variable that does not persist between sessions. 104 | */ 105 | passphrase: string; 106 | }; 107 | } 108 | 109 | /** 110 | * These are the Cordova plug-ins that are available via the global navigator object. 111 | */ 112 | interface Navigator { 113 | /** 114 | * This plugin allows logging exception information to the Crashlytics backend service. 115 | */ 116 | crashlytics: ICordovaCrashlyticsPlugin; 117 | } 118 | -------------------------------------------------------------------------------- /src/Controllers/Dialogs/BaseDialogController.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Controllers { 2 | 3 | /** 4 | * This is the base controller that all other controllers should utilize. 5 | * 6 | * It handles saving a reference to the Angular scope, newing up the given 7 | * model object type, and injecting the view model and controller onto the 8 | * scope object for use in views. 9 | * 10 | * V - The type of the view model that this controller will utilize. 11 | * D - The type of data object that will be passed in when this dialog is opened. 12 | * R - The type of the data object that will be returned when this dialog is closed. 13 | */ 14 | export class BaseDialogController extends BaseController { 15 | 16 | private dialogId: string; 17 | private modalInstance: any; 18 | private data: D; 19 | 20 | constructor(scope: ng.IScope, ViewModelType: { new (): V; }, dialogId: string) { 21 | super(scope, ViewModelType); 22 | 23 | this.dialogId = dialogId; 24 | 25 | this.scope.$on("modal.shown", _.bind(this.modal_shown, this)); 26 | this.scope.$on("modal.hidden", _.bind(this.modal_hidden, this)); 27 | } 28 | 29 | //#region Events 30 | 31 | private modal_shown(ngEvent: ng.IAngularEvent, instance: any) { 32 | 33 | // Only respond to modal.shown events for this dialog. 34 | if (this.dialogId !== instance.dialogId) { 35 | return; 36 | } 37 | 38 | // Save off a reference to the Ionic modal instance. 39 | this.modalInstance = instance; 40 | 41 | // Hold a reference to the data object that was passed in when opening the dialog. 42 | this.data = instance.dialogData; 43 | 44 | // Call the dialog shown event which descendants can override. 45 | this.dialog_shown(); 46 | } 47 | 48 | private modal_hidden(eventArgs: ng.IAngularEvent, instance: any) { 49 | 50 | // Only respond to modal.hidden events for this dialog. 51 | if (this.dialogId !== instance.dialogId) { 52 | return; 53 | } 54 | 55 | // Call the dialog hidden event which descendants can override. 56 | this.dialog_hidden(); 57 | } 58 | 59 | //#endregion 60 | 61 | //#region Protected Methods 62 | 63 | /** 64 | * Used to get the data object that this was opened with. 65 | */ 66 | public getData(): D { 67 | return this.data; 68 | } 69 | 70 | /** 71 | * Used to close the dialog. 72 | */ 73 | public close(): void; 74 | 75 | /** 76 | * Used to close the dialog. 77 | * 78 | * @param result The return result value for this dialog. 79 | */ 80 | public close(result: R): void; 81 | 82 | /** 83 | * Used to close the dialog. 84 | * 85 | * @param result The return result value for this dialog. 86 | */ 87 | public close(result?: R): void { 88 | this.modalInstance.result = result; 89 | this.modalInstance.hide(); 90 | this.modalInstance.remove(); 91 | } 92 | 93 | //#endregion 94 | 95 | //#region Override-able Methods 96 | 97 | /** 98 | * Fired when this dialog is shown. 99 | * 100 | * Can be overridden by implementing controllers. 101 | */ 102 | protected dialog_shown(): void { 103 | /* tslint:disable:no-empty */ 104 | /* tslint:enable:no-empty */ 105 | } 106 | 107 | /** 108 | * Fired when this dialog is hidden. 109 | * 110 | * Can be overridden by implementing controllers. 111 | */ 112 | protected dialog_hidden(): void { 113 | /* tslint:disable:no-empty */ 114 | /* tslint:enable:no-empty */ 115 | } 116 | 117 | //#endregion 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /tsd.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v4", 3 | "repo": "borisyankov/DefinitelyTyped", 4 | "ref": "master", 5 | "path": "typings", 6 | "bundle": "src/tsd.d.ts", 7 | "installed": { 8 | "nprogress/NProgress.d.ts": { 9 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 10 | }, 11 | "moment/moment.d.ts": { 12 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 13 | }, 14 | "lodash/lodash.d.ts": { 15 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 16 | }, 17 | "angularjs/angular.d.ts": { 18 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 19 | }, 20 | "cordova-ionic/cordova-ionic.d.ts": { 21 | "commit": "af5b8275d1f2b92738b93fddabc461fc20c553a1" 22 | }, 23 | "cordova/cordova.d.ts": { 24 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 25 | }, 26 | "cordova/plugins/BatteryStatus.d.ts": { 27 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 28 | }, 29 | "cordova/plugins/Camera.d.ts": { 30 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 31 | }, 32 | "cordova/plugins/Contacts.d.ts": { 33 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 34 | }, 35 | "cordova/plugins/Device.d.ts": { 36 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 37 | }, 38 | "cordova/plugins/DeviceMotion.d.ts": { 39 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 40 | }, 41 | "cordova/plugins/DeviceOrientation.d.ts": { 42 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 43 | }, 44 | "cordova/plugins/Dialogs.d.ts": { 45 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 46 | }, 47 | "cordova/plugins/FileTransfer.d.ts": { 48 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 49 | }, 50 | "cordova/plugins/FileSystem.d.ts": { 51 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 52 | }, 53 | "cordova/plugins/Media.d.ts": { 54 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 55 | }, 56 | "cordova/plugins/InAppBrowser.d.ts": { 57 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 58 | }, 59 | "cordova/plugins/MediaCapture.d.ts": { 60 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 61 | }, 62 | "cordova/plugins/Globalization.d.ts": { 63 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 64 | }, 65 | "cordova/plugins/NetworkInformation.d.ts": { 66 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 67 | }, 68 | "cordova/plugins/Splashscreen.d.ts": { 69 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 70 | }, 71 | "cordova/plugins/Push.d.ts": { 72 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 73 | }, 74 | "cordova/plugins/StatusBar.d.ts": { 75 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 76 | }, 77 | "cordova/plugins/WebSQL.d.ts": { 78 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 79 | }, 80 | "cordova/plugins/Vibration.d.ts": { 81 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 82 | }, 83 | "cordova-ionic/plugins/keyboard.d.ts": { 84 | "commit": "af5b8275d1f2b92738b93fddabc461fc20c553a1" 85 | }, 86 | "jquery/jquery.d.ts": { 87 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 88 | }, 89 | "angularjs/angular-mocks.d.ts": { 90 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 91 | }, 92 | "angular-ui-router/angular-ui-router.d.ts": { 93 | "commit": "aadd63ecae3feb76ea2d4be80511e266b5c2c4a7" 94 | }, 95 | "urijs/URIjs.d.ts": { 96 | "commit": "c50b111ded0a3a18b87b5abffea3150d6aca94da" 97 | }, 98 | "cryptojs/cryptojs.d.ts": { 99 | "commit": "c50b111ded0a3a18b87b5abffea3150d6aca94da" 100 | }, 101 | "chrome/chrome.d.ts": { 102 | "commit": "c50b111ded0a3a18b87b5abffea3150d6aca94da" 103 | }, 104 | "webrtc/MediaStream.d.ts": { 105 | "commit": "c50b111ded0a3a18b87b5abffea3150d6aca94da" 106 | }, 107 | "winjs/winjs.d.ts": { 108 | "commit": "52b0ea5c9719831eecf6ba7436660e30061a4b3c" 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/Controllers/Settings/HubController.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Controllers { 2 | 3 | export class HubController extends BaseController { 4 | 5 | //#region Injection 6 | 7 | public static ID = "HubController"; 8 | 9 | public static get $inject(): string[] { 10 | return [ 11 | "$scope", 12 | "$location", 13 | "$ionicHistory", 14 | Services.Plugins.ID, 15 | Services.Utilities.ID, 16 | Services.Preferences.ID, 17 | Services.UiHelper.ID 18 | ]; 19 | } 20 | 21 | constructor( 22 | $scope: ng.IScope, 23 | private $location: ng.ILocationService, 24 | private $ionicHistory: any, 25 | private Plugins: Services.Plugins, 26 | private Utilities: Services.Utilities, 27 | private Preferences: Services.Preferences, 28 | private UiHelper: Services.UiHelper) { 29 | super($scope, ViewModels.HubViewModel); 30 | } 31 | 32 | //#endregion 33 | 34 | //#region BaseController Overrides 35 | 36 | protected view_beforeEnter(event?: ng.IAngularEvent, eventArgs?: Ionic.IViewEventArguments): void { 37 | super.view_beforeEnter(event, eventArgs); 38 | 39 | this.viewModel.showSaveButton = false; 40 | this.viewModel.apiUrl = this.Preferences.alertMeApiUrl; 41 | this.viewModel.userName = this.Preferences.alertMeApiUserName; 42 | this.viewModel.password = this.Preferences.alertMeApiPassword; 43 | } 44 | 45 | //#endregion 46 | 47 | //#region Controller Methods 48 | 49 | protected apiInfo_click(): void { 50 | var infoMessage1 = "The hub is responsible for controlling communicating with the smart home devices, such as security, locks, power, thermostat, etc."; 51 | var infoMessage2 = "Currently supported hubs are ones that use the AlertMe API (such as Lowe's Iris Smart Home platform). This should be a URL to the root of the AlertMe API such as: https://api.alertme.com/v5"; 52 | var promptMessage = "Would you like to use the default URL for Lowe's Iris Smart Home platform? (https://www.irissmarthome.com/v5)"; 53 | 54 | this.UiHelper.alert(infoMessage1, "Hub Info").then(() => { 55 | this.UiHelper.alert(infoMessage2, "Hub Info").then(() => { 56 | this.UiHelper.confirm(promptMessage, "Use Default").then((result: string) => { 57 | if (result === Constants.Buttons.Yes) { 58 | this.viewModel.apiUrl = "https://www.irissmarthome.com/v5"; 59 | this.viewModel.showSaveButton = true; 60 | } 61 | }); 62 | }); 63 | }); 64 | } 65 | 66 | protected save_click(): void { 67 | 68 | if (!this.viewModel.apiUrl || !this.viewModel.userName || !this.viewModel.password) { 69 | this.UiHelper.alert("Please ensure all fields are populated."); 70 | return; 71 | } 72 | 73 | // If the password has changed, ensure that the new passwords match. 74 | if (this.viewModel.showConfirmPassword) { 75 | if (this.viewModel.password !== this.viewModel.confirmPassword) { 76 | this.UiHelper.alert("The passwords do not match, please try again."); 77 | this.viewModel.password = ""; 78 | this.viewModel.confirmPassword = ""; 79 | return; 80 | } 81 | } 82 | 83 | // Update the values in the preferences. 84 | this.Preferences.alertMeApiUrl = this.viewModel.apiUrl; 85 | this.Preferences.alertMeApiUserName = this.viewModel.userName; 86 | this.Preferences.alertMeApiPassword = this.viewModel.password; 87 | 88 | // Kick the user back to the settings list view. 89 | this.Plugins.toast.showShortBottom("Changes have been saved."); 90 | this.$ionicHistory.goBack(); 91 | } 92 | 93 | //#endregion 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /typings/custom/ionic.d.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This file contains Ionic specific APIs that do not yet have typings in the 4 | * DefinitelyTyped repository. 5 | */ 6 | declare module Ionic { 7 | 8 | /** 9 | * Describes the Ionic Platform object. 10 | * 11 | * http://ionicframework.com/docs/api/utility/ionic.Platform/ 12 | */ 13 | interface IPlatform { 14 | 15 | /** 16 | * Whether the device is ready 17 | */ 18 | isReady: boolean; 19 | 20 | /** 21 | * Whether the device is full screen. 22 | */ 23 | isFullScreen: boolean; 24 | 25 | /** 26 | * An array of all platforms found. 27 | */ 28 | platforms: string[]; 29 | 30 | /** 31 | * What grade the current platform is. 32 | */ 33 | grade: string; 34 | 35 | /** 36 | * Trigger a callback once the device is ready, or immediately if the device is already ready. 37 | * This method can be run from anywhere and does not need to be wrapped by any additional methods. 38 | * When the app is within a WebView (Cordova), it'll fire the callback once the device is ready. 39 | * If the app is within a web browser, it'll fire the callback after window.load. 40 | */ 41 | ready(callback: () => void): void; 42 | 43 | /** 44 | * Set the grade of the device: 'a', 'b', or 'c'. 'a' is the best (most css features enabled), 45 | * 'c' is the worst. By default, sets the grade depending on the current device. 46 | */ 47 | setGrade(grade): void; 48 | 49 | /** 50 | * Check if we are running within a WebView (such as Cordova). 51 | */ 52 | isWebView(): boolean; 53 | 54 | /** 55 | * Whether we are running on iPad. 56 | */ 57 | isIPad(): boolean; 58 | 59 | /** 60 | * Whether we are running on iOS. 61 | */ 62 | isIOS(): boolean; 63 | 64 | /** 65 | * Whether we are running on Android 66 | */ 67 | isAndroid(): boolean; 68 | 69 | /** 70 | * Whether we are running on Windows Phone. 71 | */ 72 | isWindowsPhone(): boolean; 73 | 74 | /** 75 | * The name of the current platform. 76 | */ 77 | platform(): string; 78 | 79 | /** 80 | * The version of the current device platform. 81 | */ 82 | version(): string; 83 | 84 | /** 85 | * Exit the application. 86 | */ 87 | exitApp(): void; 88 | 89 | /** 90 | * Shows or hides the device status bar (in Cordova). 91 | * 92 | * @param showShould Whether or not to show the status bar. 93 | */ 94 | showStatusBar(shouldShow: boolean): void; 95 | 96 | /** 97 | * Sets whether the app is full screen or not (in Cordova). 98 | * 99 | * @param showFullScreen Whether or not to set the app to full screen. Defaults to true. 100 | * @param showStatusBar Whether or not to show the device's status bar. Defaults to false. 101 | */ 102 | fullScreen(showFullScreen: boolean, showStatusBar: boolean): void; 103 | } 104 | 105 | /** 106 | * Describes the event arguments passed to $ionicView events. 107 | */ 108 | interface IViewEventArguments { 109 | viewId: string; 110 | historyId: string; 111 | stateId: string; 112 | stateName: string; 113 | stateParams: { 114 | id: string; 115 | parent: string; 116 | }; 117 | transition: string; 118 | navBarTransition: string; 119 | direction: string; 120 | shouldAnimate: boolean; 121 | transitionId: number; 122 | fromCache: boolean; 123 | enableBack: boolean; 124 | renderStart: boolean; 125 | renderEnd: boolean; 126 | viewNotified: boolean; 127 | title: string; 128 | showBack: boolean; 129 | navBarItems: any; 130 | navBarDelegate: any; 131 | showNavBar: boolean; 132 | hasHeaderBar: boolean; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/Services/Configuration.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Services { 2 | 3 | /** 4 | * Provides a way to easily get/set application configuration. 5 | * 6 | * The current backing store is local storage and/or session storage: 7 | * https://cordova.apache.org/docs/en/3.0.0/cordova_storage_storage.md.html#localStorage 8 | */ 9 | export class Configuration { 10 | 11 | //#region Injection 12 | 13 | public static ID = "Configuration"; 14 | 15 | public static get $inject(): string[] { 16 | return [ 17 | "buildVars" 18 | ]; 19 | } 20 | 21 | constructor( 22 | private buildVars: Interfaces.BuildVars) { 23 | } 24 | 25 | //#endregion 26 | 27 | //#region Local Storage Keys 28 | 29 | private static ENABLE_DEVELOPER_TOOLS = "ENABLE_DEVELOPER_TOOLS"; 30 | private static ENABLE_MOCK_HTTP_CALLS = "ENABLE_MOCK_HTTP_CALLS"; 31 | private static REQUIRE_PIN_THRESHOLD = "REQUIRE_PIN_THRESHOLD"; 32 | private static LAST_PAUSED_AT = "LAST_PAUSED_AT"; 33 | private static HAS_COMPLETED_ONBOARDING = "HAS_COMPLETED_ONBOARDING"; 34 | 35 | //#endregion 36 | 37 | //#region Defaults 38 | 39 | private static REQUIRE_PIN_THRESHOLD_DEFAULT = 10; // Default setting is 10 minutes. 40 | 41 | //#endregion 42 | 43 | //#region Framework Settings 44 | 45 | get enableDeveloperTools(): boolean { 46 | return sessionStorage.getItem(Configuration.ENABLE_DEVELOPER_TOOLS) === "true"; 47 | } 48 | 49 | set enableDeveloperTools(value: boolean) { 50 | if (value == null) { 51 | sessionStorage.removeItem(Configuration.ENABLE_DEVELOPER_TOOLS); 52 | } 53 | else { 54 | sessionStorage.setItem(Configuration.ENABLE_DEVELOPER_TOOLS, value.toString()); 55 | } 56 | } 57 | 58 | get enableMockHttpCalls(): boolean { 59 | return localStorage.getItem(Configuration.ENABLE_MOCK_HTTP_CALLS) === "true"; 60 | } 61 | 62 | set enableMockHttpCalls(value: boolean) { 63 | if (value == null) { 64 | localStorage.removeItem(Configuration.ENABLE_MOCK_HTTP_CALLS); 65 | } 66 | else { 67 | localStorage.setItem(Configuration.ENABLE_MOCK_HTTP_CALLS, value.toString()); 68 | } 69 | } 70 | 71 | get requirePinThreshold(): number { 72 | var value = localStorage.getItem(Configuration.REQUIRE_PIN_THRESHOLD); 73 | return value == null ? Configuration.REQUIRE_PIN_THRESHOLD_DEFAULT : parseInt(value, 10); 74 | } 75 | 76 | set requirePinThreshold(value: number) { 77 | if (value == null) { 78 | localStorage.removeItem(Configuration.REQUIRE_PIN_THRESHOLD); 79 | } 80 | else { 81 | localStorage.setItem(Configuration.REQUIRE_PIN_THRESHOLD, value.toString()); 82 | } 83 | } 84 | 85 | set lastPausedAt(value: moment.Moment) { 86 | if (value == null) { 87 | localStorage.removeItem(Configuration.LAST_PAUSED_AT); 88 | } 89 | else { 90 | localStorage.setItem(Configuration.LAST_PAUSED_AT, moment(value).format()); 91 | } 92 | } 93 | 94 | get lastPausedAt(): moment.Moment { 95 | var lastPausedAt: string; 96 | 97 | lastPausedAt = localStorage.getItem(Configuration.LAST_PAUSED_AT); 98 | 99 | return moment(lastPausedAt).isValid() ? moment(lastPausedAt) : null; 100 | } 101 | 102 | get hasCompletedOnboarding(): boolean { 103 | return localStorage.getItem(Configuration.HAS_COMPLETED_ONBOARDING) === "true"; 104 | } 105 | 106 | set hasCompletedOnboarding(value: boolean) { 107 | if (value == null) { 108 | localStorage.removeItem(Configuration.HAS_COMPLETED_ONBOARDING); 109 | } 110 | else { 111 | localStorage.setItem(Configuration.HAS_COMPLETED_ONBOARDING, value.toString()); 112 | } 113 | } 114 | 115 | //#endregion 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /www/templates/Settings/Dashboard-Config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 19 | 20 | 25 | 26 | 31 | 32 | 37 | 38 | 39 | 40 |
41 |

42 |
43 |
44 | Selected Item: 45 |
46 |
47 | {{viewModel.selectedItem.name}} 48 |
49 |
50 | {{viewModel.selectedItem.type | TitleCase: true }} 51 |
52 |
53 | {{viewModel.selectedItem.x}}, {{viewModel.selectedItem.y}} 54 |
55 |
56 | 60 |
61 |
62 |
63 | Visible 64 | 72 |
73 |
74 |
75 | 76 | 77 | (drag and drop devices to arrange them) 78 | 79 |

80 |
81 | 82 | 83 | 84 |
86 | 87 | 100 | 101 |
102 | 103 |
104 | 105 |
-------------------------------------------------------------------------------- /www/templates/Devices/Devices-List.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 14 |
Last updated at {{viewModel.lastUpdated | date: 'MM/dd/yyyy HH:mm:ss Z'}}
15 | 16 | 17 | 18 | 19 | Hub - {{viewModel.homeStatus.hub.name}} 20 | 21 | 22 | 23 |

Available

24 |

Not Available

25 |

{{viewModel.homeStatus.hub.availableStatus | TitleCase }}

26 | 27 |
28 | 29 | 31 | On Battery Backup 32 | 33 | 34 | 35 | 36 |

Internet Connection

37 |

Cellular/GPRS

38 |

Broadband

39 |

40 | {{viewModel.homeStatus.hub.connectionType | TitleCase }} 41 |

42 | 43 |
44 | 45 | 46 |

Software Update

47 | 48 |

Upgrade Available

49 | 50 | 51 |

Upgrading...

52 | 53 |
54 | 55 | 56 | More Information 57 | 58 | 59 | 60 | 61 | Hub Devices ({{viewModel.deviceCount}}) 62 | 63 | 64 | 67 | 68 | 69 | 70 |

{{device.name}}

71 | 72 |

73 | {{device.type | TitleCase: true }} 74 |
75 | Low Battery ({{device.batteryPercentage}}%) 76 | Missing (Signal 0%) 77 |

78 | 79 | 80 |
81 | 82 |
83 | 84 |
85 | 86 |
-------------------------------------------------------------------------------- /src/Directives/PositionableElementDirective.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Directives { 2 | 3 | export interface PositionableElementScope extends ng.IScope { 4 | left: number; 5 | top: number; 6 | onReposition: () => void; 7 | } 8 | 9 | /** 10 | * A directive for adding user-positionable capabilities (eg drag and drop) to an element. 11 | * 12 | * This was modified from the Angular directive sample: 13 | * https://docs.angularjs.org/guide/directive 14 | */ 15 | export class PositionableElementDirective implements ng.IDirective { 16 | 17 | //#region Injection 18 | 19 | public static ID = "positionableElement"; 20 | 21 | public static get $inject(): string[] { 22 | return ["$document"]; 23 | } 24 | 25 | constructor( 26 | private $document: ng.IDocumentService) { 27 | 28 | // Ensure that the link function is bound to this instance so we can 29 | // access instance variables like $parse. AngularJs normally executes 30 | // the link function in the context of the global scope. 31 | this.link = _.bind(this.link, this); 32 | } 33 | 34 | //#endregion 35 | 36 | public restrict = "A"; 37 | 38 | public scope = { 39 | left: "=left", 40 | top: "=top", 41 | onReposition: "&" 42 | }; 43 | 44 | public link(scope: PositionableElementScope, element: ng.IAugmentedJQuery, attributes: ng.IAttributes, controller: any, transclude: ng.ITranscludeFunction): void { 45 | 46 | var startX = scope.left, 47 | startY = scope.top, 48 | x = scope.left, 49 | y = scope.top; 50 | 51 | // Watch the properties so we can update the element if they change. 52 | 53 | scope.$watch(() => { return scope.left; }, () => { 54 | startX = scope.left; 55 | x = scope.left; 56 | 57 | element.css({ 58 | left: scope.left + "px" 59 | }); 60 | }); 61 | 62 | scope.$watch(() => { return scope.top; }, () => { 63 | startY = scope.top; 64 | y = scope.top; 65 | 66 | element.css({ 67 | top: scope.top + "px" 68 | }); 69 | }); 70 | 71 | // Setup the element"s initial styling. 72 | element.css({ 73 | position: "absolute", 74 | cursor: "move", 75 | left: scope.left + "px", 76 | top: scope.top + "px" 77 | }); 78 | 79 | // When the mouse moves, update the element position. 80 | var mousemove = (event) => { 81 | y = event.pageY - startY; 82 | x = event.pageX - startX; 83 | 84 | element.css({ 85 | top: y + "px", 86 | left: x + "px" 87 | }); 88 | }; 89 | 90 | // Once the mouse button is released, unwire the events and update the scope values. 91 | var mouseup = () => { 92 | var positionChanged = false; 93 | 94 | this.$document.off("mousemove", mousemove); 95 | this.$document.off("mouseup", mouseup); 96 | 97 | // Determine if this element moved. 98 | if (scope.top !== y || scope.left !== x) { 99 | positionChanged = true; 100 | } 101 | 102 | scope.top = y; 103 | scope.left = x; 104 | 105 | // If the element moved, trigger the on-reposition event. 106 | if (positionChanged) { 107 | scope.onReposition(); 108 | } 109 | 110 | scope.$apply(); 111 | }; 112 | 113 | // When the mouse is pressed we wire up the move events. 114 | element.on("mousedown", (event) => { 115 | // Prevent default dragging of selected content 116 | event.preventDefault(); 117 | startX = event.pageX - x; 118 | startY = event.pageY - y; 119 | 120 | this.$document.on("mousemove", mousemove); 121 | this.$document.on("mouseup", mouseup); 122 | }); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Controllers/Devices/DevicesListController.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Controllers { 2 | 3 | export class DevicesListController extends BaseController { 4 | 5 | //#region Injection 6 | 7 | public static ID = "DevicesListController"; 8 | 9 | public static get $inject(): string[] { 10 | return [ 11 | "$scope", 12 | Services.Utilities.ID, 13 | Services.UiHelper.ID, 14 | Services.HubDataSource.ID, 15 | Services.AlertMeApi.ID 16 | ]; 17 | } 18 | 19 | constructor( 20 | private $scope: ng.IScope, 21 | private Utilities: Services.Utilities, 22 | private UiHelper: Services.UiHelper, 23 | private HubDataSource: Services.HubDataSource, 24 | private AlertMeApi: Services.AlertMeApi) { 25 | super($scope, ViewModels.DevicesListViewModel); 26 | } 27 | 28 | //#endregion 29 | 30 | //#region Controller Events 31 | 32 | protected view_beforeEnter(event?: ng.IAngularEvent, eventArgs?: Ionic.IViewEventArguments): void { 33 | super.view_beforeEnter(event, eventArgs); 34 | 35 | if (this.HubDataSource.homeStatus == null 36 | || this.HubDataSource.homeStatusLastUpdated == null 37 | || moment().diff(this.HubDataSource.homeStatusLastUpdated, "minutes") > 10) { 38 | this.refresh(); 39 | } 40 | else { 41 | this.populateViewModel(this.HubDataSource.homeStatus, this.HubDataSource.homeStatusLastUpdated); 42 | } 43 | } 44 | 45 | //#endregion 46 | 47 | //#region Private Methods 48 | 49 | private populateViewModel(homeStatus: AlertMeApiTypes.HomeStatusGetResult, homeStatusLastUpdated: moment.Moment): void { 50 | 51 | this.viewModel.homeStatus = homeStatus; 52 | this.viewModel.lastUpdated = homeStatusLastUpdated.toDate(); 53 | this.viewModel.deviceCount = _.toArray(homeStatus.devices).length; 54 | } 55 | 56 | private refresh(): void { 57 | this.viewModel.isRefreshing = true; 58 | 59 | this.HubDataSource.refreshHomeStatus().then((result: AlertMeApiTypes.HomeStatusGetResult) => { 60 | this.viewModel.isRefreshing = false; 61 | this.scope.$broadcast(Constants.Events.SCROLL_REFRESH_COMPLETE); 62 | 63 | this.populateViewModel(result, this.HubDataSource.homeStatusLastUpdated); 64 | 65 | }, () => { 66 | this.viewModel.isRefreshing = false; 67 | this.scope.$broadcast(Constants.Events.SCROLL_REFRESH_COMPLETE); 68 | }); 69 | } 70 | 71 | //#endregion 72 | 73 | //#region Controller Helpers 74 | 75 | protected GetDeviceTypeIcon(deviceType: string): string { 76 | 77 | if (!deviceType) { 78 | return "ion-help"; 79 | } 80 | 81 | switch (deviceType) { 82 | case "ContactSensor": 83 | return "ion-checkmark-circled"; 84 | case "Camera": 85 | return "ion-ios-videocam"; 86 | case "EverspringSE812": 87 | return "ion-ios-bell"; 88 | case "EverspringST812": 89 | return "ion-waterdrop"; 90 | case "FirstAlertSmokeCOAlarm": 91 | return "ion-flame"; 92 | case "JascoBinarySwitch": 93 | return "ion-toggle-filled"; 94 | case "Keypad": 95 | return "ion-ios-keypad"; 96 | case "KwiksetDeadbolt": 97 | return "ion-locked"; 98 | case "Repeater": 99 | return "ion-wifi"; 100 | case "RtcoaCT101": 101 | return "ion-thermometer"; 102 | default: 103 | return "ion-cube"; 104 | } 105 | } 106 | 107 | //#endregion 108 | 109 | //#region Controller Events 110 | 111 | protected refresh_click(): void { 112 | this.refresh(); 113 | } 114 | 115 | protected refresher_refresh(): void { 116 | this.refresh(); 117 | } 118 | 119 | //#endregion 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Resources/misc/IrisApi-v5/docs/calls/channels.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | AlertMe.com API v5 Documentation :: calls/channels :: DRAFT 10 | 11 | 12 | 13 |

14 | API v5 Documentation draft 15 |

16 | 97 |
98 |

Please check the URL or use menu on right as the documentation you've requested does not exist

99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /Resources/misc/IrisApi-v5/docs/css.css: -------------------------------------------------------------------------------- 1 | * {border:0; padding:0; margin:0; outline:0;} 2 | html {font-size:12px; font-family:verdana,arial,sans-serif;} 3 | body {background-color:black; overflow-y:scroll;} 4 | h1 {color:#FF7F1C; background-color:black; background-image:url('images/header.png'); background-repeat:no-repeat; height:45px; padding:17px 0 0 100px; font-weight:normal; font-size:33px;} 5 | h2 {color:#888; font-size:21px; font-weight:normal;} 6 | h3 {color:#555; font-size:17px; font-weight:normal; padding-bottom:7px;} 7 | h4 {color:#FF7F1C; font-size:15px; margin-top:15px; text-transform:uppercase;} 8 | p {margin:8px 0;} 9 | li > h2 {font-size:18px;} 10 | 11 | h1 span {color:white; font-size:0.6em; font-weight:normal;} 12 | 13 | /* Put an ellipsis before each URI suffix in pages which contain a
of class "root" (where the root of the URI is specified to the reader) */ 14 | .root ~ .section h2 span.call:before {content:"..."; color:#FF7F1C; font-size:10px; letter-spacing:-1px;} 15 | 16 | div.section {margin:30px; padding:12px; min-width:350px; overflow:auto; border:2px solid #FF7F1C; background-color:white; -moz-border-radius:4px;} 17 | div.section code {color:#666; font-family:monospace;} 18 | div.section > h3 {color:#666; margin-top:12px;} 19 | div.doc {width:auto; margin-right:237px;} 20 | div.menu {position:absolute; right:0; top:0; width:225px; background-color:white; padding-top:73px; padding-bottom:200px; -moz-border-radius-bottomleft:50px; border-left:2px solid #FF7F1C; border-bottom:2px solid #FF7F1C;} 21 | div.menu > h4 {margin-left:40px;} 22 | li {margin-left:40px;} 23 | li li {margin-left:12px;} 24 | div.menu li {list-style-type:none;} 25 | 26 | a:link, a:visited {color:blue; text-decoration:none; padding:0 2px;} 27 | a:hover:link {background-color:#FF7F1C; outline:2px solid #FF7F1C; -moz-outline-radius:10px; -moz-border-radius:8px;} 28 | div.menu a {color:black;} 29 | div.menu h4 a {color:inherit;} 30 | div.menu h4 a:hover {color:black;} 31 | 32 | div.menu > ul > li { margin-bottom:7px; } 33 | 34 | table {background-color:white; padding:2px 10px; margin:5px 0 9px; border-top:1px dashed #FF7F1C; border-bottom:1px dashed #FF7F1C;} 35 | table td {padding:2px 10px 2px 0px;} 36 | div.sample, div.args, div.errors {background-color:white; padding:10px; margin:10px 10px 10px 0; border:1px dashed #FF7F1C; -moz-border-radius:6px; display:table; vertical-align:top;} 37 | p.json, p.xml {white-space:pre; padding-left:10px; font-family:monospace; font-size:14px;} 38 | p.json i {white-space:normal;font-family:sans-serif;font-size:10px;font-color:#777;} 39 | div.args dl dl dt {font-style:italic;} 40 | div.args > dl > dt {font-size:13px;} 41 | div.args dt, div.args dd {min-height:19px; padding-bottom:3px;} 42 | div.args dl dl dt, div.args dl dl dd {min-height:14px;} 43 | div.args dd {line-height:14px;} 44 | 45 | dl {margin:10px;} 46 | dt {font-weight:bold; float:left;} 47 | dd {margin-left:20em;padding-left:1em;} 48 | dl.wide dd {margin-left:35em;} 49 | dl dl dt {width:13em;} 50 | dl dl dd {margin-left:13em;} 51 | 52 | div.errors dd {margin-left:38em;} 53 | div.errors dt {width: 38em;} 54 | 55 | span.call {color:black; font-family:monospace;} 56 | span.call em {color:#888; font-style:normal;} 57 | h2 span.warning {color: red; padding:0 0 0 30px; background-image:url('images/warning.png'); background-repeat:no-repeat;} 58 | div.args em {color:#888; font-style:normal;} 59 | .header, .var {font-family:monospace;} 60 | p.header {font-size:16px; padding:10px;} 61 | span.var {font-weight:bold;} 62 | span.uri {padding:0 2px; font-family:monospace; font-weight:bold;} 63 | span.uri span.var {color:#666;} 64 | span.val {color:#666; font-weight:bold; font-style:italic;} 65 | span.val {font-size:0.9em;} 66 | span.uri span.val {font-size:inherit;} 67 | 68 | th#code {width:39px;} 69 | th#status {width:160px;} 70 | th#treat {width:80px;} 71 | th#desc {} 72 | th {text-align:left;} 73 | th, td {padding:2px 0;} 74 | 75 | .note {padding:3px 0 10px 30px; background-image:url('images/info.png'); background-repeat:no-repeat; margin: 4px 0;} 76 | p.warning {padding:3px 0 10px 30px; background-image:url('images/warning.png'); background-repeat:no-repeat; margin: 4px 0;} 77 | 78 | p.docError {font-size:30px; color:white; border:4px solid red; padding:20px; margin:20px;} 79 | 80 | #allCalls li { display:block; margin-bottom:4px;} 81 | #allCalls a { padding:0 12px 3px; -moz-border-radius:30px;} 82 | #allCalls a:hover { outline:4px; -moz-outline-radius:30px;} 83 | #allCalls > h2 {margin-bottom:1em;} 84 | #allCalls li a { color:#FF7F1C;} 85 | #allCalls li a:hover { color:black;} 86 | #allCalls span.method {display:inline-block; width:90px;} 87 | 88 | ul + p {margin-top:1em;} 89 | a > span.uri {color:black;} 90 | 91 | ul.menu ul { 92 | border-left:1px solid grey; 93 | margin-left: 12px; 94 | } 95 | ul.menu li li:before {content:'-';color:grey} 96 | ul.menu li li{margin-left:0} 97 | 98 | -------------------------------------------------------------------------------- /www/templates/Settings/Camera-Edit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 |
8 | General Information 9 |
10 | 11 | 17 | 18 |
19 | Stream Type 20 | 22 |
23 | 24 | 35 | 36 | 47 | 48 |
49 | Image / Video URL 50 | 52 |
53 | 54 | 64 | 65 |
66 | Credentials 67 | 69 |
70 | 71 | 81 | 82 | 88 | 89 | 95 | 96 |
97 | 98 |
99 | 104 | 105 | 110 |
111 | 112 |
113 | 114 |
-------------------------------------------------------------------------------- /Resources/misc/IrisApi-v5/docs/calls/questions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | AlertMe.com API v5 Documentation :: calls/questions :: DRAFT 10 | 11 | 12 | 13 |

14 | API v5 Documentation draft 15 |

16 | 97 |
98 |
99 |

GET /users/all/questions

100 |

List all possible memorable questions.

101 |
102 |

Response:

103 |

104 | [ 105 | { 106 | "id": 1, 107 | "key": "MEMORABLE_QUESTION::GRANDMOTHERS_MAIDEN_NAME" 108 | } 109 | ] 110 |

111 |
112 |
113 |
114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /src/Controllers/Devices/DevicesHubInfoController.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Controllers { 2 | 3 | export class DevicesHubInfoController extends BaseController { 4 | 5 | //#region Injection 6 | 7 | public static ID = "DevicesHubInfoController"; 8 | 9 | public static get $inject(): string[] { 10 | return [ 11 | "$scope", 12 | Services.HubDataSource.ID, 13 | Services.Utilities.ID, 14 | Services.Preferences.ID 15 | ]; 16 | } 17 | 18 | constructor( 19 | $scope: ng.IScope, 20 | private HubDataSource: Services.HubDataSource, 21 | private Utilities: Services.Utilities, 22 | private Preferences: Services.Preferences) { 23 | super($scope, ViewModels.DevicesHubInfoViewModel); 24 | } 25 | 26 | //#endregion 27 | 28 | //#region BaseController Overrides 29 | 30 | protected view_beforeEnter(event?: ng.IAngularEvent, eventArgs?: Ionic.IViewEventArguments): void { 31 | super.view_beforeEnter(event, eventArgs); 32 | 33 | if (this.HubDataSource.homeStatus == null 34 | || this.HubDataSource.homeStatusLastUpdated == null 35 | || moment().diff(this.HubDataSource.homeStatusLastUpdated, "minutes") > 10) { 36 | this.refresh(); 37 | } 38 | else { 39 | this.populateViewModel(this.HubDataSource.homeStatus, this.HubDataSource.homeStatusLastUpdated); 40 | } 41 | } 42 | 43 | //#endregion 44 | 45 | //#region Private Methods 46 | 47 | private populateViewModel(homeStatus: AlertMeApiTypes.HomeStatusGetResult, homeStatusLastUpdated: moment.Moment): void { 48 | this.viewModel.lastUpdated = homeStatusLastUpdated.toDate(); 49 | this.viewModel.hub = homeStatus.hub; 50 | } 51 | 52 | private refresh(): void { 53 | this.viewModel.isRefreshing = true; 54 | 55 | this.HubDataSource.refreshHomeStatus().then((result: AlertMeApiTypes.HomeStatusGetResult) => { 56 | this.viewModel.isRefreshing = false; 57 | this.scope.$broadcast(Constants.Events.SCROLL_REFRESH_COMPLETE); 58 | 59 | this.populateViewModel(result, this.HubDataSource.homeStatusLastUpdated); 60 | 61 | }, () => { 62 | this.viewModel.isRefreshing = false; 63 | this.scope.$broadcast(Constants.Events.SCROLL_REFRESH_COMPLETE); 64 | }); 65 | } 66 | 67 | //#endregion 68 | 69 | //#region Controller Helpers 70 | 71 | protected getVersionDisplayText(versionString: string): string { 72 | return versionString === "VERSION_UNAVAILABLE" ? "(Version Unavailable)" : versionString; 73 | } 74 | 75 | protected getBatteryIconClassName(batteryPercentage: number): string { 76 | var classNames = ""; 77 | 78 | if (!batteryPercentage) { 79 | return ""; 80 | } 81 | 82 | if (batteryPercentage >= 75) { 83 | classNames = "ion-battery-full"; 84 | } 85 | else if (batteryPercentage >= 50 && batteryPercentage < 75) { 86 | classNames = "ion-battery-half"; 87 | } 88 | else if (batteryPercentage >= 25 && batteryPercentage < 50) { 89 | classNames = "ion-battery-low"; 90 | } 91 | else if (batteryPercentage < 25) { 92 | classNames = "ion-battery-empty"; 93 | } 94 | 95 | // If it has less than 10 percent battery life remaining, then make it red. 96 | if (batteryPercentage <= 15) { 97 | classNames += " assertive"; 98 | } 99 | 100 | return classNames; 101 | } 102 | 103 | protected getFormattedDate(timestamp: number): string { 104 | 105 | if (!timestamp) { 106 | return "N/A"; 107 | } 108 | 109 | return moment.unix(timestamp).format("MMM Do YYYY, h:mm a"); 110 | } 111 | 112 | protected getFormattedUpTime(upTimeTotalSeconds: number): string { 113 | var days: number; 114 | 115 | if (!upTimeTotalSeconds) { 116 | return "N/A"; 117 | } 118 | 119 | days = upTimeTotalSeconds / 60 / 60 / 24; 120 | 121 | return days.toFixed(1) + " days"; 122 | } 123 | 124 | //#endregion 125 | 126 | //#region Controller Events 127 | 128 | protected refresh_click(): void { 129 | this.refresh(); 130 | } 131 | 132 | protected refresher_refresh(): void { 133 | this.refresh(); 134 | } 135 | 136 | //#endregion 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/Controllers/Devices/DevicesInfoController.ts: -------------------------------------------------------------------------------- 1 | module JustinCredible.SmartHomeMobile.Controllers { 2 | 3 | export interface IDevicesInfoControllerStateParams { 4 | deviceId: string; 5 | } 6 | 7 | export class DevicesInfoController extends BaseController { 8 | 9 | //#region Injection 10 | 11 | public static ID = "DevicesInfoController"; 12 | 13 | public static get $inject(): string[] { 14 | return [ 15 | "$scope", 16 | "$stateParams", 17 | Services.HubDataSource.ID, 18 | Services.Utilities.ID, 19 | Services.Preferences.ID 20 | ]; 21 | } 22 | 23 | constructor( 24 | $scope: ng.IScope, 25 | private $stateParams: IDevicesInfoControllerStateParams, 26 | private HubDataSource: Services.HubDataSource, 27 | private Utilities: Services.Utilities, 28 | private Preferences: Services.Preferences) { 29 | super($scope, ViewModels.DevicesInfoViewModel); 30 | } 31 | 32 | //#endregion 33 | 34 | //#region BaseController Overrides 35 | 36 | protected view_beforeEnter(event?: ng.IAngularEvent, eventArgs?: Ionic.IViewEventArguments): void { 37 | super.view_beforeEnter(event, eventArgs); 38 | 39 | if (this.HubDataSource.homeStatus == null 40 | || this.HubDataSource.homeStatusLastUpdated == null 41 | || moment().diff(this.HubDataSource.homeStatusLastUpdated, "minutes") > 10) { 42 | this.refresh(); 43 | } 44 | else { 45 | this.populateViewModel(this.HubDataSource.homeStatus, this.HubDataSource.homeStatusLastUpdated); 46 | } 47 | } 48 | 49 | //#endregion 50 | 51 | //#region Private Methods 52 | 53 | private populateViewModel(homeStatus: AlertMeApiTypes.HomeStatusGetResult, homeStatusLastUpdated: moment.Moment): void { 54 | this.viewModel.lastUpdated = homeStatusLastUpdated.toDate(); 55 | this.viewModel.device = _.find(homeStatus.devices, { "id": this.$stateParams.deviceId }); 56 | } 57 | 58 | private refresh(): void { 59 | this.viewModel.isRefreshing = true; 60 | 61 | this.HubDataSource.refreshHomeStatus().then((result: AlertMeApiTypes.HomeStatusGetResult) => { 62 | this.viewModel.isRefreshing = false; 63 | this.scope.$broadcast(Constants.Events.SCROLL_REFRESH_COMPLETE); 64 | 65 | this.populateViewModel(result, this.HubDataSource.homeStatusLastUpdated); 66 | 67 | }, () => { 68 | this.viewModel.isRefreshing = false; 69 | this.scope.$broadcast(Constants.Events.SCROLL_REFRESH_COMPLETE); 70 | }); 71 | } 72 | 73 | //#endregion 74 | 75 | //#region Controller Helpers 76 | 77 | protected getVersionDisplayText(versionString: string): string { 78 | return versionString === "VERSION_UNAVAILABLE" ? "(Version Unavailable)" : versionString; 79 | } 80 | 81 | protected getBatteryIconClassName(batteryPercentage: number): string { 82 | var classNames = ""; 83 | 84 | if (!batteryPercentage) { 85 | return ""; 86 | } 87 | 88 | if (batteryPercentage >= 75) { 89 | classNames = "ion-battery-full"; 90 | } 91 | else if (batteryPercentage >= 50 && batteryPercentage < 75) { 92 | classNames = "ion-battery-half"; 93 | } 94 | else if (batteryPercentage >= 25 && batteryPercentage < 50) { 95 | classNames = "ion-battery-low"; 96 | } 97 | else if (batteryPercentage < 25) { 98 | classNames = "ion-battery-empty"; 99 | } 100 | 101 | // If it has less than 10 percent battery life remaining, then make it red. 102 | if (batteryPercentage <= 15) { 103 | classNames += " assertive"; 104 | } 105 | 106 | return classNames; 107 | } 108 | 109 | protected getProtocolDisplayList(protocols: string[]): string { 110 | return protocols == null ? "" : protocols.join(", "); 111 | } 112 | 113 | protected getMissingProtocolDisplayList(missingProtocols: { [id: string]: string }): string { 114 | return missingProtocols == null ? "" : _.values(missingProtocols).join(", "); 115 | } 116 | 117 | //#endregion 118 | 119 | //#region Controller Events 120 | 121 | protected refresh_click(): void { 122 | this.refresh(); 123 | } 124 | 125 | protected refresher_refresh(): void { 126 | this.refresh(); 127 | } 128 | 129 | //#endregion 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Smart Home Mobile 4 | Used to control home automation devices. 5 | Justin Unterreiner 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | --------------------------------------------------------------------------------