hello-sense 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a8e647726f67f5252d0c42deacd02db75b199fbb
4
+ data.tar.gz: 8aafd391d5d2ab33ce704bc64850ad9be5b431f4
5
+ SHA512:
6
+ metadata.gz: 815cb57782f3aa897b984a809c3f4f1eeb4368bbac81b18b39777df479db2480cc5e0d7b46d0fbafeb721eda0c52ef87e4a12c9e1e369212c5500c4fa84f6e4d
7
+ data.tar.gz: ae0e965c4fb843128eec6066e681ee619fc889f727eb8985c03ddae2fe02a01f5ac14b4787bfcb4932cb0cfb1b299f1a8172aeda220176b853894ec694780227
@@ -0,0 +1,9 @@
1
+ # Change Log
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](http://keepachangelog.com/)
5
+ and this project adheres to [Semantic Versioning](http://semver.org/).
6
+
7
+ ## Unreleased
8
+ ### Added
9
+ - Initial release
@@ -0,0 +1,56 @@
1
+ # Contributing to `hello-sense`
2
+
3
+ Thank you for your interest in contributing to the `hello-sense` gem! We welcome any contributions: code, bug reports, documentation, and more.
4
+
5
+ ## Bug reports
6
+
7
+ There’s a number of reasons to file a bug report:
8
+
9
+ * incorrect or unexpected results
10
+ * bugs
11
+ * misleading or incorrect documentation
12
+
13
+ Opening an issue is as easy as following [this link](https://github.com/stilist/hello-sense/issues/new) and filling out the fields. You can reference the [template](.github/ISSUE_TEMPLATE.md) to see what information will be the most helpful.
14
+
15
+ ## Development environment
16
+
17
+ To install the development dependencies, run these commands:
18
+
19
+ ```bash
20
+ $ gem install bundler
21
+ $ bundle install
22
+ ```
23
+
24
+ ## Making changes
25
+
26
+ When you make changes to the code, make sure you’re keeping the [test suite](http://rspec.info) and [inline documentation](http://yardoc.org) updated.
27
+
28
+ When you’re done making your changes, run `rake build`. This automatically generates documentation (`rake doc`), [lints the code](https://github.com/bbatsov/rubocop) (`rake lint`), and runs the test suite (`rake spec`) before building the `.gem` file. If `rake build` finishes without any errors and builds the `.gem`, your changes are ready for a pull request.
29
+
30
+ ## Pull requests
31
+
32
+ We use pull requests as a simple, consistent way to make updates to the project. GitHub has some [great documentation][pull-requests] on using the Pull Request feature. We use the ‘fork and pull’ model described there.
33
+
34
+ [pull-requests]: https://help.github.com/articles/using-pull-requests/
35
+
36
+ Please make pull requests against the `master` branch. We have a [pull request template](PULL_REQUEST_TEMPLATE.md) to help guide you.
37
+
38
+ ## Coding conventions
39
+
40
+ Hello doesn't publicly document the Sense API, so this gem's documentation includes request and response data to demonstrate each API endpoint. To keep personal information out of the example data, use these values whenever you add or change documentation:
41
+
42
+ * email address: `drisha.wabudeya@example.org`
43
+ * name: [Drisha](http://www.bachpan.com/Meaning-Of-Drisha.aspx) [Wabudeya](http://www.americanlastnames.us/last-names/Ugandan/W/W-0.html)
44
+ * password: `'My super secure password!'`
45
+ * device id: `ABCDEF1234567890` or `0987654321FEDCBA`
46
+ * create timestamp: `1420099200000` (`2015-01-01T00:00:00-0800`)
47
+ * update timestamp: `1483257600000` (`2017-01-01T00:00:00-0800`)
48
+ * [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier): `FDED667B-9E91-43F5-91DE-258AC1FEE9C2` (generated with [an online tool](https://guidgenerator.com/online-guid-generator.aspx))
49
+
50
+
51
+
52
+
53
+
54
+
55
+ * https://apkpure.com/sense/is.hello.sense
56
+ * https://github.com/skylot/jadx
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Jordan Cole
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,55 @@
1
+ # `hello-sense`
2
+
3
+ ## Installation
4
+
5
+ ```shell
6
+ gem install hello-sense
7
+ ```
8
+
9
+ ## Usage
10
+
11
+ ### Authentication
12
+
13
+ The Sense API relies on an [OAuth 2](https://oauth.net/2/) [access token](https://www.oauth.com/oauth2-servers/access-tokens/).
14
+
15
+ (Note: the `client_id` and `client_secret` shown here are taken from the official Sense app's source code.)
16
+
17
+ ```ruby
18
+ # Create a session using email and password.
19
+ client = Sense::Client.new(client_id: '8d3c1664-05ae-47e4-bcdb-477489590aa4',
20
+ client_secret: '4f771f6f-5c10-4104-bbc6-3333f5b11bf9',
21
+ username: 'drisha.wabudeya@example.org',
22
+ password: 'My super secure password!')
23
+ ```
24
+
25
+ ```ruby
26
+ # Create a session using an existing access token.
27
+ client = Sense::Client.new(access_token: '2.fded667b9e9143f591de258ac1fee9c2')
28
+ ```
29
+
30
+ ```ruby
31
+ # Get the timeline for the sleep session from the night of 31 December 2016.
32
+ client.timeline('2017-01-01')
33
+
34
+ # Get the current conditions reported by the device's sensors.
35
+ client.sensors
36
+ ```
37
+
38
+ ```ruby
39
+ # Invalidate the session's access token.
40
+ client.destroy_token
41
+ ```
42
+
43
+ ```ruby
44
+ # Pull in ~38 days of sensor data, sampled at 5 minute intervals.
45
+ data = client.sensors_historical(hours: 920)
46
+ File.open('sense_sensor_data.json', 'w') { |f| f.write(JSON.dump(data) }
47
+ ```
48
+
49
+ ## Contributing
50
+
51
+ To contribute to the `hello-sense` gem, please see [`CONTRIBUTING`](CONTRIBUTING.md).
52
+
53
+ ## References
54
+
55
+ * http://jeffhuang.com/extracting_my_data_from_the_hello_sense.html
@@ -0,0 +1,24 @@
1
+ require_relative 'lib/hello_sense/version'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.date = Time.now.strftime('%Y-%m-%d')
5
+ s.description = "Ruby implementation of Sense's private API"
6
+ s.name = 'hello-sense'
7
+ s.required_ruby_version = ['>= 2.3.0', '< 2.5.0']
8
+ s.summary = 'TK'
9
+ s.version = Sense::VERSION
10
+
11
+ s.authors = ['Jordan Cole']
12
+ s.email = ['stilist@ratafia.info']
13
+ s.homepage = 'https://github.com/stilist/hello-sense'
14
+
15
+ s.files = Dir[__FILE__, 'LICENSE', '*.md', 'lib/**/*']
16
+ s.metadata['yard.run'] = 'yri'
17
+
18
+ s.add_development_dependency('byebug', '~> 9.0', '>= 9.0.0')
19
+ s.add_development_dependency('rake', '~> 11.0', '>= 11.0.0')
20
+ s.add_development_dependency('rspec-core', '~> 3.6', '>= 3.6.0')
21
+ s.add_development_dependency('rspec-expectations', '~> 3.6', '>= 3.6.0')
22
+ s.add_development_dependency('rubocop', '~> 0.48', '>= 0.48.0')
23
+ s.add_development_dependency('yard', '~> 0.9', '>= 0.9.0')
24
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Defines the namespace for the +hello-sense+ gem.
4
+ module Sense
5
+ require_relative 'hello_sense/account'
6
+ require_relative 'hello_sense/alarms'
7
+ require_relative 'hello_sense/alerts'
8
+ require_relative 'hello_sense/app'
9
+ require_relative 'hello_sense/devices'
10
+ require_relative 'hello_sense/expansions'
11
+ require_relative 'hello_sense/firmware'
12
+ require_relative 'hello_sense/insights'
13
+ require_relative 'hello_sense/notifications'
14
+ require_relative 'hello_sense/questions'
15
+ require_relative 'hello_sense/sensors'
16
+ require_relative 'hello_sense/sharing'
17
+ require_relative 'hello_sense/sleep_sounds'
18
+ require_relative 'hello_sense/speech'
19
+ require_relative 'hello_sense/stats'
20
+ require_relative 'hello_sense/store'
21
+ require_relative 'hello_sense/support'
22
+ require_relative 'hello_sense/timeline'
23
+ require_relative 'hello_sense/trends'
24
+
25
+ require_relative 'hello_sense/api_error'
26
+ require_relative 'hello_sense/constants'
27
+ require_relative 'hello_sense/session'
28
+ require_relative 'hello_sense/version'
29
+ require_relative 'hello_sense/client'
30
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sense
4
+ module Account
5
+ EMAIL_PATTERN = /^.+@.+\\..+$/
6
+ MIN_PASSWORD_LENGTH = 6
7
+
8
+ # Get the current
9
+ #
10
+ # @return [Hash]
11
+ #
12
+ # @example
13
+ # {
14
+ # "email" => "drisha.wabudeya@example.org",
15
+ # "tz" => -28800000,
16
+ # "name" => "Drisha",
17
+ # "firstname" => "Drisha",
18
+ # "lastname" => "Wabudeya",
19
+ # "gender" => "FEMALE",
20
+ # "gender_other" => "",
21
+ # "height" => 160,
22
+ # "weight" => 68000,
23
+ # "created" => 1420099200000,
24
+ # "last_modified" => 1420099200000,
25
+ # "email_verified" => true,
26
+ # "profile_photo" => nil,
27
+ # "ext_id" => "fded667b-9e91-43f5-91de-258ac1fee9c2",
28
+ # "dob" => "1990-01-01",
29
+ # "id" => "fded667b-9e91-43f5-91de-258ac1fee9c2"
30
+ # }
31
+
32
+ def account
33
+ get('/v1/account')
34
+ end
35
+
36
+ # @param data [Hash]
37
+ #
38
+ # @see #account for fields
39
+
40
+ def create_account(data)
41
+ put('/v1/account', data)
42
+ end
43
+
44
+ # @param data [Hash]
45
+ #
46
+ # @see #account for fields
47
+
48
+ def update_account(data)
49
+ post('/v1/account', data)
50
+ end
51
+
52
+ # @return [Hash]
53
+ #
54
+ # @example
55
+ # {
56
+ # "TIME_TWENTY_FOUR_HOUR" => true,
57
+ # "PUSH_SCORE" => true,
58
+ # "WEIGHT_METRIC" => false,
59
+ # "TEMP_CELSIUS" => true,
60
+ # "HEIGHT_METRIC" => false,
61
+ # "ENHANCED_AUDIO" => true,
62
+ # "PUSH_ALERT_CONDITIONS" => true
63
+ # }
64
+
65
+ def preferences
66
+ get('/v2/account/preferences')
67
+ end
68
+
69
+ def update_preferences(data)
70
+ put('/v2/account/preferences', data)
71
+ end
72
+
73
+ def update_photo(data)
74
+ post('/v1/photo/profile', data)
75
+ end
76
+
77
+ def delete_profile_picture
78
+ delete('/v1/photo/profile')
79
+ end
80
+
81
+ # @param email [String] the new email address for the account
82
+ # @return XXX
83
+
84
+ def update_email(email)
85
+ raise InvalidEmailError if !normalize(email).match(EMAIL_PATTERN)
86
+
87
+ data = account.merge('email' => email)
88
+
89
+ post('/v1/account/email', data)
90
+ end
91
+
92
+ # @param current_password [String] the user's current password
93
+ # @param new_password [String] the new password to use
94
+ #
95
+ # @note
96
+ #
97
+ # @example
98
+ # client.update_password('My super secure password!',
99
+ # 'My even better new password (my second)!')
100
+
101
+ def update_password(current_password, new_password)
102
+ raise InvalidPasswordError if normalize(new_password).length < MIN_PASSWORD_LENGTH
103
+
104
+ post('/v1/account/password', currentPassword: current_password,
105
+ newPassword: new_password)
106
+ end
107
+
108
+ # @return [Hash]
109
+ #
110
+ # @example
111
+ # {
112
+ # "timezone_offset" => -25200000,
113
+ # "timezone_id" => "America/Los_Angeles"
114
+ # }
115
+
116
+ def current_timezone
117
+ get('/v1/timezone')
118
+ end
119
+
120
+ def update_timezone(data)
121
+ post('/v1/timezone', data)
122
+ end
123
+
124
+ private
125
+
126
+ def normalize(str = '')
127
+ str.trim
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sense
4
+ module Alarms
5
+ # @return [Hash]
6
+ #
7
+ # @example
8
+ # {
9
+ # "expansions" => [],
10
+ # "voice" => [],
11
+ # "classic" => [{
12
+ # "year" => 0,
13
+ # "month" => 0,
14
+ # "day_of_month" => 0,
15
+ # "hour" => 7,
16
+ # "minute" => 30,
17
+ # "day_of_week" => [1, 2, 3, 4, 5],
18
+ # "repeated" => true,
19
+ # "enabled" => true,
20
+ # "editable" => true,
21
+ # "smart" => true,
22
+ # "sound" => {
23
+ # "id" => 5,
24
+ # "name" => "Dusk",
25
+ # "url" => ""
26
+ # },
27
+ # "id" => "FDED667B-9E91-43F5-91DE-258AC1FEE9C2",
28
+ # "source" => "MOBILE_APP",
29
+ # "expansions" => []
30
+ # }]
31
+ # }
32
+
33
+ def alarms
34
+ get('/v2/alarms')
35
+ end
36
+
37
+ # @return [Array<Hash>]
38
+ #
39
+ # @example
40
+ # [
41
+ # {
42
+ # "id" => 5,
43
+ # "name" => "Dusk",
44
+ # "url" => "https://hello-audio.s3.amazonaws.com/ringtones/Dusk.mp3?x-amz-security-token=XXX"
45
+ # },
46
+ # ]
47
+
48
+ def alarm_sounds
49
+ get('/v1/alarms/sounds')
50
+ end
51
+
52
+ def update_alarm(data)
53
+ timestamp = DateTime.now
54
+ .new_offset(0)
55
+ .iso8601
56
+ post("/v2/alarms/#{timestamp}", data)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sense
4
+ module Alerts
5
+ # @return [Array]
6
+ #
7
+ # @example
8
+ # []
9
+
10
+ def alerts
11
+ get('/v2/alerts')
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sense
4
+ class APIError < StandardError
5
+ attr_reader :code, :description, :response, :type
6
+
7
+ # @todo Document param
8
+ def initialize(response)
9
+ @code = response.code
10
+ @description = response.code_type
11
+ @response = response
12
+ @type = response.error_type
13
+ end
14
+
15
+ def message
16
+ "#{@type}: #{@description} (#{@code})"
17
+ end
18
+ alias to_s message
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sense
4
+ module App
5
+ # Check if a new version of the app has been released.
6
+ #
7
+ # @param app_version [String]
8
+ # @param language [String]
9
+ # @param platform [String]
10
+ # @return [Hash]
11
+ #
12
+ # @example
13
+ # client.check_for_update('10000000', 'en', 'android')
14
+
15
+ def check_for_update(app_version, language, platform)
16
+ post('/v1/app/checkin', app_version: app_version,
17
+ lang_code: language,
18
+ platform: platform)
19
+ end
20
+ end
21
+ end