currentcost 0.3.4

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/COPYING ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008 James Smith (james@floppy.org.uk)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,46 @@
1
+ == CurrentCost-Ruby
2
+
3
+ A gem to provide a Ruby interface to the CurrentCost energy meter
4
+ (http://www.currentcost.com)
5
+
6
+ Licensed under the MIT license (See COPYING file for details)
7
+
8
+ Author: James Smith (james@floppy.org.uk / http://www.floppy.org.uk)
9
+
10
+ Homepage: http://github.com/Floppy/currentcost-ruby
11
+
12
+ Documentation: http://docs.github.com/Floppy/currentcost-ruby
13
+
14
+ == INSTALLATION
15
+
16
+ 1) Enable gems from gemcutter, if you haven't already done so:
17
+ > sudo gem install gemcutter
18
+ > sudo gem tumble
19
+
20
+ 2) Install gem
21
+ > sudo gem install currentcost
22
+
23
+ == REQUIREMENTS
24
+
25
+ rb232 >= 0.2.3
26
+
27
+ == USAGE
28
+
29
+ You can read data from the meter by creating an instance of the CurrentCost::Meter
30
+ class, and registering an observer which will receive readings. See examples/simple.rb
31
+ or the CurrentCost::Meter documentation for a simple example of how to do this.
32
+
33
+ == TRAY MONITOR
34
+
35
+ This gem includes the 'currentcost_tray_monitor.rb' application. This is a program
36
+ which will sit in your system tray and display a coloured icon based on your power
37
+ usage. To use the program, run:
38
+
39
+ currentcost_tray_monitor.rb -p your_serial_port_name
40
+
41
+ The program requires the Gtk2 libraries for Ruby (available from
42
+ http://ruby-gnome2.sourceforge.jp/). If the EEML gem (http://github.com/Floppy/eeml-ruby)
43
+ is installed, it can also serve your power data over an HTTP connection to a system like
44
+ Pachube (http://www.pachube.com).
45
+
46
+ For more details, see http://github.com/Floppy/currentcost-ruby/wikis/currentcosttraymonitor
@@ -0,0 +1,383 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ dir = File.dirname(__FILE__) + '/../lib'
4
+ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
5
+
6
+ require 'gtk2'
7
+ require 'rubygems'
8
+ require 'currentcost'
9
+ require 'optparse'
10
+
11
+ # If EEML is available, start a simple server which will serve power information
12
+ begin
13
+ require 'eeml'
14
+ require 'webrick'
15
+ $eeml_server_enabled = true
16
+ rescue LoadError
17
+ $eeml_server_enabled = false
18
+ end
19
+
20
+
21
+ # Command-line options
22
+ options = {:port => '/dev/ttyS0'}
23
+ OptionParser.new do |opts|
24
+ opts.on("-p", "--serial_port SERIAL_PORT", "serial port") do |p|
25
+ options[:port] = p
26
+ end
27
+ if $eeml_server_enabled == true
28
+ opts.on("-h", "--http_port HTTP_PORT", "http port") do |h|
29
+ options[:http_port] = h
30
+ end
31
+ end
32
+ end.parse!
33
+
34
+ # Pixbuf images - generated from PNG files with gdk-pixbuf-csource --raw
35
+ GREY =
36
+ # Pixbuf magic (0x47646b50)
37
+ "GdkP" <<
38
+ # length: header (24) + pixel_data (1024)
39
+ "\0\0\4\30" <<
40
+ # pixdata_type (0x1010002)
41
+ "\1\1\0\2" <<
42
+ # rowstride (64)
43
+ "\0\0\0@" <<
44
+ # width (16)
45
+ "\0\0\0\20" <<
46
+ # height (16)
47
+ "\0\0\0\20" <<
48
+ # pixel_data:
49
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\351\352\351J\360\360\360\262\353\353" <<
50
+ "\353\371\347\347\347\377\337\337\337\377\322\323\322\371\310\310\310" <<
51
+ "\262\267\267\267J\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\336" <<
52
+ "\336\336\30\366\366\366\270\371\370\370\377\360\360\360\377\350\350\347" <<
53
+ "\377\337\337\337\377\327\327\327\377\316\317\317\377\306\306\306\377" <<
54
+ "\275\275\276\377\260\257\257\270\232\232\232\30\0\0\0\0\0\0\0\0\0\0\0" <<
55
+ "\0\336\336\336\30\371\370\370\335\370\370\370\377\365\365\365\377\347" <<
56
+ "\347\350\377\337\337\337\377\327\327\327\377\317\316\316\377\306\306" <<
57
+ "\306\377\275\275\276\377\265\265\265\377\255\255\255\377\241\241\241" <<
58
+ "\335\213\214\214\30\0\0\0\0\0\0\0\0\366\366\366\270\370\370\370\377\366" <<
59
+ "\366\366\377\377\377\377\377\355\355\355\377\327\326\327\377\316\316" <<
60
+ "\316\377\306\306\306\377\275\275\275\377\265\265\265\377\255\255\254" <<
61
+ "\377\244\244\244\377\234\234\234\377\217\217\217\270\0\0\0\0\351\350" <<
62
+ "\350J\370\370\370\377\360\360\360\377\374\374\374\377\377\377\377\377" <<
63
+ "\365\366\366\377\316\316\316\377\306\306\306\377\275\275\275\377\265" <<
64
+ "\265\265\377\255\255\255\377\244\244\244\377\234\234\234\377\223\223" <<
65
+ "\223\377\213\213\213\377}||J\357\357\357\262\360\360\360\377\354\354" <<
66
+ "\354\377\377\377\377\377\377\377\377\377\377\377\377\377\314\314\314" <<
67
+ "\377\275\275\275\377\275\275\275\377\276\276\276\377\244\244\244\377" <<
68
+ "\234\233\233\377\223\223\223\377\213\213\213\377\202\202\203\377www\262" <<
69
+ "\352\352\352\371\347\347\347\377\364\364\364\377\377\377\377\377\377" <<
70
+ "\377\377\377\377\377\377\377\332\332\332\377\265\265\265\377\354\354" <<
71
+ "\354\377\377\377\377\377\275\274\275\377\267\267\267\377\261\261\261" <<
72
+ "\377\254\253\253\377\246\246\246\377\237\237\237\373\347\347\347\377" <<
73
+ "\342\342\342\377\377\377\377\377\377\377\377\377\377\377\377\377\377" <<
74
+ "\377\377\377\356\356\356\377\276\276\277\377\377\377\377\377\377\377" <<
75
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" <<
76
+ "\377\377\377\377\377\377\377\377\377\336\336\336\377\354\354\355\377" <<
77
+ "\377\377\377\377\377\377\377\377\323\323\323\377\377\377\377\377\377" <<
78
+ "\377\377\377\340\340\340\377\377\377\377\377\377\377\377\377\377\377" <<
79
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" <<
80
+ "\377\377\377\377\377\325\325\325\372\371\371\371\377\377\377\377\377" <<
81
+ "\341\341\341\377\265\265\265\377\377\377\377\377\377\377\377\377\377" <<
82
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\322\322" <<
83
+ "\322\377qqr\377iii\377``a\377UUU\371\335\335\335\314\377\377\377\377" <<
84
+ "\377\377\377\377\275\275\275\377\254\254\254\377\352\352\352\377\377" <<
85
+ "\377\377\377\377\377\377\377\377\377\377\377\271\271\271\377\322\322" <<
86
+ "\322\377\200\200\200\377iih\377`aa\377XXX\377NNN\262\343\343\343\206" <<
87
+ "\377\377\377\377\356\356\356\377\254\254\254\377\244\243\243\377\274" <<
88
+ "\274\274\377\377\377\377\377\377\377\377\377\343\343\343\377yyy\377q" <<
89
+ "qq\377iii\377```\377XXX\377OOO\377GGGJ\377\377\377\34\351\351\351\347" <<
90
+ "\265\265\265\377\243\244\244\377\233\233\233\377\223\223\223\377\377" <<
91
+ "\377\377\377\377\377\377\377\246\246\245\377qqq\377iii\377```\377XXX" <<
92
+ "\377OOO\377EEE\270\0\0\0\0\0\0\0\0\224\223\224\30\237\237\237\335\233" <<
93
+ "\233\233\377\223\223\223\377\212\212\212\377\307\307\307\377\360\360" <<
94
+ "\360\377qqq\377ihh\377```\377XWX\377OPO\377EEE\335=>=\30\0\0\0\0\0\0" <<
95
+ "\0\0\0\0\0\0\204\205\205\30\214\215\214\270\212\212\212\377\201\202\202" <<
96
+ "\377yyy\377qqq\377hhh\377```\377XXX\377OOO\377EEE\270===\30\0\0\0\0\0" <<
97
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0yxyJuuu\262lll\371ggg\377^_^\377" <<
98
+ "TTS\371MMM\262EFFJ\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
99
+
100
+ GREEN =
101
+ # Pixbuf magic (0x47646b50)
102
+ "GdkP" <<
103
+ # length: header (24) + pixel_data (1024)
104
+ "\0\0\4\30" <<
105
+ # pixdata_type (0x1010002)
106
+ "\1\1\0\2" <<
107
+ # rowstride (64)
108
+ "\0\0\0@" <<
109
+ # width (16)
110
+ "\0\0\0\20" <<
111
+ # height (16)
112
+ "\0\0\0\20" <<
113
+ # pixel_data:
114
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0a\352aJ7\3607\262\5\353\5\371\0\347\0" <<
115
+ "\377\0\337\0\377\4\323\4\371.\310.\262L\267LJ\0\0\0\0\0\0\0\0\0\0\0\0" <<
116
+ "\0\0\0\0\0\0\0\0\0\0\0\0j\336j\30""5\3665\270\0\370\0\377\0\360\0\377" <<
117
+ "\0\350\0\377\0\337\0\377\0\327\0\377\0\317\0\377\0\306\0\377\0\275\0" <<
118
+ "\377&\257%\270I\232I\30\0\0\0\0\0\0\0\0\0\0\0\0j\336j\30\34\370\34\335" <<
119
+ "\0\370\0\377U\365U\377\0\347\0\377\0\337\0\377\0\327\0\377\0\316\0\377" <<
120
+ "\0\306\0\377\0\275\0\377\0\265\0\377\0\255\0\377\22\241\22\335B\214B" <<
121
+ "\30\0\0\0\0\0\0\0\0""5\3665\270\0\370\0\377q\366q\377\377\377\377\377" <<
122
+ "q\355q\377\0\326\0\377\0\316\0\377\0\306\0\377\0\275\0\377\0\265\0\377" <<
123
+ "\0\255\0\377\0\244\0\377\0\234\0\377\37\217\37\270\0\0\0\0a\350`J\0\370" <<
124
+ "\0\377\0\360\0\377\343\374\343\377\377\377\377\377\306\366\306\377\0" <<
125
+ "\316\0\377\0\306\0\377\0\275\0\377\0\265\0\377\0\255\0\377\0\244\0\377" <<
126
+ "\0\234\0\377\0\223\0\377\0\213\0\3774|3J7\3577\262\0\360\0\3779\3549" <<
127
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\34\314\34\377\0" <<
128
+ "\275\0\377\34\275\34\3779\2769\377\0\244\0\377\0\233\0\377\0\223\0\377" <<
129
+ "\0\213\0\377\0\202\0\377\33w\33\262\5\352\5\371\0\347\0\377\252\364\252" <<
130
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377q\332q\377\0\265" <<
131
+ "\0\377\306\354\306\377\377\377\377\377U\274U\377U\267U\377U\261U\377" <<
132
+ "U\253U\377U\246U\377W\237W\373\0\347\0\377\34\342\34\377\377\377\377" <<
133
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\306\356\306\377" <<
134
+ "9\2769\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" <<
135
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" <<
136
+ "\0\336\0\377\216\354\216\377\377\377\377\377\377\377\377\377U\323U\377" <<
137
+ "\377\377\377\377\377\377\377\377\252\340\252\377\377\377\377\377\377" <<
138
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" <<
139
+ "\377\377\377\377\377\377\377\377\377\377\40\325\40\372\343\371\343\377" <<
140
+ "\377\377\377\377\216\341\216\377\0\265\0\377\377\377\377\377\377\377" <<
141
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" <<
142
+ "\377\252\322\252\377\0q\0\377\0i\0\377\0`\0\377\2U\2\371\204\335\204" <<
143
+ "\314\377\377\377\377\377\377\377\377\34\275\34\377\0\254\0\377\306\352" <<
144
+ "\306\377\377\377\377\377\377\377\377\377\377\377\377\377q\271q\377\252" <<
145
+ "\322\252\377\34\200\34\377\0i\0\377\0a\0\377\0X\0\377\22N\22\262\274" <<
146
+ "\343\274\206\377\377\377\377\306\356\306\377\0\254\0\377\0\243\0\377" <<
147
+ "U\274U\377\377\377\377\377\377\377\377\377\306\343\306\377\0y\0\377\0" <<
148
+ "q\0\377\0i\0\377\0`\0\377\0X\0\377\0O\0\377\35G\35J\377\377\377\34\305" <<
149
+ "\351\305\347\34\265\34\377\0\244\0\377\0\233\0\377\0\223\0\377\377\377" <<
150
+ "\377\377\377\377\377\377U\246U\377\0q\0\377\0i\0\377\0`\0\377\0X\0\377" <<
151
+ "\0O\0\377\16E\16\270\0\0\0\0\0\0\0\0F\223F\30\22\237\22\335\0\233\0\377" <<
152
+ "\0\223\0\377\0\212\0\377\216\307\216\377\343\360\343\377\0q\0\377\0h" <<
153
+ "\0\377\0`\0\377\0W\0\377\0P\0\377\10E\10\335\35>\35\30\0\0\0\0\0\0\0" <<
154
+ "\0\0\0\0\0\77\205\77\30\36\215\36\270\0\212\0\377\0\202\0\377\0y\0\377" <<
155
+ "\0q\0\377\0h\0\377\0`\0\377\0X\0\377\0O\0\377\16E\16\270\35=\35\30\0" <<
156
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""2x2J\33u\33\262\2l\2" <<
157
+ "\371\0g\0\377\0_\0\377\1T\1\371\21M\21\262\34F\35J\0\0\0\0\0\0\0\0\0" <<
158
+ "\0\0\0\0\0\0\0"
159
+
160
+ RED =
161
+ # Pixbuf magic (0x47646b50)
162
+ "GdkP" <<
163
+ # length: header (24) + pixel_data (1024)
164
+ "\0\0\4\30" <<
165
+ # pixdata_type (0x1010002)
166
+ "\1\1\0\2" <<
167
+ # rowstride (64)
168
+ "\0\0\0@" <<
169
+ # width (16)
170
+ "\0\0\0\20" <<
171
+ # height (16)
172
+ "\0\0\0\20" <<
173
+ # pixel_data:
174
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\351aaJ\36077\262\353\5\5\371\347\0\0" <<
175
+ "\377\337\0\0\377\322\4\4\371\310..\262\267LLJ\0\0\0\0\0\0\0\0\0\0\0\0" <<
176
+ "\0\0\0\0\0\0\0\0\0\0\0\0\336jj\30\36655\270\371\0\0\377\360\0\0\377\350" <<
177
+ "\0\0\377\337\0\0\377\327\0\0\377\316\0\0\377\306\0\0\377\275\0\0\377" <<
178
+ "\260%%\270\232II\30\0\0\0\0\0\0\0\0\0\0\0\0\336jj\30\371\34\34\335\370" <<
179
+ "\0\0\377\365UU\377\347\0\0\377\337\0\0\377\327\0\0\377\317\0\0\377\306" <<
180
+ "\0\0\377\275\0\0\377\265\0\0\377\255\0\0\377\241\22\22\335\213BB\30\0" <<
181
+ "\0\0\0\0\0\0\0\36655\270\370\0\0\377\366qq\377\377\377\377\377\355qq" <<
182
+ "\377\327\0\0\377\316\0\0\377\306\0\0\377\275\0\0\377\265\0\0\377\255" <<
183
+ "\0\0\377\244\0\0\377\234\0\0\377\217\37\37\270\0\0\0\0\351``J\370\0\0" <<
184
+ "\377\360\0\0\377\374\343\343\377\377\377\377\377\365\306\306\377\316" <<
185
+ "\0\0\377\306\0\0\377\275\0\0\377\265\0\0\377\255\0\0\377\244\0\0\377" <<
186
+ "\234\0\0\377\223\0\0\377\213\0\0\377}33J\35777\262\360\0\0\377\35499" <<
187
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\314\34\34\377\275" <<
188
+ "\0\0\377\275\34\34\377\27699\377\244\0\0\377\234\0\0\377\223\0\0\377" <<
189
+ "\213\0\0\377\202\0\0\377w\33\33\262\352\5\5\371\347\0\0\377\364\252\252" <<
190
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\332qq\377\265\0" <<
191
+ "\0\377\354\306\306\377\377\377\377\377\275UU\377\267UU\377\261UU\377" <<
192
+ "\254UU\377\246UU\377\237WW\373\347\0\0\377\342\34\34\377\377\377\377" <<
193
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\356\306\306\377" <<
194
+ "\27699\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" <<
195
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" <<
196
+ "\336\0\0\377\354\216\216\377\377\377\377\377\377\377\377\377\323UU\377" <<
197
+ "\377\377\377\377\377\377\377\377\340\252\252\377\377\377\377\377\377" <<
198
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" <<
199
+ "\377\377\377\377\377\377\377\377\377\377\325\40\40\372\371\343\343\377" <<
200
+ "\377\377\377\377\341\216\216\377\265\0\0\377\377\377\377\377\377\377" <<
201
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" <<
202
+ "\377\322\252\252\377q\0\0\377i\0\0\377`\0\0\377U\2\2\371\335\204\204" <<
203
+ "\314\377\377\377\377\377\377\377\377\275\34\34\377\254\0\0\377\352\306" <<
204
+ "\306\377\377\377\377\377\377\377\377\377\377\377\377\377\271qq\377\322" <<
205
+ "\252\252\377\200\34\34\377i\0\0\377`\0\0\377X\0\0\377N\22\22\262\343" <<
206
+ "\274\274\206\377\377\377\377\356\306\306\377\254\0\0\377\244\0\0\377" <<
207
+ "\274UU\377\377\377\377\377\377\377\377\377\343\306\306\377y\0\0\377q" <<
208
+ "\0\0\377i\0\0\377`\0\0\377X\0\0\377O\0\0\377G\35\35J\377\377\377\34\351" <<
209
+ "\305\305\347\265\34\34\377\243\0\0\377\233\0\0\377\223\0\0\377\377\377" <<
210
+ "\377\377\377\377\377\377\246UU\377q\0\0\377i\0\0\377`\0\0\377X\0\0\377" <<
211
+ "O\0\0\377E\16\16\270\0\0\0\0\0\0\0\0\224FF\30\237\22\22\335\233\0\0\377" <<
212
+ "\223\0\0\377\212\0\0\377\307\216\216\377\360\343\343\377q\0\0\377i\0" <<
213
+ "\0\377`\0\0\377X\0\0\377O\0\0\377E\10\10\335=\35\35\30\0\0\0\0\0\0\0" <<
214
+ "\0\0\0\0\0\204\77\77\30\214\36\36\270\212\0\0\377\201\0\0\377y\0\0\377" <<
215
+ "q\0\0\377h\0\0\377`\0\0\377X\0\0\377O\0\0\377E\16\16\270=\35\35\30\0" <<
216
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0y22Ju\33\33\262l\2\2\371" <<
217
+ "g\0\0\377^\0\0\377T\1\1\371M\21\21\262E\35\35J\0\0\0\0\0\0\0\0\0\0\0" <<
218
+ "\0\0\0\0\0"
219
+
220
+ YELLOW =
221
+ # Pixbuf magic (0x47646b50)
222
+ "GdkP" <<
223
+ # length: header (24) + pixel_data (1024)
224
+ "\0\0\4\30" <<
225
+ # pixdata_type (0x1010002)
226
+ "\1\1\0\2" <<
227
+ # rowstride (64)
228
+ "\0\0\0@" <<
229
+ # width (16)
230
+ "\0\0\0\20" <<
231
+ # height (16)
232
+ "\0\0\0\20" <<
233
+ # pixel_data:
234
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\351\352aJ\360\3607\262\353\353\5\371" <<
235
+ "\347\347\0\377\337\337\0\377\322\323\4\371\310\310.\262\267\267LJ\0\0" <<
236
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\336\336j\30\366\3665\270" <<
237
+ "\371\370\0\377\360\360\0\377\350\350\0\377\337\337\0\377\327\327\0\377" <<
238
+ "\316\317\0\377\306\306\0\377\275\275\0\377\260\257%\270\232\232I\30\0" <<
239
+ "\0\0\0\0\0\0\0\0\0\0\0\336\336j\30\371\370\34\335\370\370\0\377\365\365" <<
240
+ "U\377\347\347\0\377\337\337\0\377\327\327\0\377\317\316\0\377\306\306" <<
241
+ "\0\377\275\275\0\377\265\265\0\377\255\255\0\377\241\241\22\335\213\214" <<
242
+ "B\30\0\0\0\0\0\0\0\0\366\3665\270\370\370\0\377\366\366q\377\377\377" <<
243
+ "\377\377\355\355q\377\327\326\0\377\316\316\0\377\306\306\0\377\275\275" <<
244
+ "\0\377\265\265\0\377\255\255\0\377\244\244\0\377\234\234\0\377\217\217" <<
245
+ "\37\270\0\0\0\0\351\350`J\370\370\0\377\360\360\0\377\374\374\343\377" <<
246
+ "\377\377\377\377\365\366\306\377\316\316\0\377\306\306\0\377\275\275" <<
247
+ "\0\377\265\265\0\377\255\255\0\377\244\244\0\377\234\234\0\377\223\223" <<
248
+ "\0\377\213\213\0\377}|3J\357\3577\262\360\360\0\377\354\3549\377\377" <<
249
+ "\377\377\377\377\377\377\377\377\377\377\377\314\314\34\377\275\275\0" <<
250
+ "\377\275\275\34\377\276\2769\377\244\244\0\377\234\233\0\377\223\223" <<
251
+ "\0\377\213\213\0\377\202\202\0\377ww\33\262\352\352\5\371\347\347\0\377" <<
252
+ "\364\364\252\377\377\377\377\377\377\377\377\377\377\377\377\377\332" <<
253
+ "\332q\377\265\265\0\377\354\354\306\377\377\377\377\377\275\274U\377" <<
254
+ "\267\267U\377\261\261U\377\254\253U\377\246\246U\377\237\237W\373\347" <<
255
+ "\347\0\377\342\342\34\377\377\377\377\377\377\377\377\377\377\377\377" <<
256
+ "\377\377\377\377\377\356\356\306\377\276\2769\377\377\377\377\377\377" <<
257
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" <<
258
+ "\377\377\377\377\377\377\377\377\377\377\336\336\0\377\354\354\216\377" <<
259
+ "\377\377\377\377\377\377\377\377\323\323U\377\377\377\377\377\377\377" <<
260
+ "\377\377\340\340\252\377\377\377\377\377\377\377\377\377\377\377\377" <<
261
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377" <<
262
+ "\377\377\377\377\325\325\40\372\371\371\343\377\377\377\377\377\341\341" <<
263
+ "\216\377\265\265\0\377\377\377\377\377\377\377\377\377\377\377\377\377" <<
264
+ "\377\377\377\377\377\377\377\377\377\377\377\377\322\322\252\377qq\0" <<
265
+ "\377ii\0\377``\0\377UU\2\371\335\335\204\314\377\377\377\377\377\377" <<
266
+ "\377\377\275\275\34\377\254\254\0\377\352\352\306\377\377\377\377\377" <<
267
+ "\377\377\377\377\377\377\377\377\271\271q\377\322\322\252\377\200\200" <<
268
+ "\34\377ii\0\377`a\0\377XX\0\377NN\22\262\343\343\274\206\377\377\377" <<
269
+ "\377\356\356\306\377\254\254\0\377\244\243\0\377\274\274U\377\377\377" <<
270
+ "\377\377\377\377\377\377\343\343\306\377yy\0\377qq\0\377ii\0\377``\0" <<
271
+ "\377XX\0\377OO\0\377GG\35J\377\377\377\34\351\351\305\347\265\265\34" <<
272
+ "\377\243\244\0\377\233\233\0\377\223\223\0\377\377\377\377\377\377\377" <<
273
+ "\377\377\246\246U\377qq\0\377ii\0\377``\0\377XX\0\377OO\0\377EE\16\270" <<
274
+ "\0\0\0\0\0\0\0\0\224\223F\30\237\237\22\335\233\233\0\377\223\223\0\377" <<
275
+ "\212\212\0\377\307\307\216\377\360\360\343\377qq\0\377ih\0\377``\0\377" <<
276
+ "XW\0\377OP\0\377EE\10\335=>\35\30\0\0\0\0\0\0\0\0\0\0\0\0\204\205\77" <<
277
+ "\30\214\215\36\270\212\212\0\377\201\202\0\377yy\0\377qq\0\377hh\0\377" <<
278
+ "``\0\377XX\0\377OO\0\377EE\16\270==\35\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0" <<
279
+ "\0\0\0\0\0\0\0\0\0\0yx2Juu\33\262ll\2\371gg\0\377^_\0\377TT\1\371MM\21" <<
280
+ "\262EF\35J\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
281
+
282
+ $grey_pixbuf = Gdk::Pixbuf.new(GREY.unpack("C*"), true)
283
+ $green_pixbuf = Gdk::Pixbuf.new(GREEN.unpack("C*"), true)
284
+ $red_pixbuf = Gdk::Pixbuf.new(RED.unpack("C*"), true)
285
+ $yellow_pixbuf = Gdk::Pixbuf.new(YELLOW.unpack("C*"), true)
286
+
287
+ # build the UI
288
+
289
+ $tray = Gtk::StatusIcon.new
290
+ $tray.pixbuf = $grey_pixbuf
291
+
292
+ $menu = Gtk::Menu.new
293
+ about_item = Gtk::ImageMenuItem.new(Gtk::Stock::ABOUT)
294
+ quit_item = Gtk::ImageMenuItem.new(Gtk::Stock::QUIT)
295
+ $menu.append(about_item)
296
+ $menu.append(Gtk::SeparatorMenuItem.new)
297
+ $menu.append(quit_item)
298
+ $menu.show_all
299
+
300
+ about_item.signal_connect('activate') {
301
+ about = Gtk::AboutDialog.new
302
+ about.signal_connect('response') {
303
+ about.destroy
304
+ }
305
+ about.name = 'CurrentCost Tray Monitor'
306
+ about.version = '0.1'
307
+ about.copyright = '© 2008 James Smith'
308
+ about.comments = 'Heavily based on CCTrayRb: http://rubyforge.org/projects/cctrayrb/'
309
+ about.license = 'MIT License - see http://github.com/Floppy/currentcost-ruby/tree/master/COPYING'
310
+ about.website = 'http://github.com/Floppy/currentcost-ruby/wikis/currentcosttraymonitor'
311
+ about.authors = ['James Smith - james@floppy.org.uk']
312
+ about.show_all
313
+ }
314
+
315
+ quit_item.signal_connect('activate') {
316
+ $meter.close
317
+ $eeml_server.shutdown if $eeml_server_enabled && $eeml_server.status == :Running
318
+ Gtk.main_quit
319
+ }
320
+
321
+ $tray.signal_connect('popup_menu') { |icon, button, time|
322
+ if button == 3 # right mouse button
323
+ $menu.popup(nil, nil, button, time)
324
+ end
325
+ }
326
+
327
+ # Meter observer
328
+ class MeterObserver
329
+ def update(reading)
330
+ # Add all channels to get real figure
331
+ watts = 0
332
+ reading.channels.each { |c| watts += c[:watts] }
333
+ # Change icon colour based on power usage
334
+ case watts
335
+ when 0..349
336
+ $tray.pixbuf = $green_pixbuf
337
+ when 350..999
338
+ $tray.pixbuf = $yellow_pixbuf
339
+ else
340
+ $tray.pixbuf = $red_pixbuf
341
+ end
342
+ # Set tooltip
343
+ $tray.tooltip = "#{watts} watts"
344
+ # Update EEML data
345
+ if $eeml_server_enabled && $eeml_server.status == :Running
346
+ $eeml_environment[0].value = watts
347
+ $eeml_environment.set_updated!
348
+ end
349
+ rescue
350
+ $tray.pixbuf = $grey_pixbuf
351
+ end
352
+ end
353
+
354
+ # Create CurrentCost meter connection
355
+ $meter = CurrentCost::Meter.new(options[:port])
356
+ obs = MeterObserver.new
357
+ $meter.add_observer(obs)
358
+
359
+ # Create EEML HTTP server if options are set
360
+ if $eeml_server_enabled && options[:http_port]
361
+ # Create EEML environment
362
+ $eeml_environment = EEML::Environment.new
363
+ # Create data object
364
+ data = EEML::Data.new(0)
365
+ data.unit = EEML::Unit.new("Watts", :symbol => 'W', :type => :derivedSI)
366
+ $eeml_environment << data
367
+ # Create WEBrick server
368
+ $eeml_server = WEBrick::HTTPServer.new( :Port => options[:http_port] )
369
+ # Create a simple webrick servlet for index.eeml
370
+ class EEMLServlet < WEBrick::HTTPServlet::AbstractServlet
371
+ def do_GET(request, response)
372
+ response.status = 200
373
+ response['Content-Type'] = "text/xml"
374
+ response.body = $eeml_environment.to_eeml
375
+ end
376
+ end
377
+ $eeml_server.mount("/index.eeml", EEMLServlet)
378
+ trap("INT") {$eeml_server.shutdown}
379
+ Thread.new{$eeml_server.start}
380
+ end
381
+
382
+ # Go
383
+ Gtk.main
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ dir = File.dirname(__FILE__) + '/../lib'
4
+ $LOAD_PATH << dir unless $LOAD_PATH.include?(dir)
5
+
6
+ require 'rubygems'
7
+ require 'currentcost/meter'
8
+
9
+ require 'optparse'
10
+
11
+ # Command-line options
12
+ options = {:port => '/dev/ttyS0'}
13
+ OptionParser.new do |opts|
14
+ opts.on("-p", "--serial_port SERIAL_PORT", "serial port") do |p|
15
+ options[:port] = p
16
+ end
17
+ end.parse!
18
+
19
+ # A simple observer class which will receive updates from the meter
20
+ class SimpleObserver
21
+ def update(reading)
22
+ # Add all channels to get real figure
23
+ watts = 0
24
+ reading.channels.each { |c| watts += c[:watts] }
25
+ # Print out measurement
26
+ puts "New reading received: #{watts} W"
27
+ end
28
+ end
29
+
30
+ # Create meter
31
+ meter = CurrentCost::Meter.new(options[:port], :cc128 => true)
32
+ # Create observer
33
+ observer = SimpleObserver.new
34
+ # Register observer with meter
35
+ meter.add_observer(observer)
36
+ # Wait a while, let some readings come in
37
+ sleep(30)
38
+ # Close the meter object to stop it receiving data
39
+ meter.close
@@ -0,0 +1,6 @@
1
+ require 'rexml/document'
2
+
3
+ require 'currentcost/version'
4
+ require 'currentcost/exceptions'
5
+ require 'currentcost/meter'
6
+ require 'currentcost/reading'
@@ -0,0 +1,7 @@
1
+ module CurrentCost
2
+
3
+ # Signifies an XML parsing error. Raised by CurrentCost::Reading.from_xml.
4
+ class ParseError < Exception
5
+ end
6
+
7
+ end
@@ -0,0 +1,79 @@
1
+ require 'rb232'
2
+ require 'rb232/text_protocol'
3
+ require 'currentcost/meter'
4
+ require 'currentcost/reading'
5
+ require 'observer'
6
+
7
+ module CurrentCost
8
+
9
+ # A class to represent a physical CurrentCost meter attached to a serial port.
10
+ # This class is Observable (see Ruby documentation for details). Client code
11
+ # should create an observer class which defines an update(CurrentCost::Reading)
12
+ # function, and call Meter.add_observer(my_observer) to receive updates when
13
+ # new readings are received from the physical meter.
14
+ #
15
+ # For example:
16
+ #
17
+ # require 'currentcost/meter'
18
+ #
19
+ # class SimpleObserver
20
+ # def update(reading)
21
+ # puts "New reading received: #{reading.channels[0][:watts]} W"
22
+ # end
23
+ # end
24
+ #
25
+ # meter = CurrentCost::Meter.new(:cc128 => true)
26
+ # observer = SimpleObserver.new
27
+ # meter.add_observer(observer)
28
+ # sleep(30) # wait a while, let some readings come in
29
+ # meter.close
30
+
31
+
32
+ class Meter
33
+
34
+ include Observable
35
+
36
+ # Constructor. 'port' is the name of the serial port that the physical
37
+ # meter is connected to.
38
+ # Connection is to a classic meter by default. To connect to a new-stlye
39
+ # CC128, pass :cc128 => true at the end
40
+ # This function will automatically start processing serial data on the
41
+ # specified port. To stop this processing, call close.
42
+ def initialize(port = '/dev/ttyS0', options = {})
43
+ @port = RB232::Port.new(port, :baud_rate => (options[:cc128] == true ? 57600 : 9600), :data_bits => 8, :stop_bits => 1, :parity => false)
44
+ @protocol = RB232::TextProtocol.new(@port, "\n")
45
+ @protocol.add_observer(self)
46
+ @protocol.start
47
+ end
48
+
49
+ # Internal use only, client code does not need to use this function. Informs
50
+ # the Meter object that a new message has been received by the serial port.
51
+ def update(message)
52
+ unless message.nil?
53
+ # Parse reading from message
54
+ @latest_reading = Reading.from_xml(message)
55
+ # Inform observers
56
+ changed
57
+ notify_observers(@latest_reading)
58
+ end
59
+ rescue CurrentCost::ParseError
60
+ nil
61
+ end
62
+
63
+ # Get the last Reading received. If no reading has been received yet,
64
+ # returns nil. If you have registered an observer with add_observer(),
65
+ # you will most likely not need this function as the reading will be
66
+ # delivered automatically to your observer's update() function.
67
+ def latest_reading
68
+ @latest_reading
69
+ end
70
+
71
+ # Stops serial data processing. Call this once you're done with the Meter
72
+ # object.
73
+ def close
74
+ @protocol.stop
75
+ end
76
+
77
+ end
78
+
79
+ end
@@ -0,0 +1,129 @@
1
+ require 'currentcost/exceptions'
2
+ require 'rexml/document'
3
+
4
+ module CurrentCost
5
+
6
+ class Reading
7
+
8
+ # Creates a reading object from an XML string.
9
+ # Raises CurrentCost::ParseError if the XML is malformed or missing
10
+ # expected content.
11
+ def self.from_xml(xml)
12
+ # Parse XML
13
+ doc = REXML::Document.new(xml)
14
+ # Create reading object
15
+ r = Reading.new
16
+ # Check version
17
+ r.software_version = REXML::XPath.first(doc, "/msg/src/sver").text rescue nil
18
+ if r.software_version.nil?
19
+ r.software_version = REXML::XPath.first(doc, "/msg/src").text
20
+ end
21
+ # Extract basic data
22
+ if r.software_version.include? "CC128"
23
+ r.days_since_birth = REXML::XPath.first(doc, "/msg/dsb").text.to_i
24
+ r.hour, r.minute, r.second = REXML::XPath.first(doc, "/msg/time").text.split(':').map{|x| x.to_i}
25
+ r.id = REXML::XPath.first(doc, "/msg/id").text rescue nil
26
+ r.type = REXML::XPath.first(doc, "/msg/type").text rescue nil
27
+ r.temperature = REXML::XPath.first(doc, "/msg/tmpr").text.to_f rescue nil
28
+ r.sensor = REXML::XPath.first(doc, "/msg/sensor").text rescue nil
29
+ # Extract history data
30
+ if REXML::XPath.first(doc, "/msg/hist")
31
+ r.history = {}
32
+ REXML::XPath.each(doc, "/msg/hist/data") do |sensor|
33
+ sensor_num = sensor.elements['sensor'].text.to_i
34
+ sensor.elements.each do |item|
35
+ match = item.name.match "([hdm])([0-9][0-9][0-9])"
36
+ if match
37
+ case match[1]
38
+ when 'h'
39
+ type = :hours
40
+ when 'd'
41
+ type = :days
42
+ when 'm'
43
+ type = :months
44
+ end
45
+ r.history[type] ||= []
46
+ r.history[type][match[2].to_i] ||= []
47
+ r.history[type][match[2].to_i][sensor_num] = item.text.to_f
48
+ end
49
+ end
50
+ end
51
+ end
52
+ else
53
+ r.days_since_birth = REXML::XPath.first(doc, "/msg/date/dsb").text.to_i
54
+ r.hour = REXML::XPath.first(doc, "/msg/date/hr").text.to_i
55
+ r.minute = REXML::XPath.first(doc, "/msg/date/min").text.to_i
56
+ r.second = REXML::XPath.first(doc, "/msg/date/sec").text.to_i
57
+ r.name = REXML::XPath.first(doc, "/msg/src/name").text
58
+ r.id = REXML::XPath.first(doc, "/msg/src/id").text
59
+ r.type = REXML::XPath.first(doc, "/msg/src/type").text
60
+ r.temperature = REXML::XPath.first(doc, "/msg/tmpr").text.to_f
61
+ # Extract history data
62
+ if REXML::XPath.first(doc, "/msg/hist")
63
+ r.history = {}
64
+ r.history[:hours] = []
65
+ REXML::XPath.each(doc, "/msg/hist/hrs/*") do |node|
66
+ r.history[:hours][node.name.slice(1,2).to_i] = [node.text.to_f]
67
+ end
68
+ r.history[:days] = []
69
+ REXML::XPath.each(doc, "/msg/hist/days/*") do |node|
70
+ r.history[:days][node.name.slice(1,2).to_i] = [node.text.to_i]
71
+ end
72
+ r.history[:months] = []
73
+ REXML::XPath.each(doc, "/msg/hist/mths/*") do |node|
74
+ r.history[:months][node.name.slice(1,2).to_i] = [node.text.to_i]
75
+ end
76
+ r.history[:years] = []
77
+ REXML::XPath.each(doc, "/msg/hist/yrs/*") do |node|
78
+ r.history[:years][node.name.slice(1,2).to_i] = [node.text.to_i]
79
+ end
80
+ end
81
+ end
82
+ # Common information
83
+ r.channels = []
84
+ REXML::XPath.each(doc, "/msg/*/watts") do |node|
85
+ r.channels << { :watts => node.text.to_i }
86
+ end
87
+ # Done
88
+ return r
89
+ rescue
90
+ raise CurrentCost::ParseError.new("Couldn't parse XML data.")
91
+ end
92
+
93
+ # Number of days since the meter was turned on
94
+ attr_accessor :days_since_birth
95
+ # Current time - hour
96
+ attr_accessor :hour
97
+ # Current time - minute
98
+ attr_accessor :minute
99
+ # Current time - second
100
+ attr_accessor :second
101
+ # Name of the device - always "CC02".
102
+ attr_accessor :name
103
+ # ID number of the device
104
+ attr_accessor :id
105
+ # Type id of the device - "1" for a standard CurrentCost meter.
106
+ attr_accessor :type
107
+ # Version of the meter software
108
+ attr_accessor :software_version
109
+ # An array of channels. channels[x][:watts] contains the current power for that channel in watts. The figure shown on the meter is the sum of the wattage for all channels.
110
+ attr_accessor :channels
111
+ # Current temperature
112
+ attr_accessor :temperature
113
+ # Sensor number
114
+ attr_accessor :sensor
115
+ # Historical data, represented as a hash. There is a hash entry for days, weeks, months, and years. Each of these is an array of sensors, each of which contains an array of historical kWh data.
116
+ attr_accessor :history
117
+
118
+ # The sum of the current wattage for all channels, as shown on the meter
119
+ def total_watts
120
+ watts = 0
121
+ channels.each { |c| watts += c[:watts] }
122
+ return watts
123
+ rescue
124
+ 0
125
+ end
126
+
127
+ end
128
+
129
+ end
@@ -0,0 +1,10 @@
1
+ module CurrentCost
2
+
3
+ module VERSION #:nodoc:
4
+ MAJOR = 0
5
+ MINOR = 3
6
+ TINY = 2
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+
10
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: currentcost
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.4
5
+ platform: ruby
6
+ authors:
7
+ - James Smith
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-15 00:00:00 +00:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rb232
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.2.4
24
+ version:
25
+ description:
26
+ email: james@floppy.org.uk
27
+ executables:
28
+ - currentcost_tray_monitor.rb
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - README
35
+ - COPYING
36
+ - lib/currentcost.rb
37
+ - lib/currentcost/meter.rb
38
+ - lib/currentcost/reading.rb
39
+ - lib/currentcost/version.rb
40
+ - lib/currentcost/exceptions.rb
41
+ - examples/simple.rb
42
+ - bin/currentcost_tray_monitor.rb
43
+ has_rdoc: true
44
+ homepage: http://github.com/Floppy/currentcost-ruby
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options: []
49
+
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ requirements: []
65
+
66
+ rubyforge_project:
67
+ rubygems_version: 1.3.5
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: Ruby interface to the CurrentCost energy meter
71
+ test_files: []
72
+