hacklet 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,16 +1,16 @@
1
1
  # Hacklet [![Build Status](https://travis-ci.org/mcolyer/hacklet.png)](https://travis-ci.org/mcolyer/hacklet)
2
2
 
3
- A library, written in Ruby, for controlling the [Modlet] (smart) outlet.
3
+ A library, written in Ruby, for controlling the [Modlet][modlet] (smart) outlet.
4
4
 
5
5
  If you haven't heard of the Modlet before, it's a smart outlet cover
6
- which allows you to convert any outlet into your house into a smart
6
+ which allows you to convert any outlet in your house into a smart
7
7
  outlet. This means that you can control whether a plug is on or off and
8
8
  you can also determine how much energy it's using with a sampling
9
9
  frequency of 10 seconds.
10
10
 
11
11
  There are alot of other similar products but this is the first one that
12
- I've see that [costs $50][amazon] and includes control as well as monitoring of
13
- the independent sockets.
12
+ I've seen that [costs $50][amazon] and includes control as well as
13
+ monitoring of the both sockets independently.
14
14
 
15
15
  ## Why
16
16
 
@@ -26,25 +26,46 @@ control and availability of the data.
26
26
 
27
27
  ## Getting Started
28
28
 
29
- Right now things are pretty rough and won't work without modifying
30
- `bin/hacklet`. Eventually the `hacklet` script will allow for specifying
31
- which Modlet and socket you'd like to read or control.
29
+ ```shell
30
+ gem install hacklet
32
31
 
33
- ```
34
- bundle install
32
+ # Load the USB-to-serial kernel driver, this only works with Linux currently
35
33
  sudo modprobe ftdi_sio vendor=0x0403 product=0x8c81
36
- bin/hacklet
34
+
35
+ # Add the device to the network, keep a copy of the network ids (ie 0xDEED)
36
+ hacklet commission
37
+
38
+ # Get the samples from the top socket on the device registered as 0xDEED
39
+ hacklet read -n 0xDEED -s 0
40
+
41
+ # Turn the top socket off
42
+ hacklet off -n 0xDEED -s 0
43
+
44
+ # And back on again
45
+ hacklet on -n 0xDEED -s 0
37
46
  ```
38
47
 
39
- ## Status
48
+ ## Contributing
49
+
50
+ All contributions are welcome (bug reports, bug fixes, documentation or
51
+ new features)! If you're looking for something to do check the [issue]
52
+ list and see if there's something already there. If you've got a new
53
+ idea, feel free to create an issue for discussion.
54
+
55
+ To get a general understanding of how things work, checkout the
56
+ [developer wiki]
57
+
58
+ ### Getting Started
40
59
 
41
- * [X] Reading Data
42
- * [X] Controlling Sockets
43
- * [X] Multiple Modlet Support
44
- * [X] Commissioning New Devices
45
- * [X] Useful utility
46
- * [ ] Set time on New Devices
47
- * [ ] Documentation
60
+ * Checkout the repository
61
+ * Install dependencies `bundle install`
62
+ * Create a feature branch `git checkout -b short-name`
63
+ * Run tests `bundle exec rake`
64
+ * Write your feature (and tests)
65
+ * Run tests `bundle exec rake`
66
+ * Create a pull request
48
67
 
49
- [Modlet]: http://themodlet.com
68
+ [modlet]: http://themodlet.com
50
69
  [amazon]: http://www.amazon.com/ThinkEco-TE1010-Modlet-Starter-White/dp/B00AAT43OA/
70
+ [issue]: https://github.com/mcolyer/hacklet/issues
71
+ [developer wiki]: https://github.com/mcolyer/hacklet/wiki
data/hacklet.gemspec CHANGED
@@ -23,4 +23,5 @@ Gem::Specification.new do |gem|
23
23
  gem.add_dependency "slop", "~>3.4.0"
24
24
  gem.add_development_dependency "rake"
25
25
  gem.add_development_dependency "rspec"
26
+ gem.add_development_dependency "timecop"
26
27
  end
@@ -38,16 +38,18 @@ module Hacklet
38
38
  def commission
39
39
  require_session
40
40
 
41
+ response = nil
41
42
  begin
42
43
  unlock_network
43
44
  Timeout.timeout(30) do
45
+ @logger.info("Listening for devices ...")
44
46
  loop do
45
- @logger.info("Listening for devices ...")
46
47
  buffer = receive(4)
47
48
  buffer += receive(buffer.bytes.to_a[3]+1)
48
49
  if buffer.bytes.to_a[1] == 0xa0
49
50
  response = BroadcastResponse.read(buffer)
50
51
  @logger.info("Found device 0x%x on network 0x%x" % [response.device_id, response.network_id])
52
+ break
51
53
  end
52
54
  end
53
55
  end
@@ -55,6 +57,8 @@ module Hacklet
55
57
  ensure
56
58
  lock_network
57
59
  end
60
+
61
+ update_time(response.network_id) if response
58
62
  end
59
63
 
60
64
  # Public: Selects the network.
@@ -159,6 +163,22 @@ module Hacklet
159
163
  BootConfirmResponse.read(receive(6))
160
164
  end
161
165
 
166
+ # Private: Updates the time of a device.
167
+ #
168
+ # This must be executed within an open session. I'm guessing it selects the
169
+ # network.
170
+ #
171
+ # network_id - 2 byte identified for the network.
172
+ #
173
+ # Returns nothing.
174
+ def update_time(network_id)
175
+ require_session
176
+
177
+ transmit(UpdateTimeRequest.new(:network_id => network_id))
178
+ UpdateTimeAckResponse.read(receive(6))
179
+ UpdateTimeResponse.read(receive(8))
180
+ end
181
+
162
182
  # Private: Initializes the serial port
163
183
  #
164
184
  # port - the String to the device to open as a serial port.
@@ -49,16 +49,16 @@ module Hacklet
49
49
  endian :big
50
50
 
51
51
  uint8 :header, :check_value => lambda { value == 0x02 }
52
- uint16 :command, :check_value => lambda { value == 0xA013 }
52
+ uint16 :command, :check_value => lambda { value == 0xA013 }
53
53
  uint8 :payload_length, :check_value => lambda { value == 11 }
54
54
 
55
55
  uint16 :network_id
56
56
  uint64 :device_id
57
57
 
58
58
  # TODO: Not sure why this is here.
59
- uint8 :data
59
+ uint8 :data
60
60
 
61
- uint8 :checksum, :check_value => lambda { calculate_checksum == checksum }
61
+ uint8 :checksum, :check_value => lambda { calculate_checksum == checksum }
62
62
  end
63
63
 
64
64
  class LockResponse < Message
@@ -68,9 +68,34 @@ module Hacklet
68
68
  uint16 :command, :check_value => lambda { value == 0xA0F9 }
69
69
  uint8 :payload_length, :check_value => lambda { value == 1 }
70
70
 
71
- uint8be :data, :check_value => lambda { value == 0x00 }
71
+ uint8 :data, :check_value => lambda { value == 0x00 }
72
72
 
73
- uint8 :checksum, :check_value => lambda { calculate_checksum == checksum }
73
+ uint8 :checksum, :check_value => lambda { calculate_checksum == checksum }
74
+ end
75
+
76
+ class UpdateTimeAckResponse < Message
77
+ endian :big
78
+
79
+ uint8 :header, :check_value => lambda { value == 0x02 }
80
+ uint16 :command, :check_value => lambda { value == 0x4022 }
81
+ uint8 :payload_length, :check_value => lambda { value == 1 }
82
+
83
+ uint8 :data, :check_value => lambda { value == 0x00 }
84
+
85
+ uint8 :checksum, :check_value => lambda { calculate_checksum == checksum }
86
+ end
87
+
88
+ class UpdateTimeResponse < Message
89
+ endian :big
90
+
91
+ uint8 :header, :check_value => lambda { value == 0x02 }
92
+ uint16 :command, :check_value => lambda { value == 0x40a2 }
93
+ uint8 :payload_length, :check_value => lambda { value == 3 }
94
+
95
+ uint16 :network_id
96
+ uint8 :data, :check_value => lambda { value == 0x00 }
97
+
98
+ uint8 :checksum, :check_value => lambda { calculate_checksum == checksum }
74
99
  end
75
100
 
76
101
  class HandshakeResponse < Message
@@ -80,9 +105,9 @@ module Hacklet
80
105
  uint16 :command, :check_value => lambda { value == 0x4003 }
81
106
  uint8 :payload_length, :check_value => lambda { value == 1 }
82
107
 
83
- uint8be :data, :check_value => lambda { value == 0x00 }
108
+ uint8 :data, :check_value => lambda { value == 0x00 }
84
109
 
85
- uint8 :checksum, :check_value => lambda { calculate_checksum == checksum }
110
+ uint8 :checksum, :check_value => lambda { calculate_checksum == checksum }
86
111
  end
87
112
 
88
113
  class AckResponse < Message
@@ -92,17 +117,17 @@ module Hacklet
92
117
  uint16 :command, :check_value => lambda { value == 0x4024 }
93
118
  uint8 :payload_length, :check_value => lambda { value == 1 }
94
119
 
95
- uint8be :data, :check_value => lambda { value == 0x00 }
120
+ uint8 :data, :check_value => lambda { value == 0x00 }
96
121
 
97
- uint8 :checksum, :check_value => lambda { calculate_checksum == checksum }
122
+ uint8 :checksum, :check_value => lambda { calculate_checksum == checksum }
98
123
  end
99
124
 
100
125
  class SamplesResponse < Message
101
126
  endian :big
102
127
 
103
- uint8 :header, :check_value => lambda { value == 0x02 }
104
- uint16 :command, :check_value => lambda { value == 0x40A4 }
105
- uint8 :payload_length
128
+ uint8 :header, :check_value => lambda { value == 0x02 }
129
+ uint16 :command, :check_value => lambda { value == 0x40A4 }
130
+ uint8 :payload_length
106
131
 
107
132
  uint16 :network_id
108
133
  uint16 :channel_id
@@ -130,9 +155,9 @@ module Hacklet
130
155
  uint16 :command, :check_value => lambda { value == 0x4023 }
131
156
  uint8 :payload_length, :check_value => lambda { value == 1 }
132
157
 
133
- uint8be :data, :check_value => lambda { value == 0x00 }
158
+ uint8 :data, :check_value => lambda { value == 0x00 }
134
159
 
135
- uint8 :checksum, :check_value => lambda { calculate_checksum == checksum }
160
+ uint8 :checksum, :check_value => lambda { calculate_checksum == checksum }
136
161
  end
137
162
 
138
163
  class BootRequest < Message
@@ -142,7 +167,7 @@ module Hacklet
142
167
  uint16 :command, :initial_value => 0x4004
143
168
  uint8 :payload_length
144
169
 
145
- uint8 :checksum, :value => :calculate_checksum
170
+ uint8 :checksum, :value => :calculate_checksum
146
171
  end
147
172
 
148
173
  class BootConfirmRequest < Message
@@ -152,7 +177,7 @@ module Hacklet
152
177
  uint16 :command, :initial_value => 0x4000
153
178
  uint8 :payload_length
154
179
 
155
- uint8 :checksum, :value => :calculate_checksum
180
+ uint8 :checksum, :value => :calculate_checksum
156
181
  end
157
182
 
158
183
  class UnlockRequest < Message
@@ -165,7 +190,7 @@ module Hacklet
165
190
  # TODO: What is this?
166
191
  uint32 :data, :initial_value => 0xFCFF9001
167
192
 
168
- uint8 :checksum, :value => :calculate_checksum
193
+ uint8 :checksum, :value => :calculate_checksum
169
194
  end
170
195
 
171
196
  class LockRequest < Message
@@ -178,7 +203,20 @@ module Hacklet
178
203
  # TODO: What is this?
179
204
  uint32 :data, :initial_value => 0xFCFF0001
180
205
 
181
- uint8 :checksum, :value => :calculate_checksum
206
+ uint8 :checksum, :value => :calculate_checksum
207
+ end
208
+
209
+ class UpdateTimeRequest < Message
210
+ endian :big
211
+
212
+ uint8 :header, :initial_value => 0x02
213
+ uint16 :command, :initial_value => 0x4022
214
+ uint8 :payload_length, :initial_value => 6
215
+
216
+ uint16 :network_id
217
+ uint32le :time, :initial_value => lambda { Time.now.to_i }
218
+
219
+ uint8 :checksum, :value => :calculate_checksum
182
220
  end
183
221
 
184
222
  class HandshakeRequest < Message
@@ -192,7 +230,7 @@ module Hacklet
192
230
  # TODO: What is this?
193
231
  uint16 :data, :initial_value => 0x0500
194
232
 
195
- uint8 :checksum, :value => :calculate_checksum
233
+ uint8 :checksum, :value => :calculate_checksum
196
234
  end
197
235
 
198
236
  class SamplesRequest < Message
@@ -207,7 +245,7 @@ module Hacklet
207
245
  # TODO: What is this?
208
246
  uint16 :data, :initial_value => 0x0A00
209
247
 
210
- uint8 :checksum, :value => :calculate_checksum
248
+ uint8 :checksum, :value => :calculate_checksum
211
249
  end
212
250
 
213
251
  class ScheduleRequest < Message
@@ -221,7 +259,7 @@ module Hacklet
221
259
  uint8 :channel_id
222
260
  array :schedule, :type => [:uint8], :initial_length => 56
223
261
 
224
- uint8 :checksum, :value => :calculate_checksum
262
+ uint8 :checksum, :value => :calculate_checksum
225
263
 
226
264
  def always_off!
227
265
  bitmap = [0x7f]*56
@@ -1,3 +1,3 @@
1
1
  module Hacklet
2
- VERSION = "0.5.0"
2
+ VERSION = "0.5.1"
3
3
  end
@@ -48,12 +48,16 @@ describe Hacklet::Dongle do
48
48
  serial_port.should_receive(:read).and_return([0x02, 0xa0, 0x13, 0x0b].pack('c'*4))
49
49
  serial_port.should_receive(:read).and_return([0x66, 0xad, 0xcc, 0x1f, 0x10, 0x00,
50
50
  0x00, 0x58, 0x4f, 0x80, 0x8e, 0xa9].pack('c'*12))
51
- serial_port.should_receive(:read).and_raise(Timeout::Error)
52
51
 
53
52
  # Lock Network
54
53
  serial_port.should_receive(:write).with([0x02, 0xA2, 0x36, 0x04, 0xFC, 0xFF, 0x00, 0x01, 0x92].pack('c'*9))
55
54
  serial_port.should_receive(:read).and_return([0x02, 0xA0, 0xF9, 0x01, 0x00, 0x58].pack('c'*6))
56
55
 
56
+ # Update Time
57
+ serial_port.should_receive(:write).and_return([0x02, 0x40, 0x22, 0x06, 0x66, 0xAD, 0xdb, 0xb4, 0xa8, 0x51, 0x0b].pack('c'*11))
58
+ serial_port.should_receive(:read).and_return([0x02, 0x40, 0x22, 0x01, 0x00, 0x63].pack('c'*6))
59
+ serial_port.should_receive(:read).and_return([0x02, 0x40, 0xA2, 0x03, 0x66, 0xAD, 0x00, 0x2a].pack('c'*8))
60
+
57
61
  serial_port.should_receive(:close)
58
62
  serial_port.stub!(:closed?).and_return(false)
59
63
  subject.should_receive(:open_serial_port).and_return(serial_port)
@@ -61,7 +65,9 @@ describe Hacklet::Dongle do
61
65
  subject.should_receive(:boot_confirm)
62
66
 
63
67
  subject.open_session do |session|
64
- session.commission
68
+ Timecop.freeze(Time.at(0x51a8b4db)) do
69
+ session.commission
70
+ end
65
71
  end
66
72
  end
67
73
 
data/spec/spec_helper.rb CHANGED
@@ -1,2 +1,3 @@
1
1
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
2
  require 'hacklet'
3
+ require 'timecop'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hacklet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-30 00:00:00.000000000 Z
12
+ date: 2013-06-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: serialport
@@ -91,6 +91,22 @@ dependencies:
91
91
  - - ! '>='
92
92
  - !ruby/object:Gem::Version
93
93
  version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: timecop
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
94
110
  description: An Open Source client for the Modlet (smart) outlet
95
111
  email:
96
112
  - matt@colyer.name