hacklet 0.5.0 → 0.5.1

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.
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