currentcost 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
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
+