├── README.md
├── app
└── index.js
├── companion
└── index.js
├── package.json
├── resources
├── icon.png
├── index.gui
├── styles.css
└── widgets.gui
└── settings
└── index.jsx
/README.md:
--------------------------------------------------------------------------------
1 | # SendMessage
2 | Send Message app for Fitbit Versa and Ionic
3 |
4 | This is a port of my Send Message app from Pebble - an HTTPS client supporting GET and POST requests (though HTTP only works to IP addresses on your own network). It is intended for technically proficient audience and use of its more advanced features requires some understanding of JSON data structures.
5 |
6 | The app allows configuration with three sets of label, URL, data segment (for POST) and headers. Touching that label will then fire off the associated request, and the status text returned will be displayed on the watch and can be dismissed by touching it.
7 |
8 | For example, the label might be 'Send help!' and the URL/data fields contain the request details to send this message to a service that creates a text message (to which you would need to subscribe). The URL will be something like 'https://server.domain.com/path' and the data might look something like {"{'key':'message','value':'Help!'} "} or {"{'key':'message','value':'~Lbl'} "} (where ~Lbl is substitited with the label text).
9 |
10 | To test your requests, you might want to use the free testing service at https://www.requestcatcher.com.
11 |
12 | The compiled version of this app is at https://gam.fitbit.com/gallery/app/08e2723d-c0f3-429c-9cb8-e93086fd604c.
13 |
14 | The Pebble version of this app could also substitute location-specific information. I may implement this later if there's enough interest.
15 |
--------------------------------------------------------------------------------
/app/index.js:
--------------------------------------------------------------------------------
1 | import document from "document";
2 | import * as messaging from "messaging";
3 | import { vibration } from "haptics";
4 |
5 | console.log(`Setting up...`);
6 | let background1 = document.getElementById("clickbg1");
7 | let background2 = document.getElementById("clickbg2");
8 | let background3 = document.getElementById("clickbg3");
9 | let Label1 = document.getElementById("Label1");
10 | let Label2 = document.getElementById("Label2");
11 | let Label3 = document.getElementById("Label3");
12 | let responseDisplay = document.getElementById("responseDisplay");
13 |
14 | // Message is received
15 | messaging.peerSocket.onmessage = evt => {
16 | console.log(`App received: ${JSON.stringify(evt)}`);
17 | if (evt.data.key === "Label1" && evt.data.newValue)
18 | Label1.text = JSON.parse(evt.data.newValue).name;
19 | if (evt.data.key === "Label2" && evt.data.newValue)
20 | Label2.text = JSON.parse(evt.data.newValue).name;
21 | if (evt.data.key === "Label3" && evt.data.newValue)
22 | Label3.text = JSON.parse(evt.data.newValue).name;
23 | if (evt.data.key === "Response" && evt.data.value) {
24 | console.log("Response = " + evt.data.value)
25 | responseDisplay.style.display = "inline"
26 | responseDisplay.text = evt.data.value;
27 | }
28 | if (Label1.text == "" && Label2.text == "" && Label3.text == "") {
29 | Label1.text = " Please"
30 | Label2.text = " set"
31 | Label3.text = " configuration."
32 | }
33 | };
34 |
35 |
36 | // Message socket opens
37 | messaging.peerSocket.onopen = () => {
38 | console.log("App Socket Open");
39 | };
40 |
41 | // Message socket closes
42 | messaging.peerSocket.onclose = () => {
43 | console.log("App Socket Closed");
44 | };
45 |
46 | background1.onclick = function(evt) {
47 | sendVal({key:'msg', value:'1'})
48 | console.log("Click Label 1");
49 | vibration.start("confirmation");
50 | }
51 |
52 | background2.onclick = function(evt) {
53 | sendVal({key:'msg', value:'2'})
54 | console.log("Click Label 2");
55 | vibration.start("confirmation");
56 | }
57 |
58 | background3.onclick = function(evt) {
59 | sendVal({key:'msg', value:'3'})
60 | console.log("Click Label 3");
61 | vibration.start("confirmation");
62 | }
63 |
64 | responseDisplay.onclick = function(evt) {
65 | responseDisplay.style.display = "none"
66 | console.log("Click Response");
67 | vibration.start("confirmation");
68 | }
69 |
70 | Label1.onclick = background1.onclick
71 | Label2.onclick = background2.onclick
72 | Label3.onclick = background3.onclick
73 |
74 | // Send data to device using Messaging API
75 | function sendVal(data) {
76 | console.log(JSON.stringify(data))
77 | if (messaging.peerSocket.readyState === messaging.peerSocket.OPEN) {
78 | messaging.peerSocket.send(data);
79 | } else {
80 | responseDisplay.style.display = "inline"
81 | responseDisplay.text = "Phone?";
82 | }
83 | }
84 |
85 |
--------------------------------------------------------------------------------
/companion/index.js:
--------------------------------------------------------------------------------
1 | import * as messaging from "messaging";
2 | import { settingsStorage } from "settings";
3 |
4 | // Message socket opens
5 | messaging.peerSocket.onopen = () => {
6 | console.log("Companion Socket Open");
7 | restoreSettings();
8 | };
9 |
10 | // Message socket closes
11 | messaging.peerSocket.onclose = () => {
12 | console.log("Companion Socket Closed");
13 | };
14 |
15 | // A user changes settings
16 | settingsStorage.onchange = evt => {
17 | let data = {
18 | key: evt.key,
19 | newValue: evt.newValue
20 | };
21 | sendVal(data);
22 | };
23 |
24 | // Restore any previously saved settings and send to the device
25 | function restoreSettings() {
26 | sendVal({key:'Label1', newValue:settingsStorage.getItem('Label1')})
27 | sendVal({key:'Label2', newValue:settingsStorage.getItem('Label2')})
28 | sendVal({key:'Label3', newValue:settingsStorage.getItem('Label3')})
29 | }
30 |
31 | // Send data to device using Messaging API
32 | function sendVal(data) {
33 | if (messaging.peerSocket.readyState === messaging.peerSocket.OPEN) {
34 | messaging.peerSocket.send(data);
35 | }
36 | }
37 |
38 | // Message is received
39 | messaging.peerSocket.onmessage = evt => {
40 | console.log(`Phone received: ${JSON.stringify(evt.data)}`);
41 | let msg = evt.data.value
42 | sendVal({key:'Response', value:'Sending ' + msg + "..."})
43 | let label = ""
44 | try {label = JSON.parse(settingsStorage.getItem('Label'+msg)).name} catch(err) {}
45 | let url = ""
46 | try {url = JSON.parse(settingsStorage.getItem('URL'+msg)).name} catch(err) {}
47 | url = url.replace(/~Lbl/g,encodeURIComponent(label));
48 | let data = ""
49 | try {data = JSON.parse(settingsStorage.getItem('Data'+msg)).name} catch(err) {}
50 | let headers = ""
51 | try {headers = JSON.parse(settingsStorage.getItem('Headers'+msg)).name} catch(err) {}
52 | console.log("URL = "+ url)
53 | console.log("Data = " + data)
54 | console.log("Headers = " + headers)
55 | if (data == "")
56 | if (headers == "")
57 | fetch(url, {method: "GET"})
58 | .then(function(response) {sendVal({key:'Response', value:response.statusText})})
59 | else
60 | fetch(url, {method: "GET", headers: JSON.parse(headers)})
61 | .then(function(response) {sendVal({key:'Response', value:response.statusText})})
62 | else {
63 | data = data.replace(/~Lbl/g,label);
64 | if (headers == "")
65 | fetch(url, {method: "POST", body: data})
66 | .then(function(response) {sendVal({key:'Response', value:response.statusText})})
67 | else
68 | fetch(url, {method: "POST", headers: JSON.parse(headers), body: data})
69 | .then(function(response) {sendVal({key:'Response', value:response.statusText})})
70 | }
71 | };
72 |
73 |
74 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "fitbit": {
3 | "appUUID": "08e2723d-c0f3-429c-9cb8-e93086fd604c",
4 | "appType": "app",
5 | "appDisplayName": "Send Message",
6 | "iconFile": "resources/icon.png",
7 | "wipeColor": "#607d8b",
8 | "requestedPermissions": [
9 | "access_internet"
10 | ],
11 | "buildTargets": [
12 | "higgs",
13 | "meson"
14 | ],
15 | "i18n": {
16 | "en": {
17 | "name": "Send Message"
18 | }
19 | }
20 | },
21 | "devDependencies": {
22 | "@fitbit/sdk": "~3.0.0"
23 | }
24 | }
--------------------------------------------------------------------------------
/resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PeterSumm/SendMessage/8c9d76581fbd0e739c37b4730f92a12613a48d9a/resources/icon.png
--------------------------------------------------------------------------------
/resources/index.gui:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/resources/styles.css:
--------------------------------------------------------------------------------
1 | .background {
2 | viewport-fill: black;
3 | }
4 |
5 | #clickbg1 {
6 | width: 100%;
7 | height: 33%;
8 | y: 0%;
9 | fill: orange;
10 | opacity: 10
11 | }
12 |
13 | #clickbg2 {
14 | width: 100%;
15 | height: 34%;
16 | y: 33%;
17 | fill: red;
18 | opacity: 10
19 | }
20 |
21 | #clickbg3 {
22 | width: 100%;
23 | height: 33%;
24 | y: 67%;
25 | fill: green;
26 | opacity: 10
27 | }
28 |
29 | /* Labels */
30 | #Label1 {
31 | font-size: 33;
32 | font-family: Colfax-Medium;
33 | text-length: 30;
34 | text-anchor: start;
35 | x: 10;
36 | y: 20%;
37 | fill: black;
38 | }
39 |
40 | #Label2 {
41 | font-size: 33;
42 | font-family: Colfax-Medium;
43 | text-length: 30;
44 | text-anchor: start;
45 | x: 10;
46 | y: 54%;
47 | fill: black;
48 | }
49 |
50 | #Label3 {
51 | font-size: 33;
52 | font-family: Colfax-Medium;
53 | text-length: 30;
54 | text-anchor: start;
55 | x: 10;
56 | y: 87%;
57 | fill: black;
58 | }
59 |
60 | #responseDisplay {
61 | font-size: 50;
62 | font-family: Colfax-Medium;
63 | text-length: 12;
64 | text-anchor: middle;
65 | x: 50%;
66 | y: 55%;
67 | fill: white;
68 | display: none;
69 | }
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/resources/widgets.gui:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/settings/index.jsx:
--------------------------------------------------------------------------------
1 | function mySettings(props) {
2 | return (
3 |
4 | Message 1}>
6 |
10 |
14 |
18 |
22 |
23 | Message 2}>
25 |
29 |
33 |
37 |
41 |
42 | Message 3}>
44 |
48 |
52 |
56 |
60 |
61 | Instructions}>
63 |
64 | This is an HTTP/HTTPS client supporting GET and POST requests (though HTTP only works to IP addresses on your own network).
65 | It is intended for a technically proficient audience and use of its more advanced features requires some understanding of JSON
66 | data structures.
67 | The app allows configuration with three sets of label, URL, data segment (for POST) and headers. Touching that label will then
68 | fire off the associated request, and the status text returned will be displayed on the watch and can be dismissed by touching it.
69 | For example, the label might be 'Send help!' and the URL/data fields contain the request details to send this message to a service
70 | that creates a text message (to which you would need to subscribe).
71 | The URL is something like 'https://server.domain.com/path' and the data might look something like {"{'key':'message','value':'Help!'} "}
72 | or {"{'key':'message','value':'~Lbl'} "} (where ~Lbl is substitited with the label text).
73 | To test your requests, you might want to use the free testing service at https://www.requestcatcher.com.
74 | The Pebble version of this app could also substitute location-specific information. I may implement this later if there's enough interest.
75 |
76 |
77 |
78 | );
79 | }
80 |
81 | registerSettingsPage(mySettings);
82 |
--------------------------------------------------------------------------------