├── .gitignore ├── README.md ├── jshintrc ├── package.json ├── resources ├── data │ ├── dict_danish │ ├── dict_english │ ├── dict_french │ ├── dict_german │ ├── dict_portuguese │ └── dict_spanish ├── fonts │ └── LECO_1976-Regular.otf └── images │ ├── ALARM_CLOCK.png │ ├── CAR_RENTAL.png │ ├── CLOUDY_DAY.png │ ├── HEAVY_RAIN.png │ ├── HEAVY_SNOW.png │ ├── LIGHT_RAIN.png │ ├── LIGHT_SNOW.png │ ├── PARTLY_CLOUDY.png │ ├── SCHEDULED_FLIGHT.png │ ├── SHOE.png │ ├── TIMELINE_SUN.png │ ├── bubble_rect.png │ ├── bubble_rect_chalk.png │ ├── dictation_icon.png │ ├── dog.png │ ├── down_indicator~rect.png │ ├── down_indicator~round.png │ ├── emery outline.pdn │ ├── emery outline.png │ ├── info_icon.png │ ├── question_mark_icon.png │ ├── snowy emery concept.pdn │ ├── snowy emery concept.png │ ├── snowy_bg_basalt~basalt.png │ ├── snowy_bg_basalt~bw.png │ ├── snowy_bg_basalt~chalk.png │ ├── snowy_notification.png │ └── surface_hub_mock.png ├── src ├── c │ └── c │ │ ├── actionmenu.c │ │ ├── actionmenu.h │ │ ├── dash.c │ │ ├── dash.h │ │ ├── dictation.c │ │ ├── dictation.h │ │ ├── health.c │ │ ├── health.h │ │ ├── kiezelpay.c │ │ ├── lang.c │ │ ├── lang.h │ │ ├── main.c │ │ ├── main.h │ │ ├── messaging.c │ │ ├── messaging.h │ │ ├── settings.c │ │ ├── settings.h │ │ ├── ui.c │ │ ├── ui.h │ │ ├── widget.c │ │ └── widget.h └── pkjs │ ├── app-callbacks.js │ ├── app-config-da.js │ ├── app-config-de.js │ ├── app-config-es.js │ ├── app-config-fr.js │ ├── app-config-pt.js │ ├── app-config.js │ ├── app-conversion.js │ ├── app-custom-clay.js │ ├── app-ifttt.js │ ├── app-intel-da.js │ ├── app-intel-de.js │ ├── app-intel-es.js │ ├── app-intel-fr.js │ ├── app-intel-pt.js │ ├── app-intel.js │ ├── app-localize.js │ ├── app-messaging.js │ ├── app-parser.js │ ├── app-settings.js │ ├── app-teams.js │ ├── app-timeline.js │ └── app.js ├── worker_src └── c │ └── w │ └── worker.c └── wscript /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore build generated files 2 | build/ 3 | dist/ 4 | dist.zip 5 | 6 | # Ignore Screenshots 7 | pebble-screenshot_* 8 | pebble_screenshot_* 9 | 10 | # Ignore waf lock file 11 | .lock-waf* 12 | 13 | # Ignore installed node modules 14 | node_modules/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Snowy 4.0 2 | -------------------------------------------------------------------------------- /jshintrc: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Example jshint configuration file for Pebble development. 4 | * 5 | * Check out the full documentation at http://www.jshint.com/docs/options/ 6 | */ 7 | { 8 | // Declares the existence of the globals available in PebbleKit JS. 9 | "globals": {"Float32Array": true, "Uint16Array": true, "exports": true, "Uint8ClampedArray": true, "Float64Array": true, "require": true, "setTimeout": true, "module": true, "Int8Array": true, "WebSocket": true, "localStorage": true, "navigator": true, "Uint32Array": true, "Int32Array": true, "setInterval": true, "Uint8Array": true, "XMLHttpRequest": true, "Int16Array": true, "Pebble": true, "console": true}, 10 | 11 | // Do not mess with standard JavaScript objects (Array, Date, etc) 12 | "freeze": true, 13 | 14 | // Do not use eval! Keep this warning turned on (ie: false) 15 | "evil": false, 16 | 17 | /* 18 | * The options below are more style/developer dependent. 19 | * Customize to your liking. 20 | */ 21 | 22 | // All variables should be in camelcase - too specific for CloudPebble builds to fail 23 | // "camelcase": true, 24 | 25 | // Do not allow blocks without { } - too specific for CloudPebble builds to fail. 26 | // "curly": true, 27 | 28 | // Prohibits the use of immediate function invocations without wrapping them in parentheses 29 | "immed": true, 30 | 31 | // Don't enforce indentation, because it's not worth failing builds over 32 | // (especially given our somewhat lacklustre support for it) 33 | "indent": false, 34 | 35 | // Do not use a variable before it's defined 36 | "latedef": "nofunc", 37 | 38 | // Spot undefined variables 39 | "undef": "true", 40 | 41 | // Spot unused variables 42 | "unused": "true" 43 | } 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Mathew Reiss", 3 | "dependencies": { 4 | "kiezelpay-core": "^2.0.3", 5 | "pebble-clay": "^1.0.2", 6 | "pebble-dash-api": "^1.0.1", 7 | "pebble-events": "^1.0.2", 8 | "pebble-localize": "^0.0.6" 9 | }, 10 | "keywords": [], 11 | "name": "snowy", 12 | "pebble": { 13 | "capabilities": [ 14 | "location", 15 | "configurable", 16 | "health" 17 | ], 18 | "displayName": "Snowy", 19 | "enableMultiJS": true, 20 | "messageKeys": [ 21 | "Lang", 22 | "Transcript", 23 | "Title", 24 | "Body", 25 | "Error", 26 | "Fullscreen", 27 | "HandsFree", 28 | "NightMode", 29 | "Custom[4]", 30 | "TemperatureUnit", 31 | "FontSize", 32 | "QuickExit", 33 | "FlickToDismiss", 34 | "ConfirmDictation", 35 | "Widget", 36 | "StepGoal", 37 | "StepGoalRequest", 38 | "InactivityMonitor", 39 | "StepsRequest", 40 | "DistanceRequest", 41 | "LocationRequest", 42 | "PALChargeRequest", 43 | "Timer", 44 | "Alarm", 45 | "HeartRateRequest", 46 | "Directions", 47 | "SleepRequest", 48 | "HealthNotified", 49 | "HealthStepGoalReached", 50 | "HomeAddress", 51 | "WorkAddress", 52 | "InactivityStart", 53 | "InactivityEnd", 54 | "WidgetExtra", 55 | "MasterKeyPIN", 56 | "IftttKey", 57 | "WuKey", 58 | "WolframKey", 59 | "TravelKey", 60 | "HabitsKey", 61 | "Settings", 62 | "Message", 63 | "Request", 64 | "DashBatteryRequest", 65 | "DashSetWifiRequest", 66 | "DashSetRingerRequest", 67 | "DashSetHotspotRequest", 68 | "CustomCount", 69 | "DashSMSRequest", 70 | "DashCalendarRequest", 71 | "Reddit", 72 | "IftttPlus", 73 | "MasterKeyEmail", 74 | "MasterKeyEnabled", 75 | "DistanceUnit", 76 | "WorkerLaunchReason", 77 | "StepGoalNotified", 78 | "InactivityLastAlert", 79 | "Custom1", 80 | "Custom2", 81 | "Custom3", 82 | "Custom4" 83 | ], 84 | "projectType": "native", 85 | "resources": { 86 | "media": [ 87 | { 88 | "file": "data/dict_danish", 89 | "name": "DICT_DANISH_BETA", 90 | "targetPlatforms": null, 91 | "type": "raw" 92 | }, 93 | { 94 | "file": "images/HEAVY_RAIN.png", 95 | "name": "HEAVY_RAIN", 96 | "targetPlatforms": null, 97 | "type": "bitmap" 98 | }, 99 | { 100 | "file": "images/PARTLY_CLOUDY.png", 101 | "name": "PARTLY_CLOUDY", 102 | "targetPlatforms": null, 103 | "type": "bitmap" 104 | }, 105 | { 106 | "file": "images/HEAVY_SNOW.png", 107 | "name": "HEAVY_SNOW", 108 | "targetPlatforms": null, 109 | "type": "bitmap" 110 | }, 111 | { 112 | "file": "images/LIGHT_RAIN.png", 113 | "name": "LIGHT_RAIN", 114 | "targetPlatforms": null, 115 | "type": "bitmap" 116 | }, 117 | { 118 | "file": "images/SCHEDULED_FLIGHT.png", 119 | "name": "PLANE", 120 | "targetPlatforms": null, 121 | "type": "bitmap" 122 | }, 123 | { 124 | "file": "images/TIMELINE_SUN.png", 125 | "name": "SUNNY", 126 | "targetPlatforms": null, 127 | "type": "bitmap" 128 | }, 129 | { 130 | "file": "images/LIGHT_SNOW.png", 131 | "name": "LIGHT_SNOW", 132 | "targetPlatforms": null, 133 | "type": "bitmap" 134 | }, 135 | { 136 | "file": "images/CLOUDY_DAY.png", 137 | "name": "CLOUDY", 138 | "targetPlatforms": null, 139 | "type": "bitmap" 140 | }, 141 | { 142 | "file": "images/ALARM_CLOCK.png", 143 | "name": "CLOCK", 144 | "targetPlatforms": null, 145 | "type": "bitmap" 146 | }, 147 | { 148 | "file": "images/CAR_RENTAL.png", 149 | "name": "CAR", 150 | "targetPlatforms": null, 151 | "type": "bitmap" 152 | }, 153 | { 154 | "file": "images/SHOE.png", 155 | "name": "SHOE", 156 | "targetPlatforms": null, 157 | "type": "bitmap" 158 | }, 159 | { 160 | "file": "images/snowy_notification.png", 161 | "menuIcon": true, 162 | "name": "ICON", 163 | "targetPlatforms": null, 164 | "type": "bitmap" 165 | }, 166 | { 167 | "file": "images/snowy_bg_basalt.png", 168 | "name": "BG", 169 | "targetPlatforms": null, 170 | "type": "bitmap" 171 | }, 172 | { 173 | "file": "images/info_icon.png", 174 | "name": "INFO", 175 | "targetPlatforms": null, 176 | "type": "bitmap" 177 | }, 178 | { 179 | "file": "images/question_mark_icon.png", 180 | "name": "SAMPLE", 181 | "targetPlatforms": null, 182 | "type": "bitmap" 183 | }, 184 | { 185 | "file": "images/down_indicator.png", 186 | "name": "INDICATOR", 187 | "targetPlatforms": null, 188 | "type": "bitmap" 189 | }, 190 | { 191 | "file": "images/dictation_icon.png", 192 | "name": "MIC", 193 | "targetPlatforms": null, 194 | "type": "bitmap" 195 | }, 196 | { 197 | "file": "fonts/LECO_1976-Regular.otf", 198 | "name": "FONT_CUSTOM_LECO_14", 199 | "targetPlatforms": null, 200 | "type": "font" 201 | }, 202 | { 203 | "file": "data/dict_spanish", 204 | "name": "DICT_SPANISH", 205 | "targetPlatforms": null, 206 | "type": "raw" 207 | }, 208 | { 209 | "file": "data/dict_portuguese", 210 | "name": "DICT_PORTUGUESE_BETA", 211 | "targetPlatforms": null, 212 | "type": "raw" 213 | }, 214 | { 215 | "file": "data/dict_german", 216 | "name": "DICT_GERMAN", 217 | "targetPlatforms": null, 218 | "type": "raw" 219 | }, 220 | { 221 | "file": "data/dict_french", 222 | "name": "DICT_FRENCH", 223 | "targetPlatforms": null, 224 | "type": "raw" 225 | }, 226 | { 227 | "file": "data/dict_english", 228 | "name": "DICT_ENGLISH", 229 | "targetPlatforms": null, 230 | "type": "raw" 231 | } 232 | ] 233 | }, 234 | "sdkVersion": "3", 235 | "targetPlatforms": [ 236 | "basalt", 237 | "chalk" 238 | ], 239 | "uuid": "70c08ef8-8c80-46ac-83f7-1af4b43949cb", 240 | "watchapp": { 241 | "watchface": false 242 | } 243 | }, 244 | "version": "4.0.0" 245 | } 246 | -------------------------------------------------------------------------------- /resources/data/dict_danish: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/data/dict_danish -------------------------------------------------------------------------------- /resources/data/dict_english: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/data/dict_english -------------------------------------------------------------------------------- /resources/data/dict_french: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/data/dict_french -------------------------------------------------------------------------------- /resources/data/dict_german: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/data/dict_german -------------------------------------------------------------------------------- /resources/data/dict_portuguese: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/data/dict_portuguese -------------------------------------------------------------------------------- /resources/data/dict_spanish: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/data/dict_spanish -------------------------------------------------------------------------------- /resources/fonts/LECO_1976-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/fonts/LECO_1976-Regular.otf -------------------------------------------------------------------------------- /resources/images/ALARM_CLOCK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/ALARM_CLOCK.png -------------------------------------------------------------------------------- /resources/images/CAR_RENTAL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/CAR_RENTAL.png -------------------------------------------------------------------------------- /resources/images/CLOUDY_DAY.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/CLOUDY_DAY.png -------------------------------------------------------------------------------- /resources/images/HEAVY_RAIN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/HEAVY_RAIN.png -------------------------------------------------------------------------------- /resources/images/HEAVY_SNOW.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/HEAVY_SNOW.png -------------------------------------------------------------------------------- /resources/images/LIGHT_RAIN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/LIGHT_RAIN.png -------------------------------------------------------------------------------- /resources/images/LIGHT_SNOW.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/LIGHT_SNOW.png -------------------------------------------------------------------------------- /resources/images/PARTLY_CLOUDY.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/PARTLY_CLOUDY.png -------------------------------------------------------------------------------- /resources/images/SCHEDULED_FLIGHT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/SCHEDULED_FLIGHT.png -------------------------------------------------------------------------------- /resources/images/SHOE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/SHOE.png -------------------------------------------------------------------------------- /resources/images/TIMELINE_SUN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/TIMELINE_SUN.png -------------------------------------------------------------------------------- /resources/images/bubble_rect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/bubble_rect.png -------------------------------------------------------------------------------- /resources/images/bubble_rect_chalk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/bubble_rect_chalk.png -------------------------------------------------------------------------------- /resources/images/dictation_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/dictation_icon.png -------------------------------------------------------------------------------- /resources/images/dog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/dog.png -------------------------------------------------------------------------------- /resources/images/down_indicator~rect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/down_indicator~rect.png -------------------------------------------------------------------------------- /resources/images/down_indicator~round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/down_indicator~round.png -------------------------------------------------------------------------------- /resources/images/emery outline.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/emery outline.pdn -------------------------------------------------------------------------------- /resources/images/emery outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/emery outline.png -------------------------------------------------------------------------------- /resources/images/info_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/info_icon.png -------------------------------------------------------------------------------- /resources/images/question_mark_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/question_mark_icon.png -------------------------------------------------------------------------------- /resources/images/snowy emery concept.pdn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/snowy emery concept.pdn -------------------------------------------------------------------------------- /resources/images/snowy emery concept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/snowy emery concept.png -------------------------------------------------------------------------------- /resources/images/snowy_bg_basalt~basalt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/snowy_bg_basalt~basalt.png -------------------------------------------------------------------------------- /resources/images/snowy_bg_basalt~bw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/snowy_bg_basalt~bw.png -------------------------------------------------------------------------------- /resources/images/snowy_bg_basalt~chalk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/snowy_bg_basalt~chalk.png -------------------------------------------------------------------------------- /resources/images/snowy_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/snowy_notification.png -------------------------------------------------------------------------------- /resources/images/surface_hub_mock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-dev/snowy/2dd0d404c86c6e15883c5ab10a676d0235e47502/resources/images/surface_hub_mock.png -------------------------------------------------------------------------------- /src/c/c/actionmenu.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "actionmenu.h" 4 | #include "ui.h" 5 | #include "messaging.h" 6 | #include "lang.h" 7 | #include "dash.h" 8 | 9 | extern Window *window; 10 | 11 | extern char homeTextBuffer[32]; 12 | 13 | ActionMenu *amenu; 14 | ActionMenuLevel *alevel, *alevel2, *alevel3, *customlevel; 15 | 16 | char custom[4][64]; 17 | 18 | int custom_size = 0; 19 | 20 | typedef enum { 21 | ActionTypeFunFact, 22 | ActionTypeWeather, 23 | ActionTypeReminder, 24 | ActionTypeDefine, 25 | ActionTypeMore, 26 | ActionTypeTranslate, 27 | ActionTypeCalculate, 28 | ActionTypeEat, 29 | ActionTypeNote, 30 | ActionTypeSteps, 31 | ActionTypeConvert, 32 | ActionTypeHome, 33 | ActionTypeTip, 34 | ActionTypeNull, 35 | ActionTypeCustom1, 36 | ActionTypeCustom2, 37 | ActionTypeCustom3, 38 | ActionTypeCustom4 39 | } ActionType; 40 | 41 | ActionType ActionTypesCustom[4] = {ActionTypeCustom1, ActionTypeCustom2, ActionTypeCustom3, ActionTypeCustom4}; 42 | 43 | bool is_action_menu_open = false; 44 | 45 | void acb(ActionMenu *action_menu, const ActionMenuItem *action, void *context){ 46 | ActionType type = (ActionType)action_menu_item_get_action_data(action); 47 | 48 | if(type == ActionTypeFunFact){ 49 | send_request(_("tell me current us president")); 50 | } 51 | else if(type == ActionTypeWeather){ 52 | send_request(_("what is the weather")); 53 | } 54 | else if(type == ActionTypeReminder){ 55 | send_request(_("remind me to call Mom at 2 pm tomorrow")); 56 | } 57 | else if(type == ActionTypeDefine){ 58 | send_request(_("define artificial intelligence")); 59 | } 60 | else if(type == ActionTypeTranslate){ 61 | send_request(_("how do you say hello in french")); 62 | } 63 | else if(type == ActionTypeCalculate){ 64 | send_request(lang == ENGLISH ? "what is new today" : _("calculate 2+2")); 65 | } 66 | else if(type == ActionTypeEat){ 67 | send_request(_("where should i eat")); 68 | } 69 | else if(type == ActionTypeNote){ 70 | send_request(_("take a note Pebble rocks")); 71 | } 72 | else if(type == ActionTypeSteps){ 73 | send_request(_("how many steps have i walked today")); 74 | } 75 | else if(type == ActionTypeConvert){ 76 | send_request(_("convert 5 kilometers to miles")); 77 | } 78 | else if(type == ActionTypeHome){ 79 | send_request(_("how do i get home")); 80 | } 81 | else if(type == ActionTypeTip){ 82 | send_request(_("what is 20 % of $17.95")); 83 | } 84 | else if(type == ActionTypeCustom1){ 85 | APP_LOG(APP_LOG_LEVEL_DEBUG, "Custom 0: %s", custom[0]); 86 | send_request(custom[0]); 87 | } 88 | else if(type == ActionTypeCustom2){ 89 | send_request(custom[1]); 90 | } 91 | else if(type == ActionTypeCustom3){ 92 | send_request(custom[2]); 93 | } 94 | else if(type == ActionTypeCustom4){ 95 | send_request(custom[3]); 96 | } 97 | 98 | if(type != ActionTypeNull){ 99 | error = false; 100 | update_text(_("Fetching data..."), ""); 101 | 102 | window_stack_push(window, true); 103 | } 104 | } 105 | 106 | void show_action_menu(){ 107 | ActionMenuConfig aconfig = (ActionMenuConfig){ 108 | .root_level = custom_size > 0 ? customlevel : alevel, 109 | .colors = { 110 | .background = GColorTiffanyBlue, 111 | .foreground = GColorBlack 112 | }, 113 | .align = ActionMenuAlignTop 114 | }; 115 | 116 | APP_LOG(APP_LOG_LEVEL_DEBUG, "A CONFIG: %d", (int)heap_bytes_free()); 117 | 118 | amenu = action_menu_open(&aconfig); 119 | 120 | APP_LOG(APP_LOG_LEVEL_DEBUG, "A MENU: %d", (int)heap_bytes_free()); 121 | 122 | is_action_menu_open = true; 123 | } 124 | 125 | bool hide_action_menu(){ 126 | if(is_action_menu_open){ 127 | action_menu_close(amenu, false); 128 | is_action_menu_open = false; 129 | return true; 130 | } 131 | return false; 132 | } 133 | 134 | void init_action_menu(){ 135 | custom_size = persist_exists(MESSAGE_KEY_CustomCount) ? persist_read_int(MESSAGE_KEY_CustomCount) : 0; 136 | 137 | if(custom_size > 0) customlevel = action_menu_level_create(custom_size+1); 138 | 139 | for(int i = 0; i < custom_size; i++){ 140 | persist_read_string(MESSAGE_KEY_Custom + i, custom[i-1], sizeof(custom[i-1])); 141 | action_menu_level_add_action(customlevel, custom[i-1], acb, (void *)ActionTypesCustom[i-1]); 142 | } 143 | 144 | alevel = action_menu_level_create(6); 145 | 146 | if(custom_size > 0) action_menu_level_add_child(customlevel, alevel, _("More Commands")); 147 | 148 | action_menu_level_add_action(alevel, _("Tell me: Current US President"), acb, (void *)ActionTypeFunFact); 149 | action_menu_level_add_action(alevel, _("What is the weather?"), acb, (void *)ActionTypeWeather); 150 | action_menu_level_add_action(alevel, _("Remind me to call Mom tomorrow."), acb, (void *)ActionTypeReminder); 151 | action_menu_level_add_action(alevel, lang == GERMAN ? "Definiere Intelligenz" : _("Define artificial intelligence."), acb, (void *)ActionTypeDefine); 152 | 153 | alevel2 = action_menu_level_create(6); 154 | 155 | action_menu_level_add_child(alevel, alevel2, _("More Commands")); 156 | 157 | if(custom_size > 0) action_menu_level_add_action(alevel, _("Back to Home screen"), acb, (void *)ActionTypeNull); 158 | 159 | action_menu_level_add_action(alevel2, _("How do you say 'Hello' in French?"), acb, (void *)ActionTypeTranslate); 160 | action_menu_level_add_action(alevel2, lang == ENGLISH ? "What's new today?" : _("What is 2 + 2?"), acb, (void *)ActionTypeCalculate); 161 | action_menu_level_add_action(alevel2, _("Where should I eat lunch?"), acb, (void *)ActionTypeEat); 162 | action_menu_level_add_action(alevel2, _("Take a note: Pebble rocks!"), acb, (void *)ActionTypeNote); 163 | 164 | alevel3 = action_menu_level_create(5); 165 | 166 | action_menu_level_add_child(alevel2, alevel3, _("More Commands")); 167 | 168 | action_menu_level_add_action(alevel2, _("Back to Home screen"), acb, (void *)ActionTypeNull); 169 | 170 | action_menu_level_add_action(alevel3, _("How many steps have I walked?"), acb, (void *)ActionTypeSteps); 171 | action_menu_level_add_action(alevel3, "5 KM -> Mi", acb, (void *)ActionTypeConvert); 172 | action_menu_level_add_action(alevel3, _("How do I get home?"), acb, (void *)ActionTypeHome); 173 | action_menu_level_add_action(alevel3, _("What is 20% of $17.95?"), acb, (void *)ActionTypeTip); 174 | 175 | action_menu_level_add_action(alevel3, _("Back to Home screen"), acb, (void *)ActionTypeNull); 176 | } 177 | 178 | void deinit_action_menu(){ 179 | action_menu_hierarchy_destroy(custom_size > 0 ? customlevel : alevel, NULL, NULL); 180 | } -------------------------------------------------------------------------------- /src/c/c/actionmenu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void show_action_menu(); 4 | 5 | bool hide_action_menu(); 6 | 7 | void init_action_menu(); 8 | void deinit_action_menu(); -------------------------------------------------------------------------------- /src/c/c/dash.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "dash.h" 4 | #include "ui.h" 5 | 6 | char dash_title[32], dash_body[64]; 7 | 8 | static void dash_get_data_callback(DataType type, DataValue result){ 9 | strncpy(dash_title, "Dash API", sizeof(dash_title)); 10 | 11 | switch(type){ 12 | case DataTypeBatteryPercent: 13 | snprintf(dash_body, sizeof(dash_body), "Phone Battery is at %d%%.", result.integer_value); 14 | break; 15 | 16 | case DataTypeUnreadSMSCount: 17 | snprintf(dash_body, sizeof(dash_body), "You have %d unread messages.", result.integer_value); 18 | break; 19 | 20 | case DataTypeNextCalendarEventOneLine: 21 | snprintf(dash_body, sizeof(dash_body), "Your next Calendar Event is: %s", result.string_value); 22 | break; 23 | 24 | case DataTypeNextCalendarEventTwoLine: APP_LOG(APP_LOG_LEVEL_INFO, "Next Calendar Event is %s", result.string_value); break; 25 | case DataTypeWifiNetworkName: APP_LOG(APP_LOG_LEVEL_INFO, "Wifi Network: %s", result.string_value); break; 26 | case DataTypeStoragePercentUsed: APP_LOG(APP_LOG_LEVEL_INFO, "Storage Free: %d%%", result.integer_value); break; 27 | case DataTypeStorageFreeGBString: APP_LOG(APP_LOG_LEVEL_INFO, "Storage Free (GB): %s", result.string_value); break; 28 | case DataTypeGSMOperatorName: APP_LOG(APP_LOG_LEVEL_INFO, "Operator Name: %s", result.string_value); break; 29 | case DataTypeGSMStrength: APP_LOG(APP_LOG_LEVEL_INFO, "Operator Strength: %d", result.integer_value); break; 30 | } 31 | 32 | update_text(dash_title, dash_body); 33 | } 34 | /* 35 | static void dash_get_feature_callback(FeatureType type, FeatureState state){ 36 | switch(type){ 37 | case FeatureTypeWifi: APP_LOG(APP_LOG_LEVEL_INFO, "Wifi is %s", state == FeatureStateOn ? "ON" : "OFF"); break; 38 | case FeatureTypeBluetooth: APP_LOG(APP_LOG_LEVEL_INFO, "Bluetooth is %s", state == FeatureStateOn ? "ON" : "OFF"); break; 39 | case FeatureTypeRinger: APP_LOG(APP_LOG_LEVEL_INFO, "Ringer is %s", state == FeatureStateRingerLoud ? "Loud" : state == FeatureStateRingerVibrate ? "Vibrate" : "Silent"); break; 40 | case FeatureTypeAutoSync: APP_LOG(APP_LOG_LEVEL_INFO, "Auto Sync is %s", state == FeatureStateOn ? "ON" : "OFF"); break; 41 | case FeatureTypeHotSpot: APP_LOG(APP_LOG_LEVEL_INFO, "HotSpot is %s", state == FeatureStateOn ? "ON" : "OFF"); break; 42 | case FeatureTypeAutoBrightness: APP_LOG(APP_LOG_LEVEL_INFO, "Auto Brightness is %s", state == FeatureStateOn? "ON" : "OFF"); break; 43 | } 44 | } 45 | */ 46 | static void dash_set_feature_callback(FeatureType type, FeatureState state){ 47 | strncpy(dash_title, "Dash API", sizeof(dash_title)); 48 | 49 | switch(type){ 50 | case FeatureTypeWifi: 51 | snprintf(dash_body, sizeof(dash_body), "WiFi is %s.", state == FeatureStateOn ? "on" : "off"); 52 | break; 53 | 54 | case FeatureTypeHotSpot: 55 | snprintf(dash_body, sizeof(dash_body), "Hotspot is %s.", state == FeatureStateOn ? "on" : "off"); 56 | break; 57 | 58 | case FeatureTypeRinger: 59 | snprintf(dash_body, sizeof(dash_body), "Ringer set to %s.", state == FeatureStateRingerLoud ? "loud" : state == FeatureStateRingerVibrate ? "vibrate" : "silent"); 60 | break; 61 | 62 | case FeatureTypeBluetooth: APP_LOG(APP_LOG_LEVEL_INFO, "Bluetooth is %s", state == FeatureStateOn ? "ON" : "OFF"); break; 63 | case FeatureTypeAutoSync: APP_LOG(APP_LOG_LEVEL_INFO, "Auto Sync is %s", state == FeatureStateOn ? "ON" : "OFF"); break; 64 | case FeatureTypeAutoBrightness: APP_LOG(APP_LOG_LEVEL_INFO, "Auto Brightness is %s", state == FeatureStateOn? "ON" : "OFF"); break; 65 | } 66 | 67 | update_text(dash_title, dash_body); 68 | } 69 | 70 | static void dash_error_callback(ErrorCode code){ 71 | switch(code){ 72 | case ErrorCodeSuccess: APP_LOG(APP_LOG_LEVEL_INFO, "Success!"); break; 73 | 74 | case ErrorCodeSendingFailed: 75 | APP_LOG(APP_LOG_LEVEL_ERROR, "Error: Send Failed"); 76 | error = true; 77 | update_text("Dash API Error", "Error: Send Failed"); 78 | break; 79 | 80 | case ErrorCodeUnavailable: 81 | APP_LOG(APP_LOG_LEVEL_ERROR, "Error: Unavailable"); 82 | error = true; 83 | update_text("Dash API Error", "Error: Unavailable"); 84 | break; 85 | 86 | case ErrorCodeNoPermissions: 87 | APP_LOG(APP_LOG_LEVEL_ERROR, "Error: No Permission"); 88 | error = true; 89 | update_text("Dash API Error", "Error: No Permission"); 90 | break; 91 | 92 | case ErrorCodeWrongVersion: 93 | APP_LOG(APP_LOG_LEVEL_ERROR, "Error: Wrong Version"); 94 | error = true; 95 | update_text("Dash API Error", "Error: Wrong Version"); 96 | break; 97 | } 98 | } 99 | 100 | void get_battery_via_dash(){ 101 | dash_api_get_data(DataTypeBatteryPercent, dash_get_data_callback); 102 | } 103 | 104 | void set_wifi_via_dash(bool on){ 105 | dash_api_set_feature(FeatureTypeWifi, on ? FeatureStateOn : FeatureStateOff, dash_set_feature_callback); 106 | } 107 | 108 | void set_ringer_via_dash(int ring){ 109 | switch(ring){ 110 | case SILENT: dash_api_set_feature(FeatureTypeRinger, FeatureStateRingerSilent, dash_set_feature_callback); break; 111 | case VIBRATE: dash_api_set_feature(FeatureTypeRinger, FeatureStateRingerVibrate, dash_set_feature_callback); break; 112 | case LOUD: dash_api_set_feature(FeatureTypeRinger, FeatureStateRingerLoud, dash_set_feature_callback); break; 113 | } 114 | } 115 | 116 | void set_hotspot_via_dash(bool on){ 117 | dash_api_set_feature(FeatureTypeHotSpot, on ? FeatureStateOn : FeatureStateOff, dash_set_feature_callback); 118 | } 119 | 120 | void get_sms_count_via_dash(){ 121 | dash_api_get_data(DataTypeUnreadSMSCount, dash_get_data_callback); 122 | } 123 | 124 | void get_calendar_via_dash(){ 125 | dash_api_get_data(DataTypeNextCalendarEventOneLine, dash_get_data_callback); 126 | } 127 | 128 | void init_dash_api(){ 129 | dash_api_init("Snowy", dash_error_callback); 130 | } -------------------------------------------------------------------------------- /src/c/c/dash.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define SILENT 0 4 | #define VIBRATE 1 5 | #define LOUD 2 6 | 7 | void init_dash_api(); 8 | void get_battery_via_dash(); 9 | void set_wifi_via_dash(bool on); 10 | void set_ringer_via_dash(int ring); 11 | void set_hotspot_via_dash(bool on); 12 | void get_sms_count_via_dash(); 13 | void get_calendar_via_dash(); -------------------------------------------------------------------------------- /src/c/c/dictation.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "dictation.h" 4 | #include "ui.h" 5 | #include "messaging.h" 6 | #include "lang.h" 7 | #include "main.h" 8 | #include "settings.h" 9 | 10 | static DictationSession *session; 11 | 12 | extern Window *window; 13 | 14 | void dscb(DictationSession *session, DictationSessionStatus status, char *transcript, void *context){ 15 | if(status == DictationSessionStatusSuccess){ 16 | send_request(transcript); 17 | 18 | error = false; 19 | update_text(_("Fetching Data..."), ""); 20 | } 21 | else{ 22 | error = true; 23 | 24 | switch(status){ 25 | case DictationSessionStatusFailureTranscriptionRejected: if(launch_reason() != APP_LAUNCH_QUICK_LAUNCH) update_text(_("If at first you don't succeed..."), _("Seems like you cancelled the dictation session. To try again - just click Select.")); break; 26 | 27 | case DictationSessionStatusFailureTranscriptionRejectedWithError: update_text(_("If at first you don't succeed..."), _("Seems like something went wrong with the dictation session. To try again - just click Select.")); break; 28 | 29 | case DictationSessionStatusFailureNoSpeechDetected: update_text(_("I promise I don't bite!"), _("I didn't hear you say anything! Maybe speak a little louder, or hold your Pebble closer to your mouth.")); break; 30 | 31 | case DictationSessionStatusFailureConnectivityError: update_text(_("If a dog barks in the forest..."), _("I can't seem to connect to the dictation service right now. Maybe make sure the Pebble app is running on your phone?")); break; 32 | 33 | case DictationSessionStatusFailureDisabled: update_text(_("I don't know THAT trick!"), _("It looks like you have dictation disabled. To enable dictation, please make sure 'Send Usage Logs to Pebble' is enabled in the phone app.")); break; 34 | 35 | case DictationSessionStatusFailureInternalError: update_text(_("Urhhmm, I think I might be sick..."), _("Dictation encountered an internal error. You can retry dictation by clicking Select, or restarting the app.")); break; 36 | 37 | default: update_text(_("I'm just as confused as you are."), _("An unknown error occurred with dictation. Try restarting the Pebble app on your phone, or your Pebble watch itself.")); break; 38 | } 39 | } 40 | 41 | if(launch_reason() != APP_LAUNCH_QUICK_LAUNCH) window_stack_push(window, true); 42 | } 43 | 44 | void start_dictation(){ 45 | dictation_session_start(session); 46 | } 47 | 48 | void init_dictation(){ 49 | session = dictation_session_create(128, dscb, NULL); 50 | 51 | dictation_session_enable_confirmation(session, settings_confirm_dictation); 52 | } 53 | 54 | void deinit_dictation(){ 55 | dictation_session_destroy(session); 56 | } -------------------------------------------------------------------------------- /src/c/c/dictation.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void start_dictation(); 4 | 5 | void set_dictation_confirm(bool confirm); 6 | 7 | void init_dictation(); 8 | void deinit_dictation(); -------------------------------------------------------------------------------- /src/c/c/health.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "health.h" 4 | #include "messaging.h" 5 | #include "ui.h" 6 | #include "main.h" 7 | #include "lang.h" 8 | 9 | HealthValue num_steps, num_meters, num_sleep, num_deep_sleep, num_hr; 10 | float num_meters_fractional; 11 | char healthTitle[64], healthBody[128]; 12 | 13 | HealthMinuteData *data; 14 | 15 | int goal = 0; 16 | 17 | char stepTitle[16]; 18 | char sleepBody[128]; 19 | 20 | extern Window *window; 21 | 22 | extern bool is_vibrating; 23 | 24 | void show_sleep(){ 25 | HealthServiceAccessibilityMask sleep = health_service_metric_accessible(HealthMetricSleepSeconds, time_start_of_today(), time(NULL)); 26 | if(sleep & HealthServiceAccessibilityMaskAvailable){ 27 | num_sleep = health_service_sum_today(HealthMetricSleepSeconds);//, time_start_of_today() - 12*60*60, time_start_of_today() + 12*60*60); 28 | 29 | if(num_sleep == 0){ 30 | update_text(_("Well, your Pebble slept well!"), _("I don't see any sleep data from last night - you'll need to wear your Pebble for it to record your sleep!")); 31 | return; 32 | } 33 | 34 | HealthServiceAccessibilityMask deep_sleep = health_service_metric_accessible(HealthMetricSleepRestfulSeconds, time_start_of_today(), time(NULL)); 35 | if(deep_sleep & HealthServiceAccessibilityMaskAvailable){ 36 | //Sleep + Deep Sleep 37 | num_deep_sleep = health_service_sum_today(HealthMetricSleepRestfulSeconds);//, time_start_of_today() - 12*60*60, time_start_of_today() + 12*60*60); 38 | 39 | if(num_deep_sleep != 0) snprintf(sleepBody, sizeof(sleepBody), _("...for %d hours and %d minutes, %d hours and %d minutes of which were in deep sleep."), (int)(num_sleep/3600), (int)(num_sleep%3600)/60, (int)(num_deep_sleep/3600), (int)(num_deep_sleep%3600)/60); 40 | else snprintf(sleepBody, sizeof(sleepBody), _("...for %d hours and %d minutes."), (int)(num_sleep/3600), (int)(num_sleep%3600)/60); 41 | } 42 | else{ 43 | //Sleep 44 | snprintf(sleepBody, sizeof(sleepBody), _("...for %d hours and %d minutes."), (int)(num_sleep/3600), (int)(num_sleep%3600)/60); 45 | } 46 | update_text(_("Last night, you slept..."), sleepBody); 47 | } 48 | else{ 49 | switch(sleep){ 50 | case HealthServiceAccessibilityMaskNotAvailable: 51 | strncpy(healthTitle, _("Data Not Available"), sizeof(healthTitle)); 52 | strncpy(healthBody, _("Sorry, it seems there is no Health data available from last night."), sizeof(healthBody)); 53 | break; 54 | case HealthServiceAccessibilityMaskNotSupported: 55 | strncpy(healthTitle, _("Data Not Supported"), sizeof(healthTitle)); 56 | strncpy(healthBody, _("Sorry, it seems that Health data is unsupported on this device."), sizeof(healthBody)); 57 | break; 58 | case HealthServiceAccessibilityMaskNoPermission: 59 | strncpy(healthTitle, _("Health Disabled"), sizeof(healthTitle)); 60 | strncpy(healthBody, _("To give me permission to check Pebble Health for you, please grant me permission in the Pebble Time app."), sizeof(healthBody)); 61 | break; 62 | default: 63 | strncpy(healthTitle, _("Unknown Error"), sizeof(healthTitle)); 64 | strncpy(healthBody, _("I should have access to your Pebble Health data, but something went wrong!"), sizeof(healthBody)); 65 | error = true; 66 | break; 67 | } 68 | update_text(healthTitle, healthBody); 69 | } 70 | } 71 | 72 | void show_steps(){ 73 | HealthServiceAccessibilityMask steps = health_service_metric_accessible(HealthMetricStepCount, time_start_of_today(), time(NULL)); 74 | if(steps & HealthServiceAccessibilityMaskAvailable){ 75 | 76 | num_steps = health_service_sum_today(HealthMetricStepCount); 77 | 78 | if(num_steps >= 1000) snprintf(healthTitle, sizeof(healthTitle), _("%d,%03d Steps"), (int)(num_steps/1000), (int)(num_steps%1000)); 79 | else snprintf(healthTitle, sizeof(healthTitle), _("%d Steps"), (int)num_steps); 80 | 81 | if(num_steps == 0){ 82 | strncpy(healthBody, _("Every day is a great day to be active! How far will you go today?"), sizeof(healthBody)); 83 | } 84 | else if(num_steps >= 10000){ 85 | strncpy(healthBody, _("Wow, that's a lot of steps! Keep going strong!!!"), sizeof(healthBody)); 86 | } 87 | else if(num_steps >= 8000){ 88 | strncpy(healthBody, _("Awesome job, keep it up!"), sizeof(healthBody)); 89 | } 90 | else if(num_steps >= 6000){ 91 | strncpy(healthBody, _("Great work! How much farther can you go tomorrow?"), sizeof(healthBody)); 92 | } 93 | else if(num_steps >= 4000){ 94 | strncpy(healthBody, _("Keep it up! You're doing great."), sizeof(healthBody)); 95 | } 96 | else if(num_steps >= 2000){ 97 | strncpy(healthBody, _("Start strong, and never give up!"), sizeof(healthBody)); 98 | } 99 | else{ 100 | strncpy(healthBody, _("Good work, keep going!"), sizeof(healthBody)); 101 | } 102 | update_text(healthTitle, healthBody); 103 | } 104 | else{ 105 | switch(steps){ 106 | case HealthServiceAccessibilityMaskNotAvailable: 107 | strncpy(healthTitle, _("Data Not Available"), sizeof(healthTitle)); 108 | strncpy(healthBody, _("Sorry, it seems there is no Health data available for today."), sizeof(healthBody)); 109 | break; 110 | case HealthServiceAccessibilityMaskNotSupported: 111 | strncpy(healthTitle, _("Data Not Supported"), sizeof(healthTitle)); 112 | strncpy(healthBody, _("Sorry, it seems that Health data is unsupported on this device."), sizeof(healthBody)); 113 | break; 114 | case HealthServiceAccessibilityMaskNoPermission: 115 | strncpy(healthTitle, _("Health Disabled"), sizeof(healthTitle)); 116 | strncpy(healthBody, _("To give me permission to check Pebble Health for you, please grant me permission in the Pebble Time app."), sizeof(healthBody)); 117 | break; 118 | default: 119 | strncpy(healthTitle, _("Unknown Error"), sizeof(healthTitle)); 120 | strncpy(healthBody, _("I should have access to your Pebble Health data, but something went wrong!"), sizeof(healthBody)); 121 | error = true; 122 | break; 123 | } 124 | update_text(healthTitle, healthBody); 125 | } 126 | } 127 | 128 | void show_distance(){ 129 | HealthServiceAccessibilityMask distance = health_service_any_activity_accessible(HealthMetricWalkedDistanceMeters, time_start_of_today(), time(NULL)); 130 | if(distance & HealthServiceAccessibilityMaskAvailable){ 131 | num_meters = health_service_sum_today(HealthMetricWalkedDistanceMeters); 132 | 133 | bool is_imperial = health_service_get_measurement_system_for_display(HealthMetricWalkedDistanceMeters) == MeasurementSystemImperial; 134 | 135 | if(is_imperial){ 136 | num_meters_fractional = (float)(num_meters * 0.000621371f); 137 | } 138 | else{ 139 | num_meters_fractional = (float)(num_meters * 0.001f); 140 | } 141 | 142 | if( (int)(num_meters_fractional * 100) % 10 >= 5 ) num_meters_fractional += 0.05f; 143 | 144 | snprintf(healthTitle, sizeof(healthTitle), _("%d.%01d %s"), (int)(num_meters_fractional), (int)(num_meters_fractional*10)%10, is_imperial ? _("Miles") : _("Kilometers")); 145 | if(num_meters == 0){ 146 | strncpy(healthBody, _("Every day is a great day to be active! How far will you go today?"), sizeof(healthBody)); 147 | } 148 | else if( (!is_imperial && num_meters_fractional >= 6) || (is_imperial && num_meters_fractional >= 4) ){ 149 | strncpy(healthBody, _("Wow, that's pretty far! Keep going strong!!!"), sizeof(healthBody)); 150 | } 151 | else if( (!is_imperial && num_meters_fractional >= 5) || (is_imperial && num_meters_fractional >= 3) ){ 152 | strncpy(healthBody, _("Awesome job, keep it up!"), sizeof(healthBody)); 153 | } 154 | else if( (!is_imperial && num_meters_fractional >= 3) || (is_imperial && num_meters_fractional >= 2) ){ 155 | strncpy(healthBody, _("Great work! How much farther can you go tomorrow?"), sizeof(healthBody)); 156 | } 157 | else if( (!is_imperial && num_meters_fractional >= 2) || (is_imperial && num_meters_fractional >= 1) ){ 158 | strncpy(healthBody, _("Keep it up! You're doing great."), sizeof(healthBody)); 159 | } 160 | else if( (!is_imperial && num_meters_fractional >= 1) || (is_imperial && num_meters_fractional >= 0.5f) ){ 161 | strncpy(healthBody, _("Start strong, and never give up!"), sizeof(healthBody)); 162 | } 163 | else{ 164 | strncpy(healthBody, _("Good work, keep going!"), sizeof(healthBody)); 165 | } 166 | update_text(healthTitle, healthBody); 167 | } 168 | else{ 169 | switch(distance){ 170 | case HealthServiceAccessibilityMaskNotAvailable: 171 | strncpy(healthTitle, _("Data Not Available"), sizeof(healthTitle)); 172 | strncpy(healthBody, _("Sorry, it seems there is no Health data available for today."), sizeof(healthBody)); 173 | break; 174 | case HealthServiceAccessibilityMaskNotSupported: 175 | strncpy(healthTitle, _("Data Not Supported"), sizeof(healthTitle)); 176 | strncpy(healthBody, _("Sorry, it seems that Health data is unsupported on this device."), sizeof(healthBody)); 177 | break; 178 | case HealthServiceAccessibilityMaskNoPermission: 179 | strncpy(healthTitle, _("Health Disabled"), sizeof(healthTitle)); 180 | strncpy(healthBody, _("To give me permission to check Pebble Health for you, please grant me permission in the Pebble Time app."), sizeof(healthBody)); 181 | break; 182 | default: 183 | strncpy(healthTitle, _("Unknown Error"), sizeof(healthTitle)); 184 | strncpy(healthBody, _("I should have access to your Pebble Health data, but something went wrong!"), sizeof(healthBody)); 185 | error = true; 186 | break; 187 | } 188 | update_text(healthTitle, healthBody); 189 | } 190 | } 191 | /* 192 | void show_heart_rate(){ 193 | HealthServiceAccessibilityMask heart = health_service_metric_accessible(HealthMetricHeartRateBPM, time_start_of_today(), time(NULL)); 194 | if(heart & HealthServiceAccessibilityMaskAvailable){ 195 | 196 | num_hr = health_service_peek_current_value(HealthMetricHeartRateBPM); 197 | 198 | int data_points = health_service_get_minute_history(data, 12*60*60, time_start_of_today(), time(NULL)); 199 | 200 | int max_bpm = 0, sum_bpm = 0, avg_bpm = 0, valid_data_points = 0; 201 | 202 | for(int i = 0; i < data_points; i++){ 203 | if(!data[i].is_invalid){ 204 | if(data[i].heart_rate_bpm > max_bpm) max_bpm = data[i].heart_rate_bpm; 205 | sum_bpm += data[i].heart_rate_bpm; 206 | valid_data_points++; 207 | } 208 | } 209 | 210 | avg_bpm = sum_bpm / valid_data_points; 211 | 212 | snprintf(healthBody, sizeof(healthBody), "Current: %d BPM\n\nAverage: %d BPM\nPeak: %d BPM", (int)num_hr, avg_bpm, max_bpm); 213 | 214 | update_text("Heart Rate", healthBody); 215 | } 216 | else{ 217 | switch(heart){ 218 | case HealthServiceAccessibilityMaskNotAvailable: 219 | strncpy(healthTitle, _("Data Not Available"), sizeof(healthTitle)); 220 | strncpy(healthBody, _("Sorry, it seems there is no Health data available for today."), sizeof(healthBody)); 221 | break; 222 | case HealthServiceAccessibilityMaskNotSupported: 223 | strncpy(healthTitle, _("Data Not Supported"), sizeof(healthTitle)); 224 | strncpy(healthBody, _("Sorry, it seems that Health data is unsupported on this device."), sizeof(healthBody)); 225 | break; 226 | case HealthServiceAccessibilityMaskNoPermission: 227 | strncpy(healthTitle, _("Health Disabled"), sizeof(healthTitle)); 228 | strncpy(healthBody, _("To give me permission to check Pebble Health for you, please grant me permission in the Pebble Time app."), sizeof(healthBody)); 229 | break; 230 | default: 231 | strncpy(healthTitle, _("Unknown Error"), sizeof(healthTitle)); 232 | strncpy(healthBody, _("I should have access to your Pebble Health data, but something went wrong!"), sizeof(healthBody)); 233 | error = true; 234 | break; 235 | } 236 | update_text(healthTitle, healthBody); 237 | } 238 | } 239 | */ 240 | void show_step_goal(){ 241 | if(goal != 0){ 242 | if(goal < 1000) snprintf(stepTitle, sizeof(stepTitle), _("%d Steps"), goal); 243 | else snprintf(stepTitle, sizeof(stepTitle), _("%d,%03d Steps"), (int)(goal/1000), (int)(goal%1000)); 244 | 245 | update_text(stepTitle, _("That's a great goal! I know you can do it!!!")); 246 | } 247 | else{ 248 | //No Step Goal 249 | update_text(_("No Step Goal"), _("To add a Step Goal, please use my Settings page! Challenge yourself to reach your limits! You can do it!!!")); 250 | } 251 | } 252 | 253 | void health_handler(){ 254 | if(persist_exists(MESSAGE_KEY_HealthNotified) && persist_read_bool(MESSAGE_KEY_HealthNotified)) return; 255 | 256 | persist_write_bool(MESSAGE_KEY_HealthStepGoalReached, true); 257 | 258 | if(goal < 1000) snprintf(stepTitle, sizeof(stepTitle), _("%d Steps"), goal); 259 | else snprintf(stepTitle, sizeof(stepTitle), _("%d,%03d Steps"), (int)(goal/1000), (int)(goal%1000)); 260 | 261 | error = false; 262 | update_text(stepTitle, _("Congratulations! You reached your step goal for today!!!")); 263 | 264 | if(!window_stack_contains_window(window)) window_stack_push(window, true); 265 | 266 | is_vibrating = true; 267 | //app_timer_register(3000, incoming_message, NULL); 268 | 269 | vibes_long_pulse(); 270 | 271 | persist_write_bool(MESSAGE_KEY_HealthNotified, true); 272 | } 273 | 274 | void set_goal(int new_goal){ 275 | goal = new_goal; 276 | persist_write_int(MESSAGE_KEY_StepGoal, goal); 277 | } 278 | 279 | void init_health(){ 280 | goal = persist_exists(MESSAGE_KEY_StepGoal) ? persist_read_int(MESSAGE_KEY_StepGoal) : 0; 281 | } -------------------------------------------------------------------------------- /src/c/c/health.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void set_goal(int new_goal); 4 | 5 | void health_handler(); 6 | 7 | void show_sleep(); 8 | 9 | void show_steps(); 10 | 11 | void show_distance(); 12 | 13 | void show_heart_rate(); 14 | 15 | void show_step_goal(); 16 | 17 | void init_health(); -------------------------------------------------------------------------------- /src/c/c/kiezelpay.c: -------------------------------------------------------------------------------- 1 | /* 2 | * KiezelPay Integration Library - v2.0 - Copyright Kiezel 2016 3 | * 4 | * BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 5 | * WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE 6 | * LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 7 | * HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" 8 | * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, 9 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE 11 | * RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. 12 | * SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL 13 | * NECESSARY SERVICING, REPAIR OR CORRECTION. 14 | * 15 | * IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 16 | * WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY 17 | * MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE 18 | * LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, 19 | * INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR 20 | * INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS 21 | * OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 22 | * YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH 23 | * ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN 24 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | /** 31 | Set to 1 to enable verbose logging, handy for tracking down issues with the kiezelpay integration 32 | 33 | Set to 0 before releasing 34 | */ 35 | #define KIEZELPAY_LOG_VERBOSE 0 36 | 37 | /** 38 | When set to 1, you can test the purchasing of your app without having to pay for real. 39 | When set to 1, trial times will always be 30 seconds (only when time trial is enabled here and on the server) 40 | When set to 1, periodic status re-checks will happen every 60 seconds (only when enabled) 41 | 42 | Test purchases only, set to 0 before releasing or users can get your app for free! 43 | */ 44 | #define KIEZELPAY_TEST_MODE 0 45 | 46 | /** 47 | Set to 1 to remove all code which has to do with the time based trial. This saves memory and code space in case you don't use it. 48 | You need to manually signal that the trial is over and purchase needs to start by calling kiezelpay_start_purchase(); 49 | 50 | Set to 0 to let kiezelpay automatically initiate the purchase as soon as the time-based trial configured on the server has ended. 51 | In case no time based trial is configured on the server, this will require the user to purchase the app immediately after installing it. 52 | */ 53 | #define KIEZELPAY_DISABLE_TIME_TRIAL 1 54 | 55 | /** 56 | Set to 1 when you want to remove all code that has to do with message display to save memory and code space, in this mode the message display needs to be handled by yourself! 57 | 58 | Set to 0 to let the kiezelpay lib show messages itself in case you don't handle them (e.g. purchasing, messages about internet connection issues, ...) 59 | */ 60 | #define KIEZELPAY_DISABLE_MESSAGES 1 61 | 62 | /** 63 | Set to 1 to disable period re-checks of the licensed status on the server. This saves memory and code space. 64 | 65 | RECOMMENDED: Set to 0 to let kiezelpay automatically recheck the licensed state of this app online after it was purchased, 66 | this prevents users from tampering with the stored license data (e.g. copy it from another licensed device) and makes it more secure. 67 | The duration between these checks is managed by the KiezelPay server, but will always be 24 hours or longer. 68 | This check will not lock down the app in case there is no internet available or the server cannot be reached for any other reason, 69 | only when the kiezelpay effectively returns the status "unlicensed" will the user be shown the purchase dialog 70 | */ 71 | #define KIEZELPAY_DISABLE_PERIODIC_CHECKS 0 72 | 73 | 74 | /** 75 | Default messages shown to the user in different stages of the purchase or when errors occur. 76 | Only used when the default KiezelPay messages are enabled (#define KIEZELPAY_DISABLE_MESSAGES 0) 77 | 78 | You can change the messages to your liking here. Make sure to check if the changed message still fits the display of all different pebble watches. 79 | */ 80 | #define KIEZELPAY_UNKNOWN_ERROR_MSG "An unknown error occurred" 81 | #define KIEZELPAY_BLUETOOTH_UNAVAILABLE_MSG "There is a problem with the connection between your watch and your phone" 82 | #define KIEZELPAY_INTERNET_UNAVAILABLE_MSG "There is a problem with the internet connection of your phone" 83 | /** 84 | It is also possible to configure a custom url in your product settings on our website so its shows your personalized purchase page to the customers. 85 | if you do so, remember to also change the URL below so it matches. 86 | */ 87 | #define KIEZELPAY_CODE_AVAILABLE_MSG "To continue using Snowy please visit kzl.io/snowy and enter this code:" 88 | #define KIEZELPAY_PURCHASE_STARTED_MSG "Please complete the purchase process to unlock Snowy" 89 | #define KIEZELPAY_LICENSED_MSG "Thank you for your purchase!" 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | /***************************************************/ 98 | /* KIEZELPAY GENERATED CODE BELOW - DO NOT MODIFY! */ 99 | /***************************************************/ 100 | 101 | #define KP_GENERATED_MAJOR 2 102 | 103 | #define KIEZELPAY_APPID 1668121194 104 | 105 | 106 | static uint8_t kiezelpay_secret[16] = {22, 230, 212, 72, 59, 104, 63, 217, 148, 8, 8, 237, 139, 81, 122, 233}; 107 | 108 | #include 109 | 110 | static bool kiezelpay_validate_msg(kiezelpay_msg_data *msg) { 111 | LOG("%s", __func__); 112 | //before checking the hash, do some sanity checks 113 | bool valid_format = (msg != NULL && msg->checksum != NULL); 114 | 115 | if (!valid_format) { 116 | return false; 117 | } 118 | //prepare sha-256 context 119 | SHA256_CTX ctx_msg_check; 120 | sha256_init(&ctx_msg_check); 121 | uint32_t int_for_bytes; 122 | sha256_update(&ctx_msg_check, kiezelpay_secret + 6, 1); 123 | int_for_bytes = msg->status; 124 | sha256_update(&ctx_msg_check, (uint8_t*)&int_for_bytes, 1); 125 | int_for_bytes = kiezelpay_get_status_flags(); 126 | sha256_update(&ctx_msg_check, (uint8_t*)&int_for_bytes + 2, 1); 127 | sha256_update(&ctx_msg_check, kiezelpay_secret + 0, 1); 128 | sha256_update(&ctx_msg_check, kiezelpay_secret + 3, 1); 129 | sha256_update(&ctx_msg_check, (uint8_t*)&kiezelpay_msg_random + 0, 1); 130 | if (msg->status == 0) { //unlicensed 131 | int_for_bytes = msg->purchase_code; 132 | sha256_update(&ctx_msg_check, (uint8_t*)&int_for_bytes + 2, 1); 133 | } 134 | sha256_update(&ctx_msg_check, kiezelpay_secret + 10, 1); 135 | if (msg->status == 0) { //unlicensed 136 | int_for_bytes = msg->purchase_code; 137 | sha256_update(&ctx_msg_check, (uint8_t*)&int_for_bytes + 1, 1); 138 | } 139 | #if KIEZELPAY_DISABLE_TIME_TRIAL == 0 140 | if (msg->status == 1) { //trial 141 | sha256_update(&ctx_msg_check, (uint8_t*)&(msg->trial_duration) + 1, 1); 142 | } 143 | #endif 144 | int_for_bytes = kiezelpay_get_status_flags(); 145 | sha256_update(&ctx_msg_check, (uint8_t*)&int_for_bytes + 3, 1); 146 | if (msg->status == 1 || msg->status == 2) { //trial or licensed 147 | sha256_update(&ctx_msg_check, (uint8_t*)&(msg->validity_period), 1); 148 | } 149 | sha256_update(&ctx_msg_check, (uint8_t*)&kiezelpay_msg_random + 1, 1); 150 | #if KIEZELPAY_DISABLE_TIME_TRIAL == 0 151 | if (msg->status == 1) { //trial 152 | sha256_update(&ctx_msg_check, (uint8_t*)&(msg->trial_duration) + 3, 1); 153 | } 154 | #endif 155 | sha256_update(&ctx_msg_check, kiezelpay_secret + 2, 1); 156 | sha256_update(&ctx_msg_check, (uint8_t*)&(kiezelpay_current_state.device_id) + 1, 1); 157 | #if KIEZELPAY_DISABLE_TIME_TRIAL == 0 158 | if (msg->status == 1) { //trial 159 | sha256_update(&ctx_msg_check, (uint8_t*)&(msg->trial_duration) + 2, 1); 160 | } 161 | #endif 162 | sha256_update(&ctx_msg_check, kiezelpay_secret + 8, 1); 163 | sha256_update(&ctx_msg_check, kiezelpay_secret + 13, 1); 164 | int_for_bytes = kiezelpay_get_status_flags(); 165 | sha256_update(&ctx_msg_check, (uint8_t*)&int_for_bytes + 1, 1); 166 | sha256_update(&ctx_msg_check, kiezelpay_secret + 12, 1); 167 | sha256_update(&ctx_msg_check, kiezelpay_secret + 11, 1); 168 | sha256_update(&ctx_msg_check, kiezelpay_secret + 14, 1); 169 | #if KIEZELPAY_DISABLE_TIME_TRIAL == 0 170 | if (msg->status == 1) { //trial 171 | sha256_update(&ctx_msg_check, (uint8_t*)&(msg->trial_duration) + 0, 1); 172 | } 173 | #endif 174 | if (msg->status == 0) { //unlicensed 175 | int_for_bytes = msg->purchase_code; 176 | sha256_update(&ctx_msg_check, (uint8_t*)&int_for_bytes + 0, 1); 177 | } 178 | sha256_update(&ctx_msg_check, kiezelpay_secret + 1, 1); 179 | sha256_update(&ctx_msg_check, kiezelpay_secret + 9, 1); 180 | sha256_update(&ctx_msg_check, (uint8_t*)&(kiezelpay_current_state.device_id) + 3, 1); 181 | int_for_bytes = kiezelpay_get_status_flags(); 182 | sha256_update(&ctx_msg_check, (uint8_t*)&int_for_bytes + 0, 1); 183 | if (msg->status == 0) { //unlicensed 184 | int_for_bytes = msg->purchase_code; 185 | sha256_update(&ctx_msg_check, (uint8_t*)&int_for_bytes + 3, 1); 186 | } 187 | sha256_update(&ctx_msg_check, kiezelpay_secret + 15, 1); 188 | sha256_update(&ctx_msg_check, kiezelpay_secret + 5, 1); 189 | sha256_update(&ctx_msg_check, (uint8_t*)&(kiezelpay_current_state.device_id) + 2, 1); 190 | sha256_update(&ctx_msg_check, kiezelpay_secret + 4, 1); 191 | sha256_update(&ctx_msg_check, kiezelpay_secret + 7, 1); 192 | sha256_update(&ctx_msg_check, (uint8_t*)&(kiezelpay_current_state.device_id) + 0, 1); 193 | //calculate sha-256 hash 194 | uint8_t hash[SHA256_BLOCK_SIZE]; 195 | sha256_final(&ctx_msg_check, hash); 196 | 197 | //compare this hash with the checksum returned by the server 198 | for (uint32_t i = 0; i < SHA256_BLOCK_SIZE; i++) { 199 | if (msg->checksum[i] != hash[i]) return false; 200 | } 201 | return true; 202 | } 203 | 204 | void kiezelpay_init() { 205 | kiezelpay_internal_init(KIEZELPAY_APPID, kiezelpay_secret, kiezelpay_validate_msg); 206 | } -------------------------------------------------------------------------------- /src/c/c/lang.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "lang.h" 3 | 4 | const char *LANG_LIST[NUM_LANG] = {"English","Deutsch","Español","Français","Dansk (Beta)","Português (Beta)"}; 5 | 6 | int lang = -1; 7 | 8 | int get_lang_resource(){ 9 | switch(lang){ 10 | case ENGLISH: return RESOURCE_ID_DICT_ENGLISH; 11 | case SPANISH: return RESOURCE_ID_DICT_SPANISH; 12 | case FRENCH: return RESOURCE_ID_DICT_FRENCH; 13 | case GERMAN: return RESOURCE_ID_DICT_GERMAN; 14 | case PORTUGUESE: return RESOURCE_ID_DICT_PORTUGUESE_BETA; 15 | case DANISH: return RESOURCE_ID_DICT_DANISH_BETA; 16 | default: return RESOURCE_ID_DICT_ENGLISH; 17 | } 18 | } -------------------------------------------------------------------------------- /src/c/c/lang.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define NUM_LANG 6 4 | 5 | extern const char *LANG_LIST[NUM_LANG]; 6 | 7 | enum { 8 | ENGLISH = 0, 9 | SPANISH = 1, 10 | FRENCH = 2, 11 | GERMAN = 3, 12 | PORTUGUESE = 4, 13 | DANISH = 5 14 | }; 15 | extern int lang; 16 | 17 | int get_lang_resource(); -------------------------------------------------------------------------------- /src/c/c/main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | char widget_date_1[8]; 4 | char widget_date_2[16]; 5 | char widget_date_3[16]; 6 | char widget_date_4[16]; 7 | char widget_date_5[16]; 8 | char widget_steps[8]; 9 | char widget_dist[16]; 10 | char widget_hr[16]; 11 | char widget_alt_tz[16]; 12 | char widget_battery[8]; 13 | 14 | extern bool is_directions; 15 | 16 | void home_config(void *context); 17 | void config(void *context); -------------------------------------------------------------------------------- /src/c/c/messaging.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "messaging.h" 4 | #include "dictation.h" 5 | #include "lang.h" 6 | #include "settings.h" 7 | #include "main.h" 8 | #include "ui.h" 9 | #include "widget.h" 10 | #include "actionmenu.h" 11 | #include "dash.h" 12 | #include "health.h" 13 | 14 | extern Window *home_window, *window; 15 | extern bool error; 16 | 17 | char temp_title[64], temp_body[2048]; 18 | 19 | void send_request(const char *transcript){ 20 | Tuplet transcript_tuplet = TupletCString(MESSAGE_KEY_Transcript, transcript); 21 | DictionaryIterator *request; 22 | app_message_outbox_begin(&request); 23 | dict_write_tuplet(request, &transcript_tuplet); 24 | dict_write_end(request); 25 | app_message_outbox_send(); 26 | } 27 | 28 | void inbox(DictionaryIterator *iter, void *context){ 29 | error = false; 30 | 31 | APP_LOG(APP_LOG_LEVEL_DEBUG, "Incoming Message..."); 32 | 33 | /* 34 | Tuple *t = dict_read_first(iter); 35 | while(t != NULL){ 36 | APP_LOG(APP_LOG_LEVEL_DEBUG, "Key: %d, Int Value: %d, CString Value: %s", (int)t->key, (int)t->value->int32, t->value->cstring); 37 | t = dict_read_next(iter); 38 | } 39 | */ 40 | 41 | //Settings Keys 42 | 43 | if(dict_find(iter, MESSAGE_KEY_Settings)){ 44 | APP_LOG(APP_LOG_LEVEL_DEBUG, "Settings Received"); 45 | Tuple *font_size_t = dict_find(iter, MESSAGE_KEY_FontSize); 46 | if(font_size_t){ 47 | settings_small_font = font_size_t->value->int32; 48 | persist_write_bool(MESSAGE_KEY_FontSize, settings_small_font); 49 | 50 | set_size(settings_small_font); 51 | } 52 | 53 | Tuple *hands_free_t = dict_find(iter, MESSAGE_KEY_HandsFree); 54 | if(hands_free_t){ 55 | settings_hands_free = hands_free_t->value->int32; 56 | persist_write_bool(MESSAGE_KEY_HandsFree, settings_hands_free); 57 | } 58 | 59 | Tuple *flick_dismiss_t = dict_find(iter, MESSAGE_KEY_FlickToDismiss); 60 | if(flick_dismiss_t){ 61 | settings_flick_dismiss = flick_dismiss_t->value->int32; 62 | persist_write_bool(MESSAGE_KEY_FlickToDismiss, settings_flick_dismiss); 63 | } 64 | 65 | Tuple *quick_exit_t = dict_find(iter, MESSAGE_KEY_QuickExit); 66 | if(quick_exit_t){ 67 | settings_quick_exit = quick_exit_t->value->int32; 68 | persist_write_bool(MESSAGE_KEY_QuickExit, settings_quick_exit); 69 | } 70 | 71 | Tuple *confirm_t = dict_find(iter, MESSAGE_KEY_ConfirmDictation); 72 | if(confirm_t){ 73 | settings_confirm_dictation = confirm_t->value->int32; 74 | persist_write_bool(MESSAGE_KEY_ConfirmDictation, settings_confirm_dictation); 75 | 76 | deinit_dictation(); 77 | init_dictation(); 78 | } 79 | 80 | Tuple *fullscreen_t = dict_find(iter, MESSAGE_KEY_Fullscreen); 81 | if(fullscreen_t){ 82 | settings_fullscreen = fullscreen_t->value->int32; 83 | persist_write_bool(MESSAGE_KEY_Fullscreen, settings_fullscreen); 84 | 85 | reinit_round_fullscreen(); 86 | } 87 | 88 | Tuple *night_mode_t = dict_find(iter, MESSAGE_KEY_NightMode); 89 | if(night_mode_t){ 90 | settings_night_mode = night_mode_t->value->int32; 91 | persist_write_bool(MESSAGE_KEY_NightMode, settings_night_mode); 92 | 93 | set_night_mode_palette(); 94 | } 95 | 96 | Tuple *widget_t = dict_find(iter, MESSAGE_KEY_Widget); 97 | if(widget_t){ 98 | set_widget(atoi(widget_t->value->cstring)); 99 | 100 | Tuple *widget_extra_t = dict_find(iter, MESSAGE_KEY_WidgetExtra); 101 | if(widget_extra_t){ 102 | 103 | } 104 | } 105 | 106 | Tuple *step_goal_t = dict_find(iter, MESSAGE_KEY_StepGoal); 107 | if(step_goal_t){ 108 | persist_write_int(MESSAGE_KEY_StepGoal, atoi(step_goal_t->value->cstring)); 109 | init_health(); 110 | } 111 | 112 | //TODO 113 | Tuple *inactivity_t = dict_find(iter, MESSAGE_KEY_InactivityMonitor); 114 | if(inactivity_t){ 115 | 116 | Tuple *inactivity_start_t = dict_find(iter, MESSAGE_KEY_InactivityStart); 117 | if(inactivity_start_t){ 118 | persist_write_int(MESSAGE_KEY_InactivityStart, atoi(inactivity_start_t->value->cstring)); 119 | } 120 | 121 | Tuple *inactivity_end_t = dict_find(iter, MESSAGE_KEY_InactivityEnd); 122 | if(inactivity_end_t){ 123 | persist_write_int(MESSAGE_KEY_InactivityEnd, atoi(inactivity_end_t->value->cstring)); 124 | } 125 | 126 | persist_write_bool(MESSAGE_KEY_InactivityMonitor, inactivity_t->value->int32); 127 | } 128 | 129 | int custom_count = 0; 130 | for(int i = 0; i < 4; i++){ 131 | Tuple *custom_t = dict_find(iter, MESSAGE_KEY_Custom + i); 132 | if(custom_t && strlen(custom_t->value->cstring) != 0){ 133 | APP_LOG(APP_LOG_LEVEL_DEBUG, "Custom %d: %s", (int)i, custom_t->value->cstring); 134 | persist_write_string(MESSAGE_KEY_Custom + i, custom_t->value->cstring); 135 | custom_count++; 136 | } 137 | else{ 138 | i = 5; 139 | } 140 | } 141 | 142 | persist_write_int(MESSAGE_KEY_CustomCount, custom_count); 143 | 144 | hide_action_menu(); 145 | deinit_action_menu(); 146 | init_action_menu(); 147 | } 148 | 149 | //Message Keys 150 | 151 | if(dict_find(iter, MESSAGE_KEY_Message)){ 152 | APP_LOG(APP_LOG_LEVEL_DEBUG, "Message Received"); 153 | //TODO 154 | Tuple *title_t = dict_find(iter, MESSAGE_KEY_Title); 155 | if(title_t){ 156 | strncpy(temp_title, title_t->value->cstring, sizeof(temp_title)); 157 | 158 | Tuple *body_t = dict_find(iter, MESSAGE_KEY_Body); 159 | if(body_t){ 160 | strncpy(temp_body, body_t->value->cstring, sizeof(temp_body)); 161 | } 162 | else{ 163 | strncpy(temp_body, "", sizeof(temp_body)); 164 | } 165 | 166 | Tuple *directions_t = dict_find(iter, MESSAGE_KEY_Directions); 167 | if(directions_t){ 168 | is_directions = directions_t->value->int32; 169 | } 170 | 171 | update_text(temp_title, temp_body); 172 | } 173 | 174 | Tuple *alarm_t = dict_find(iter, MESSAGE_KEY_Alarm); 175 | if(alarm_t){ 176 | int minutes = atoi(alarm_t->value->cstring); 177 | 178 | if(minutes > 0){ 179 | time_t alarm_end = minutes; 180 | WakeupId alarm_id = wakeup_schedule(alarm_end, 1, true); 181 | 182 | persist_write_int(MESSAGE_KEY_Alarm, alarm_id); 183 | } 184 | else{ 185 | if(persist_exists(MESSAGE_KEY_Alarm)){ 186 | wakeup_cancel(persist_read_int(MESSAGE_KEY_Alarm)); 187 | } 188 | } 189 | } 190 | 191 | Tuple *timer_t = dict_find(iter, MESSAGE_KEY_Timer); 192 | if(timer_t){ 193 | int minutes = atoi(timer_t->value->cstring); 194 | 195 | if(minutes > 0){ 196 | time_t timer_end = time(NULL) + minutes * 60; 197 | WakeupId timer_id = wakeup_schedule(timer_end, 0, true); 198 | 199 | persist_write_int(MESSAGE_KEY_Timer, timer_id); 200 | } 201 | else{ 202 | if(persist_exists(MESSAGE_KEY_Timer)){ 203 | wakeup_cancel(persist_read_int(MESSAGE_KEY_Timer)); 204 | } 205 | } 206 | } 207 | 208 | Tuple *error_t = dict_find(iter, MESSAGE_KEY_Error); 209 | if(error_t){ 210 | error = true; 211 | } 212 | 213 | vibes_short_pulse(); 214 | 215 | window_stack_push(window, true); 216 | } 217 | 218 | //Request Keys 219 | 220 | if(dict_find(iter, MESSAGE_KEY_Request)){ 221 | APP_LOG(APP_LOG_LEVEL_DEBUG, "Request Received"); 222 | //TODO 223 | Tuple *step_goal_request_t = dict_find(iter, MESSAGE_KEY_StepGoalRequest); 224 | if(step_goal_request_t){ 225 | show_step_goal(); 226 | } 227 | 228 | //TODO 229 | Tuple *steps_request_t = dict_find(iter, MESSAGE_KEY_StepsRequest); 230 | if(steps_request_t){ 231 | show_steps(); 232 | } 233 | 234 | //TODO 235 | Tuple *distance_request_t = dict_find(iter, MESSAGE_KEY_DistanceRequest); 236 | if(distance_request_t){ 237 | show_distance(); 238 | } 239 | 240 | //TODO 241 | Tuple *heart_request_t = dict_find(iter, MESSAGE_KEY_HeartRateRequest); 242 | if(heart_request_t){ 243 | //show_heart_rate(); 244 | } 245 | 246 | //TODO 247 | Tuple *sleep_request_t = dict_find(iter, MESSAGE_KEY_SleepRequest); 248 | if(sleep_request_t){ 249 | show_sleep(); 250 | } 251 | 252 | //TODO 253 | Tuple *location_request_t = dict_find(iter, MESSAGE_KEY_LocationRequest); 254 | if(location_request_t){ 255 | 256 | } 257 | 258 | //TODO 259 | Tuple *charge_request_t = dict_find(iter, MESSAGE_KEY_PALChargeRequest); 260 | if(charge_request_t){ 261 | 262 | } 263 | 264 | Tuple *dash_battery_request_t = dict_find(iter, MESSAGE_KEY_DashBatteryRequest); 265 | if(dash_battery_request_t){ 266 | get_battery_via_dash(); 267 | } 268 | 269 | Tuple *dash_set_wifi_request_t = dict_find(iter, MESSAGE_KEY_DashSetWifiRequest); 270 | if(dash_set_wifi_request_t){ 271 | set_wifi_via_dash(dash_set_wifi_request_t->value->int32); 272 | } 273 | 274 | Tuple *dash_set_ringer_request_t = dict_find(iter, MESSAGE_KEY_DashSetRingerRequest); 275 | if(dash_set_ringer_request_t){ 276 | set_ringer_via_dash(atoi(dash_set_ringer_request_t->value->cstring)); 277 | } 278 | 279 | Tuple *dash_set_hotspot_request_t = dict_find(iter, MESSAGE_KEY_DashSetHotspotRequest); 280 | if(dash_set_hotspot_request_t){ 281 | set_hotspot_via_dash(dash_set_hotspot_request_t->value->int32); 282 | } 283 | 284 | Tuple *dash_sms_request_t = dict_find(iter, MESSAGE_KEY_DashSMSRequest); 285 | if(dash_sms_request_t){ 286 | get_sms_count_via_dash(); 287 | } 288 | 289 | Tuple *dash_calendar_t = dict_find(iter, MESSAGE_KEY_DashCalendarRequest); 290 | if(dash_calendar_t){ 291 | get_calendar_via_dash(); 292 | } 293 | 294 | } 295 | 296 | layer_mark_dirty(window_get_root_layer(home_window)); 297 | layer_mark_dirty(window_get_root_layer(window)); 298 | } 299 | 300 | void init_messaging(){ 301 | 302 | } -------------------------------------------------------------------------------- /src/c/c/messaging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void send_request(const char *transcript); 4 | 5 | void inbox(DictionaryIterator *iter, void *context); 6 | 7 | void timeout_check(); 8 | 9 | void init_messaging(); 10 | void deinit_messaging(); -------------------------------------------------------------------------------- /src/c/c/settings.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "settings.h" 3 | 4 | void init_settings(){ 5 | settings_night_mode = persist_exists(MESSAGE_KEY_NightMode) ? persist_read_bool(MESSAGE_KEY_NightMode) : false; 6 | settings_fullscreen = persist_exists(MESSAGE_KEY_Fullscreen) ? persist_read_bool(MESSAGE_KEY_Fullscreen) : false; 7 | settings_small_font = persist_exists(MESSAGE_KEY_FontSize) ? persist_read_bool(MESSAGE_KEY_FontSize) : true; 8 | settings_hands_free = persist_exists(MESSAGE_KEY_HandsFree) ? persist_read_bool(MESSAGE_KEY_HandsFree) : false; 9 | settings_confirm_dictation = persist_exists(MESSAGE_KEY_ConfirmDictation) ? persist_read_bool(MESSAGE_KEY_ConfirmDictation) : false; 10 | settings_quick_exit = persist_exists(MESSAGE_KEY_QuickExit) ? persist_read_bool(MESSAGE_KEY_QuickExit) : false; 11 | settings_flick_dismiss = persist_exists(MESSAGE_KEY_FlickToDismiss) ? persist_read_bool(MESSAGE_KEY_FlickToDismiss) : false; 12 | } -------------------------------------------------------------------------------- /src/c/c/settings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | bool settings_night_mode; 4 | 5 | bool settings_fullscreen; 6 | 7 | bool settings_small_font; 8 | 9 | bool settings_hands_free; 10 | 11 | bool settings_confirm_dictation; 12 | 13 | bool settings_quick_exit; 14 | 15 | bool settings_flick_dismiss; 16 | 17 | void init_settings(); -------------------------------------------------------------------------------- /src/c/c/ui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef PBL_RECT 4 | #define HOME_X 18 5 | #define HOME_Y 38 6 | #elif PBL_ROUND 7 | #define HOME_X 36 8 | #define HOME_Y 45 9 | #endif 10 | 11 | bool error; 12 | 13 | int timestampY, timestampSize; 14 | 15 | char titleBuffer[128], bodyBuffer[1024], timestampBuffer[16]; 16 | 17 | extern GFont custom_leco; 18 | 19 | extern int scroll; 20 | 21 | extern int langOffsetX, langOffsetY; 22 | 23 | extern char homeTextBuffer[32]; 24 | extern char homeTimeBuffer[16]; 25 | 26 | extern char titleTimeBuffer[16]; 27 | 28 | void set_lang_offsets(); 29 | 30 | void update_text(const char *title, const char *body); 31 | 32 | void set_night_mode_palette(); 33 | 34 | void set_size(bool new); 35 | 36 | void init_ui(); 37 | void reinit_round_fullscreen(); 38 | void deinit_ui(); -------------------------------------------------------------------------------- /src/c/c/widget.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "widget.h" 3 | #include "messaging.h" 4 | #include "ui.h" 5 | #include "main.h" 6 | 7 | #ifdef PBL_RECT 8 | #define WIDGET_BOX GRect(19,72,25,25) 9 | #define WIDGET_WEATHER_BOX GRect(21,72,25,25) 10 | #elif PBL_ROUND 11 | #define WIDGET_BOX GRect(33,79,25,25) 12 | #define WIDGET_WEATHER_BOX GRect(34,79,25,25) 13 | #endif 14 | 15 | int current_widget; 16 | 17 | GBitmap *current_widget_icon; 18 | 19 | //Initialize current_widget 20 | 21 | void set_widget_icon(); 22 | 23 | void init_widget(){ 24 | if(persist_exists(MESSAGE_KEY_Widget)) current_widget = persist_read_int(MESSAGE_KEY_Widget); 25 | else current_widget = WidgetNoneText; 26 | 27 | set_widget(current_widget); 28 | 29 | switch(current_widget){ 30 | case WidgetWeather: send_request("#weather"); break; 31 | case WidgetStockPrice: send_request("#stock"); break; 32 | case WidgetTravel: send_request("#travel"); break; 33 | } 34 | } 35 | 36 | void deinit_widget(){ 37 | if(current_widget == WidgetTimer || current_widget == WidgetSteps || current_widget == WidgetDistance || current_widget == WidgetHeartRate || current_widget == WidgetWeather || current_widget == WidgetCommute){ 38 | gbitmap_destroy(current_widget_icon); 39 | } 40 | } 41 | 42 | //Set new widget 43 | 44 | void set_widget(int num){ 45 | current_widget = num; 46 | 47 | persist_write_int(MESSAGE_KEY_Widget, num); 48 | 49 | set_widget_icon(); 50 | 51 | APP_LOG(APP_LOG_LEVEL_INFO, "Widget: %d", num); 52 | } 53 | 54 | //Set appropriate icon, based on current_widget 55 | 56 | void set_widget_icon(){ 57 | if(current_widget == WidgetTimer){ 58 | //Return Clock icon 59 | current_widget_icon = gbitmap_create_with_resource(RESOURCE_ID_CLOCK); 60 | } 61 | else if(current_widget == WidgetSteps || current_widget == WidgetDistance){ 62 | //Return Shoe icon 63 | current_widget_icon = gbitmap_create_with_resource(RESOURCE_ID_SHOE); 64 | } 65 | else if(current_widget == WidgetHeartRate){ 66 | //Return Heart icon 67 | current_widget_icon = gbitmap_create_with_resource(RESOURCE_ID_SHOE); //TEMPORARY 68 | } 69 | else if(current_widget == WidgetWeather){ 70 | //Return Weather icon 71 | current_widget_icon = gbitmap_create_with_resource(RESOURCE_ID_PARTLY_CLOUDY); 72 | } 73 | else if(current_widget == WidgetCommute){ 74 | //Return Car icon 75 | current_widget_icon = gbitmap_create_with_resource(RESOURCE_ID_CAR); 76 | } 77 | } 78 | 79 | //Draw Widget 80 | 81 | void graphics_draw_widget(GContext *ctx){ 82 | 83 | if(current_widget == WidgetCountdown){ //Full Width 84 | #ifdef PBL_RECT 85 | graphics_draw_text(ctx, "52D 3H 2M", custom_leco, GRect(HOME_X-16+38 - 38, HOME_Y + 32 + 5 - 1, 144-ACTION_BAR_WIDTH-8-48 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 86 | #elif PBL_ROUND 87 | graphics_draw_text(ctx, "52D 3H 2M", custom_leco, GRect(HOME_X-14 + 34 - 37, HOME_Y + 33 + 4 + 1 - 1, 144-ACTION_BAR_WIDTH-6 - 38 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 88 | #endif 89 | } 90 | else if(current_widget == WidgetDate1){ //Full Width 91 | #ifdef PBL_RECT 92 | graphics_draw_text(ctx, widget_date_1, custom_leco, GRect(HOME_X-16+38 - 38, HOME_Y + 32 + 5 - 1, 144-ACTION_BAR_WIDTH-8-48 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 93 | #elif PBL_ROUND 94 | graphics_draw_text(ctx, widget_date_1, custom_leco, GRect(HOME_X-14 + 34 - 37, HOME_Y + 33 + 4 + 1 - 1, 144-ACTION_BAR_WIDTH-6 - 38 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 95 | #endif 96 | } 97 | else if(current_widget == WidgetDate2){ //Full Width 98 | #ifdef PBL_RECT 99 | graphics_draw_text(ctx, widget_date_2, custom_leco, GRect(HOME_X-16+38 - 38, HOME_Y + 32 + 5 - 1, 144-ACTION_BAR_WIDTH-8-48 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 100 | #elif PBL_ROUND 101 | graphics_draw_text(ctx, widget_date_2, custom_leco, GRect(HOME_X-14 + 34 - 37, HOME_Y + 33 + 4 + 1 - 1, 144-ACTION_BAR_WIDTH-6 - 38 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 102 | #endif 103 | } 104 | else if(current_widget == WidgetDate3){ //Full Width 105 | #ifdef PBL_RECT 106 | graphics_draw_text(ctx, widget_date_3, custom_leco, GRect(HOME_X-16+38 - 38, HOME_Y + 32 + 5 - 1, 144-ACTION_BAR_WIDTH-8-48 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 107 | #elif PBL_ROUND 108 | graphics_draw_text(ctx, widget_date_3, custom_leco, GRect(HOME_X-14 + 34 - 37, HOME_Y + 33 + 4 + 1 - 1, 144-ACTION_BAR_WIDTH-6 - 38 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 109 | #endif 110 | } 111 | else if(current_widget == WidgetDate4){ //Full Width 112 | #ifdef PBL_RECT 113 | graphics_draw_text(ctx, widget_date_4, custom_leco, GRect(HOME_X-16+38 - 38, HOME_Y + 32 + 5 - 1, 144-ACTION_BAR_WIDTH-8-48 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 114 | #elif PBL_ROUND 115 | graphics_draw_text(ctx, widget_date_4, custom_leco, GRect(HOME_X-14 + 34 - 37, HOME_Y + 33 + 4 + 1 - 1, 144-ACTION_BAR_WIDTH-6 - 38 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 116 | #endif 117 | } 118 | else if(current_widget == WidgetDate5){ 119 | #ifdef PBL_RECT 120 | graphics_draw_text(ctx, widget_date_5, custom_leco, GRect(HOME_X-16+38 - 38, HOME_Y + 32 + 5 - 1, 144-ACTION_BAR_WIDTH-8-48 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 121 | #elif PBL_ROUND 122 | graphics_draw_text(ctx, widget_date_5, custom_leco, GRect(HOME_X-14 + 34 - 37, HOME_Y + 33 + 4 + 1 - 1, 144-ACTION_BAR_WIDTH-6 - 38 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 123 | #endif 124 | } 125 | else if(current_widget == WidgetSteps){ //Shoe Icon 126 | #ifdef PBL_RECT 127 | graphics_draw_text(ctx, widget_steps, custom_leco, GRect(HOME_X-16+38 + 1, HOME_Y + 32 + 5, 144-ACTION_BAR_WIDTH-8-48, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 128 | #elif PBL_ROUND 129 | graphics_draw_text(ctx, widget_steps, custom_leco, GRect(HOME_X-14 + 34 + 1, HOME_Y + 33 + 4 + 1, 144-ACTION_BAR_WIDTH-6 - 38, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 130 | #endif 131 | 132 | graphics_context_set_compositing_mode(ctx, GCompOpSet); 133 | graphics_draw_bitmap_in_rect(ctx, current_widget_icon, WIDGET_BOX); 134 | } 135 | else if(current_widget == WidgetDistance){ //Shoe Icon 136 | #ifdef PBL_RECT 137 | graphics_draw_text(ctx, widget_dist, custom_leco, GRect(HOME_X-16+38 + 1, HOME_Y + 32 + 5, 144-ACTION_BAR_WIDTH-8-48, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 138 | #elif PBL_ROUND 139 | graphics_draw_text(ctx, widget_dist, custom_leco, GRect(HOME_X-14 + 34 + 1, HOME_Y + 33 + 4 + 1, 144-ACTION_BAR_WIDTH-6 - 38, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 140 | #endif 141 | 142 | graphics_context_set_compositing_mode(ctx, GCompOpSet); 143 | graphics_draw_bitmap_in_rect(ctx, current_widget_icon, WIDGET_BOX); 144 | } 145 | /* 146 | else if(current_widget == WidgetHeartRate){ //Heart Icon 147 | #ifdef PBL_RECT 148 | graphics_draw_text(ctx, widget_hr, custom_leco, GRect(HOME_X-16+38 + 1, HOME_Y + 32 + 5, 144-ACTION_BAR_WIDTH-8-48, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 149 | #elif PBL_ROUND 150 | graphics_draw_text(ctx, widget_hr, custom_leco, GRect(HOME_X-14 + 34 + 1, HOME_Y + 33 + 4 + 1, 144-ACTION_BAR_WIDTH-6 - 38, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 151 | #endif 152 | 153 | graphics_context_set_compositing_mode(ctx, GCompOpSet); 154 | graphics_draw_bitmap_in_rect(ctx, current_widget_icon, WIDGET_BOX); 155 | } 156 | */ 157 | else if(current_widget == WidgetWeather){ //Appropriate Weather Icon 158 | #ifdef PBL_RECT 159 | graphics_draw_text(ctx, "102* F", custom_leco, GRect(HOME_X-16+38 + 1 + 2, HOME_Y + 32 + 5, 144-ACTION_BAR_WIDTH-8-48, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 160 | #elif PBL_ROUND 161 | graphics_draw_text(ctx, "102* F", custom_leco, GRect(HOME_X-14 + 34 + 1 + 2, HOME_Y + 33 + 4 + 1, 144-ACTION_BAR_WIDTH-6 - 38, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 162 | #endif 163 | 164 | graphics_context_set_compositing_mode(ctx, GCompOpSet); 165 | graphics_draw_bitmap_in_rect(ctx, current_widget_icon, WIDGET_WEATHER_BOX); 166 | } 167 | else if(current_widget == WidgetStockPrice){ //Full Width 168 | #ifdef PBL_RECT 169 | graphics_draw_text(ctx, "MSFT 255.42", custom_leco, GRect(HOME_X-16+38 - 38, HOME_Y + 32 + 5 - 1, 144-ACTION_BAR_WIDTH-8-48 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 170 | #elif PBL_ROUND 171 | graphics_draw_text(ctx, "MSFT 255.42", custom_leco, GRect(HOME_X-14 + 34 - 37, HOME_Y + 33 + 4 + 1 - 1, 144-ACTION_BAR_WIDTH-6 - 38 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 172 | #endif 173 | } 174 | else if(current_widget == WidgetTimezone){ //Full Width 175 | #ifdef PBL_RECT 176 | graphics_draw_text(ctx, widget_alt_tz, custom_leco, GRect(HOME_X-16+38 - 38, HOME_Y + 32 + 5 - 1, 144-ACTION_BAR_WIDTH-8-48 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 177 | #elif PBL_ROUND 178 | graphics_draw_text(ctx, widget_alt_tz, custom_leco, GRect(HOME_X-14 + 34 - 37, HOME_Y + 33 + 4 + 1 - 1, 144-ACTION_BAR_WIDTH-6 - 38 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 179 | #endif 180 | } 181 | else if(current_widget == WidgetBattery){ //Battery Icon 182 | #ifdef PBL_RECT 183 | graphics_draw_text(ctx, widget_battery, custom_leco, GRect(HOME_X-16+38 + 1 + 4, HOME_Y + 32 + 5, 144-ACTION_BAR_WIDTH-8-48, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 184 | #elif PBL_ROUND 185 | graphics_draw_text(ctx, widget_battery, custom_leco, GRect(HOME_X-14 + 34 + 1 + 4, HOME_Y + 33 + 4 + 1, 144-ACTION_BAR_WIDTH-6 - 38, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 186 | #endif 187 | 188 | graphics_context_set_stroke_color(ctx, GColorBlack); 189 | graphics_context_set_stroke_width(ctx, 3); 190 | graphics_draw_rect(ctx, GRect(HOME_X+4,HOME_Y+40+PBL_IF_RECT_ELSE(-1,0),24,14)); 191 | graphics_draw_line(ctx, GPoint(HOME_X+4+24+1,HOME_Y+44+PBL_IF_RECT_ELSE(-1,0)), GPoint(HOME_X+4+24+1,HOME_Y+44+6+PBL_IF_RECT_ELSE(-1,0))); 192 | 193 | graphics_context_set_stroke_width(ctx, 1); 194 | for(int i = 0; i < battery_state_service_peek().charge_percent/10; i++){ 195 | graphics_draw_line(ctx, GPoint(HOME_X+7+(2*i),HOME_Y+42+PBL_IF_RECT_ELSE(-1,0)), GPoint(HOME_X+7+(2*i),HOME_Y+42+10+PBL_IF_RECT_ELSE(-1,0))); 196 | } 197 | } 198 | else if(current_widget == WidgetSmartWidget){ //??? 199 | #ifdef PBL_RECT 200 | graphics_draw_text(ctx, "???", custom_leco, GRect(HOME_X-16+38 - 38, HOME_Y + 32 + 5 - 1, 144-ACTION_BAR_WIDTH-8-48 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 201 | #elif PBL_ROUND 202 | graphics_draw_text(ctx, "???", custom_leco, GRect(HOME_X-14 + 34 - 37, HOME_Y + 33 + 4 + 1 - 1, 144-ACTION_BAR_WIDTH-6 - 38 + 50, 60), GTextOverflowModeTrailingEllipsis, GTextAlignmentCenter, NULL); 203 | #endif 204 | 205 | //graphics_context_set_compositing_mode(ctx, GCompOpSet); 206 | //graphics_draw_bitmap_in_rect(ctx, current_widget_icon, WIDGET_BOX); 207 | } 208 | } -------------------------------------------------------------------------------- /src/c/c/widget.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum { 4 | WidgetNoneText = 0, 5 | WidgetNoneTime = 1, 6 | WidgetTimer = 2, 7 | WidgetCountdown = 3, 8 | WidgetDate1 = 4, 9 | WidgetDate2 = 5, 10 | WidgetDate3 = 6, 11 | WidgetDate4 = 7, 12 | WidgetSteps = 8, 13 | WidgetDistance = 9, 14 | WidgetHeartRate = 10, 15 | WidgetTravel = 11, 16 | WidgetWeather = 12, 17 | WidgetStockPrice = 13, 18 | WidgetCommute = 14, 19 | WidgetTimezone = 15, 20 | WidgetBattery = 16, 21 | WidgetSmartWidget = 17, 22 | WidgetDate5 = 18 23 | }; 24 | 25 | extern int current_widget; 26 | 27 | void init_widget(); 28 | void deinit_widget(); 29 | 30 | void set_widget(int num); 31 | 32 | void graphics_draw_widget(GContext *ctx); -------------------------------------------------------------------------------- /src/pkjs/app-config-da.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | "type" : "section", 4 | "items" : [ 5 | { 6 | "type" : "heading", 7 | "defaultValue" : "Options" 8 | }, 9 | { 10 | "type" : "select", 11 | "messageKey" : "TemperatureUnit", 12 | "label" : "Temperature", 13 | "defaultValue" : "f", 14 | "options" : [ 15 | { 16 | "label" : "Fahrenheit", 17 | "value" : "f" 18 | }, 19 | { 20 | "label" : "Celsius", 21 | "value" : "c" 22 | } 23 | ] 24 | }, 25 | { 26 | "type" : "select", 27 | "messageKey" : "FontSize", 28 | "label" : "Font Size", 29 | "defaultValue" : 1, 30 | "options" : [ 31 | { 32 | "label" : "Small", 33 | "value" : 1 34 | }, 35 | { 36 | "label" : "Large", 37 | "value" : 0 38 | } 39 | ] 40 | }, 41 | { 42 | "type" : "select", 43 | "messageKey" : "DistanceUnit", 44 | "label" : "Distance", 45 | "defaultValue" : 0, 46 | "options" : [ 47 | { 48 | "label" : "Imperial", 49 | "value" : 0 50 | }, 51 | { 52 | "label" : "Metric", 53 | "value" : 1 54 | } 55 | ] 56 | } 57 | ] 58 | }, 59 | { 60 | "type" : "section", 61 | "items" : [ 62 | { 63 | "type" : "heading", 64 | "defaultValue" : "Features" 65 | }, 66 | { 67 | "type" : "toggle", 68 | "messageKey" : "HandsFree", 69 | "label" : "Hands Free Mode", 70 | "description" : "Launch Snowy with a flick of the wrist, and flick again to speak a command." 71 | }, 72 | { 73 | "type" : "toggle", 74 | "messageKey" : "FlickToDismiss", 75 | "label" : "Flick to Dismiss", 76 | "description" : "Once you receive a reply from Snowy, flick your wrist to dismiss it." 77 | }, 78 | { 79 | "type" : "toggle", 80 | "messageKey" : "QuickExit", 81 | "label" : "Quick Exit", 82 | "description" : "Once you receive a reply from Snowy, hitting the Back button will exit the app." 83 | }, 84 | { 85 | "type" : "toggle", 86 | "messageKey" : "ConfirmDictation", 87 | "label" : "Confirm Dictation", 88 | "description" : "After you speak a command, your speech will be displayed on-screen before Snowy receives it." 89 | }, 90 | { 91 | "type" : "toggle", 92 | "messageKey" : "Fullscreen", 93 | "label" : "Fullscreen Replies", 94 | "description" : "Replies will take up the entire screen, rather than the default Notification style." 95 | }, 96 | { 97 | "type" : "toggle", 98 | "messageKey" : "NightMode", 99 | "label" : "Night Mode", 100 | "description" : "Replies will appear in White text on a Black background." 101 | } 102 | ] 103 | }, 104 | { 105 | "type" : "section", 106 | "items" : [ 107 | { 108 | "type" : "heading", 109 | "defaultValue" : "Home Screen" 110 | }, 111 | { 112 | "type" : "select", 113 | "messageKey" : "Widget", 114 | "defaultValue" : 0, 115 | "id" : "widget", 116 | "label" : "Widget", 117 | "description" : "* Requires appropriate API Key.", 118 | "options" : [ 119 | { 120 | "label" : "None (Text)", 121 | "value" : 0 122 | }, 123 | { 124 | "label" : "None (Time)", 125 | "value" : 1 126 | }, 127 | /*{ 128 | "label" : "Timer", 129 | "value" : 2 130 | },*/ 131 | { 132 | "label" : "Countdown", 133 | "value" : 3 134 | }, 135 | { 136 | "label" : "Date (JAN 1)", 137 | "value" : 4 138 | }, 139 | { 140 | "label" : "Date (TUESDAY)", 141 | "value" : 5 142 | }, 143 | { 144 | "label" : "Date (TUES, JAN 1)", 145 | "value" : 18 146 | }, 147 | { 148 | "label" : "Date (10/25/16)", 149 | "value" : 6 150 | }, 151 | { 152 | "label" : "Date (25-10-16)", 153 | "value" : 7 154 | }, 155 | { 156 | "label" : "Steps", 157 | "value" : 8 158 | }, 159 | { 160 | "label" : "Distance", 161 | "value" : 9 162 | }, 163 | { 164 | "label" : "Heart Rate", 165 | "value" : 10 166 | }, 167 | /*{ 168 | "label" : "Travel*", 169 | "value" : 11 170 | },*/ 171 | { 172 | "label" : "Weather*", 173 | "value" : 12 174 | }, 175 | { 176 | "label" : "Stock Price", 177 | "value" : 13 178 | }, 179 | /*{ 180 | "label" : "Commute Time**", 181 | "value" : 14 182 | },*/ 183 | { 184 | "label" : "Alt. Timezone", 185 | "value" : 15 186 | }, 187 | { 188 | "label" : "Watch Battery %", 189 | "value" : 16 190 | }, 191 | /*{ 192 | "label" : "Smart Widget (Beta)", 193 | "value" : 17 194 | }*/ 195 | ] 196 | }, 197 | { 198 | "type" : "input", 199 | "id" : "countdownInput", 200 | "label" : "Countdown Date", 201 | "attributes" : { 202 | "placeholder" : "e.g. December 25" 203 | } 204 | }, 205 | { 206 | "type" : "input", 207 | "id" : "stockInput", 208 | "label" : "Stock Symbol", 209 | "attributes" : { 210 | "placeholder" : "e.g. AAPL", 211 | "maxlength" : 5 212 | } 213 | }, 214 | { 215 | "type" : "select", 216 | "id" : "timezoneInput", 217 | "options" : [ 218 | { 219 | "value" : "-12", 220 | "label" : "UTC-12" 221 | }, 222 | { 223 | "value" : "-11", 224 | "label" : "UTC-11" 225 | }, 226 | { 227 | "value" : "-10", 228 | "label" : "UTC-10 (e.g. Papeete, Honolulu)" 229 | }, 230 | { 231 | "value" : "-9", 232 | "label" : "UTC-9 (e.g. Anchorage)" 233 | }, 234 | { 235 | "value" : "-8", 236 | "label" : "UTC-8 (e.g. Los Angeles, Vancouver)" 237 | }, 238 | { 239 | "value" : "-7", 240 | "label" : "UTC-7 (e.g. Phoenix, Calgary)" 241 | }, 242 | { 243 | "value" : "-6", 244 | "label" : "UTC-6 (e.g. Chicago, Guatemala City)" 245 | }, 246 | { 247 | "value" : "-5", 248 | "label" : "UTC-5 (e.g. New York, Lima, Toronto)" 249 | }, 250 | { 251 | "value" : "-4", 252 | "label" : "UTC-4 (e.g. Santiago, La Paz, Manaus)" 253 | }, 254 | { 255 | "value" : "-3", 256 | "label" : "UTC-3 (e.g. Buenos Aires, São Paulo)" 257 | }, 258 | { 259 | "value" : "-2", 260 | "label" : "UTC-2" 261 | }, 262 | { 263 | "value" : "-1", 264 | "label" : "UTC-1" 265 | }, 266 | { 267 | "value" : "0", 268 | "label" : "UTC±0 (e.g. Casablanca, Dublin, London)" 269 | }, 270 | { 271 | "value" : "1", 272 | "label" : "UTC+1 (e.g. Berlin, Madrid, Paris, Rome)" 273 | }, 274 | { 275 | "value" : "2", 276 | "label" : "UTC+2 (e.g. Athens, Cairo, Istanbul)" 277 | }, 278 | { 279 | "value" : "3", 280 | "label" : "UTC+3 (e.g. Moscow, Nairobi, Baghdad)" 281 | }, 282 | { 283 | "value" : "4", 284 | "label" : "UTC+4 (e.g. Dubai)" 285 | }, 286 | { 287 | "value" : "5", 288 | "label" : "UTC+5 (e.g. Karachi, Yekaterinburg)" 289 | }, 290 | { 291 | "value" : "6", 292 | "label" : "UTC+6 (e.g. Almaty, Dhaka, Omsk)" 293 | }, 294 | { 295 | "value" : "7", 296 | "label" : "UTC+7 (e.g. Jakarta, Bangkok, Hanoi)" 297 | }, 298 | { 299 | "value" : "8", 300 | "label" : "UTC+8 (e.g. Beijing, Taipei, Singapore)" 301 | }, 302 | { 303 | "value" : "9", 304 | "label" : "UTC+9 (e.g. Seoul, Tokyo)" 305 | }, 306 | { 307 | "value" : "10", 308 | "label" : "UTC+10 (e.g. Sydney, Melbourne)" 309 | }, 310 | { 311 | "value" : "11", 312 | "label" : "UTC+11 (e.g. Noumea)" 313 | }, 314 | { 315 | "value" : "12", 316 | "label" : "UTC+12 (e.g. Auckland, Suva)" 317 | }, 318 | { 319 | "value" : "13", 320 | "label" : "UTC+13" 321 | }, 322 | { 323 | "value" : "14", 324 | "label" : "UTC+14" 325 | } 326 | ] 327 | } 328 | ] 329 | }, 330 | { 331 | "type" : "section", 332 | "items" : [ 333 | { 334 | "type" : "heading", 335 | "defaultValue" : "Health" 336 | }, 337 | { 338 | "type" : "input", 339 | "messageKey" : "StepGoal", 340 | "label" : "Step Goal", 341 | "attributes" : { 342 | "type" : "number" 343 | } 344 | }, 345 | { 346 | "type" : "toggle", 347 | "messageKey" : "InactivityMonitor", 348 | "label" : "Inactivity Monitor", 349 | "id" : "inactivityMonitor", 350 | "description" : "Snowy will ask you to go for a walk if you've been mostly inactive for an hour." 351 | }, 352 | { 353 | "type" : "input", 354 | "label" : "Start Time", 355 | "id" : "inactivityStart", 356 | "messageKey" : "InactivityStart", 357 | "attributes" : { 358 | "type" : "time" 359 | } 360 | }, 361 | { 362 | "type" : "input", 363 | "label" : "End Time", 364 | "id" : "inactivityEnd", 365 | "messageKey" : "InactivityEnd", 366 | "attributes" : { 367 | "type" : "time" 368 | } 369 | } 370 | ] 371 | }, 372 | { 373 | "type" : "section", 374 | "items" : [ 375 | { 376 | "type" : "heading", 377 | "defaultValue" : "Custom Commands" 378 | }, 379 | { 380 | "type" : "text", 381 | "defaultValue" : "Set up to four custom commands to appear in the Sample Command Menu (Down on the Home Screen)." 382 | }, 383 | { 384 | "type" : "input", 385 | "messageKey" : "Custom[0]", 386 | "attributes" : { 387 | "placeholder" : "e.g. Remind me to call the office at 2 pm" 388 | } 389 | }, 390 | { 391 | "type" : "input", 392 | "messageKey" : "Custom[1]", 393 | "attributes" : { 394 | "placeholder" : "e.g. Set a timer for 20 minutes" 395 | } 396 | 397 | }, 398 | { 399 | "type" : "input", 400 | "messageKey" : "Custom[2]", 401 | "attributes" : { 402 | "placeholder" : "e.g. Please turn my lights on" 403 | } 404 | }, 405 | { 406 | "type" : "input", 407 | "messageKey" : "Custom[3]", 408 | "attributes" : { 409 | "placeholder" : "e.g. Tell me the capital of Argentina" 410 | } 411 | }, 412 | { 413 | "type" : "input", 414 | "messageKey" : "Reddit", 415 | "label" : "Set a custom subreddit for \"What's new?\" command.", 416 | "attributes" : { 417 | "placeholder" : "e.g. news (default), worldnews, pebble, etc." 418 | } 419 | } 420 | ] 421 | }, 422 | { 423 | "type" : "section", 424 | "items" : [ 425 | { 426 | "type" : "heading", 427 | "defaultValue" : "Locations" 428 | }, 429 | { 430 | "type" : "input", 431 | "messageKey" : "HomeAddress", 432 | "label" : "Home Address" 433 | }, 434 | { 435 | "type" : "input", 436 | "messageKey" : "WorkAddress", 437 | "label" : "Work Address" 438 | } 439 | ] 440 | }, 441 | { 442 | "type" : "section", 443 | "items" : [ 444 | { 445 | "type" : "heading", 446 | "defaultValue" : "API Keys" 447 | }, 448 | { 449 | "type" : "toggle", 450 | "id" : "master", 451 | "messageKey" : "MasterKeyEnabled", 452 | "label" : "Enter API Keys via Master Key", 453 | "description" : "Please visit https://pmkey.xyz for more info.", 454 | "defaultValue" : true 455 | }, 456 | { 457 | "type" : "input", 458 | "label" : "Master Key Email", 459 | "id" : "masterEmail", 460 | "messageKey" : "MasterKeyEmail", 461 | "attributes" : { 462 | "type" : "email" 463 | } 464 | }, 465 | { 466 | "type" : "input", 467 | "label" : "Master Key PIN", 468 | "id" : "masterPIN", 469 | "messageKey" : "MasterKeyPIN", 470 | "attributes" : { 471 | "type" : "number" 472 | } 473 | }, 474 | { 475 | "type" : "button", 476 | "id" : "masterButton", 477 | "defaultValue" : "Sync API Keys" 478 | }, 479 | { 480 | "type" : "input", 481 | "label" : "IFTTT", 482 | "messageKey" : "IftttKey", 483 | "description" : "Use Snowy to trigger IFTTT Recipes! More information at mydogsnowy.com/ifttt.", 484 | "id" : "ifttt" 485 | }, 486 | { 487 | "type" : "toggle", 488 | "label" : "IFTTT Plus", 489 | "messageKey" : "IftttPlus", 490 | "id" : "iftttPlus", 491 | "description" : "Automatically send an event to your Maker Channel with every Reminder (snowy_reminder), Calendar Event (snowy_calendar), and Alarm (snowy_alarm).", 492 | "defaultValue" : true 493 | }, 494 | { 495 | "type" : "input", 496 | "label" : "Weather Underground", 497 | "messageKey" : "WuKey", 498 | "description" : "Snowy has a default Weather Underground API Key, but in order to receive more detailed weather reports and use the Weather widget, you'll need your own (it's free).", 499 | "id" : "weather" 500 | }, 501 | { 502 | "type" : "input", 503 | "label" : "Wolfram Alpha", 504 | "messageKey" : "WolframKey", 505 | "description" : "Snowy has a default Wolfram Alpha API Key, but if you'd like to track your own usage or just help relieve the burden on Snowy, you can provide your own.", 506 | "id" : "wolfram" 507 | }, 508 | { 509 | "type" : "input", 510 | "label" : "Travel Priority Access", 511 | "messageKey" : "TravelKey", 512 | "description" : "If you're a First Class user of Ronny Carr's Travel App, you can provide Snowy with your Travel ID to ask about flight times or gate information!", 513 | "id" : "travel" 514 | }, 515 | { 516 | "type" : "input", 517 | "label" : "My Habits", 518 | "messageKey" : "HabitsKey", 519 | "description" : "If you use Habits by Stephen Rees-Carter and have the My Habits upgrade, you can use Snowy to check in your current progress or streak count!", 520 | "id" : "habits" 521 | } 522 | ] 523 | }, 524 | { 525 | "type" : "submit", 526 | "defaultValue" : "Save Settings" 527 | } 528 | ]; -------------------------------------------------------------------------------- /src/pkjs/app-config-de.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | "type" : "section", 4 | "items" : [ 5 | { 6 | "type" : "heading", 7 | "defaultValue" : "Options" 8 | }, 9 | { 10 | "type" : "select", 11 | "messageKey" : "TemperatureUnit", 12 | "label" : "Temperature", 13 | "defaultValue" : "f", 14 | "options" : [ 15 | { 16 | "label" : "Fahrenheit", 17 | "value" : "f" 18 | }, 19 | { 20 | "label" : "Celsius", 21 | "value" : "c" 22 | } 23 | ] 24 | }, 25 | { 26 | "type" : "select", 27 | "messageKey" : "FontSize", 28 | "label" : "Font Size", 29 | "defaultValue" : 1, 30 | "options" : [ 31 | { 32 | "label" : "Small", 33 | "value" : 1 34 | }, 35 | { 36 | "label" : "Large", 37 | "value" : 0 38 | } 39 | ] 40 | }, 41 | { 42 | "type" : "select", 43 | "messageKey" : "DistanceUnit", 44 | "label" : "Distance", 45 | "defaultValue" : 0, 46 | "options" : [ 47 | { 48 | "label" : "Imperial", 49 | "value" : 0 50 | }, 51 | { 52 | "label" : "Metric", 53 | "value" : 1 54 | } 55 | ] 56 | } 57 | ] 58 | }, 59 | { 60 | "type" : "section", 61 | "items" : [ 62 | { 63 | "type" : "heading", 64 | "defaultValue" : "Features" 65 | }, 66 | { 67 | "type" : "toggle", 68 | "messageKey" : "HandsFree", 69 | "label" : "Hands Free Mode", 70 | "description" : "Launch Snowy with a flick of the wrist, and flick again to speak a command." 71 | }, 72 | { 73 | "type" : "toggle", 74 | "messageKey" : "FlickToDismiss", 75 | "label" : "Flick to Dismiss", 76 | "description" : "Once you receive a reply from Snowy, flick your wrist to dismiss it." 77 | }, 78 | { 79 | "type" : "toggle", 80 | "messageKey" : "QuickExit", 81 | "label" : "Quick Exit", 82 | "description" : "Once you receive a reply from Snowy, hitting the Back button will exit the app." 83 | }, 84 | { 85 | "type" : "toggle", 86 | "messageKey" : "ConfirmDictation", 87 | "label" : "Confirm Dictation", 88 | "description" : "After you speak a command, your speech will be displayed on-screen before Snowy receives it." 89 | }, 90 | { 91 | "type" : "toggle", 92 | "messageKey" : "Fullscreen", 93 | "label" : "Fullscreen Replies", 94 | "description" : "Replies will take up the entire screen, rather than the default Notification style." 95 | }, 96 | { 97 | "type" : "toggle", 98 | "messageKey" : "NightMode", 99 | "label" : "Night Mode", 100 | "description" : "Replies will appear in White text on a Black background." 101 | } 102 | ] 103 | }, 104 | { 105 | "type" : "section", 106 | "items" : [ 107 | { 108 | "type" : "heading", 109 | "defaultValue" : "Home Screen" 110 | }, 111 | { 112 | "type" : "select", 113 | "messageKey" : "Widget", 114 | "defaultValue" : 0, 115 | "id" : "widget", 116 | "label" : "Widget", 117 | "description" : "* Requires appropriate API Key.", 118 | "options" : [ 119 | { 120 | "label" : "None (Text)", 121 | "value" : 0 122 | }, 123 | { 124 | "label" : "None (Time)", 125 | "value" : 1 126 | }, 127 | /*{ 128 | "label" : "Timer", 129 | "value" : 2 130 | },*/ 131 | { 132 | "label" : "Countdown", 133 | "value" : 3 134 | }, 135 | { 136 | "label" : "Date (JAN 1)", 137 | "value" : 4 138 | }, 139 | { 140 | "label" : "Date (TUESDAY)", 141 | "value" : 5 142 | }, 143 | { 144 | "label" : "Date (TUES, JAN 1)", 145 | "value" : 18 146 | }, 147 | { 148 | "label" : "Date (10/25/16)", 149 | "value" : 6 150 | }, 151 | { 152 | "label" : "Date (25-10-16)", 153 | "value" : 7 154 | }, 155 | { 156 | "label" : "Steps", 157 | "value" : 8 158 | }, 159 | { 160 | "label" : "Distance", 161 | "value" : 9 162 | }, 163 | { 164 | "label" : "Heart Rate", 165 | "value" : 10 166 | }, 167 | /*{ 168 | "label" : "Travel*", 169 | "value" : 11 170 | },*/ 171 | { 172 | "label" : "Weather*", 173 | "value" : 12 174 | }, 175 | { 176 | "label" : "Stock Price", 177 | "value" : 13 178 | }, 179 | /*{ 180 | "label" : "Commute Time**", 181 | "value" : 14 182 | },*/ 183 | { 184 | "label" : "Alt. Timezone", 185 | "value" : 15 186 | }, 187 | { 188 | "label" : "Watch Battery %", 189 | "value" : 16 190 | }, 191 | /*{ 192 | "label" : "Smart Widget (Beta)", 193 | "value" : 17 194 | }*/ 195 | ] 196 | }, 197 | { 198 | "type" : "input", 199 | "id" : "countdownInput", 200 | "label" : "Countdown Date", 201 | "attributes" : { 202 | "placeholder" : "e.g. December 25" 203 | } 204 | }, 205 | { 206 | "type" : "input", 207 | "id" : "stockInput", 208 | "label" : "Stock Symbol", 209 | "attributes" : { 210 | "placeholder" : "e.g. AAPL", 211 | "maxlength" : 5 212 | } 213 | }, 214 | { 215 | "type" : "select", 216 | "id" : "timezoneInput", 217 | "options" : [ 218 | { 219 | "value" : "-12", 220 | "label" : "UTC-12" 221 | }, 222 | { 223 | "value" : "-11", 224 | "label" : "UTC-11" 225 | }, 226 | { 227 | "value" : "-10", 228 | "label" : "UTC-10 (e.g. Papeete, Honolulu)" 229 | }, 230 | { 231 | "value" : "-9", 232 | "label" : "UTC-9 (e.g. Anchorage)" 233 | }, 234 | { 235 | "value" : "-8", 236 | "label" : "UTC-8 (e.g. Los Angeles, Vancouver)" 237 | }, 238 | { 239 | "value" : "-7", 240 | "label" : "UTC-7 (e.g. Phoenix, Calgary)" 241 | }, 242 | { 243 | "value" : "-6", 244 | "label" : "UTC-6 (e.g. Chicago, Guatemala City)" 245 | }, 246 | { 247 | "value" : "-5", 248 | "label" : "UTC-5 (e.g. New York, Lima, Toronto)" 249 | }, 250 | { 251 | "value" : "-4", 252 | "label" : "UTC-4 (e.g. Santiago, La Paz, Manaus)" 253 | }, 254 | { 255 | "value" : "-3", 256 | "label" : "UTC-3 (e.g. Buenos Aires, São Paulo)" 257 | }, 258 | { 259 | "value" : "-2", 260 | "label" : "UTC-2" 261 | }, 262 | { 263 | "value" : "-1", 264 | "label" : "UTC-1" 265 | }, 266 | { 267 | "value" : "0", 268 | "label" : "UTC±0 (e.g. Casablanca, Dublin, London)" 269 | }, 270 | { 271 | "value" : "1", 272 | "label" : "UTC+1 (e.g. Berlin, Madrid, Paris, Rome)" 273 | }, 274 | { 275 | "value" : "2", 276 | "label" : "UTC+2 (e.g. Athens, Cairo, Istanbul)" 277 | }, 278 | { 279 | "value" : "3", 280 | "label" : "UTC+3 (e.g. Moscow, Nairobi, Baghdad)" 281 | }, 282 | { 283 | "value" : "4", 284 | "label" : "UTC+4 (e.g. Dubai)" 285 | }, 286 | { 287 | "value" : "5", 288 | "label" : "UTC+5 (e.g. Karachi, Yekaterinburg)" 289 | }, 290 | { 291 | "value" : "6", 292 | "label" : "UTC+6 (e.g. Almaty, Dhaka, Omsk)" 293 | }, 294 | { 295 | "value" : "7", 296 | "label" : "UTC+7 (e.g. Jakarta, Bangkok, Hanoi)" 297 | }, 298 | { 299 | "value" : "8", 300 | "label" : "UTC+8 (e.g. Beijing, Taipei, Singapore)" 301 | }, 302 | { 303 | "value" : "9", 304 | "label" : "UTC+9 (e.g. Seoul, Tokyo)" 305 | }, 306 | { 307 | "value" : "10", 308 | "label" : "UTC+10 (e.g. Sydney, Melbourne)" 309 | }, 310 | { 311 | "value" : "11", 312 | "label" : "UTC+11 (e.g. Noumea)" 313 | }, 314 | { 315 | "value" : "12", 316 | "label" : "UTC+12 (e.g. Auckland, Suva)" 317 | }, 318 | { 319 | "value" : "13", 320 | "label" : "UTC+13" 321 | }, 322 | { 323 | "value" : "14", 324 | "label" : "UTC+14" 325 | } 326 | ] 327 | } 328 | ] 329 | }, 330 | { 331 | "type" : "section", 332 | "items" : [ 333 | { 334 | "type" : "heading", 335 | "defaultValue" : "Health" 336 | }, 337 | { 338 | "type" : "input", 339 | "messageKey" : "StepGoal", 340 | "label" : "Step Goal", 341 | "attributes" : { 342 | "type" : "number" 343 | } 344 | }, 345 | { 346 | "type" : "toggle", 347 | "messageKey" : "InactivityMonitor", 348 | "label" : "Inactivity Monitor", 349 | "id" : "inactivityMonitor", 350 | "description" : "Snowy will ask you to go for a walk if you've been mostly inactive for an hour." 351 | }, 352 | { 353 | "type" : "input", 354 | "label" : "Start Time", 355 | "id" : "inactivityStart", 356 | "messageKey" : "InactivityStart", 357 | "attributes" : { 358 | "type" : "time" 359 | } 360 | }, 361 | { 362 | "type" : "input", 363 | "label" : "End Time", 364 | "id" : "inactivityEnd", 365 | "messageKey" : "InactivityEnd", 366 | "attributes" : { 367 | "type" : "time" 368 | } 369 | } 370 | ] 371 | }, 372 | { 373 | "type" : "section", 374 | "items" : [ 375 | { 376 | "type" : "heading", 377 | "defaultValue" : "Custom Commands" 378 | }, 379 | { 380 | "type" : "text", 381 | "defaultValue" : "Set up to four custom commands to appear in the Sample Command Menu (Down on the Home Screen)." 382 | }, 383 | { 384 | "type" : "input", 385 | "messageKey" : "Custom[0]", 386 | "attributes" : { 387 | "placeholder" : "e.g. Remind me to call the office at 2 pm" 388 | } 389 | }, 390 | { 391 | "type" : "input", 392 | "messageKey" : "Custom[1]", 393 | "attributes" : { 394 | "placeholder" : "e.g. Set a timer for 20 minutes" 395 | } 396 | 397 | }, 398 | { 399 | "type" : "input", 400 | "messageKey" : "Custom[2]", 401 | "attributes" : { 402 | "placeholder" : "e.g. Please turn my lights on" 403 | } 404 | }, 405 | { 406 | "type" : "input", 407 | "messageKey" : "Custom[3]", 408 | "attributes" : { 409 | "placeholder" : "e.g. Tell me the capital of Argentina" 410 | } 411 | }, 412 | { 413 | "type" : "input", 414 | "messageKey" : "Reddit", 415 | "label" : "Set a custom subreddit for \"What's new?\" command.", 416 | "attributes" : { 417 | "placeholder" : "e.g. news (default), worldnews, pebble, etc." 418 | } 419 | } 420 | ] 421 | }, 422 | { 423 | "type" : "section", 424 | "items" : [ 425 | { 426 | "type" : "heading", 427 | "defaultValue" : "Locations" 428 | }, 429 | { 430 | "type" : "input", 431 | "messageKey" : "HomeAddress", 432 | "label" : "Home Address" 433 | }, 434 | { 435 | "type" : "input", 436 | "messageKey" : "WorkAddress", 437 | "label" : "Work Address" 438 | } 439 | ] 440 | }, 441 | { 442 | "type" : "section", 443 | "items" : [ 444 | { 445 | "type" : "heading", 446 | "defaultValue" : "API Keys" 447 | }, 448 | { 449 | "type" : "toggle", 450 | "id" : "master", 451 | "messageKey" : "MasterKeyEnabled", 452 | "label" : "Enter API Keys via Master Key", 453 | "description" : "Please visit https://pmkey.xyz for more info.", 454 | "defaultValue" : true 455 | }, 456 | { 457 | "type" : "input", 458 | "label" : "Master Key Email", 459 | "id" : "masterEmail", 460 | "messageKey" : "MasterKeyEmail", 461 | "attributes" : { 462 | "type" : "email" 463 | } 464 | }, 465 | { 466 | "type" : "input", 467 | "label" : "Master Key PIN", 468 | "id" : "masterPIN", 469 | "messageKey" : "MasterKeyPIN", 470 | "attributes" : { 471 | "type" : "number" 472 | } 473 | }, 474 | { 475 | "type" : "button", 476 | "id" : "masterButton", 477 | "defaultValue" : "Sync API Keys" 478 | }, 479 | { 480 | "type" : "input", 481 | "label" : "IFTTT", 482 | "messageKey" : "IftttKey", 483 | "description" : "Use Snowy to trigger IFTTT Recipes! More information at mydogsnowy.com/ifttt.", 484 | "id" : "ifttt" 485 | }, 486 | { 487 | "type" : "toggle", 488 | "label" : "IFTTT Plus", 489 | "messageKey" : "IftttPlus", 490 | "id" : "iftttPlus", 491 | "description" : "Automatically send an event to your Maker Channel with every Reminder (snowy_reminder), Calendar Event (snowy_calendar), and Alarm (snowy_alarm).", 492 | "defaultValue" : true 493 | }, 494 | { 495 | "type" : "input", 496 | "label" : "Weather Underground", 497 | "messageKey" : "WuKey", 498 | "description" : "Snowy has a default Weather Underground API Key, but in order to receive more detailed weather reports and use the Weather widget, you'll need your own (it's free).", 499 | "id" : "weather" 500 | }, 501 | { 502 | "type" : "input", 503 | "label" : "Wolfram Alpha", 504 | "messageKey" : "WolframKey", 505 | "description" : "Snowy has a default Wolfram Alpha API Key, but if you'd like to track your own usage or just help relieve the burden on Snowy, you can provide your own.", 506 | "id" : "wolfram" 507 | }, 508 | { 509 | "type" : "input", 510 | "label" : "Travel Priority Access", 511 | "messageKey" : "TravelKey", 512 | "description" : "If you're a First Class user of Ronny Carr's Travel App, you can provide Snowy with your Travel ID to ask about flight times or gate information!", 513 | "id" : "travel" 514 | }, 515 | { 516 | "type" : "input", 517 | "label" : "My Habits", 518 | "messageKey" : "HabitsKey", 519 | "description" : "If you use Habits by Stephen Rees-Carter and have the My Habits upgrade, you can use Snowy to check in your current progress or streak count!", 520 | "id" : "habits" 521 | } 522 | ] 523 | }, 524 | { 525 | "type" : "submit", 526 | "defaultValue" : "Save Settings" 527 | } 528 | ]; -------------------------------------------------------------------------------- /src/pkjs/app-config-es.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | "type" : "section", 4 | "items" : [ 5 | { 6 | "type" : "heading", 7 | "defaultValue" : "Options" 8 | }, 9 | { 10 | "type" : "select", 11 | "messageKey" : "TemperatureUnit", 12 | "label" : "Temperature", 13 | "defaultValue" : "f", 14 | "options" : [ 15 | { 16 | "label" : "Fahrenheit", 17 | "value" : "f" 18 | }, 19 | { 20 | "label" : "Celsius", 21 | "value" : "c" 22 | } 23 | ] 24 | }, 25 | { 26 | "type" : "select", 27 | "messageKey" : "FontSize", 28 | "label" : "Font Size", 29 | "defaultValue" : 1, 30 | "options" : [ 31 | { 32 | "label" : "Small", 33 | "value" : 1 34 | }, 35 | { 36 | "label" : "Large", 37 | "value" : 0 38 | } 39 | ] 40 | }, 41 | { 42 | "type" : "select", 43 | "messageKey" : "DistanceUnit", 44 | "label" : "Distance", 45 | "defaultValue" : 0, 46 | "options" : [ 47 | { 48 | "label" : "Imperial", 49 | "value" : 0 50 | }, 51 | { 52 | "label" : "Metric", 53 | "value" : 1 54 | } 55 | ] 56 | } 57 | ] 58 | }, 59 | { 60 | "type" : "section", 61 | "items" : [ 62 | { 63 | "type" : "heading", 64 | "defaultValue" : "Features" 65 | }, 66 | { 67 | "type" : "toggle", 68 | "messageKey" : "HandsFree", 69 | "label" : "Hands Free Mode", 70 | "description" : "Launch Snowy with a flick of the wrist, and flick again to speak a command." 71 | }, 72 | { 73 | "type" : "toggle", 74 | "messageKey" : "FlickToDismiss", 75 | "label" : "Flick to Dismiss", 76 | "description" : "Once you receive a reply from Snowy, flick your wrist to dismiss it." 77 | }, 78 | { 79 | "type" : "toggle", 80 | "messageKey" : "QuickExit", 81 | "label" : "Quick Exit", 82 | "description" : "Once you receive a reply from Snowy, hitting the Back button will exit the app." 83 | }, 84 | { 85 | "type" : "toggle", 86 | "messageKey" : "ConfirmDictation", 87 | "label" : "Confirm Dictation", 88 | "description" : "After you speak a command, your speech will be displayed on-screen before Snowy receives it." 89 | }, 90 | { 91 | "type" : "toggle", 92 | "messageKey" : "Fullscreen", 93 | "label" : "Fullscreen Replies", 94 | "description" : "Replies will take up the entire screen, rather than the default Notification style." 95 | }, 96 | { 97 | "type" : "toggle", 98 | "messageKey" : "NightMode", 99 | "label" : "Night Mode", 100 | "description" : "Replies will appear in White text on a Black background." 101 | } 102 | ] 103 | }, 104 | { 105 | "type" : "section", 106 | "items" : [ 107 | { 108 | "type" : "heading", 109 | "defaultValue" : "Home Screen" 110 | }, 111 | { 112 | "type" : "select", 113 | "messageKey" : "Widget", 114 | "defaultValue" : 0, 115 | "id" : "widget", 116 | "label" : "Widget", 117 | "description" : "* Requires appropriate API Key.", 118 | "options" : [ 119 | { 120 | "label" : "None (Text)", 121 | "value" : 0 122 | }, 123 | { 124 | "label" : "None (Time)", 125 | "value" : 1 126 | }, 127 | /*{ 128 | "label" : "Timer", 129 | "value" : 2 130 | },*/ 131 | { 132 | "label" : "Countdown", 133 | "value" : 3 134 | }, 135 | { 136 | "label" : "Date (JAN 1)", 137 | "value" : 4 138 | }, 139 | { 140 | "label" : "Date (TUESDAY)", 141 | "value" : 5 142 | }, 143 | { 144 | "label" : "Date (TUES, JAN 1)", 145 | "value" : 18 146 | }, 147 | { 148 | "label" : "Date (10/25/16)", 149 | "value" : 6 150 | }, 151 | { 152 | "label" : "Date (25-10-16)", 153 | "value" : 7 154 | }, 155 | { 156 | "label" : "Steps", 157 | "value" : 8 158 | }, 159 | { 160 | "label" : "Distance", 161 | "value" : 9 162 | }, 163 | { 164 | "label" : "Heart Rate", 165 | "value" : 10 166 | }, 167 | /*{ 168 | "label" : "Travel*", 169 | "value" : 11 170 | },*/ 171 | { 172 | "label" : "Weather*", 173 | "value" : 12 174 | }, 175 | { 176 | "label" : "Stock Price", 177 | "value" : 13 178 | }, 179 | /*{ 180 | "label" : "Commute Time**", 181 | "value" : 14 182 | },*/ 183 | { 184 | "label" : "Alt. Timezone", 185 | "value" : 15 186 | }, 187 | { 188 | "label" : "Watch Battery %", 189 | "value" : 16 190 | }, 191 | /*{ 192 | "label" : "Smart Widget (Beta)", 193 | "value" : 17 194 | }*/ 195 | ] 196 | }, 197 | { 198 | "type" : "input", 199 | "id" : "countdownInput", 200 | "label" : "Countdown Date", 201 | "attributes" : { 202 | "placeholder" : "e.g. December 25" 203 | } 204 | }, 205 | { 206 | "type" : "input", 207 | "id" : "stockInput", 208 | "label" : "Stock Symbol", 209 | "attributes" : { 210 | "placeholder" : "e.g. AAPL", 211 | "maxlength" : 5 212 | } 213 | }, 214 | { 215 | "type" : "select", 216 | "id" : "timezoneInput", 217 | "options" : [ 218 | { 219 | "value" : "-12", 220 | "label" : "UTC-12" 221 | }, 222 | { 223 | "value" : "-11", 224 | "label" : "UTC-11" 225 | }, 226 | { 227 | "value" : "-10", 228 | "label" : "UTC-10 (e.g. Papeete, Honolulu)" 229 | }, 230 | { 231 | "value" : "-9", 232 | "label" : "UTC-9 (e.g. Anchorage)" 233 | }, 234 | { 235 | "value" : "-8", 236 | "label" : "UTC-8 (e.g. Los Angeles, Vancouver)" 237 | }, 238 | { 239 | "value" : "-7", 240 | "label" : "UTC-7 (e.g. Phoenix, Calgary)" 241 | }, 242 | { 243 | "value" : "-6", 244 | "label" : "UTC-6 (e.g. Chicago, Guatemala City)" 245 | }, 246 | { 247 | "value" : "-5", 248 | "label" : "UTC-5 (e.g. New York, Lima, Toronto)" 249 | }, 250 | { 251 | "value" : "-4", 252 | "label" : "UTC-4 (e.g. Santiago, La Paz, Manaus)" 253 | }, 254 | { 255 | "value" : "-3", 256 | "label" : "UTC-3 (e.g. Buenos Aires, São Paulo)" 257 | }, 258 | { 259 | "value" : "-2", 260 | "label" : "UTC-2" 261 | }, 262 | { 263 | "value" : "-1", 264 | "label" : "UTC-1" 265 | }, 266 | { 267 | "value" : "0", 268 | "label" : "UTC±0 (e.g. Casablanca, Dublin, London)" 269 | }, 270 | { 271 | "value" : "1", 272 | "label" : "UTC+1 (e.g. Berlin, Madrid, Paris, Rome)" 273 | }, 274 | { 275 | "value" : "2", 276 | "label" : "UTC+2 (e.g. Athens, Cairo, Istanbul)" 277 | }, 278 | { 279 | "value" : "3", 280 | "label" : "UTC+3 (e.g. Moscow, Nairobi, Baghdad)" 281 | }, 282 | { 283 | "value" : "4", 284 | "label" : "UTC+4 (e.g. Dubai)" 285 | }, 286 | { 287 | "value" : "5", 288 | "label" : "UTC+5 (e.g. Karachi, Yekaterinburg)" 289 | }, 290 | { 291 | "value" : "6", 292 | "label" : "UTC+6 (e.g. Almaty, Dhaka, Omsk)" 293 | }, 294 | { 295 | "value" : "7", 296 | "label" : "UTC+7 (e.g. Jakarta, Bangkok, Hanoi)" 297 | }, 298 | { 299 | "value" : "8", 300 | "label" : "UTC+8 (e.g. Beijing, Taipei, Singapore)" 301 | }, 302 | { 303 | "value" : "9", 304 | "label" : "UTC+9 (e.g. Seoul, Tokyo)" 305 | }, 306 | { 307 | "value" : "10", 308 | "label" : "UTC+10 (e.g. Sydney, Melbourne)" 309 | }, 310 | { 311 | "value" : "11", 312 | "label" : "UTC+11 (e.g. Noumea)" 313 | }, 314 | { 315 | "value" : "12", 316 | "label" : "UTC+12 (e.g. Auckland, Suva)" 317 | }, 318 | { 319 | "value" : "13", 320 | "label" : "UTC+13" 321 | }, 322 | { 323 | "value" : "14", 324 | "label" : "UTC+14" 325 | } 326 | ] 327 | } 328 | ] 329 | }, 330 | { 331 | "type" : "section", 332 | "items" : [ 333 | { 334 | "type" : "heading", 335 | "defaultValue" : "Health" 336 | }, 337 | { 338 | "type" : "input", 339 | "messageKey" : "StepGoal", 340 | "label" : "Step Goal", 341 | "attributes" : { 342 | "type" : "number" 343 | } 344 | }, 345 | { 346 | "type" : "toggle", 347 | "messageKey" : "InactivityMonitor", 348 | "label" : "Inactivity Monitor", 349 | "id" : "inactivityMonitor", 350 | "description" : "Snowy will ask you to go for a walk if you've been mostly inactive for an hour." 351 | }, 352 | { 353 | "type" : "input", 354 | "label" : "Start Time", 355 | "id" : "inactivityStart", 356 | "messageKey" : "InactivityStart", 357 | "attributes" : { 358 | "type" : "time" 359 | } 360 | }, 361 | { 362 | "type" : "input", 363 | "label" : "End Time", 364 | "id" : "inactivityEnd", 365 | "messageKey" : "InactivityEnd", 366 | "attributes" : { 367 | "type" : "time" 368 | } 369 | } 370 | ] 371 | }, 372 | { 373 | "type" : "section", 374 | "items" : [ 375 | { 376 | "type" : "heading", 377 | "defaultValue" : "Custom Commands" 378 | }, 379 | { 380 | "type" : "text", 381 | "defaultValue" : "Set up to four custom commands to appear in the Sample Command Menu (Down on the Home Screen)." 382 | }, 383 | { 384 | "type" : "input", 385 | "messageKey" : "Custom[0]", 386 | "attributes" : { 387 | "placeholder" : "e.g. Remind me to call the office at 2 pm" 388 | } 389 | }, 390 | { 391 | "type" : "input", 392 | "messageKey" : "Custom[1]", 393 | "attributes" : { 394 | "placeholder" : "e.g. Set a timer for 20 minutes" 395 | } 396 | 397 | }, 398 | { 399 | "type" : "input", 400 | "messageKey" : "Custom[2]", 401 | "attributes" : { 402 | "placeholder" : "e.g. Please turn my lights on" 403 | } 404 | }, 405 | { 406 | "type" : "input", 407 | "messageKey" : "Custom[3]", 408 | "attributes" : { 409 | "placeholder" : "e.g. Tell me the capital of Argentina" 410 | } 411 | }, 412 | { 413 | "type" : "input", 414 | "messageKey" : "Reddit", 415 | "label" : "Set a custom subreddit for \"What's new?\" command.", 416 | "attributes" : { 417 | "placeholder" : "e.g. news (default), worldnews, pebble, etc." 418 | } 419 | } 420 | ] 421 | }, 422 | { 423 | "type" : "section", 424 | "items" : [ 425 | { 426 | "type" : "heading", 427 | "defaultValue" : "Locations" 428 | }, 429 | { 430 | "type" : "input", 431 | "messageKey" : "HomeAddress", 432 | "label" : "Home Address" 433 | }, 434 | { 435 | "type" : "input", 436 | "messageKey" : "WorkAddress", 437 | "label" : "Work Address" 438 | } 439 | ] 440 | }, 441 | { 442 | "type" : "section", 443 | "items" : [ 444 | { 445 | "type" : "heading", 446 | "defaultValue" : "API Keys" 447 | }, 448 | { 449 | "type" : "toggle", 450 | "id" : "master", 451 | "messageKey" : "MasterKeyEnabled", 452 | "label" : "Enter API Keys via Master Key", 453 | "description" : "Please visit https://pmkey.xyz for more info.", 454 | "defaultValue" : true 455 | }, 456 | { 457 | "type" : "input", 458 | "label" : "Master Key Email", 459 | "id" : "masterEmail", 460 | "messageKey" : "MasterKeyEmail", 461 | "attributes" : { 462 | "type" : "email" 463 | } 464 | }, 465 | { 466 | "type" : "input", 467 | "label" : "Master Key PIN", 468 | "id" : "masterPIN", 469 | "messageKey" : "MasterKeyPIN", 470 | "attributes" : { 471 | "type" : "number" 472 | } 473 | }, 474 | { 475 | "type" : "button", 476 | "id" : "masterButton", 477 | "defaultValue" : "Sync API Keys" 478 | }, 479 | { 480 | "type" : "input", 481 | "label" : "IFTTT", 482 | "messageKey" : "IftttKey", 483 | "description" : "Use Snowy to trigger IFTTT Recipes! More information at mydogsnowy.com/ifttt.", 484 | "id" : "ifttt" 485 | }, 486 | { 487 | "type" : "toggle", 488 | "label" : "IFTTT Plus", 489 | "messageKey" : "IftttPlus", 490 | "id" : "iftttPlus", 491 | "description" : "Automatically send an event to your Maker Channel with every Reminder (snowy_reminder), Calendar Event (snowy_calendar), and Alarm (snowy_alarm).", 492 | "defaultValue" : true 493 | }, 494 | { 495 | "type" : "input", 496 | "label" : "Weather Underground", 497 | "messageKey" : "WuKey", 498 | "description" : "Snowy has a default Weather Underground API Key, but in order to receive more detailed weather reports and use the Weather widget, you'll need your own (it's free).", 499 | "id" : "weather" 500 | }, 501 | { 502 | "type" : "input", 503 | "label" : "Wolfram Alpha", 504 | "messageKey" : "WolframKey", 505 | "description" : "Snowy has a default Wolfram Alpha API Key, but if you'd like to track your own usage or just help relieve the burden on Snowy, you can provide your own.", 506 | "id" : "wolfram" 507 | }, 508 | { 509 | "type" : "input", 510 | "label" : "Travel Priority Access", 511 | "messageKey" : "TravelKey", 512 | "description" : "If you're a First Class user of Ronny Carr's Travel App, you can provide Snowy with your Travel ID to ask about flight times or gate information!", 513 | "id" : "travel" 514 | }, 515 | { 516 | "type" : "input", 517 | "label" : "My Habits", 518 | "messageKey" : "HabitsKey", 519 | "description" : "If you use Habits by Stephen Rees-Carter and have the My Habits upgrade, you can use Snowy to check in your current progress or streak count!", 520 | "id" : "habits" 521 | } 522 | ] 523 | }, 524 | { 525 | "type" : "submit", 526 | "defaultValue" : "Save Settings" 527 | } 528 | ]; -------------------------------------------------------------------------------- /src/pkjs/app-config-fr.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | "type" : "section", 4 | "items" : [ 5 | { 6 | "type" : "heading", 7 | "defaultValue" : "Options" 8 | }, 9 | { 10 | "type" : "select", 11 | "messageKey" : "TemperatureUnit", 12 | "label" : "Temperature", 13 | "defaultValue" : "f", 14 | "options" : [ 15 | { 16 | "label" : "Fahrenheit", 17 | "value" : "f" 18 | }, 19 | { 20 | "label" : "Celsius", 21 | "value" : "c" 22 | } 23 | ] 24 | }, 25 | { 26 | "type" : "select", 27 | "messageKey" : "FontSize", 28 | "label" : "Font Size", 29 | "defaultValue" : 1, 30 | "options" : [ 31 | { 32 | "label" : "Small", 33 | "value" : 1 34 | }, 35 | { 36 | "label" : "Large", 37 | "value" : 0 38 | } 39 | ] 40 | }, 41 | { 42 | "type" : "select", 43 | "messageKey" : "DistanceUnit", 44 | "label" : "Distance", 45 | "defaultValue" : 0, 46 | "options" : [ 47 | { 48 | "label" : "Imperial", 49 | "value" : 0 50 | }, 51 | { 52 | "label" : "Metric", 53 | "value" : 1 54 | } 55 | ] 56 | } 57 | ] 58 | }, 59 | { 60 | "type" : "section", 61 | "items" : [ 62 | { 63 | "type" : "heading", 64 | "defaultValue" : "Features" 65 | }, 66 | { 67 | "type" : "toggle", 68 | "messageKey" : "HandsFree", 69 | "label" : "Hands Free Mode", 70 | "description" : "Launch Snowy with a flick of the wrist, and flick again to speak a command." 71 | }, 72 | { 73 | "type" : "toggle", 74 | "messageKey" : "FlickToDismiss", 75 | "label" : "Flick to Dismiss", 76 | "description" : "Once you receive a reply from Snowy, flick your wrist to dismiss it." 77 | }, 78 | { 79 | "type" : "toggle", 80 | "messageKey" : "QuickExit", 81 | "label" : "Quick Exit", 82 | "description" : "Once you receive a reply from Snowy, hitting the Back button will exit the app." 83 | }, 84 | { 85 | "type" : "toggle", 86 | "messageKey" : "ConfirmDictation", 87 | "label" : "Confirm Dictation", 88 | "description" : "After you speak a command, your speech will be displayed on-screen before Snowy receives it." 89 | }, 90 | { 91 | "type" : "toggle", 92 | "messageKey" : "Fullscreen", 93 | "label" : "Fullscreen Replies", 94 | "description" : "Replies will take up the entire screen, rather than the default Notification style." 95 | }, 96 | { 97 | "type" : "toggle", 98 | "messageKey" : "NightMode", 99 | "label" : "Night Mode", 100 | "description" : "Replies will appear in White text on a Black background." 101 | } 102 | ] 103 | }, 104 | { 105 | "type" : "section", 106 | "items" : [ 107 | { 108 | "type" : "heading", 109 | "defaultValue" : "Home Screen" 110 | }, 111 | { 112 | "type" : "select", 113 | "messageKey" : "Widget", 114 | "defaultValue" : 0, 115 | "id" : "widget", 116 | "label" : "Widget", 117 | "description" : "* Requires appropriate API Key.", 118 | "options" : [ 119 | { 120 | "label" : "None (Text)", 121 | "value" : 0 122 | }, 123 | { 124 | "label" : "None (Time)", 125 | "value" : 1 126 | }, 127 | /*{ 128 | "label" : "Timer", 129 | "value" : 2 130 | },*/ 131 | { 132 | "label" : "Countdown", 133 | "value" : 3 134 | }, 135 | { 136 | "label" : "Date (JAN 1)", 137 | "value" : 4 138 | }, 139 | { 140 | "label" : "Date (TUESDAY)", 141 | "value" : 5 142 | }, 143 | { 144 | "label" : "Date (TUES, JAN 1)", 145 | "value" : 18 146 | }, 147 | { 148 | "label" : "Date (10/25/16)", 149 | "value" : 6 150 | }, 151 | { 152 | "label" : "Date (25-10-16)", 153 | "value" : 7 154 | }, 155 | { 156 | "label" : "Steps", 157 | "value" : 8 158 | }, 159 | { 160 | "label" : "Distance", 161 | "value" : 9 162 | }, 163 | { 164 | "label" : "Heart Rate", 165 | "value" : 10 166 | }, 167 | /*{ 168 | "label" : "Travel*", 169 | "value" : 11 170 | },*/ 171 | { 172 | "label" : "Weather*", 173 | "value" : 12 174 | }, 175 | { 176 | "label" : "Stock Price", 177 | "value" : 13 178 | }, 179 | /*{ 180 | "label" : "Commute Time**", 181 | "value" : 14 182 | },*/ 183 | { 184 | "label" : "Alt. Timezone", 185 | "value" : 15 186 | }, 187 | { 188 | "label" : "Watch Battery %", 189 | "value" : 16 190 | }, 191 | /*{ 192 | "label" : "Smart Widget (Beta)", 193 | "value" : 17 194 | }*/ 195 | ] 196 | }, 197 | { 198 | "type" : "input", 199 | "id" : "countdownInput", 200 | "label" : "Countdown Date", 201 | "attributes" : { 202 | "placeholder" : "e.g. December 25" 203 | } 204 | }, 205 | { 206 | "type" : "input", 207 | "id" : "stockInput", 208 | "label" : "Stock Symbol", 209 | "attributes" : { 210 | "placeholder" : "e.g. AAPL", 211 | "maxlength" : 5 212 | } 213 | }, 214 | { 215 | "type" : "select", 216 | "id" : "timezoneInput", 217 | "options" : [ 218 | { 219 | "value" : "-12", 220 | "label" : "UTC-12" 221 | }, 222 | { 223 | "value" : "-11", 224 | "label" : "UTC-11" 225 | }, 226 | { 227 | "value" : "-10", 228 | "label" : "UTC-10 (e.g. Papeete, Honolulu)" 229 | }, 230 | { 231 | "value" : "-9", 232 | "label" : "UTC-9 (e.g. Anchorage)" 233 | }, 234 | { 235 | "value" : "-8", 236 | "label" : "UTC-8 (e.g. Los Angeles, Vancouver)" 237 | }, 238 | { 239 | "value" : "-7", 240 | "label" : "UTC-7 (e.g. Phoenix, Calgary)" 241 | }, 242 | { 243 | "value" : "-6", 244 | "label" : "UTC-6 (e.g. Chicago, Guatemala City)" 245 | }, 246 | { 247 | "value" : "-5", 248 | "label" : "UTC-5 (e.g. New York, Lima, Toronto)" 249 | }, 250 | { 251 | "value" : "-4", 252 | "label" : "UTC-4 (e.g. Santiago, La Paz, Manaus)" 253 | }, 254 | { 255 | "value" : "-3", 256 | "label" : "UTC-3 (e.g. Buenos Aires, São Paulo)" 257 | }, 258 | { 259 | "value" : "-2", 260 | "label" : "UTC-2" 261 | }, 262 | { 263 | "value" : "-1", 264 | "label" : "UTC-1" 265 | }, 266 | { 267 | "value" : "0", 268 | "label" : "UTC±0 (e.g. Casablanca, Dublin, London)" 269 | }, 270 | { 271 | "value" : "1", 272 | "label" : "UTC+1 (e.g. Berlin, Madrid, Paris, Rome)" 273 | }, 274 | { 275 | "value" : "2", 276 | "label" : "UTC+2 (e.g. Athens, Cairo, Istanbul)" 277 | }, 278 | { 279 | "value" : "3", 280 | "label" : "UTC+3 (e.g. Moscow, Nairobi, Baghdad)" 281 | }, 282 | { 283 | "value" : "4", 284 | "label" : "UTC+4 (e.g. Dubai)" 285 | }, 286 | { 287 | "value" : "5", 288 | "label" : "UTC+5 (e.g. Karachi, Yekaterinburg)" 289 | }, 290 | { 291 | "value" : "6", 292 | "label" : "UTC+6 (e.g. Almaty, Dhaka, Omsk)" 293 | }, 294 | { 295 | "value" : "7", 296 | "label" : "UTC+7 (e.g. Jakarta, Bangkok, Hanoi)" 297 | }, 298 | { 299 | "value" : "8", 300 | "label" : "UTC+8 (e.g. Beijing, Taipei, Singapore)" 301 | }, 302 | { 303 | "value" : "9", 304 | "label" : "UTC+9 (e.g. Seoul, Tokyo)" 305 | }, 306 | { 307 | "value" : "10", 308 | "label" : "UTC+10 (e.g. Sydney, Melbourne)" 309 | }, 310 | { 311 | "value" : "11", 312 | "label" : "UTC+11 (e.g. Noumea)" 313 | }, 314 | { 315 | "value" : "12", 316 | "label" : "UTC+12 (e.g. Auckland, Suva)" 317 | }, 318 | { 319 | "value" : "13", 320 | "label" : "UTC+13" 321 | }, 322 | { 323 | "value" : "14", 324 | "label" : "UTC+14" 325 | } 326 | ] 327 | } 328 | ] 329 | }, 330 | { 331 | "type" : "section", 332 | "items" : [ 333 | { 334 | "type" : "heading", 335 | "defaultValue" : "Health" 336 | }, 337 | { 338 | "type" : "input", 339 | "messageKey" : "StepGoal", 340 | "label" : "Step Goal", 341 | "attributes" : { 342 | "type" : "number" 343 | } 344 | }, 345 | { 346 | "type" : "toggle", 347 | "messageKey" : "InactivityMonitor", 348 | "label" : "Inactivity Monitor", 349 | "id" : "inactivityMonitor", 350 | "description" : "Snowy will ask you to go for a walk if you've been mostly inactive for an hour." 351 | }, 352 | { 353 | "type" : "input", 354 | "label" : "Start Time", 355 | "id" : "inactivityStart", 356 | "messageKey" : "InactivityStart", 357 | "attributes" : { 358 | "type" : "time" 359 | } 360 | }, 361 | { 362 | "type" : "input", 363 | "label" : "End Time", 364 | "id" : "inactivityEnd", 365 | "messageKey" : "InactivityEnd", 366 | "attributes" : { 367 | "type" : "time" 368 | } 369 | } 370 | ] 371 | }, 372 | { 373 | "type" : "section", 374 | "items" : [ 375 | { 376 | "type" : "heading", 377 | "defaultValue" : "Custom Commands" 378 | }, 379 | { 380 | "type" : "text", 381 | "defaultValue" : "Set up to four custom commands to appear in the Sample Command Menu (Down on the Home Screen)." 382 | }, 383 | { 384 | "type" : "input", 385 | "messageKey" : "Custom[0]", 386 | "attributes" : { 387 | "placeholder" : "e.g. Remind me to call the office at 2 pm" 388 | } 389 | }, 390 | { 391 | "type" : "input", 392 | "messageKey" : "Custom[1]", 393 | "attributes" : { 394 | "placeholder" : "e.g. Set a timer for 20 minutes" 395 | } 396 | 397 | }, 398 | { 399 | "type" : "input", 400 | "messageKey" : "Custom[2]", 401 | "attributes" : { 402 | "placeholder" : "e.g. Please turn my lights on" 403 | } 404 | }, 405 | { 406 | "type" : "input", 407 | "messageKey" : "Custom[3]", 408 | "attributes" : { 409 | "placeholder" : "e.g. Tell me the capital of Argentina" 410 | } 411 | }, 412 | { 413 | "type" : "input", 414 | "messageKey" : "Reddit", 415 | "label" : "Set a custom subreddit for \"What's new?\" command.", 416 | "attributes" : { 417 | "placeholder" : "e.g. news (default), worldnews, pebble, etc." 418 | } 419 | } 420 | ] 421 | }, 422 | { 423 | "type" : "section", 424 | "items" : [ 425 | { 426 | "type" : "heading", 427 | "defaultValue" : "Locations" 428 | }, 429 | { 430 | "type" : "input", 431 | "messageKey" : "HomeAddress", 432 | "label" : "Home Address" 433 | }, 434 | { 435 | "type" : "input", 436 | "messageKey" : "WorkAddress", 437 | "label" : "Work Address" 438 | } 439 | ] 440 | }, 441 | { 442 | "type" : "section", 443 | "items" : [ 444 | { 445 | "type" : "heading", 446 | "defaultValue" : "API Keys" 447 | }, 448 | { 449 | "type" : "toggle", 450 | "id" : "master", 451 | "messageKey" : "MasterKeyEnabled", 452 | "label" : "Enter API Keys via Master Key", 453 | "description" : "Please visit https://pmkey.xyz for more info.", 454 | "defaultValue" : true 455 | }, 456 | { 457 | "type" : "input", 458 | "label" : "Master Key Email", 459 | "id" : "masterEmail", 460 | "messageKey" : "MasterKeyEmail", 461 | "attributes" : { 462 | "type" : "email" 463 | } 464 | }, 465 | { 466 | "type" : "input", 467 | "label" : "Master Key PIN", 468 | "id" : "masterPIN", 469 | "messageKey" : "MasterKeyPIN", 470 | "attributes" : { 471 | "type" : "number" 472 | } 473 | }, 474 | { 475 | "type" : "button", 476 | "id" : "masterButton", 477 | "defaultValue" : "Sync API Keys" 478 | }, 479 | { 480 | "type" : "input", 481 | "label" : "IFTTT", 482 | "messageKey" : "IftttKey", 483 | "description" : "Use Snowy to trigger IFTTT Recipes! More information at mydogsnowy.com/ifttt.", 484 | "id" : "ifttt" 485 | }, 486 | { 487 | "type" : "toggle", 488 | "label" : "IFTTT Plus", 489 | "messageKey" : "IftttPlus", 490 | "id" : "iftttPlus", 491 | "description" : "Automatically send an event to your Maker Channel with every Reminder (snowy_reminder), Calendar Event (snowy_calendar), and Alarm (snowy_alarm).", 492 | "defaultValue" : true 493 | }, 494 | { 495 | "type" : "input", 496 | "label" : "Weather Underground", 497 | "messageKey" : "WuKey", 498 | "description" : "Snowy has a default Weather Underground API Key, but in order to receive more detailed weather reports and use the Weather widget, you'll need your own (it's free).", 499 | "id" : "weather" 500 | }, 501 | { 502 | "type" : "input", 503 | "label" : "Wolfram Alpha", 504 | "messageKey" : "WolframKey", 505 | "description" : "Snowy has a default Wolfram Alpha API Key, but if you'd like to track your own usage or just help relieve the burden on Snowy, you can provide your own.", 506 | "id" : "wolfram" 507 | }, 508 | { 509 | "type" : "input", 510 | "label" : "Travel Priority Access", 511 | "messageKey" : "TravelKey", 512 | "description" : "If you're a First Class user of Ronny Carr's Travel App, you can provide Snowy with your Travel ID to ask about flight times or gate information!", 513 | "id" : "travel" 514 | }, 515 | { 516 | "type" : "input", 517 | "label" : "My Habits", 518 | "messageKey" : "HabitsKey", 519 | "description" : "If you use Habits by Stephen Rees-Carter and have the My Habits upgrade, you can use Snowy to check in your current progress or streak count!", 520 | "id" : "habits" 521 | } 522 | ] 523 | }, 524 | { 525 | "type" : "submit", 526 | "defaultValue" : "Save Settings" 527 | } 528 | ]; -------------------------------------------------------------------------------- /src/pkjs/app-config-pt.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | "type" : "section", 4 | "items" : [ 5 | { 6 | "type" : "heading", 7 | "defaultValue" : "Options" 8 | }, 9 | { 10 | "type" : "select", 11 | "messageKey" : "TemperatureUnit", 12 | "label" : "Temperature", 13 | "defaultValue" : "f", 14 | "options" : [ 15 | { 16 | "label" : "Fahrenheit", 17 | "value" : "f" 18 | }, 19 | { 20 | "label" : "Celsius", 21 | "value" : "c" 22 | } 23 | ] 24 | }, 25 | { 26 | "type" : "select", 27 | "messageKey" : "FontSize", 28 | "label" : "Font Size", 29 | "defaultValue" : 1, 30 | "options" : [ 31 | { 32 | "label" : "Small", 33 | "value" : 1 34 | }, 35 | { 36 | "label" : "Large", 37 | "value" : 0 38 | } 39 | ] 40 | }, 41 | { 42 | "type" : "select", 43 | "messageKey" : "DistanceUnit", 44 | "label" : "Distance", 45 | "defaultValue" : 0, 46 | "options" : [ 47 | { 48 | "label" : "Imperial", 49 | "value" : 0 50 | }, 51 | { 52 | "label" : "Metric", 53 | "value" : 1 54 | } 55 | ] 56 | } 57 | ] 58 | }, 59 | { 60 | "type" : "section", 61 | "items" : [ 62 | { 63 | "type" : "heading", 64 | "defaultValue" : "Features" 65 | }, 66 | { 67 | "type" : "toggle", 68 | "messageKey" : "HandsFree", 69 | "label" : "Hands Free Mode", 70 | "description" : "Launch Snowy with a flick of the wrist, and flick again to speak a command." 71 | }, 72 | { 73 | "type" : "toggle", 74 | "messageKey" : "FlickToDismiss", 75 | "label" : "Flick to Dismiss", 76 | "description" : "Once you receive a reply from Snowy, flick your wrist to dismiss it." 77 | }, 78 | { 79 | "type" : "toggle", 80 | "messageKey" : "QuickExit", 81 | "label" : "Quick Exit", 82 | "description" : "Once you receive a reply from Snowy, hitting the Back button will exit the app." 83 | }, 84 | { 85 | "type" : "toggle", 86 | "messageKey" : "ConfirmDictation", 87 | "label" : "Confirm Dictation", 88 | "description" : "After you speak a command, your speech will be displayed on-screen before Snowy receives it." 89 | }, 90 | { 91 | "type" : "toggle", 92 | "messageKey" : "Fullscreen", 93 | "label" : "Fullscreen Replies", 94 | "description" : "Replies will take up the entire screen, rather than the default Notification style." 95 | }, 96 | { 97 | "type" : "toggle", 98 | "messageKey" : "NightMode", 99 | "label" : "Night Mode", 100 | "description" : "Replies will appear in White text on a Black background." 101 | } 102 | ] 103 | }, 104 | { 105 | "type" : "section", 106 | "items" : [ 107 | { 108 | "type" : "heading", 109 | "defaultValue" : "Home Screen" 110 | }, 111 | { 112 | "type" : "select", 113 | "messageKey" : "Widget", 114 | "defaultValue" : 0, 115 | "id" : "widget", 116 | "label" : "Widget", 117 | "description" : "* Requires appropriate API Key.", 118 | "options" : [ 119 | { 120 | "label" : "None (Text)", 121 | "value" : 0 122 | }, 123 | { 124 | "label" : "None (Time)", 125 | "value" : 1 126 | }, 127 | /*{ 128 | "label" : "Timer", 129 | "value" : 2 130 | },*/ 131 | { 132 | "label" : "Countdown", 133 | "value" : 3 134 | }, 135 | { 136 | "label" : "Date (JAN 1)", 137 | "value" : 4 138 | }, 139 | { 140 | "label" : "Date (TUESDAY)", 141 | "value" : 5 142 | }, 143 | { 144 | "label" : "Date (TUES, JAN 1)", 145 | "value" : 18 146 | }, 147 | { 148 | "label" : "Date (10/25/16)", 149 | "value" : 6 150 | }, 151 | { 152 | "label" : "Date (25-10-16)", 153 | "value" : 7 154 | }, 155 | { 156 | "label" : "Steps", 157 | "value" : 8 158 | }, 159 | { 160 | "label" : "Distance", 161 | "value" : 9 162 | }, 163 | { 164 | "label" : "Heart Rate", 165 | "value" : 10 166 | }, 167 | /*{ 168 | "label" : "Travel*", 169 | "value" : 11 170 | },*/ 171 | { 172 | "label" : "Weather*", 173 | "value" : 12 174 | }, 175 | { 176 | "label" : "Stock Price", 177 | "value" : 13 178 | }, 179 | /*{ 180 | "label" : "Commute Time**", 181 | "value" : 14 182 | },*/ 183 | { 184 | "label" : "Alt. Timezone", 185 | "value" : 15 186 | }, 187 | { 188 | "label" : "Watch Battery %", 189 | "value" : 16 190 | }, 191 | /*{ 192 | "label" : "Smart Widget (Beta)", 193 | "value" : 17 194 | }*/ 195 | ] 196 | }, 197 | { 198 | "type" : "input", 199 | "id" : "countdownInput", 200 | "label" : "Countdown Date", 201 | "attributes" : { 202 | "placeholder" : "e.g. December 25" 203 | } 204 | }, 205 | { 206 | "type" : "input", 207 | "id" : "stockInput", 208 | "label" : "Stock Symbol", 209 | "attributes" : { 210 | "placeholder" : "e.g. AAPL", 211 | "maxlength" : 5 212 | } 213 | }, 214 | { 215 | "type" : "select", 216 | "id" : "timezoneInput", 217 | "options" : [ 218 | { 219 | "value" : "-12", 220 | "label" : "UTC-12" 221 | }, 222 | { 223 | "value" : "-11", 224 | "label" : "UTC-11" 225 | }, 226 | { 227 | "value" : "-10", 228 | "label" : "UTC-10 (e.g. Papeete, Honolulu)" 229 | }, 230 | { 231 | "value" : "-9", 232 | "label" : "UTC-9 (e.g. Anchorage)" 233 | }, 234 | { 235 | "value" : "-8", 236 | "label" : "UTC-8 (e.g. Los Angeles, Vancouver)" 237 | }, 238 | { 239 | "value" : "-7", 240 | "label" : "UTC-7 (e.g. Phoenix, Calgary)" 241 | }, 242 | { 243 | "value" : "-6", 244 | "label" : "UTC-6 (e.g. Chicago, Guatemala City)" 245 | }, 246 | { 247 | "value" : "-5", 248 | "label" : "UTC-5 (e.g. New York, Lima, Toronto)" 249 | }, 250 | { 251 | "value" : "-4", 252 | "label" : "UTC-4 (e.g. Santiago, La Paz, Manaus)" 253 | }, 254 | { 255 | "value" : "-3", 256 | "label" : "UTC-3 (e.g. Buenos Aires, São Paulo)" 257 | }, 258 | { 259 | "value" : "-2", 260 | "label" : "UTC-2" 261 | }, 262 | { 263 | "value" : "-1", 264 | "label" : "UTC-1" 265 | }, 266 | { 267 | "value" : "0", 268 | "label" : "UTC±0 (e.g. Casablanca, Dublin, London)" 269 | }, 270 | { 271 | "value" : "1", 272 | "label" : "UTC+1 (e.g. Berlin, Madrid, Paris, Rome)" 273 | }, 274 | { 275 | "value" : "2", 276 | "label" : "UTC+2 (e.g. Athens, Cairo, Istanbul)" 277 | }, 278 | { 279 | "value" : "3", 280 | "label" : "UTC+3 (e.g. Moscow, Nairobi, Baghdad)" 281 | }, 282 | { 283 | "value" : "4", 284 | "label" : "UTC+4 (e.g. Dubai)" 285 | }, 286 | { 287 | "value" : "5", 288 | "label" : "UTC+5 (e.g. Karachi, Yekaterinburg)" 289 | }, 290 | { 291 | "value" : "6", 292 | "label" : "UTC+6 (e.g. Almaty, Dhaka, Omsk)" 293 | }, 294 | { 295 | "value" : "7", 296 | "label" : "UTC+7 (e.g. Jakarta, Bangkok, Hanoi)" 297 | }, 298 | { 299 | "value" : "8", 300 | "label" : "UTC+8 (e.g. Beijing, Taipei, Singapore)" 301 | }, 302 | { 303 | "value" : "9", 304 | "label" : "UTC+9 (e.g. Seoul, Tokyo)" 305 | }, 306 | { 307 | "value" : "10", 308 | "label" : "UTC+10 (e.g. Sydney, Melbourne)" 309 | }, 310 | { 311 | "value" : "11", 312 | "label" : "UTC+11 (e.g. Noumea)" 313 | }, 314 | { 315 | "value" : "12", 316 | "label" : "UTC+12 (e.g. Auckland, Suva)" 317 | }, 318 | { 319 | "value" : "13", 320 | "label" : "UTC+13" 321 | }, 322 | { 323 | "value" : "14", 324 | "label" : "UTC+14" 325 | } 326 | ] 327 | } 328 | ] 329 | }, 330 | { 331 | "type" : "section", 332 | "items" : [ 333 | { 334 | "type" : "heading", 335 | "defaultValue" : "Health" 336 | }, 337 | { 338 | "type" : "input", 339 | "messageKey" : "StepGoal", 340 | "label" : "Step Goal", 341 | "attributes" : { 342 | "type" : "number" 343 | } 344 | }, 345 | { 346 | "type" : "toggle", 347 | "messageKey" : "InactivityMonitor", 348 | "label" : "Inactivity Monitor", 349 | "id" : "inactivityMonitor", 350 | "description" : "Snowy will ask you to go for a walk if you've been mostly inactive for an hour." 351 | }, 352 | { 353 | "type" : "input", 354 | "label" : "Start Time", 355 | "id" : "inactivityStart", 356 | "messageKey" : "InactivityStart", 357 | "attributes" : { 358 | "type" : "time" 359 | } 360 | }, 361 | { 362 | "type" : "input", 363 | "label" : "End Time", 364 | "id" : "inactivityEnd", 365 | "messageKey" : "InactivityEnd", 366 | "attributes" : { 367 | "type" : "time" 368 | } 369 | } 370 | ] 371 | }, 372 | { 373 | "type" : "section", 374 | "items" : [ 375 | { 376 | "type" : "heading", 377 | "defaultValue" : "Custom Commands" 378 | }, 379 | { 380 | "type" : "text", 381 | "defaultValue" : "Set up to four custom commands to appear in the Sample Command Menu (Down on the Home Screen)." 382 | }, 383 | { 384 | "type" : "input", 385 | "messageKey" : "Custom[0]", 386 | "attributes" : { 387 | "placeholder" : "e.g. Remind me to call the office at 2 pm" 388 | } 389 | }, 390 | { 391 | "type" : "input", 392 | "messageKey" : "Custom[1]", 393 | "attributes" : { 394 | "placeholder" : "e.g. Set a timer for 20 minutes" 395 | } 396 | 397 | }, 398 | { 399 | "type" : "input", 400 | "messageKey" : "Custom[2]", 401 | "attributes" : { 402 | "placeholder" : "e.g. Please turn my lights on" 403 | } 404 | }, 405 | { 406 | "type" : "input", 407 | "messageKey" : "Custom[3]", 408 | "attributes" : { 409 | "placeholder" : "e.g. Tell me the capital of Argentina" 410 | } 411 | }, 412 | { 413 | "type" : "input", 414 | "messageKey" : "Reddit", 415 | "label" : "Set a custom subreddit for \"What's new?\" command.", 416 | "attributes" : { 417 | "placeholder" : "e.g. news (default), worldnews, pebble, etc." 418 | } 419 | } 420 | ] 421 | }, 422 | { 423 | "type" : "section", 424 | "items" : [ 425 | { 426 | "type" : "heading", 427 | "defaultValue" : "Locations" 428 | }, 429 | { 430 | "type" : "input", 431 | "messageKey" : "HomeAddress", 432 | "label" : "Home Address" 433 | }, 434 | { 435 | "type" : "input", 436 | "messageKey" : "WorkAddress", 437 | "label" : "Work Address" 438 | } 439 | ] 440 | }, 441 | { 442 | "type" : "section", 443 | "items" : [ 444 | { 445 | "type" : "heading", 446 | "defaultValue" : "API Keys" 447 | }, 448 | { 449 | "type" : "toggle", 450 | "id" : "master", 451 | "messageKey" : "MasterKeyEnabled", 452 | "label" : "Enter API Keys via Master Key", 453 | "description" : "Please visit https://pmkey.xyz for more info.", 454 | "defaultValue" : true 455 | }, 456 | { 457 | "type" : "input", 458 | "label" : "Master Key Email", 459 | "id" : "masterEmail", 460 | "messageKey" : "MasterKeyEmail", 461 | "attributes" : { 462 | "type" : "email" 463 | } 464 | }, 465 | { 466 | "type" : "input", 467 | "label" : "Master Key PIN", 468 | "id" : "masterPIN", 469 | "messageKey" : "MasterKeyPIN", 470 | "attributes" : { 471 | "type" : "number" 472 | } 473 | }, 474 | { 475 | "type" : "button", 476 | "id" : "masterButton", 477 | "defaultValue" : "Sync API Keys" 478 | }, 479 | { 480 | "type" : "input", 481 | "label" : "IFTTT", 482 | "messageKey" : "IftttKey", 483 | "description" : "Use Snowy to trigger IFTTT Recipes! More information at mydogsnowy.com/ifttt.", 484 | "id" : "ifttt" 485 | }, 486 | { 487 | "type" : "toggle", 488 | "label" : "IFTTT Plus", 489 | "messageKey" : "IftttPlus", 490 | "id" : "iftttPlus", 491 | "description" : "Automatically send an event to your Maker Channel with every Reminder (snowy_reminder), Calendar Event (snowy_calendar), and Alarm (snowy_alarm).", 492 | "defaultValue" : true 493 | }, 494 | { 495 | "type" : "input", 496 | "label" : "Weather Underground", 497 | "messageKey" : "WuKey", 498 | "description" : "Snowy has a default Weather Underground API Key, but in order to receive more detailed weather reports and use the Weather widget, you'll need your own (it's free).", 499 | "id" : "weather" 500 | }, 501 | { 502 | "type" : "input", 503 | "label" : "Wolfram Alpha", 504 | "messageKey" : "WolframKey", 505 | "description" : "Snowy has a default Wolfram Alpha API Key, but if you'd like to track your own usage or just help relieve the burden on Snowy, you can provide your own.", 506 | "id" : "wolfram" 507 | }, 508 | { 509 | "type" : "input", 510 | "label" : "Travel Priority Access", 511 | "messageKey" : "TravelKey", 512 | "description" : "If you're a First Class user of Ronny Carr's Travel App, you can provide Snowy with your Travel ID to ask about flight times or gate information!", 513 | "id" : "travel" 514 | }, 515 | { 516 | "type" : "input", 517 | "label" : "My Habits", 518 | "messageKey" : "HabitsKey", 519 | "description" : "If you use Habits by Stephen Rees-Carter and have the My Habits upgrade, you can use Snowy to check in your current progress or streak count!", 520 | "id" : "habits" 521 | } 522 | ] 523 | }, 524 | { 525 | "type" : "submit", 526 | "defaultValue" : "Save Settings" 527 | } 528 | ]; -------------------------------------------------------------------------------- /src/pkjs/app-conversion.js: -------------------------------------------------------------------------------- 1 | var _ = require('./app-localize')._; 2 | 3 | module.exports = [ 4 | { 5 | name :_("meters"), // 6 | options : [ 7 | { 8 | name :_("yards"), 9 | multiplier : 1.09361 10 | }, 11 | { 12 | name :_("kilometers"), 13 | multiplier : 0.001 14 | }, 15 | { 16 | name :_("feet"), 17 | multiplier : 3.28084 18 | }, 19 | { 20 | name :_("inches"), 21 | multiplier : 39.3701 22 | }, 23 | { 24 | name :_("miles"), 25 | multiplier : 0.000621371 26 | }, 27 | { 28 | name :_("centimeters"), 29 | multiplier : 100 30 | }, 31 | { 32 | name :_("millimeters"), 33 | multiplier : 1000 34 | } 35 | ] 36 | }, 37 | { 38 | name :"kilometers", 39 | options : [ 40 | { 41 | name :"meters", 42 | multiplier : 1000 43 | }, 44 | { 45 | name :"centimeters", 46 | multiplier : 100000 47 | }, 48 | { 49 | name :"millimeters", 50 | multiplier : 1000000 51 | }, 52 | { 53 | name :"miles", 54 | multiplier : 0.621371 55 | }, 56 | { 57 | name :"yards", 58 | multiplier : 1093.61 59 | }, 60 | { 61 | name :"feet", 62 | multiplier : 3280.84 63 | }, 64 | { 65 | name :"inches", 66 | multiplier : 39370.1 67 | } 68 | ] 69 | }, 70 | { 71 | name :_("centimeters"), 72 | options : [ 73 | { 74 | name :_("kilometers"), 75 | multiplier : 0.00001 76 | }, 77 | { 78 | name :_("millimeters"), 79 | multiplier : 0.1 80 | }, 81 | { 82 | name :_("meters"), 83 | multiplier : 0.01 84 | }, 85 | { 86 | name :_("miles"), 87 | multiplier : 0.0000062137 88 | }, 89 | { 90 | name :_("yards"), 91 | multiplier : 0.0109361 92 | }, 93 | { 94 | name :_("feet"), 95 | multiplier : 0.0328084 96 | }, 97 | { 98 | name :_("inches"), 99 | multiplier : 0.393701 100 | } 101 | ] 102 | }, 103 | { 104 | name :_("miles"), 105 | options : [ 106 | { 107 | name :_("kilometers"), 108 | multiplier : 1.60934 109 | }, 110 | { 111 | name :_("meters"), 112 | multiplier : 1609.34 113 | }, 114 | { 115 | name :_("centimeters"), 116 | multiplier : 160934 117 | }, 118 | { 119 | name :_("millimeters"), 120 | multiplier : 1609340 121 | }, 122 | { 123 | name :_("yards"), 124 | multiplier : 1760 125 | }, 126 | { 127 | name :_("feet"), 128 | multiplier : 5280 129 | }, 130 | { 131 | name :_("inches"), 132 | multiplier : 63360 133 | } 134 | ] 135 | }, 136 | { 137 | name :_("yards"), 138 | options : [ 139 | { 140 | name :_("kilometers"), 141 | multiplier : 0.0009144 142 | }, 143 | { 144 | name :_("meters"), 145 | multiplier : 0.9144 146 | }, 147 | { 148 | name :_("miles"), 149 | multiplier : 0.000568182 150 | }, 151 | { 152 | name :_("centimeters"), 153 | multiplier : 91.44 154 | }, 155 | { 156 | name :_("millimeters"), 157 | multiplier : 914.4 158 | }, 159 | { 160 | name :_("feet"), 161 | multiplier : 3 162 | }, 163 | { 164 | name :_("inches"), 165 | multiplier : 36 166 | } 167 | ] 168 | }, 169 | { 170 | name :_("feet"), 171 | options : [ 172 | { 173 | name :_("kilometers"), 174 | multiplier : 0.0003048 175 | }, 176 | { 177 | name :_("meters"), 178 | multiplier : 0.3048 179 | }, 180 | { 181 | name :_("miles"), 182 | multiplier : 0.000189394 183 | }, 184 | { 185 | name :_("centimeters"), 186 | multiplier : 30.48 187 | }, 188 | { 189 | name :_("millimeters"), 190 | multiplier : 304.8 191 | }, 192 | { 193 | name :_("yards"), 194 | multiplier : 0.333333 195 | }, 196 | { 197 | name :_("inches"), 198 | multiplier : 12 199 | } 200 | ] 201 | }, 202 | { 203 | name :_("inches"), 204 | options : [ 205 | { 206 | name :_("kilometers"), 207 | multiplier : 0.0000254 208 | }, 209 | { 210 | name :_("meters"), 211 | multiplier : 0.0254 212 | }, 213 | { 214 | name :_("miles"), 215 | multiplier : 0.000015783 216 | }, 217 | { 218 | name :_("centimeters"), 219 | multiplier : 2.54 220 | }, 221 | { 222 | name :_("millimeters"), 223 | multiplier : 25.4 224 | }, 225 | { 226 | name :_("feet"), 227 | multiplier : 0.0833333 228 | }, 229 | { 230 | name :_("yards"), 231 | multiplier : 0.0277778 232 | } 233 | ] 234 | }, 235 | { 236 | name :_("millimeters"), 237 | options : [ 238 | { 239 | name :_("kilometers"), 240 | multiplier : 0.000001 241 | }, 242 | { 243 | name :_("centimeters"), 244 | multiplier : 0.1 245 | }, 246 | { 247 | name :_("meters"), 248 | multiplier : 0.001 249 | }, 250 | { 251 | name :_("miles"), 252 | multiplier : 0.00000062137 253 | }, 254 | { 255 | name :_("yards"), 256 | multiplier : 0.00109361 257 | }, 258 | { 259 | name :_("feet"), 260 | multiplier : 0.00328084 261 | }, 262 | { 263 | name :_("inches"), 264 | multiplier : 0.0393701 265 | } 266 | ] 267 | }, 268 | { 269 | name :_("pounds"), // 270 | options : [ 271 | { 272 | name :_("ounces"), 273 | multiplier : 16 274 | }, 275 | { 276 | name :_("grams"), 277 | multiplier : 453.592 278 | }, 279 | { 280 | name :_("kilograms"), 281 | multiplier : 0.453592 282 | } 283 | ] 284 | }, 285 | { 286 | name :_("ounces"), 287 | options : [ 288 | { 289 | name :_("pounds"), 290 | multiplier : 0.0625 291 | }, 292 | { 293 | name :_("grams"), 294 | multiplier : 28.3495 295 | }, 296 | { 297 | name :_("kilograms"), 298 | multiplier : 0.0283495 299 | } 300 | ] 301 | }, 302 | { 303 | name :_("grams"), 304 | options : [ 305 | { 306 | name :_("pounds"), 307 | multiplier : 0.00220462 308 | }, 309 | { 310 | name :_("ounces"), 311 | multiplier : 0.035274 312 | }, 313 | { 314 | name :_("kilograms"), 315 | multiplier : 0.001 316 | } 317 | ] 318 | }, 319 | { 320 | name :_("kilograms"), 321 | options : [ 322 | { 323 | name :_("grams"), 324 | multiplier : 1000 325 | }, 326 | { 327 | name :_("ounces"), 328 | multiplier : 35.274 329 | }, 330 | { 331 | name :_("pounds"), 332 | multiplier : 2.20462 333 | } 334 | ] 335 | }, 336 | { 337 | name :_("gallons"), 338 | options : [ 339 | { 340 | name :_("quarts"), 341 | multiplier : 4 342 | }, 343 | { 344 | name :_("pints"), 345 | multiplier : 8 346 | }, 347 | { 348 | name :_("cups"), 349 | multiplier : 15.7725 350 | }, 351 | { 352 | name :_("fluid-ounces"), 353 | multiplier : 128 354 | }, 355 | { 356 | name :_("tablespoons"), 357 | multiplier : 256 358 | }, 359 | { 360 | name :_("teaspoons"), 361 | multiplier : 768 362 | }, 363 | { 364 | name :_("liters"), 365 | multiplier : 3.78541 366 | }, 367 | { 368 | name :_("milliliters"), 369 | multiplier : 3785.41 370 | } 371 | ] 372 | }, 373 | { 374 | name :_("quarts"), 375 | options : [ 376 | { 377 | name :_("gallons"), 378 | multiplier : 0.25 379 | }, 380 | { 381 | name :_("pints"), 382 | multiplier : 2 383 | }, 384 | { 385 | name :_("cups"), 386 | multiplier : 3.94314 387 | }, 388 | { 389 | name :_("fluid-ounces"), 390 | multiplier : 32 391 | }, 392 | { 393 | name :_("tablespoons"), 394 | multiplier : 64 395 | }, 396 | { 397 | name :_("teaspoons"), 398 | multiplier : 192 399 | }, 400 | { 401 | name :_("liters"), 402 | multiplier : 0.946353 403 | }, 404 | { 405 | name :_("milliliters"), 406 | multiplier : 946.353 407 | } 408 | ] 409 | }, 410 | { 411 | name :_("pints"), 412 | options : [ 413 | { 414 | name :_("gallons"), 415 | multiplier : 0.125 416 | }, 417 | { 418 | name :_("quarts"), 419 | multiplier : 0.5 420 | }, 421 | { 422 | name :_("cups"), 423 | multiplier : 1.97157 424 | }, 425 | { 426 | name :_("fluid-ounces"), 427 | multiplier : 16 428 | }, 429 | { 430 | name :_("tablespoons"), 431 | multiplier : 32 432 | }, 433 | { 434 | name :_("teaspoons"), 435 | multiplier : 96 436 | }, 437 | { 438 | name :_("liters"), 439 | multiplier : 0.473176 440 | }, 441 | { 442 | name :_("milliliters"), 443 | multiplier : 473.176 444 | } 445 | ] 446 | }, 447 | { 448 | name :_("cups"), 449 | options : [ 450 | { 451 | name :_("gallons"), 452 | multiplier : 0.0634013 453 | }, 454 | { 455 | name :_("quarts"), 456 | multiplier : 0.253605 457 | }, 458 | { 459 | name :_("pints"), 460 | multiplier : 0.50721 461 | }, 462 | { 463 | name :_("fluid-ounces"), 464 | multiplier : 8.11537 465 | }, 466 | { 467 | name :_("tablespoons"), 468 | multiplier : 16.2307 469 | }, 470 | { 471 | name :_("teaspoons"), 472 | multiplier : 48.6922 473 | }, 474 | { 475 | name :_("liters"), 476 | multiplier : 0.24 477 | }, 478 | { 479 | name :_("milliliters"), 480 | multiplier : 240 481 | } 482 | ] 483 | }, 484 | { 485 | name :_("fluid-ounces"), 486 | options : [ 487 | { 488 | name :_("gallons"), 489 | multiplier : 0.0078125 490 | }, 491 | { 492 | name :_("quarts"), 493 | multiplier : 0.03125 494 | }, 495 | { 496 | name :_("pints"), 497 | multiplier : 0.0625 498 | }, 499 | { 500 | name :_("cups"), 501 | multiplier : 0.123223 502 | }, 503 | { 504 | name :_("tablespoons"), 505 | multiplier : 2 506 | }, 507 | { 508 | name :_("teaspoons"), 509 | multiplier : 6 510 | }, 511 | { 512 | name :_("liters"), 513 | multiplier : 0.0295735 514 | }, 515 | { 516 | name :_("milliliters"), 517 | multiplier : 29.5735 518 | } 519 | ] 520 | }, 521 | { 522 | name :_("tablespoons"), 523 | options : [ 524 | { 525 | name :_("gallons"), 526 | multiplier : 0.00390625 527 | }, 528 | { 529 | name :_("quarts"), 530 | multiplier : 0.015625 531 | }, 532 | { 533 | name :_("pints"), 534 | multiplier : 0.03125 535 | }, 536 | { 537 | name :_("cups"), 538 | multiplier : 0.0616115 539 | }, 540 | { 541 | name :_("fluid-ounces"), 542 | multiplier : 0.5 543 | }, 544 | { 545 | name :_("teaspoons"), 546 | multiplier : 3 547 | }, 548 | { 549 | name :_("liters"), 550 | multiplier : 0.0147868 551 | }, 552 | { 553 | name :_("milliliters"), 554 | multiplier : 14.7868 555 | } 556 | ] 557 | }, 558 | { 559 | name :_("teaspoons"), 560 | options : [ 561 | { 562 | name :_("gallons"), 563 | multiplier : 0.00130208 564 | }, 565 | { 566 | name :_("quarts"), 567 | multiplier : 0.00520833 568 | }, 569 | { 570 | name :_("pints"), 571 | multiplier : 0.0104167 572 | }, 573 | { 574 | name :_("cups"), 575 | multiplier : 0.0205372 576 | }, 577 | { 578 | name :_("fluid-ounces"), 579 | multiplier : 0.166667 580 | }, 581 | { 582 | name :_("tablespoons"), 583 | multiplier : 0.333333 584 | }, 585 | { 586 | name :_("liters"), 587 | multiplier : 0.00492892 588 | }, 589 | { 590 | name :_("milliliters"), 591 | multiplier : 4.92892 592 | } 593 | ] 594 | }, 595 | { 596 | name :_("liters"), 597 | options : [ 598 | { 599 | name :_("gallons"), 600 | multiplier : 0.264172 601 | }, 602 | { 603 | name :_("quarts"), 604 | multiplier : 1.05669 605 | }, 606 | { 607 | name :_("pints"), 608 | multiplier : 2.11338 609 | }, 610 | { 611 | name :_("cups"), 612 | multiplier : 4.16667 613 | }, 614 | { 615 | name :_("fluid-ounces"), 616 | multiplier : 33.814 617 | }, 618 | { 619 | name :_("tablespoons"), 620 | multiplier : 67.628 621 | }, 622 | { 623 | name :_("teaspoons"), 624 | multiplier : 202.884 625 | }, 626 | { 627 | name :_("millileters"), 628 | multiplier : 1000 629 | } 630 | ] 631 | }, 632 | { 633 | name :_("milliliters"), 634 | options : [ 635 | { 636 | name :_("gallons"), 637 | multiplier : 0.000264172 638 | }, 639 | { 640 | name :_("quarts"), 641 | multiplier : 0.00105669 642 | }, 643 | { 644 | name :_("pints"), 645 | multiplier : 0.00211338 646 | }, 647 | { 648 | name :_("cups"), 649 | multiplier : 0.00416667 650 | }, 651 | { 652 | name :_("fluid-ounces"), 653 | multiplier : 0.033814 654 | }, 655 | { 656 | name :_("tablespoons"), 657 | multiplier : 0.067628 658 | }, 659 | { 660 | name :_("teaspoons"), 661 | multiplier : 0.202884 662 | }, 663 | { 664 | name :_("liters"), 665 | multiplier : 0.001 666 | } 667 | ] 668 | }, 669 | { 670 | name :_("fahrenheit"), // 671 | options : [ 672 | { 673 | name :_("celsius"), 674 | multiplier : 1 675 | }, 676 | { 677 | name :_("kelvin"), 678 | multiplier : 1 679 | } 680 | ], 681 | tempFlag : true 682 | }, 683 | { 684 | name :_("celsius"), 685 | options : [ 686 | { 687 | name :_("fahrenheit"), 688 | multiplier : 1 689 | }, 690 | { 691 | name :_("kelvin"), 692 | multiplier : 1 693 | } 694 | ], 695 | tempFlag : true 696 | }, 697 | { 698 | name :_("kelvin"), 699 | options : [ 700 | { 701 | name :_("fahrenheit"), 702 | multiplier : 1 703 | }, 704 | { 705 | name :_("celsius"), 706 | multiplier : 1 707 | } 708 | ], 709 | tempFlag : true 710 | } 711 | ]; -------------------------------------------------------------------------------- /src/pkjs/app-custom-clay.js: -------------------------------------------------------------------------------- 1 | module.exports = function(e){ 2 | var clayConfig = this; 3 | 4 | clayConfig.on(clayConfig.EVENTS.AFTER_BUILD, function(){ 5 | var widget = clayConfig.getItemById('widget'); 6 | 7 | var countdownInput = clayConfig.getItemById('countdownInput'); 8 | var stockInput = clayConfig.getItemById('stockInput'); 9 | var timezoneInput = clayConfig.getItemById('timezoneInput'); 10 | 11 | if(widget.get() === 3){ //Countdown 12 | stockInput.hide(); 13 | timezoneInput.hide(); 14 | } 15 | else if(widget.get() === 13){ //Stock 16 | countdownInput.hide(); 17 | timezoneInput.hide(); 18 | } 19 | else if(widget.get() === 15){ //Timezone 20 | countdownInput.hide(); 21 | stockInput.hide(); 22 | } 23 | else{ 24 | countdownInput.hide(); 25 | stockInput.hide(); 26 | timezoneInput.hide(); 27 | } 28 | 29 | widget.on('change', function(){ 30 | if(widget.get() === 3){ 31 | countdownInput.show(); 32 | stockInput.hide(); 33 | timezoneInput.hide(); 34 | } 35 | else if(widget.get() === 13){ 36 | stockInput.show(); 37 | countdownInput.hide(); 38 | timezoneInput.hide(); 39 | } 40 | else if(widget.get() === 15){ 41 | timezoneInput.show(); 42 | countdownInput.hide(); 43 | stockInput.hide(); 44 | } 45 | else{ 46 | countdownInput.hide(); 47 | stockInput.hide(); 48 | timezoneInput.hide(); 49 | } 50 | }); 51 | 52 | var inactivityMonitor = clayConfig.getItemById('inactivityMonitor'); 53 | 54 | var inactivityStart = clayConfig.getItemById('inactivityStart'); 55 | var inactivityEnd = clayConfig.getItemById('inactivityEnd'); 56 | 57 | if(!inactivityMonitor.get()){ 58 | inactivityStart.hide(); 59 | inactivityEnd.hide(); 60 | } 61 | 62 | inactivityMonitor.on('change', function(){ 63 | if(!inactivityMonitor.get()){ 64 | inactivityStart.hide(); 65 | inactivityEnd.hide(); 66 | } 67 | else{ 68 | inactivityStart.show(); 69 | inactivityEnd.show(); 70 | } 71 | }); 72 | 73 | var master = clayConfig.getItemById('master'); 74 | 75 | var masterEmail = clayConfig.getItemById('masterEmail'); 76 | var masterPIN = clayConfig.getItemById('masterPIN'); 77 | var masterButton = clayConfig.getItemById('masterButton'); 78 | 79 | var ifttt = clayConfig.getItemById('ifttt'); 80 | var iftttPlus = clayConfig.getItemById('iftttPlus'); 81 | var wolfram = clayConfig.getItemById('wolfram'); 82 | var weather = clayConfig.getItemById('weather'); 83 | var habits = clayConfig.getItemById('habits'); 84 | var travel = clayConfig.getItemById('travel'); 85 | 86 | if(master.get()){ 87 | ifttt.hide(); 88 | iftttPlus.hide(); 89 | wolfram.hide(); 90 | weather.hide(); 91 | habits.hide(); 92 | travel.hide(); 93 | } 94 | else{ 95 | masterEmail.hide(); 96 | masterPIN.hide(); 97 | masterButton.hide(); 98 | } 99 | 100 | master.on('change', function(){ 101 | if(master.get()){ 102 | ifttt.hide(); 103 | iftttPlus.hide(); 104 | wolfram.hide(); 105 | weather.hide(); 106 | habits.hide(); 107 | travel.hide(); 108 | 109 | masterEmail.show(); 110 | masterPIN.show(); 111 | masterButton.show(); 112 | } 113 | else{ 114 | ifttt.show(); 115 | iftttPlus.show(); 116 | wolfram.show(); 117 | weather.show(); 118 | habits.show(); 119 | travel.show(); 120 | 121 | masterEmail.hide(); 122 | masterPIN.hide(); 123 | masterButton.hide(); 124 | } 125 | }); 126 | 127 | masterButton.on('click', function(){ 128 | if(masterEmail.get() === ""){ 129 | alert('Missing: email address'); 130 | return; 131 | } 132 | if(masterPIN.get() === ""){ 133 | alert('Missing: PIN'); 134 | return; 135 | } 136 | 137 | var xhr = new XMLHttpRequest(); 138 | var url = "https://pmkey.xyz/search/?email=" + masterEmail.get() + "&pin=" + masterPIN.get(); 139 | xhr.open("GET", url, true); 140 | 141 | xhr.onreadystatechange = function(){ 142 | if(xhr.readyState === 4 && xhr.status === 200){ 143 | var result = JSON.parse(xhr.responseText); 144 | 145 | if(result.success){ 146 | ifttt.set(result.keys.web.ifttt); 147 | wolfram.set(result.keys.web.wolfram); 148 | weather.set(result.keys.weather.wu); 149 | habits.set(result.keys.pebble.habits); 150 | travel.set(result.keys.pebble.travel); 151 | 152 | alert('Success!'); 153 | } 154 | else{ 155 | alert('Error: ' + result.error); 156 | } 157 | } 158 | }; 159 | 160 | xhr.send(); 161 | }); 162 | }); 163 | }; -------------------------------------------------------------------------------- /src/pkjs/app-ifttt.js: -------------------------------------------------------------------------------- 1 | var App = require('./app-messaging'); 2 | var _ = require('./app-localize')._; 3 | var Config = require('./app-settings'); 4 | 5 | function thermostat(q, forceParams){ 6 | try{ 7 | var temp = parseInt(q.array[q.array.indexOf(_('to'))+1], true); 8 | 9 | var url = "http://maker.ifttt.com/trigger/snowy_thermostat/with/key/" + Config.getConfig().IftttKey; 10 | var xhr = new XMLHttpRequest(); 11 | 12 | var params = "value1=" + temp; 13 | var data = { "value1" : temp }; 14 | 15 | xhr.open("POST", url, false); 16 | 17 | if(forceParams === undefined){ 18 | xhr.setRequestHeader("Content-Type", "application/json"); 19 | xhr.send(decodeURIComponent(JSON.stringify(data)));//params); 20 | } 21 | else{ 22 | xhr.send(params); 23 | } 24 | 25 | if(xhr.responseText.indexOf('Congratulations!') !== -1){ 26 | App.sendMessage( { "Title" : _("Event Fired!"), "Body" : _("Ok, I've asked IFTTT to set your thermostat to ") + temp + _(" degrees.\n[snowy_thermostat]") } ); 27 | } 28 | else if(forceParams === undefined){ 29 | thermostat(q, false, true); 30 | } 31 | else{ 32 | console.error("Error from Maker API"); 33 | App.sendErrorMessage( { "Title" : _("If this, then huh?"), "Body" : _("Something went wrong with your Maker Channel. Please try again.\n\nError\n") + _("Recipe failed!") } ); 34 | } 35 | } 36 | catch(err){ 37 | console.error(err + "\n" + err.stack); 38 | if(Config.getConfig().IftttKey === "") App.sendErrorMessage( { "Title" : _("If this, then huh?"), "Body" : _("To integrate Snowy with IFTTT, you'll need to input your Maker Key on the settings page.") } ); 39 | else App.sendErrorMessage( { "TITLE" : _("If this, then huh?"), "BODY" : _("Something went wrong with your Maker Channel. Please try again.\n\nError\n") + err + "\n" + err.stack } ); 40 | } 41 | } 42 | 43 | function lights(q){ 44 | try{ 45 | var xhr, url; 46 | 47 | if(q.array.indexOf(_('on')) !== -1){ 48 | xhr = new XMLHttpRequest(); 49 | url = "http://maker.ifttt.com/trigger/snowy_lights_on/with/key/" + Config.getConfig().IftttKey; 50 | xhr.open("POST", url, false); 51 | xhr.send(); 52 | 53 | App.sendMessage( { "Title" : _("Event Fired!"), "Body" : _("Ok, I've asked IFTTT to turn ON your lights.\n[snowy_lights_on]") } ); 54 | } 55 | else if(q.array.indexOf(_('off')) !== -1){ 56 | xhr = new XMLHttpRequest(); 57 | url = "http://maker.ifttt.com/trigger/snowy_lights_off/with/key/" + Config.getConfig().IftttKey; 58 | xhr.open("POST", url, false); 59 | xhr.send(); 60 | 61 | if(xhr.responseText.indexOf('Congratulations!') !== -1){ 62 | App.sendMessage( { "Titlte" : _("Event Fired!"), "Body" : _("Ok, I've asked IFTTT to turn OFF your lights.\n[snowy_lights_off]") } ); 63 | } 64 | else{ 65 | console.error("Error from Maker API"); 66 | App.sendErrorMessage( { "Title" : _("If this, then huh?"), "Body" : _("Something went wrong with your Maker Channel. Please try again.\n\nError\n") + _("Recipe failed!") } ); 67 | } 68 | } 69 | else{ 70 | console.error("Lights - Unknown On or Off"); 71 | App.sendErrorMessage( { "Title" : _("If this, then huh?"), "Body" : _("Sorry - I didn't understand what you wanted to do.") } ); 72 | return; 73 | } 74 | } 75 | catch(err){ 76 | console.error(err + "\n" + err.stack); 77 | if(Config.getConfig().IftttKey === "") App.sendErrorMessage( { "Title" : _("If this, then huh?"), "Body" : _("To integrate Snowy with IFTTT, you'll need to input your Maker Key on the settings page.") } ); 78 | else App.sendMessage( { "Title" : _("If this, then huh?"), "Body" : _("Something went wrong with your Maker Channel. Please try again.\n\nError\n") + err + "\n" + err.stack } ); 79 | } 80 | } 81 | 82 | function phone(q){ 83 | try{ 84 | var xhr = new XMLHttpRequest(); 85 | var url = "http://maker.ifttt.com/trigger/snowy_find_my_phone/with/key/" + Config.getConfig().IftttKey; 86 | xhr.open("POST", url, false); 87 | xhr.send(); 88 | 89 | if(xhr.responseText.indexOf('Congratulations!') !== -1){ 90 | App.sendMessage( { "Title" : _("Event Fired!"), "Body" : _("Ok, I've asked IFTTT to call your phone so you can find it.\n[snowy_find_my_phone]") } ); 91 | } 92 | else{ 93 | console.error("Error from Maker API"); 94 | App.sendErrorMessage( { "Title" : _("If this, then huh?"), "Body" : _("Something went wrong with your Maker Channel. Please try again.\n\nError\n") + _("Recipe failed!") } ); 95 | } 96 | } 97 | catch(err){ 98 | console.error(err + "\n" + err.stack); 99 | if(Config.getConfig().IftttKey === "") App.sendErrorMessage( { "Title" : _("If this, then huh?"), "Body" : _("To integrate Snowy with IFTTT, you'll need to input your Maker Key on the settings page.") } ); 100 | else App.sendErrorMessage( { "Title" : _("If this, then huh?"), "Body" : _("Something went wrong with your Maker Channel. Please try again.\n\nError\n") + err + "\n" + err.stack } ); 101 | } 102 | } 103 | 104 | function relay(q, forceParams){ 105 | try{ 106 | var message = q.array.join(' '); 107 | 108 | var url = "http://maker.ifttt.com/trigger/snowy_thermostat/with/key/" + Config.getConfig().IftttKey; 109 | var xhr = new XMLHttpRequest(); 110 | 111 | var params = "value1=" + message; 112 | var data = { "value1" : message }; 113 | 114 | xhr.open("POST", url, false); 115 | 116 | if(forceParams === undefined){ 117 | xhr.setRequestHeader("Content-Type", "application/json"); 118 | xhr.send(decodeURIComponent(JSON.stringify(data)));//params); 119 | } 120 | else{ 121 | xhr.send(params); 122 | } 123 | 124 | if(xhr.responseText.indexOf('Congratulations!') !== -1){ 125 | App.sendMessage( { "Title" : _("Event Fired!"), "Body" : _("Ok, I've asked IFTTT to set your thermostat to ") + temp + _(" degrees.\n[snowy_thermostat]") } ); 126 | } 127 | else if(forceParams === undefined){ 128 | thermostat(q, false, true); 129 | } 130 | else{ 131 | console.error("Error from Maker API"); 132 | App.sendErrorMessage( { "Title" : _("If this, then huh?"), "Body" : _("Something went wrong with your Maker Channel. Please try again.\n\nError\n") + _("Recipe failed!") } ); 133 | } 134 | } 135 | catch(err){ 136 | console.error(err + "\n" + err.stack); 137 | if(Config.getConfig().IftttKey === "") App.sendErrorMessage( { "Title" : _("If this, then huh?"), "Body" : _("To integrate Snowy with IFTTT, you'll need to input your Maker Key on the settings page.") } ); 138 | else App.sendErrorMessage( { "TITLE" : _("If this, then huh?"), "BODY" : _("Something went wrong with your Maker Channel. Please try again.\n\nError\n") + err + "\n" + err.stack } ); 139 | } 140 | } 141 | 142 | function custom(q, forceParams){ 143 | try{ 144 | var params = ""; 145 | var data = ""; 146 | 147 | if(q.array.indexOf(_('saying')) !== -1){ 148 | params = "value1=" + encodeURIComponent(upperCase(q.array.slice(q.array.indexOf(_('saying'))+1).join(' '))); 149 | data = { "value1" : encodeURIComponent(upperCase(q.array.slice(q.array.indexOf(_('saying'))+1).join(' '))) }; 150 | q.array.splice(q.array.indexOf(_('saying'))+1); 151 | } 152 | else if(q.array.indexOf(_('to')) !== -1){ 153 | params = "value1=" + parseInt(q.array[q.array.lastIndexOf(_('to'))+1]); 154 | data = { "value1" : parseInt(q.array[q.array.lastIndexOf(_('to'))+1])}; 155 | q.array.splice(q.array.lastIndexOf(_('to'))+1); 156 | } 157 | else if(Config.getLang() == "en" && q.array[q.array.length-1].length === 3 && q.array[q.array.length-1].charAt(0) === '2'){ 158 | params = "value1=" + parseInt(q.array[q.array.length-1], true); 159 | data = { "value1" : parseInt(q.array[q.array.length-1], true)}; 160 | q.array.splice(q.array.lastIndexOf('to')+1); 161 | } 162 | else if(q.array.indexOf(_('values')) !== -1){ 163 | var val1, val2, val3; 164 | 165 | val1 = q.array.slice(q.array.indexOf(_('values'))+1,q.array.indexOf(_('and'))).join(' '); 166 | 167 | if(isNaN(parseInt(val1))) val1 = encodeURIComponent(val1); 168 | else val1 = parseInt(val1); 169 | 170 | val2 = q.array.slice(q.array.indexOf(_('and'))+1, q.array.indexOf(_('and')) !== q.array.lastIndexOf(_('and')) ? q.array.lastIndexOf(_('and')) : q.array.length-1).join(' '); 171 | 172 | if(isNaN(parseInt(val2))) val2 = encodeURIComponent(val2); 173 | else val2 = parseInt(val2); 174 | 175 | if(q.array.indexOf(_('and')) !== q.array.lastIndexOf(_('and'))){ 176 | val3 = q.array.slice(q.array.lastIndexOf(_('and'))+1).join(' '); 177 | 178 | if(isNaN(parseInt(val3))) val3 = encodeURIComponent(val3); 179 | else val3 = parseInt(val3); 180 | } 181 | else val3 = ""; 182 | 183 | params = "value1=" + val1 + "&value2=" + val2 + "&value3=" + val3; 184 | data = { "value1" : val1, "value2" : val2, "value3" : val3 }; 185 | q.array.splice(q.array.lastIndexOf(_('values'))+1); 186 | } 187 | 188 | console.log(params); 189 | 190 | var customEvent = q.array.join('_'); 191 | 192 | var xhr = new XMLHttpRequest(); 193 | var url = "http://maker.ifttt.com/trigger/snowy_" + customEvent + "/with/key/" + Config.getConfig().IftttKey; 194 | xhr.open("POST", url, false); 195 | 196 | if(forceParams === undefined){ 197 | xhr.setRequestHeader("Content-Type", "application/json"); 198 | xhr.send(decodeURIComponent(JSON.stringify(data)));//params); 199 | } 200 | else{ 201 | xhr.send(params); 202 | } 203 | 204 | if(xhr.responseText.indexOf('Congratulations!') !== -1){ 205 | App.sendMessage( { "Title" : _("Event Fired!"), "Body" : _("Ok, I've asked IFTTT to trigger the event 'snowy_") + customEvent + "'." } ); 206 | } 207 | else if(forceParams === undefined){ 208 | custom(q, true); 209 | } 210 | else{ 211 | console.error("Error from Maker API"); 212 | App.sendErrorMessage( { "Title" : _("If this, then huh?"), "Body" : _("Something went wrong with your request. The event 'snowy_") + customEvent + _("' was not recognized by the IFTTT server.") } ); 213 | } 214 | } 215 | catch(err){ 216 | console.error(err + "\n" + err.stack); 217 | if(Config.getConfig().IftttKey === "") App.sendErrorMessage( { "Title" : _("If this, then huh?"), "Body" : _("To integrate Snowy with IFTTT, you'll need to input your Maker Key on the settings page.") } ); 218 | else App.sendErrorMessage( { "Title" : _("If this, then huh?"), "Body" : _("Something went wrong with your Maker Channel. Please try again.\n\nError\n") + err + "\n" + err.stack } ); 219 | } 220 | } 221 | 222 | function upperCase(string){ 223 | return string.charAt(0).toUpperCase() + string.substring(1); 224 | } 225 | 226 | var ifttt_intel = [ 227 | { 228 | name : "Thermostat", 229 | keywords : [ 230 | { string : "thermostat", value : 5 }, 231 | { string : "on", value : -3 }, 232 | { string : "off", value : -3 }, 233 | { string : "values", value : -5 } 234 | ], 235 | callback : thermostat, 236 | score : 0 237 | }, //Thermostat 238 | { 239 | name : "Lights", 240 | keywords : [ 241 | { string : "lights", value : 3 }, 242 | { string : "on", value : 3 }, 243 | { string : "off", value : 3 }, 244 | { string : "dim", value : -3 }, 245 | { string : "set", value : -3 }, 246 | { string : "bedroom", value : -3 }, 247 | { string : "living room", value : -3 }, 248 | { string : "den", value : -3 }, 249 | { string : "garage", value : -3 }, 250 | { string : "bathroom", value : -3 }, 251 | { string : "basement", value : -3 }, 252 | { string : "kitchen", value : -3 }, 253 | { string : "room", value : -1 }, 254 | { string : "values", value : -5 }, 255 | { string : "wifi", value : -3 }, 256 | { string : "wi-fi", value : -3 }, 257 | { string : "car", value : -3 }, 258 | { string : "AC", value : -3 }, 259 | { string : "heat", value : -3 } 260 | ], 261 | callback : lights, 262 | score : 0 263 | }, //Lights 264 | { 265 | name : "Find My Phone", 266 | keywords : [ 267 | { string : "phone", value : 3 }, 268 | { string : "find", value : 3 }, 269 | { string : "call", value : 3 }, 270 | { string : "values", value : -5 } 271 | ], 272 | callback : phone, 273 | score : 0 274 | }, //Find My Phone 275 | { 276 | name : "Relay", 277 | keywords : [ 278 | { string : "relay", value : 5 } 279 | ], 280 | callback : relay, 281 | score : 0 282 | }, 283 | { 284 | name : "Custom", 285 | keywords : [], 286 | callback : custom, 287 | score : 4 288 | } //Custom 289 | ]; 290 | 291 | module.exports = { 292 | getIftttIntel : function(){ 293 | return ifttt_intel; 294 | } 295 | }; -------------------------------------------------------------------------------- /src/pkjs/app-intel-fr.js: -------------------------------------------------------------------------------- 1 | var Callbacks = require('./app-callbacks'); 2 | 3 | module.exports = [ 4 | { 5 | name : "Translate", 6 | keywords : [ 7 | { string : "comment dit-on", value : 5 }, 8 | { string : "que veut dire", value : 3 }, 9 | { string : "traduit", value : 5 }, 10 | { string : "traduire", value : 5 }, 11 | { string : "en", value : 0 } 12 | ], 13 | callback : Callbacks.translate, 14 | score : 0 15 | }, //Translate 16 | { 17 | name : "Eat", 18 | keywords : [ 19 | { string : "où devrais-je manger", value : 5 }, 20 | { string : "où puis-je trouver un endroit pour manger", value : 5 }, 21 | { string : "trouver", value : 3 }, 22 | { string : "manger", value : 3 }, 23 | { string : "restaurants", value : 3 }, 24 | { string : "j'ai faim", value : 3 } 25 | ], 26 | callback : Callbacks.eat, 27 | score : 0 28 | }, //Eat 29 | { 30 | name : "Directions", 31 | keywords : [ 32 | { string : "comment aller-là", value : 5 }, 33 | { string : "comment aller au", value : 5 }, 34 | { string : "comment aller à la", value : 5 }, 35 | { string : "comment aller", value : 3 }, 36 | { string : "comment rentrer à la", value : 5 }, 37 | { string : "comment rentrer", value : 3 }, 38 | { string : "au", value : 1 }, 39 | { string : "à la", value : 1 }, 40 | { string : "à", value : 0 }, 41 | ], 42 | callback : Callbacks.directions, 43 | score : 0 44 | }, //Directions BETA 45 | { 46 | name : "Unit Conversion", 47 | keywords : [ 48 | { string : "convertir", value : 5 }, 49 | { string : "convertis", value : 5 } 50 | ], 51 | callback : Callbacks.conversion, 52 | score : 0 53 | }, //Unit Conversion BETA 54 | { 55 | name : "Sports", 56 | keywords : [ 57 | { string : "quel est le score du match", value : 5 }, 58 | { string : "quel est le score", value : 3 }, 59 | { string : "quand", value : 1 }, 60 | { string : "va rejouer", value : 3 }, 61 | { string : "contre qui", value : 1}, 62 | { string : "score", value : 3 }, 63 | { string : "match", value : 3 }, 64 | { string : "a joué la dernière fois", value : 3 } 65 | ], 66 | callback : Callbacks.sports, 67 | score : 0 68 | }, //Sports BETA 69 | { 70 | name : "Health", 71 | keywords : [ 72 | { string : "combien de pas ai-je marché aujourd'hui", value : 5 }, 73 | { string : "combien de pas ai-je fait", value : 5 }, 74 | { string : "combien de pas", value : 3 }, 75 | { string : "quelle distance ai-je parcouru aujourd-hui", value : 5 }, 76 | { string : "quelle distance", value : 3 }, 77 | { string : "quel est mon objectif de pas", value : 5 }, 78 | { string : "objectif de pas", value : 3 }, 79 | { string : "comment ai-je dormi hier soir", value : 5 }, 80 | { string : "dormi", value : 3 }, 81 | { string : "hier soir", value : 3 }, 82 | { string : "meta de pasos", value : 3 } 83 | ], 84 | callback : Callbacks.health, 85 | score : 0 86 | }, //Health BETA 87 | { 88 | name : "Introduction", 89 | keywords : [ 90 | { string : "dites bonjour", value : 3 }, 91 | { string : "présente toi", value : 5 }, 92 | { string : "en", value : -1 } 93 | ], 94 | callback : Callbacks.introduce, 95 | score : 0 96 | }, //Introduction 97 | { 98 | name : "Description", 99 | keywords : [ 100 | { string : "que peux-tu faire", value : 5 }, 101 | { string : "que puis-je dire", value : 5 } 102 | ], 103 | callback : Callbacks.description, 104 | score : 0 105 | }, //Description 106 | { 107 | name : "Spell", 108 | keywords : [ 109 | { string : "épeler", value : 5 }, 110 | { string : "comment épelle-t-on", value : 5 }, 111 | { string : "épelle-t-on", value : 3 } 112 | ], 113 | callback : Callbacks.spell, 114 | score : 0 115 | }, //Spell 116 | { 117 | name : "Define", 118 | keywords : [ 119 | { string : "définition d'", value : 5 }, 120 | { string : "défintiond'", value : 5 }, 121 | { string : "donne moi la définition de", value : 5 }, 122 | { string : "définition de", value : 5 }, 123 | { string : "définition", value : 3 }, 124 | { string : "de", value : 0 } 125 | ], 126 | callback : Callbacks.defineFR, 127 | score : 0 128 | }, //Definition 129 | { 130 | name : "Time", 131 | keywords : [ 132 | { string : "quelle heure est-il à", value : 5 }, 133 | { string : "quelle heure est", value : 5 }, 134 | { string : "quelle heure", value : 3 } 135 | ], 136 | callback : Callbacks.time, 137 | score : 0 138 | }, //Time 139 | { 140 | name : "Date", 141 | keywords : [ 142 | { string : "quel jour sommes nous", value : 5 }, 143 | { string : "quelle est la date", value : 5 }, 144 | { string : "la date", value : 1 }, 145 | { string : "quel jour", value : 3 } 146 | ], 147 | callback : Callbacks.date, 148 | score : 0 149 | }, //Date 150 | { 151 | name : "Set Timer", 152 | keywords : [ 153 | { string : "lance un minuteur de", value : 5 }, 154 | { string : "lance un minuteur d'", value : 5 }, 155 | { string : "lance un minuteur", value : 3 }, 156 | { string : "minuteur", value : 1 } 157 | ], 158 | callback : Callbacks.setTimer2, 159 | score : 0 160 | }, //Set Timer 161 | { 162 | name : "Check Timer", 163 | keywords : [ 164 | { string : "vérife le minuteur", value : 5 }, 165 | { string : "vérife", value : 3 }, 166 | { string : "minuteur", value : 1 }, 167 | { string : "combien de temps reste-t-il", value : 5 } 168 | ], 169 | callback : Callbacks.checkTimer, 170 | score : 0 171 | }, //Check Timer 172 | { 173 | name : "Cancel Timer", 174 | keywords : [ 175 | { string : "annule le minuteur", value : 5 }, 176 | { string : "annule", value : 3 }, 177 | { string : "minuteur" , value : 1 }, 178 | { string : "supprime le minuteur", value : 5 } 179 | ], 180 | callback : Callbacks.cancelTimer, 181 | score : 0 182 | }, //Cancel Timer 183 | { 184 | name : "Finish Timer", 185 | keywords : [ 186 | { string : "finish timer", value : 5 } 187 | ], 188 | callback : Callbacks.finishTimer, 189 | score : 0 190 | }, //Finish Timer 191 | { 192 | name : "Set Alarm", 193 | keywords : [ 194 | { string : "régler une alarme", value : 5 }, 195 | { string : "programme une alarme", value : 5 }, 196 | { string : "crée une alarme", value : 5 }, 197 | { string : "régler", value : 3 }, 198 | { string : "programme", value : 3 }, 199 | { string : "une alarme", value : 3 }, 200 | { string : "alarme", value : 1 }, 201 | { string : "réveille moi", value : 3 }, 202 | ], 203 | callback : Callbacks.setAlarm, 204 | score : 0 205 | }, //Set Alarm 206 | { 207 | name : "Cancel Alarm", 208 | keywords : [ 209 | { string : "annule l'alarme", value : 5 }, 210 | { string : "annule", value : 3 }, 211 | { string : "l'alarme", value : 1 }, 212 | { string : "alarme", value : 1 }, 213 | { stirng : "arrête l'alarme", value : 5 } 214 | ], 215 | callback: Callbacks.cancelAlarm, 216 | score : 0 217 | }, //Cancel Alarm 218 | { 219 | name : "Note", 220 | keywords : [ 221 | { string : "note", value : 5 }, 222 | { string : "noter", value : 5 }, 223 | ], 224 | callback : Callbacks.note, 225 | score : 0 226 | }, //Note 227 | { 228 | name : "Reminder", 229 | keywords : [ 230 | { string : "rappelle-moi de", value : 5 }, 231 | { string : "rappelle-moi d'", value : 5 }, 232 | { string : "rappelle-moi", value : 5 }, 233 | { string : "rappelle moi", value : 5 }, 234 | { string : "crée un rappel", value : 1}, 235 | { string : "rappelle", value : 3 }, 236 | { string : "rappel", value : 1 } 237 | ], 238 | callback : Callbacks.reminder2FR, 239 | score : 0 240 | }, //Reminder 241 | { 242 | name : "Calendar", 243 | keywords : [ 244 | { string : "programme une", value : 5 }, 245 | { string : "programme", value : 3 }, 246 | { string : "ajoute", value : 5 }, 247 | { string : "dans mon calendrier", value : 5 }, 248 | { string : "mon calendrier", value : 3 }, 249 | { string : "calendrier", value : 1 } 250 | ], 251 | callback : Callbacks.calendar2, 252 | score : 0 253 | }, //Calendar 254 | { 255 | name : "Cancel", 256 | keywords : [ 257 | { string : "annule ça", value : 5 } 258 | ], 259 | callback : Callbacks.cancel, 260 | score : 0 261 | }, //Cancel 262 | { 263 | name : "Calculate", 264 | keywords : [ 265 | { string : "combien font", value : 1 }, 266 | { string : "combien fait", value : 1 }, 267 | { string : "+", value : 5 }, 268 | { string : "×", value : 5 }, 269 | { string : "÷", value : 5 }, 270 | { string : " -", value : 5 }, 271 | { string : "% de", value : 5 }, 272 | { string : "%", value : 3 } 273 | ], 274 | callback : Callbacks.calculate, 275 | score : 0 276 | }, //Calculate 277 | { 278 | name : "Random Number", 279 | keywords : [ 280 | { string : "choisir un nombre", value : 5 }, 281 | { string : "choisi un nombre", value : 5 }, 282 | { string : "donner un nombre au hasard", value : 5}, 283 | { string : "nombre au hasard", value : 3 }, 284 | ], 285 | callback : Callbacks.random2, 286 | score : 0 287 | }, //Random Number 288 | { 289 | name : "Flip a Coin", 290 | keywords : [ 291 | { string : "lancer une pièce", value : 5 }, 292 | { string : "lancer", value : 1}, 293 | { string : "pièce", value : 3} 294 | ], 295 | callback : Callbacks.coin, 296 | score : 0 297 | }, //Flip a Coin 298 | { 299 | name : "Weather", 300 | keywords : [ 301 | { string : "combien font", value : 3 }, 302 | { string : "combien fait", value : 3 }, 303 | { string : "quel temps fait-il", value : 5 }, 304 | { string : "quel est la météo en", value : 5 }, 305 | { string : "quel est la météo pour", value : 5 }, 306 | { string : "quel est la météo", value : 5 }, 307 | { string : "quelle température fait-t-il dehos", value : 5 }, 308 | { string : "météo", value : 3 }, 309 | { string : "en", value : 1 }, 310 | { string : "pour", value : 1 } 311 | ], 312 | callback : Callbacks.weatherWU, 313 | score : 0 314 | }, //Weather 315 | { 316 | name : "Forecast", 317 | keywords : [ 318 | { string : "quelle est la météo pour demain", value : 5 }, 319 | { string : "quel temps fera-t-il demain", value : 5 }, 320 | { string : "météo", value : 1 }, 321 | { string : "demain", value : 3 }, 322 | { string : "en", value : 1 } 323 | ], 324 | callback : Callbacks.forecastWU, 325 | score : 0 326 | }, //Forecast 327 | { 328 | name : "IFTTT Maker", 329 | keywords: [ 330 | { string : "s'il te plaît", value : 10 }, 331 | { string : "s'il te plait", value : 10 }, 332 | { string : "s'il vous plaît", value : 10 }, 333 | { string : "s'il vous plait", value : 10 }, 334 | { string : "comment dit-on", value : -10 }, 335 | ], 336 | callback : Callbacks.ifttt, 337 | score : 0 338 | }, //IFTTT 339 | { 340 | name : "Stock Prices", 341 | keywords : [ 342 | { string : "ce qui est le prix des actions de", value : 5 }, 343 | { string : "de", value : 0 }, 344 | { string : "des actions", value : 3 }, 345 | { string : "prix", value : 3 } 346 | ], 347 | callback : Callbacks.stock, 348 | score : 0 349 | }, //Stock Prices 350 | { 351 | name : "Add To List", 352 | keywords : [ 353 | { string : "rajoute", value : 5 }, 354 | { string : "ajoute", value : 3 }, 355 | { string : "à ma liste", value : 5 }, 356 | { string : "à la liste", value : 3 }, 357 | { string : "liste", value : 1 } 358 | ], 359 | callback : Callbacks.addTodo, 360 | score : 0 361 | }, //Add To List 362 | { 363 | name : "Check List", 364 | keywords : [ 365 | { string : "affiche la liste", value : 5 }, 366 | { string : "qu'y a-t-il sur ma liste", value : 5 }, 367 | { string : "liste", value : 3 } 368 | ], 369 | callback : Callbacks.checkTodo, 370 | score : 0 371 | }, //Check List 372 | { 373 | name : "Remove From List", 374 | keywords : [ 375 | { string : "supprime", value : 5 }, 376 | { string : "de la liste", value : 5 }, 377 | { string : "de ma liste", value : 5 }, 378 | { string : "liste", value : 1 }, 379 | ], 380 | callback : Callbacks.removeTodo, 381 | score : 0 382 | }, //Remove From List 383 | { 384 | name : "Clear List", 385 | keywords : [ 386 | { string : "efface toute ma liste", value : 5 }, 387 | { string : "efface ma liste", value : 5 }, 388 | { string : "efface toute la liste", value : 5 }, 389 | { string : "efface la liste", value : 5 }, 390 | { string : "efface", value : 5 }, 391 | { string : "vide la liste", value : 5 }, 392 | { string : "vide", value : 3 }, 393 | { string : "liste", value : 1 } 394 | ], 395 | callback : Callbacks.clearTodo, 396 | score : 0 397 | }, //Clear List 398 | { 399 | name : "Habits", 400 | keywords : [ 401 | { string : "liste de toutes mes habitudes", value : 5 }, 402 | { string : "qui toutes mes habitutdes sont", value : 5 }, 403 | { string : "énumérer mes habitudes", value : 5 }, 404 | { string : "quelles sont mes habitudes", value : 5 }, 405 | { string : "quand ma prochaine habitude", value : 5 }, 406 | { string : "prochaine habitude", value : 3 }, 407 | { string : "liste des habitudes", value : 3 }, 408 | { string : "toutes mes habitudes", value : 3 }, 409 | { string : "mes habitudes", value : 3 }, 410 | { string : "ce qui est ma série actuelle pour", value : 3 }, 411 | { string : "quel est mon nombre actuel", value : 3 }, 412 | { string : "strier", value : 1 }, 413 | { string : "courant", value : 1 }, 414 | { string : "contar", value : 1 }, 415 | { string : "habitudes", value : 1 }, 416 | { string : "pour", value : 0 }, 417 | { string : "quel est", value : 0 }, 418 | { string : "mes", value : 0 } 419 | ], 420 | callback : Callbacks.habits, 421 | score : 0 422 | } 423 | ]; -------------------------------------------------------------------------------- /src/pkjs/app-messaging.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | sendMessage : function(dict){ 3 | dict.Message = '#'; 4 | Pebble.sendAppMessage(dict); 5 | }, 6 | sendErrorMessage : function(dict){ 7 | dict.Message = "#"; 8 | dict.Error = "#"; 9 | Pebble.sendAppMessage(dict); 10 | }, 11 | sendSettings : function(dict){ 12 | dict.Settings = '#'; 13 | Pebble.sendAppMessage(dict); 14 | }, 15 | sendRequest : function(dict){ 16 | dict.Request = '#'; 17 | Pebble.sendAppMessage(dict); 18 | } 19 | }; -------------------------------------------------------------------------------- /src/pkjs/app-settings.js: -------------------------------------------------------------------------------- 1 | var Settings = { 2 | "Lang" : "en", 3 | 4 | "Lat" : 0, 5 | "Lon" : 0, 6 | 7 | "RecentAddress" : "", 8 | "RecentName" : "", 9 | 10 | "CELSIUS" : 0, 11 | "FAHRENHEIT" : 1, 12 | "TempUnit" : 1, 13 | 14 | "SMALL" : 0, 15 | "LARGE" : 1, 16 | "FontSize" : 0, 17 | 18 | "IMPERIAL" : 0, 19 | "METRIC" : 1, 20 | "DistanceUnit" : 0, 21 | 22 | "Subreddit" : "news", 23 | 24 | "HomeAddress" : "", 25 | "WorkAddress" : "", 26 | 27 | "IftttKey" : "", 28 | "IftttPlus" : false, 29 | 30 | "WuKey" : "", 31 | 32 | "WolframKey" : "", 33 | 34 | "TravelKey" : "", 35 | 36 | "HabitsKey" : "" 37 | }; 38 | 39 | module.exports = { 40 | getConfig : function(){ 41 | return Settings; 42 | }, 43 | setConfig : function(newConfig){ 44 | for(var i in newConfig){ 45 | Settings[i] = newConfig[i]; 46 | } 47 | }, 48 | getLang : function(){ 49 | return Settings.Lang; 50 | }, 51 | setLang : function(newLang){ 52 | Settings.Lang = newLang; 53 | }, 54 | getCoords : function(){ 55 | return { "Lat" : Settings.Lat, "Lon" : Settings.Lon }; 56 | }, 57 | setCoords : function(newLat, newLon){ 58 | Settings.Lat = newLat; 59 | Settings.Lon = newLon; 60 | }, 61 | getRecent : function(){ 62 | return { "Address" : Settings.RecentAddress, "Name" : Settings.RecentName }; 63 | }, 64 | setRecent : function(addr, name){ 65 | Settings.RecentAddress = addr; 66 | Settings.RecentName = name; 67 | } 68 | }; -------------------------------------------------------------------------------- /src/pkjs/app-timeline.js: -------------------------------------------------------------------------------- 1 | var _ = require('./app-localize')._; 2 | var Config = require('./app-settings'); 3 | var App = require('./app-messaging'); 4 | var Parser = require('./app-parser'); 5 | 6 | var API_URL_ROOT = 'https://timeline-api.getpebble.com/'; 7 | 8 | var deleteCount = 0, insertCount = 0; 9 | 10 | module.exports = { 11 | insertUserPin : function(pin, callback){ 12 | try{ 13 | var url = API_URL_ROOT + 'v1/user/pins/' + pin.id; 14 | 15 | var xhr = new XMLHttpRequest(); 16 | xhr.open('PUT', url, false); 17 | 18 | Pebble.getTimelineToken(function(token){ 19 | xhr.setRequestHeader('Content-Type', 'application/json'); 20 | xhr.setRequestHeader('X-User-Token', '' + token); 21 | xhr.send(JSON.stringify(pin)); 22 | 23 | if(xhr.status === 503 || xhr.status === 429){ 24 | if(insertCount < 3){ 25 | insertCount++; 26 | this.insertUserPin(pin, callback); 27 | return; 28 | } 29 | else{ 30 | insertCount = 0; 31 | App.sendErrorMessage( { "Title" : _("Timeline Down!"), "Body" : _("Pebble's servers are down at the moment. Please try your request again later!") } ); 32 | return; 33 | } 34 | } 35 | else if(xhr.status !== 200){ 36 | App.sendErrorMessage( { "Title" : _("Timeline Error!"), "Body" : _("Something's not quite right with that Timeline Pin!\nError: ") + xhr.status } ); 37 | return; 38 | } 39 | 40 | callback(xhr.responseText); 41 | }, function(error){ 42 | App.sendErrorMessage( { "Title" : _("Uh-oh"), "Body" : _("Something went wrong with your Timeline. Try again?") } ); 43 | }); 44 | 45 | } 46 | catch(err){ 47 | console.error(err + "\n" + err.stack); 48 | App.sendErrorMessage( { "Title" : _("Uh-oh"), "Body" : _("Something went wrong with your Timeline. Try again?") } ); 49 | } 50 | }, 51 | deleteUserPin : function(pin, callback){ 52 | try{ 53 | var url = API_URL_ROOT + 'v1/user/pins/' + pin.id; 54 | 55 | var xhr = new XMLHttpRequest(); 56 | xhr.open('DELETE', url, false); 57 | 58 | Pebble.getTimelineToken(function(token){ 59 | xhr.setRequestHeader('Content-Type', 'application/json'); 60 | xhr.setRequestHeader('X-User-Token', '' + token); 61 | xhr.send(JSON.stringify(pin)); 62 | 63 | if(xhr.status === 503 || xhr.status === 429){ 64 | if(deleteCount < 3){ 65 | deleteCount++; 66 | this.deleteUserPin(pin, callback); 67 | return; 68 | } 69 | else{ 70 | deleteCount = 0; 71 | App.sendErrorMessage( { "Title" : _("Timeline Down!"), "Body" : _("Pebble's servers are down at the moment. Please try your request again later!") } ); 72 | return; 73 | } 74 | } 75 | else if(xhr.status !== 200){ 76 | App.sendErrorMessage( { "Title" : _("Timeline Error!"), "Body" : _("Something's not quite right with that Timeline Pin!\nError: ") + xhr.status } ); 77 | return; 78 | } 79 | 80 | callback(xhr.responseText); 81 | }, function(error){ 82 | App.sendErrorMessage( { "Title" : _("Uh-oh"), "Body" : _("Something went wrong with your Timeline. Try again?") } ); 83 | }); 84 | } 85 | catch(err){ 86 | console.error(err + "\n" + err.stack); 87 | App.sendErrorMessage( { "Title" : _("Uh-oh"), "Body" : _("Something went wrong with your Timeline. Try again?") } ); 88 | } 89 | }, 90 | reminderPin : function(start, body){ 91 | try{ 92 | var id = "snowy_reminder_" + Date.now(); 93 | localStorage.lastPin = id; 94 | localStorage.lastBody = body; 95 | 96 | var pin = { 97 | "id" : id, 98 | "time" : start.toISOString(), 99 | "duration" : 1, 100 | "layout" : { 101 | "title" : body, 102 | "subtitle" : _("via Snowy"), 103 | "type" : "genericPin", 104 | "tinyIcon" : "system://images/NOTIFICATION_REMINDER", 105 | "largeIcon": "system://images/NOTIFICATION_REMINDER", 106 | "foregroundColor" : "#000000", 107 | "backgroundColor" : "#00AAAA" 108 | }, 109 | "reminders" : [ 110 | { 111 | "time" : start.toISOString(), 112 | "layout" : { 113 | "type" : "genericReminder", 114 | "tinyIcon" : "system://images/NOTIFICATION_REMINDER", 115 | "largeIcon": "system://images/NOTIFICATION_REMINDER", 116 | "title" : body 117 | } 118 | } 119 | ], 120 | "actions": [ 121 | { 122 | "title": _("Open Snowy"), 123 | "type": "openWatchApp", 124 | "launchCode": 0 125 | } 126 | ] 127 | }; 128 | 129 | if(Config.getConfig().IftttPlus){ 130 | try{ 131 | var url = "http://maker.ifttt.com/trigger/" + _("snowy_reminder") + "/with/key/" + Config.getConfig().IftttKey; 132 | var xhr = new XMLHttpRequest(); 133 | xhr.open("POST", url, true); 134 | 135 | var data = { "value1" : body, "value2" : start.toGCalString() }; 136 | 137 | xhr.setRequestHeader("Content-Type", "application/json"); 138 | xhr.send(decodeURIComponent(JSON.stringify(data))); 139 | } 140 | catch(e){} 141 | } 142 | 143 | this.insertUserPin(pin, function(response){ 144 | App.sendMessage( { "Title" : _("Ok, I'll remind you!"), "Body" : _("Reminder to \"") + body + _("\" set for ") + start.toLocaleString() } ); 145 | }); 146 | } 147 | catch(err){ 148 | console.error(err + "\n" + err.stack); 149 | App.sendErrorMessage( { "Title" : _("Uh-oh"), "Body" : _("Something went wrong - I really want to remind you to do that thing at that time, but you'll have to try again.") } ); 150 | } 151 | }, 152 | calendarPin : function(start, diff, body){ 153 | try{ 154 | var id = "snowy_calendar_" + Date.now(); 155 | 156 | if(body === "") body = _("Meeting!"); 157 | 158 | localStorage.lastPin = id; 159 | localStorage.lastBody = body; 160 | 161 | var end = new Date(start.getTime() + diff*60*1000); 162 | 163 | var pin = { 164 | "id" : id, 165 | "time" : start.toISOString(), 166 | "duration" : diff, 167 | "layout" : { 168 | "title" : body, 169 | "locationName" : _("via Snowy"), 170 | "type" : "calendarPin", 171 | "tinyIcon" : "system://images/SCHEDULED_EVENT", 172 | "largeIcon": "system://images/SCHEDULED_EVENT", 173 | "foregroundColor" : "#000000", 174 | "backgroundColor" : "#00AAAA" 175 | }, 176 | "reminders" : [ 177 | { 178 | "time" : new Date(start.getTime() - 15*60*1000).toISOString(), 179 | "layout" : { 180 | "type" : "genericReminder", 181 | "tinyIcon" : "system://images/NOTIFICATION_REMINDER", 182 | "largeIcon": "system://images/NOTIFICATION_REMINDER", 183 | "title" : body 184 | } 185 | } 186 | ], 187 | "actions": [ 188 | { 189 | "title": _("Open Snowy"), 190 | "type": "openWatchApp", 191 | "launchCode": 0 192 | } 193 | ] 194 | }; 195 | 196 | if(Config.getConfig().IftttPlus){ 197 | try{ 198 | var url = "http://maker.ifttt.com/trigger/" + _("snowy_calendar") + "/with/key/" + Config.getConfig().IftttKey; 199 | var xhr = new XMLHttpRequest(); 200 | xhr.open("POST", url, true); 201 | 202 | var data = { "value1" : body, "value2" : Parser.createGCalString(start, end) }; 203 | 204 | xhr.setRequestHeader("Content-Type", "application/json"); 205 | xhr.send(decodeURIComponent(JSON.stringify(data))); 206 | } 207 | catch(e){} 208 | } 209 | 210 | this.insertUserPin(pin, function(response){ 211 | App.sendMessage( { "Title" : _("Ok, calendar updated!"), "Body" : "\"" + body + _("\" from ") + start.toLocaleTimeString()+ _(" to ") + end.toLocaleTimeString() + _(" on ") + start.toLocaleDateString() + "." } ); 212 | }); 213 | } 214 | catch(err){ 215 | console.error(err + "\n" + err.stack); 216 | App.sendErrorMessage( { "Title" : _("Uh-oh"), "Body" : _("Something went wrong - That sounds like an important event to add to your calendar, though! Please try again.") } ); 217 | } 218 | } 219 | }; -------------------------------------------------------------------------------- /src/pkjs/app.js: -------------------------------------------------------------------------------- 1 | var KiezelPay = require('kiezelpay-core'); 2 | var kiezelpay = new KiezelPay(false); //Bool = Logging 3 | 4 | var Clay = require('pebble-clay'); 5 | 6 | var clayConfig = require('./app-config'); 7 | 8 | var intel = require('./app-intel'); 9 | 10 | var Parser = require('./app-parser'); 11 | 12 | var customClay = require('./app-custom-clay'); 13 | var messageKeys = require('message_keys'); 14 | var clay = new Clay(clayConfig, customClay, { autoHandleEvents: false }); 15 | 16 | var lang = "en"; 17 | var dict; 18 | 19 | var ENGLISH = 0; 20 | var SPANISH = 1; 21 | var FRENCH = 2; 22 | var GERMAN = 3; 23 | var PORTUGUESE = 4; 24 | var DANISH = 5; 25 | 26 | var _ = require('./app-localize')._; 27 | 28 | var App = require('./app-messaging'); 29 | 30 | var Config = require('./app-settings'); 31 | 32 | Pebble.addEventListener('ready', function(e){ 33 | if(localStorage.settings){ 34 | var tempConfig = JSON.parse(localStorage.settings); 35 | Config.setConfig(tempConfig); 36 | } 37 | 38 | lang = Config.getLang(); 39 | 40 | if(lang !== "en"){ 41 | clay.config = require('./app-config-' + lang); 42 | intel = require('./app-intel-' + lang); 43 | } 44 | 45 | var lat, lon; 46 | 47 | if(navigator && navigator.geolocation) navigator.geolocation.watchPosition( 48 | function(pos){ //Success - High Acc 49 | lat = pos.coords.latitude; 50 | lon = pos.coords.longitude; 51 | Config.setCoords(lat,lon); 52 | }, 53 | function(pos){ //Fail - High Acc 54 | navigator.geolocation.watchPosition( 55 | function(pos){ //Success - Low Acc 56 | lat = pos.coords.latitude; 57 | lon = pos.coords.longitude; 58 | Config.setCoords(lat,lon); 59 | }, 60 | function(pos){}, 61 | { 62 | maximumAge:360000, 63 | timeout:10000, 64 | enableHighAccuracy: false 65 | } 66 | ); 67 | }, 68 | { 69 | maximumAge:360000, 70 | timeout:5000, 71 | enableHighAccuracy: true 72 | } 73 | ); 74 | }); 75 | 76 | Pebble.addEventListener('showConfiguration', function(e){ 77 | if(localStorage.settings){ 78 | var tempConfig = JSON.parse(localStorage.settings); 79 | Config.setConfig(tempConfig); 80 | } 81 | 82 | lang = Config.getLang(); 83 | 84 | if(lang !== "en"){ 85 | clay.config = require('./app-config-' + lang); 86 | intel = require('./app-intel-' + lang); 87 | } 88 | 89 | Pebble.openURL(clay.generateUrl()); 90 | }); 91 | 92 | Pebble.addEventListener('webviewclosed', function(e){ 93 | if(e && !e.response) return; 94 | 95 | dict = clay.getSettings(e.response); 96 | 97 | if(dict.InactivityStart !== undefined) dict.InactivityStart = dict.InactivityStart.replace(/:/g, ''); 98 | if(dict.InactivityEnd !== undefined) dict.InactivityEnd = dict.InactivityEnd.replace(/:/g, ''); 99 | 100 | var tempConfig = Config.getConfig(); 101 | 102 | tempConfig.TempUnit = dict[messageKeys.TemperatureUnit] === "f" ? Config.FAHRENHEIT : Config.CELSIUS; 103 | tempConfig.FontSize = dict[messageKeys.FontSize] === 1 ? Config.SMALL : Config.LARGE; 104 | tempConfig.Subreddit = dict[messageKeys.Reddit]; 105 | tempConfig.HomeAddress = dict[messageKeys.HomeAddress]; 106 | tempConfig.WorkAddress = dict[messageKeys.WorkAddress]; 107 | tempConfig.IftttKey = dict[messageKeys.IftttKey]; 108 | tempConfig.IftttPlus = dict[messageKeys.IftttPlus]; 109 | tempConfig.WuKey = dict[messageKeys.WuKey]; 110 | tempConfig.WolframKey = dict[messageKeys.WolframKey]; 111 | tempConfig.TravelKey = dict[messageKeys.TravelKey]; 112 | tempConfig.HabitsKey = dict[messageKeys.HabitsKey]; 113 | 114 | localStorage.settings = JSON.stringify(tempConfig); 115 | Config.setConfig(tempConfig); 116 | 117 | App.sendSettings(dict); 118 | App.sendMessage( { "Title" : _("Settings Saved!") } ); 119 | }); 120 | 121 | Pebble.addEventListener('appmessage', function(e){ 122 | try{ 123 | var dict = e.payload; 124 | 125 | console.log(JSON.stringify(dict)); 126 | 127 | if(dict.Lang){ 128 | console.log("Lang?"); 129 | switch(dict[messageKeys.Lang]){ 130 | case ENGLISH: lang = "en"; break; 131 | case SPANISH: lang = "es"; break; 132 | case GERMAN: lang = "de"; break; 133 | case FRENCH: lang = "fr"; break; 134 | case PORTUGUESE: lang = "pt"; break; 135 | case DANISH: lang = "da"; break; 136 | default: lang = "en"; break; 137 | } 138 | 139 | Config.setLang(lang); 140 | localStorage.lang = lang; 141 | } 142 | else if(dict.Transcript){ 143 | var request = dict.Transcript.toLowerCase().replace(/'s/g, " is"); 144 | 145 | lang = Config.getLang(); 146 | 147 | if(lang !== "en"){ 148 | clay.config = require('./app-config-' + lang); 149 | intel = require('./app-intel-' + lang); 150 | } 151 | 152 | switch(lang){ 153 | case "en" : 154 | request = request 155 | .replace(/\bone\b/g, '1') 156 | .replace(/\btwo\b/g, '2') 157 | .replace(/\bthree\b/g, '3') 158 | .replace(/\bfour\b/g, '4') 159 | .replace(/\bfive\b/g, '5') 160 | .replace(/\bsix\b/g, '6') 161 | .replace(/\bseven\b/g, '7') 162 | .replace(/\beight\b/g, '8') 163 | .replace(/\bnine\b/g, '9') 164 | .replace(/\bten\b/g, '10'); 165 | break; 166 | case "fr" : 167 | request = request 168 | .replace(/\bun\b/g, '1') 169 | .replace(/\bdeux\b/g, '2') 170 | .replace(/\btrois\b/g, '3') 171 | .replace(/\bquatre\b/g, '4') 172 | .replace(/\bcinq\b/g, '5') 173 | .replace(/\bsix\b/g, '6') 174 | .replace(/\bsept\b/g, '7') 175 | .replace(/\bhuit\b/g, '8') 176 | .replace(/\bneuf\b/g, '9') 177 | .replace(/\bdix\b/g, '10'); 178 | break; 179 | case "es" : 180 | request = request 181 | .replace(/\buno\b/g, '1') 182 | .replace(/\bdos\b/g, '2') 183 | .replace(/\btres\b/g, '3') 184 | .replace(/\bcuatro\b/g, '4') 185 | .replace(/\bcinco\b/g, '5') 186 | .replace(/\bseis\b/g, '6') 187 | .replace(/\bsiete\b/g, '7') 188 | .replace(/\bocho\b/g, '8') 189 | .replace(/\bnueve\b/g, '9') 190 | .replace(/\bdiez\b/g, '10'); 191 | break; 192 | case "de" : 193 | request = request 194 | .replace(/\beins\b/g, '1') 195 | .replace(/\bzwei\b/g, '2') 196 | .replace(/\bdrei\b/g, '3') 197 | .replace(/\bvier\b/g, '4') 198 | .replace(/\bfünf\b/g, '5') 199 | .replace(/\bsechs\b/g, '6') 200 | .replace(/\bsieben\b/g, '7') 201 | .replace(/\bacht\b/g, '8') 202 | .replace(/\bneun\b/g, '9') 203 | .replace(/\bzehn\b/g, '10') 204 | .replace(/\belf\b/g, '11') 205 | .replace(/\bzwölf\b/g, '12'); 206 | break; 207 | case "pt" : 208 | request = request 209 | .replace(/\bum\b/g, '1') 210 | .replace(/\bdois\b/g, '2') 211 | .replace(/\btrês\b/g, '3') 212 | .replace(/\bquatro\b/g, '4') 213 | .replace(/\bcinco\b/g, '5') 214 | .replace(/\bseis\b/g, '6') 215 | .replace(/\bsete\b/g, '7') 216 | .replace(/\boito\b/g, '8') 217 | .replace(/\bnove\b/g, '9') 218 | .replace(/\bdez\b/g, '10'); 219 | break; 220 | case "da" : 221 | request = request 222 | .replace(/\ben\b/g, '1') 223 | .replace(/\bto\b/g, '2') 224 | .replace(/\btre\b/g, '3') 225 | .replace(/\bfire\b/g, '4') 226 | .replace(/\bfem\b/g, '5') 227 | .replace(/\bseks\b/g, '6') 228 | .replace(/\bsyv\b/g, '7') 229 | .replace(/\botte\b/g, '8') 230 | .replace(/\bni\b/g, '9') 231 | .replace(/\bti\b/g, '10'); 232 | break; 233 | default: 234 | request = request 235 | .replace(/\bone\b/g, '1') 236 | .replace(/\btwo\b/g, '2') 237 | .replace(/\bthree\b/g, '3') 238 | .replace(/\bfour\b/g, '4') 239 | .replace(/\bfive\b/g, '5') 240 | .replace(/\bsix\b/g, '6') 241 | .replace(/\bseven\b/g, '7') 242 | .replace(/\beight\b/g, '8') 243 | .replace(/\bnine\b/g, '9') 244 | .replace(/\bten\b/g, '10'); 245 | break; 246 | } 247 | 248 | console.log("Request: " + request); 249 | 250 | if(request.indexOf('#') === 0){ 251 | switch(request.substring(1)){ 252 | case "weather" : console.log("Weather Widget"); break; 253 | case "stock" : console.log("Stock Widget"); break; 254 | } 255 | } 256 | else{ 257 | console.log("Precheck"); 258 | Parser.check(intel, request, dict.Transcript); 259 | } 260 | } 261 | } 262 | catch(err){ 263 | console.error(err + "\n" + err.stack); 264 | } 265 | }); -------------------------------------------------------------------------------- /worker_src/c/w/worker.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MESSAGE_KEY_WorkerLaunchReason 10058 4 | #define MESSAGE_KEY_StepGoal 10018 5 | #define MESSAGE_KEY_HandsFree 10010 6 | #define MESSAGE_KEY_InactivityMonitor 10020 7 | #define MESSAGE_KEY_InactivityStart 10034 8 | #define MESSAGE_KEY_InactivityEnd 10035 9 | #define MESSAGE_KEY_StepGoalNotified 10059 10 | #define MESSAGE_KEY_InactivityLastAlert 10060 11 | 12 | int goal; 13 | 14 | int start_hour, start_min, end_hour, end_min; 15 | 16 | HealthMinuteData *minute_data; 17 | const uint32_t max_records = 60; 18 | 19 | int average_steps = 0; 20 | 21 | int sum_steps = 0, num_valid = 1; 22 | 23 | void tap_handler(AccelAxisType axis, int32_t direction){ 24 | if(persist_exists(MESSAGE_KEY_HandsFree) && persist_read_bool(MESSAGE_KEY_HandsFree) && bluetooth_connection_service_peek()){ 25 | persist_write_int(MESSAGE_KEY_WorkerLaunchReason, 0); 26 | worker_launch_app(); 27 | } 28 | } 29 | 30 | void time_handle_tick(struct tm *tick_time, TimeUnits units){ 31 | if(persist_exists(MESSAGE_KEY_StepGoal) && persist_read_int(MESSAGE_KEY_StepGoal) > 0){ 32 | HealthServiceAccessibilityMask steps = health_service_metric_accessible(HealthMetricStepCount, time_start_of_today(), time(NULL)); 33 | if(steps & HealthServiceAccessibilityMaskAvailable){ 34 | int num_steps = health_service_sum_today(HealthMetricStepCount); 35 | if(num_steps > goal){ 36 | if(!persist_exists(MESSAGE_KEY_StepGoalNotified) || !persist_read_bool(MESSAGE_KEY_StepGoalNotified)){ 37 | persist_write_int(MESSAGE_KEY_WorkerLaunchReason, 1); 38 | persist_write_bool(MESSAGE_KEY_StepGoalNotified, true); 39 | worker_launch_app(); 40 | return; 41 | } 42 | } 43 | else{ 44 | persist_write_bool(MESSAGE_KEY_StepGoalNotified, false); 45 | } 46 | } 47 | } 48 | 49 | if(persist_exists(MESSAGE_KEY_InactivityMonitor) && persist_read_bool(MESSAGE_KEY_InactivityMonitor)){ 50 | if( (tick_time->tm_hour > start_hour) || (tick_time->tm_hour = start_hour && tick_time->tm_min >= start_min) ){ 51 | if( (tick_time->tm_hour < end_hour) || (tick_time->tm_hour = end_hour && tick_time->tm_min < end_min) ){ 52 | if(!persist_exists(MESSAGE_KEY_InactivityLastAlert) || persist_read_int(MESSAGE_KEY_InactivityLastAlert)+(60*60) <= time(NULL)){ 53 | 54 | time_t end = time(NULL); 55 | time_t start = end - SECONDS_PER_HOUR; 56 | 57 | uint32_t num_records = health_service_get_minute_history(minute_data, max_records, &start, &end); 58 | 59 | sum_steps = 0; 60 | num_valid = 1; 61 | 62 | for(uint32_t i = 0; i < num_records; i++){ 63 | sum_steps += minute_data[i].steps; 64 | num_valid++; 65 | } 66 | 67 | if(num_valid > 1 ) num_valid--; 68 | average_steps = sum_steps / num_valid; 69 | 70 | if(average_steps < 8){ 71 | persist_write_int(MESSAGE_KEY_WorkerLaunchReason, 2); 72 | persist_write_int(MESSAGE_KEY_InactivityLastAlert, time(NULL)); 73 | worker_launch_app(); 74 | return; 75 | } 76 | } 77 | } 78 | } 79 | } 80 | } 81 | 82 | void worker_init(){ 83 | accel_tap_service_subscribe(tap_handler); 84 | 85 | goal = persist_exists(MESSAGE_KEY_StepGoal) ? persist_read_int(MESSAGE_KEY_StepGoal) : 0; 86 | 87 | start_hour = persist_exists(MESSAGE_KEY_InactivityStart) ? (persist_read_int(MESSAGE_KEY_InactivityStart)/100) + 1 : 0; 88 | start_min = persist_exists(MESSAGE_KEY_InactivityStart) ? persist_read_int(MESSAGE_KEY_InactivityStart)%100 : 0; 89 | 90 | end_hour = persist_exists(MESSAGE_KEY_InactivityEnd) ? persist_read_int(MESSAGE_KEY_InactivityEnd)/100 : 0; 91 | end_min = persist_exists(MESSAGE_KEY_InactivityEnd) ? persist_read_int(MESSAGE_KEY_InactivityEnd)%100 : 0; 92 | 93 | time_t now = time(NULL); 94 | struct tm *ltime = localtime(&now); 95 | time_handle_tick(ltime, MINUTE_UNIT); 96 | 97 | tick_timer_service_subscribe(MINUTE_UNIT, time_handle_tick); 98 | } 99 | 100 | void worker_deinit(){ 101 | accel_tap_service_unsubscribe(); 102 | tick_timer_service_unsubscribe(); 103 | } 104 | 105 | int main(void){ 106 | worker_init(); 107 | worker_event_loop(); 108 | worker_deinit(); 109 | } -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | # 2 | # This file is the default set of rules to compile a Pebble project. 3 | # 4 | # Feel free to customize this to your needs. 5 | # 6 | 7 | import os.path 8 | try: 9 | from sh import CommandNotFound, jshint, cat, ErrorReturnCode_2 10 | hint = jshint 11 | except (ImportError, CommandNotFound): 12 | hint = None 13 | 14 | top = '.' 15 | out = 'build' 16 | 17 | 18 | def options(ctx): 19 | ctx.load('pebble_sdk') 20 | 21 | 22 | def configure(ctx): 23 | ctx.load('pebble_sdk') 24 | 25 | 26 | def build(ctx): 27 | if False and hint is not None: 28 | try: 29 | hint([node.abspath() for node in ctx.path.ant_glob("src/**/*.js")], _tty_out=False) # no tty because there are none in the cloudpebble sandbox. 30 | except ErrorReturnCode_2 as e: 31 | ctx.fatal("\nJavaScript linting failed (you can disable this in Project Settings):\n" + e.stdout) 32 | 33 | ctx.load('pebble_sdk') 34 | 35 | build_worker = os.path.exists('worker_src') 36 | binaries = [] 37 | 38 | for p in ctx.env.TARGET_PLATFORMS: 39 | ctx.set_env(ctx.all_envs[p]) 40 | ctx.set_group(ctx.env.PLATFORM_NAME) 41 | app_elf = '{}/pebble-app.elf'.format(ctx.env.BUILD_DIR) 42 | ctx.pbl_program(source=ctx.path.ant_glob('src/c/**/*.c'), target=app_elf) 43 | 44 | if build_worker: 45 | worker_elf = '{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR) 46 | binaries.append({'platform': p, 'app_elf': app_elf, 'worker_elf': worker_elf}) 47 | ctx.pbl_worker(source=ctx.path.ant_glob('worker_src/c/**/*.c'), target=worker_elf) 48 | else: 49 | binaries.append({'platform': p, 'app_elf': app_elf}) 50 | 51 | ctx.set_group('bundle') 52 | ctx.pbl_bundle(binaries=binaries, js=ctx.path.ant_glob(['src/pkjs/**/*.js', 'src/pkjs/**/*.json']), js_entry_file='src/pkjs/app.js') 53 | --------------------------------------------------------------------------------