From: Karl S. <kar...@gm...> - 2010-12-03 17:35:33
|
I am trying to get a single Omnistat 2 functioning with MisterHouse. Any help will be greatly appreciated. When opening omnistat_setup_web.pl I get the following error message "http error in http eval of omnistat_setup_web.pl: 12/03/10 11:40:36 AM: Omnistat[1]->send_cmd did not get ack reply to command 01 20 3b 0e 6a () at /opt/misterhouse/mh/bin/../lib/Omnistat.pm line 554, line 216." When opening omnistat_sched_web.pl I get a very simular message "http error in http eval of omnistat_sched_web.pl: 12/03/10 12:30:24 PM: Omnistat[1]->send_cmd did not get ack reply to command 01 20 15 0c 42 () at /opt/misterhouse/mh/bin/../lib/Omnistat.pm line 554, line 444." I have compared the commands between the RC-80 series and the Omnistat2 series and have not found any significant differences in the basic commands. I have double checked the wiring between the Omnistat2 and the serial port and everything seems to be wired correctly. The Omnistat is set up at Address 1. Thanks Karl |
From: Daniel D. <da...@ma...> - 2010-12-03 17:44:35
|
Make sure to check the Omnistat is in the correct communication mode. On Dec 3, 2010, at 12:35, Karl Suchy <kar...@gm...> wrote: > I am trying to get a single Omnistat 2 functioning with MisterHouse. Any help will be greatly appreciated. > > When opening omnistat_setup_web.pl I get the following error message "http error in http eval of omnistat_setup_web.pl: 12/03/10 11:40:36 AM: Omnistat[1]->send_cmd did not get ack reply to command 01 20 3b 0e 6a () at /opt/misterhouse/mh/bin/../lib/Omnistat.pm line 554, line 216." > > When opening omnistat_sched_web.pl I get a very simular message "http error in http eval of omnistat_sched_web.pl: 12/03/10 12:30:24 PM: Omnistat[1]->send_cmd did not get ack reply to command 01 20 15 0c 42 () at /opt/misterhouse/mh/bin/../lib/Omnistat.pm line 554, line 444." > > I have compared the commands between the RC-80 series and the Omnistat2 series and have not found any significant differences in the basic commands. > > I have double checked the wiring between the Omnistat2 and the serial port and everything seems to be wired correctly. The Omnistat is set up at Address 1. > > Thanks > Karl > ------------------------------------------------------------------------------ > Increase Visibility of Your 3D Game App & Earn a Chance To Win $500! > Tap into the largest installed PC base & get more eyes on your game by > optimizing for Intel(R) Graphics Technology. Get started today with the > Intel(R) Software Partner Program. Five $500 cash prizes are up for grabs. > http://p.sf.net/sfu/intelisp-dev2dev > ________________________________________________________ > To unsubscribe from this list, go to: http://sourceforge.net/mail/?group_id=1365 > |
From: Marc M. <ma...@me...> - 2010-12-03 17:45:25
|
On Fri, Dec 03, 2010 at 12:35:27PM -0500, Karl Suchy wrote: > I am trying to get a single Omnistat 2 functioning with MisterHouse. Any > help will be greatly appreciated. > > When opening omnistat_setup_web.pl I get the following error message "http > error in http eval of omnistat_setup_web.pl: 12/03/10 11:40:36 AM: > Omnistat[1]->send_cmd did not get ack reply to command 01 20 3b 0e 6a () at > /opt/misterhouse/mh/bin/../lib/Omnistat.pm line 554, line 216." > > When opening omnistat_sched_web.pl I get a very simular message "http error > in http eval of omnistat_sched_web.pl: 12/03/10 12:30:24 PM: > Omnistat[1]->send_cmd did not get ack reply to command 01 20 15 0c 42 () at > /opt/misterhouse/mh/bin/../lib/Omnistat.pm line 554, line 444." > > I have compared the commands between the RC-80 series and the Omnistat2 > series and have not found any significant differences in the basic > commands. > > I have double checked the wiring between the Omnistat2 and the serial port > and everything seems to be wired correctly. The Omnistat is set up at > Address 1. Long story short, the Omnistat2 has never been tested with the current code in mh. It might work out of the box, or may require minor modifications but basically you'll have to do the leg work. My recommendation is to skip the web page for now, start reading Omnistat.pm, and try sending very simple get and set calls until they work. It's probably not too hard, but it might take a bit of time (make sure you pull up the command set for RC-80 and Omnistat2 to compare them and see where code differences might be required). Best, Marc -- "A mouse is a device used to point at the xterm you want to type in" - A.S.R. Microsoft is to operating systems & security .... .... what McDonalds is to gourmet cooking Home page: http://marc.merlins.org/ |
From: Marc M. <ma...@me...> - 2010-12-03 17:46:38
|
On Fri, Dec 03, 2010 at 09:45:18AM -0800, Marc MERLIN wrote: > On Fri, Dec 03, 2010 at 12:35:27PM -0500, Karl Suchy wrote: > > I am trying to get a single Omnistat 2 functioning with MisterHouse. Any > > help will be greatly appreciated. > > > > When opening omnistat_setup_web.pl I get the following error message "http > > error in http eval of omnistat_setup_web.pl: 12/03/10 11:40:36 AM: > > Omnistat[1]->send_cmd did not get ack reply to command 01 20 3b 0e 6a () at > > /opt/misterhouse/mh/bin/../lib/Omnistat.pm line 554, line 216." I should add that '()' shows that no reply at all was received from the omnistat, so you may indeed have a basic communication problem. Marc -- "A mouse is a device used to point at the xterm you want to type in" - A.S.R. Microsoft is to operating systems & security .... .... what McDonalds is to gourmet cooking Home page: http://marc.merlins.org/ |
From: Karl S. <kar...@gm...> - 2010-12-03 18:11:52
|
Thanks for the feed back. The Omnistat is in the correct communication mode. I think I will start messing around with Omnistat.pm and trying to send simple get and set commands and see what I get back. Karl On Fri, Dec 3, 2010 at 12:46 PM, Marc MERLIN <ma...@me...> wrote: > On Fri, Dec 03, 2010 at 09:45:18AM -0800, Marc MERLIN wrote: > > On Fri, Dec 03, 2010 at 12:35:27PM -0500, Karl Suchy wrote: > > > I am trying to get a single Omnistat 2 functioning with MisterHouse. > Any > > > help will be greatly appreciated. > > > > > > When opening omnistat_setup_web.pl I get the following error message > "http > > > error in http eval of omnistat_setup_web.pl: 12/03/10 11:40:36 AM: > > > Omnistat[1]->send_cmd did not get ack reply to command 01 20 3b 0e 6a > () at > > > /opt/misterhouse/mh/bin/../lib/Omnistat.pm line 554, line 216." > > I should add that '()' shows that no reply at all was received from the > omnistat, so you may indeed have a basic communication problem. > > Marc > -- > "A mouse is a device used to point at the xterm you want to type in" - > A.S.R. > Microsoft is to operating systems & security .... > .... what McDonalds is to gourmet > cooking > Home page: http://marc.merlins.org/ > > > ------------------------------------------------------------------------------ > Increase Visibility of Your 3D Game App & Earn a Chance To Win $500! > Tap into the largest installed PC base & get more eyes on your game by > optimizing for Intel(R) Graphics Technology. Get started today with the > Intel(R) Software Partner Program. Five $500 cash prizes are up for grabs. > http://p.sf.net/sfu/intelisp-dev2dev > ________________________________________________________ > To unsubscribe from this list, go to: > http://sourceforge.net/mail/?group_id=1365 > > |
From: Mickey A. <AO...@mi...> - 2010-12-04 18:05:36
|
Karl, I have this working on my setup...I'll try to send out the code changes later today. I did add some new stuff, like changing the background color based on the outside air temperature. Mickey Argo On 12/3/10 12:11 PM, Karl Suchy wrote: > Thanks for the feed back. The Omnistat is in the correct > communication mode. I think I will start messing around with > Omnistat.pm and trying to send simple get and set commands and see > what I get back. > > Karl > > On Fri, Dec 3, 2010 at 12:46 PM, Marc MERLIN <ma...@me... > <mailto:ma...@me...>> wrote: > > On Fri, Dec 03, 2010 at 09:45:18AM -0800, Marc MERLIN wrote: > > On Fri, Dec 03, 2010 at 12:35:27PM -0500, Karl Suchy wrote: > > > I am trying to get a single Omnistat 2 functioning with > MisterHouse. Any > > > help will be greatly appreciated. > > > > > > When opening omnistat_setup_web.pl > <http://omnistat_setup_web.pl> I get the following error message "http > > > error in http eval of omnistat_setup_web.pl > <http://omnistat_setup_web.pl>: 12/03/10 11:40:36 AM: > > > Omnistat[1]->send_cmd did not get ack reply to command 01 20 > 3b 0e 6a () at > > > /opt/misterhouse/mh/bin/../lib/Omnistat.pm line 554, line 216." > > I should add that '()' shows that no reply at all was received > from the > omnistat, so you may indeed have a basic communication problem. > > Marc > -- > "A mouse is a device used to point at the xterm you want to type > in" - A.S.R. > Microsoft is to operating systems & security .... > .... what McDonalds is to > gourmet cooking > Home page: http://marc.merlins.org/ > > ------------------------------------------------------------------------------ > Increase Visibility of Your 3D Game App & Earn a Chance To Win $500! > Tap into the largest installed PC base & get more eyes on your game by > optimizing for Intel(R) Graphics Technology. Get started today > with the > Intel(R) Software Partner Program. Five $500 cash prizes are up > for grabs. > http://p.sf.net/sfu/intelisp-dev2dev > ________________________________________________________ > To unsubscribe from this list, go to: > http://sourceforge.net/mail/?group_id=1365 > > > > ------------------------------------------------------------------------------ > Increase Visibility of Your 3D Game App& Earn a Chance To Win $500! > Tap into the largest installed PC base& get more eyes on your game by > optimizing for Intel(R) Graphics Technology. Get started today with the > Intel(R) Software Partner Program. Five $500 cash prizes are up for grabs. > http://p.sf.net/sfu/intelisp-dev2dev > > > ________________________________________________________ > To unsubscribe from this list, go to: http://sourceforge.net/mail/?group_id=1365 > |
From: Karl S. <kar...@gm...> - 2010-12-05 15:39:32
|
Mickey, Thanks for sending that out. I will work that in once I get the OmniStat Communicating with the computer. Do you recall having to do anything special with the OmniStat 2 to get it to talk? My version does not have a communication jumper on the pcb, instead there was a wire jumper between the "N/C" and "black" pins as labeled on the pcb. In settings it is set up for serial at 300 baud. I have tried using Realterm to send commands to the Stat with no response back. Thanks Karl On Sat, Dec 4, 2010 at 10:15 PM, Mickey Argo <mh...@ar...> wrote: > I didn't exactly code this for wide distribution...there are complications > with my employer and areas they might be working on in the near future. If > you want to clean this up for inclusion into the trunk code, please feel > free and take the credit! Compare the attached Omnistat.pm with the trunk > version. omnistat2.pl is in my code directory. I have the following in > my private ini: > > Omnistat_serial_port=COM1 > Omnistat_24hr=1 > # disable internal program > #Omnistat_non_program=[0,1] > Omnistat_no_stat_log=3 > Omnistat_stat_log=$config_parms{data_dir}/omnistatlog.txt > omnistat_allowed_errors = 999999999999 > hvac_allowed_errors = 999999999999 > show_all_errors = yes > > > Mickey Argo wrote: > > Karl, > > I have this working on my setup...I'll try to send out the code changes > later today. I did add some new stuff, like changing the background color > based on the outside air temperature. > > Mickey Argo > > On 12/3/10 12:11 PM, Karl Suchy wrote: > > Thanks for the feed back. The Omnistat is in the correct communication > mode. I think I will start messing around with Omnistat.pm and trying to > send simple get and set commands and see what I get back. > > Karl > > On Fri, Dec 3, 2010 at 12:46 PM, Marc MERLIN <ma...@me...> wrote: > >> On Fri, Dec 03, 2010 at 09:45:18AM -0800, Marc MERLIN wrote: >> > On Fri, Dec 03, 2010 at 12:35:27PM -0500, Karl Suchy wrote: >> > > I am trying to get a single Omnistat 2 functioning with MisterHouse. >> Any >> > > help will be greatly appreciated. >> > > >> > > When opening omnistat_setup_web.pl I get the following error message >> "http >> > > error in http eval of omnistat_setup_web.pl: 12/03/10 11:40:36 AM: >> > > Omnistat[1]->send_cmd did not get ack reply to command 01 20 3b 0e 6a >> () at >> > > /opt/misterhouse/mh/bin/../lib/Omnistat.pm line 554, line 216." >> >> I should add that '()' shows that no reply at all was received from the >> omnistat, so you may indeed have a basic communication problem. >> >> Marc >> -- >> "A mouse is a device used to point at the xterm you want to type in" - >> A.S.R. >> Microsoft is to operating systems & security .... >> .... what McDonalds is to gourmet >> cooking >> Home page: http://marc.merlins.org/ >> >> >> ------------------------------------------------------------------------------ >> Increase Visibility of Your 3D Game App & Earn a Chance To Win $500! >> Tap into the largest installed PC base & get more eyes on your game by >> optimizing for Intel(R) Graphics Technology. Get started today with the >> Intel(R) Software Partner Program. Five $500 cash prizes are up for grabs. >> http://p.sf.net/sfu/intelisp-dev2dev >> ________________________________________________________ >> To unsubscribe from this list, go to: >> http://sourceforge.net/mail/?group_id=1365 >> >> > > ------------------------------------------------------------------------------ > Increase Visibility of Your 3D Game App & Earn a Chance To Win $500! > Tap into the largest installed PC base & get more eyes on your game by > optimizing for Intel(R) Graphics Technology. Get started today with the > Intel(R) Software Partner Program. Five $500 cash prizes are up for grabs.http://p.sf.net/sfu/intelisp-dev2dev > > > ________________________________________________________ > To unsubscribe from this list, go to: http://sourceforge.net/mail/?group_id=1365 > > ------------------------------ > > ------------------------------------------------------------------------------ > What happens now with your Lotus Notes apps - do you make another costly > upgrade, or settle for being marooned without product support? Time to move > off Lotus Notes and onto the cloud with Force.com, apps are easier to build, > use, and manage than apps on traditional platforms. Sign up for the Lotus > Notes Migration Kit to learn more. http://p.sf.net/sfu/salesforce-d2d > > ------------------------------ > > ________________________________________________________ > To unsubscribe from this list, go to: http://sourceforge.net/mail/?group_id=1365 > > > # Category=HVAC > > =begin comment > > Mickey Argo July 2010 > > This code is to run the Omnistat2 thermostats. I used code credited to the > following people; > Kent Noonan, Joel Davidson, Dan Arnold, Marc MERLIN > > Not done in this module; > Handle multiple thermostats > > > =cut > > > $omnistat2=new Omnistat; > > my $saved_setpoint_cool = 0; > my $saved_setpoint_heat = 0; > my $saved_program_mode = ''; > my $saved_HVAC_command = ''; > my $saved_indoor_temp = 0; > my $thermo_model = ''; > my $program_mode = ''; > my $hvac_return_data = ''; > #one time settings > if ($Reload or $Reread) { > #$omnistat2->cooling_anticipator('10'); Not used in two stage > #$omnistat2->heating_anticipator('10'); Not used in 2 stage > save_HVAC ("[PROGRAM] System had restarted"); > $omnistat2->cooling_cycle_time('8'); > $omnistat2->heating_cycle_time('8'); > $omnistat2->day_cool_setpoint('80'); > $omnistat2->day_heat_setpoint('68'); > $omnistat2->night_cool_setpoint('81'); > $omnistat2->night_heat_setpoint('66'); > $omnistat2->away_cool_setpoint('82'); > $omnistat2->away_heat_setpoint('65'); > $omnistat2->vaca_cool_setpoint('84'); > $omnistat2->vaca_heat_setpoint('63'); > $omnistat2->outdoor_temp($Weather{TempOutdoor}); > $hvac_return_data = $omnistat2->get_stat_type; > save_HVAC ("[PROGRAM] Thermostat Model is $hvac_return_data"); > $hvac_return_data = $omnistat2->get_program_mode; > save_HVAC ("[PROGRAM] Program mode is $hvac_return_data"); > $hvac_return_data = $omnistat2->get_occupancy_mode; > save_HVAC ("[PROGRAM] Occupancy is set to $hvac_return_data"); > $hvac_return_data = $omnistat2->get_fan_mode; > save_HVAC ("[PROGRAM] Fan is set to $hvac_return_data"); > } > > if ($New_Day) { > $omnistat2->set_time; > } > > > > $v_omnistat_fan=new Voice_Cmd('Set Thermostat fan [on,auto,cycle]'); > if ($state = said $v_omnistat_fan) { > $omnistat2->fan($state); > speak("app=weather Setting HVAC fan to $state"); > } > $v_omnistat_hold=new Voice_Cmd('Set Thermostat hold [on,off]'); > if ($state = said $v_omnistat_hold) { > $omnistat2->hold($state); > } > $v_omnistat_mode=new Voice_Cmd('Set Thermostat mode [off,heat,cool,auto]'); > if ($state = said $v_omnistat_mode) { > $omnistat2->mode($state); > speak("app=weather Setting HVAC mode to $state"); > } > $v_omnistat_cool_sp=new Voice_Cmd("Set Thermostat cool setpoint to > [74,76,78,79,80,81,82,83]"); > if ($state = said $v_omnistat_cool_sp) { > $omnistat2->cool_setpoint($state); > speak("app=weather Setting HVAC cool set point to $state degrees"); > } > $v_omnistat_heat_sp=new Voice_Cmd("Set Thermostat heat setpoint to > [65,66,67,68,69,70,71,72]"); > if ($state = said $v_omnistat_heat_sp) { > $omnistat2->heat_setpoint($state); > speak("app=weather Setting HVAC heat set point to $state degrees"); > } > $v_omnistat_get_occupancy=new Voice_Cmd("What is the occupancy mode"); > if ($state = said $v_omnistat_get_occupancy) { > $hvac_return_data = $omnistat2->get_occupancy_mode; > speak("app=weather The occupancy mode is $hvac_return_data"); > } > $v_omnistat_set_occupancy=new Voice_Cmd("Set occupancy mode to > [day,night,away,vacation]"); > if ($state = said $v_omnistat_set_occupancy) { > $omnistat2->set_occupancy_mode($state); > save_HVAC ("[PROGRAM] System occupancy mode set to $state"); > speak("app=weather Setting occupancy mode to $state"); > } > $v_omnistat_set_program_mode=new Voice_Cmd("Set program mode to > [none,schedule,occupancy]"); > if ($state = said $v_omnistat_set_program_mode) { > $omnistat2->set_program_mode($state); > save_HVAC ("[PROGRAM] System program mode set to $state"); > speak("app=weather Setting program mode to $state"); > } > > $v_omnistat_background=new Voice_Cmd("Set Thermostat background to > [Blue,Green,Purple,Red,Orange,Yellow]"); > if ($state = said $v_omnistat_background) { > my $background_hex = "0x00"; > if ($state = 'Blue'){ > $background_hex = "0x44"; > } elsif ($state = 'Green'){ > $background_hex = "0x25"; > } elsif ($state = 'Purple'){ > $background_hex = "0x5a"; > } elsif ($state = 'Red'){ > $background_hex = "0x01"; > } elsif ($state = 'Orange'){ > $background_hex = "0x03"; > } elsif ($state = 'Yellow'){ > $background_hex = "0x05"; > } > $omnistat2->set_reg("0x8c", $background_hex); > } > > > # update data once a minute, log changes only. > if ($New_Minute) { > # we make the extended group1 call that also retreives the stat's > output status > my ($cool_sp, $heat_sp, $mode, $fan, $hold, $temp, $output) = > $omnistat2->read_group1("true"); > > my $stat_type = $omnistat2->get_stat_type; > # This mashes $hold and $mode together from registers cached in the > group1 call and outputs a combined string > $mode = $omnistat2->get_mode; > > #Save temps every minute > save_HVAC ("[TEMP-IN] $temp"); > # save_HVAC ("[TEMP-OUT] $Weather{TempOutdoor}"); > > #Update the thermostat data > $omnistat2->outdoor_temp($Weather{TempOutdoor}); > > > #check and log changed data > if ($saved_setpoint_cool ne $cool_sp){ > save_HVAC ("[SETPOINT] Cool Setpoint has been changed to > $cool_sp"); > $saved_setpoint_cool = $cool_sp; > } > if ($saved_setpoint_heat ne $heat_sp){ > save_HVAC ("[SETPOINT] Heat Setpoint has been changed to > $heat_sp"); > $saved_setpoint_heat = $heat_sp; > } > if ($saved_HVAC_command ne $output){ > save_HVAC ("[HVAC] HVAC Command is now $output"); > $saved_HVAC_command = $output; > } > if ($saved_program_mode ne $mode){ > save_HVAC ("[HVAC] HVAC Mode is now $mode"); > $saved_program_mode = $mode; > } > } > > #Change the backlight > if (new_minute 15) { > my $background_color = "0x00"; > if ($Weather{TempOutdoor} >= 95){ > $background_color = "0x01"; #Red > } > elsif ($Weather{TempOutdoor} >= 85){ > $background_color = "0x05"; #Yellow > } > elsif ($Weather{TempOutdoor} >= 65){ > $background_color = "0x25"; #Green > } > elsif ($Weather{TempOutdoor} >= 55){ > $background_color = "0x5a"; #Purple > } > elsif ($Weather{TempOutdoor} < 55){ > $background_color = "0x44"; #Blue > } > else{ > $background_color = "0x03"; #Orange > } > $omnistat2->set_reg("0x8c", $background_color); > } > > sub normal_HVAC_control > { > $omnistat2->set_occupancy_mode('day'); > print_log "[HVAC] Setting HVAC to day setbacks" ; > save_HVAC ("[PROGRAM] Occupancy is set to day"); > } > > sub setback_HVAC_control { > $omnistat2->set_occupancy_mode('away'); > print_log "[HVAC] Setting HVAC to away setbacks"; > save_HVAC ("[PROGRAM] Occupancy is set to away"); > } > > sub setback_night_HVAC_control { > $omnistat2->set_occupancy_mode('night'); > print_log "[HVAC] Setting HVAC to night setbacks"; > save_HVAC ("[PROGRAM] Occupancy is set to night"); > } > > =begin comment > > # 132 columns max > > 123456789112345678921234567893123456789412345678951234567896123456789712345678981234567899123456789012345678911234567892123456789312 > > # this file, if distributed separately, replaces > misterhouse/lib/Omnistat.pm > > Module for HAI RC-Series Electronic Communicating Thermostats (Omnistat) > Specifically written with/for RC-80 but should work with any of them. > http://www.homeauto.com/Products/HAIAccessories/Omnistat/rc80.htm > > Newer Omnistat2 thermostats have a slightly different protocol and may need > some work. They look nicer, but they are pricier (vs $50 for an RC-80 on > ebay) > and don't offer functionality that's useful to most people -- merlin > > > ################### > > Use these mh.ini parameters to enable this code: > Omnistat_serial_port=/dev/ttyUSB0 > > There are optional settings for the Omnistat for mh.private.ini: > If these settings aren't in mh.private.ini the default is 0 (false) > > # use celcius for temperatures > Omnistat_celcius=[0,1] > # use 24hour clock for times > Omnistat_24hr=[0,1] > # disable internal program > Omnistat_non_program=[0,1] > # Real Time Pricing mode > Omnistat_rtp_mode=[0,1] > # hide clock on thermostat > Omnistat_hide_clock=[0,1] > # You can set how much gets logged > Omnistat_no_stat_log=[0,1,2,3] > > # For debugging, add omnistat to debug in mh.private.ini, as in > debug=insteon,omnistat > # instead of debug=insteon > > This module is used 2 ways > 1) from a web interface > 2) from mh/code/public/omnistat.pl which you need to install in your code > directory > > > > ================================================================================ > TODOs > > ================================================================================ > > TODO: Add hooks for caching instead of relying on pl file > TODO: ini parameter for range of registers to read (default to temp) > TODO: Adjust clock speed? Not sure if possible (reg 14), may need to be > done in pl > TODO: Modify set_reg to accept muliple registers (hasn't been really needed > so far) > TODO: Ini Parameter to turn on/off set outdoor temp (may be in pl) > TODO: The sleep situation has been much improved, but if someone smart > could replace > the sleep with a proper callback so as not to stall mh, that would > rule > > > ================================================================================ > Changelog > > ================================================================================ > > 2009/08/03 - Marc MERLIN > ======================== > - send_cmd is now a method too so that we can compare the return value > against $$self{addr} > - improved command ack parsing failure error reporting > - oops, got omnistat_log function to actually respect log_level > - added omnistat_debug function > - hold function now only sets hold if it's different from cached value, > this is because we get > frequent calls to hold off and want to avoid actually sending them if hold > was already off > - restore|cool|heat_setpoints now unhold the that before programming it (or > it won't work) and > then put it back on hold depending on the Omnistat_set_does_not_hold > setting in mh.private.ini > > > 2009/07/25 - Marc MERLIN > ======================== > - optimized sleep/wait in send_cmd to be as little as needed. It is now as > cheap to > read 2 registers separately as 10 registers in a row: 0.666s > (before, reading 2 registers separately took 4 seconds) > - NOTE: if you were calling send_cmd, you need to change your call to > prepend the number > of characters you expect back (this is needed by the timing improvements) > - fixed get_stat_type method > - A lot more error handling and status reporting, including making sure > that you get all > the data back that you're supposed to get, and that set_reg actually gets > some kind of ack > - omnistat.pl allows for temporarily changing setpoints until the next > schedule change with > Omnistat_set_does_not_hold=1 in mh.private.ini > - omnistat_log allows for logging stat data with Omnistat_no_stat_log which > defaults to 1 > - fixed an insidious bug in set_time that sent an integer for the week > instead of an hex string > perl helpfully converted that and then tried to H2 pack "5" which is > invalid and sent a short command > - added checks for set_reg and friends to make sure registers and values > are hex strings > - auto convertions in perl are too helpful and can bite you in the butt, so > all register values are now > required to be passed as 0xXX whether they might have worked before, or > not > > > 2009/07/22 - Marc MERLIN > ======================== > - ripped out old one register update per minute cache > - added on demand cache and cache prefetching for setpoints and temperature > - fixed bugs / cleanups > - merged read_group1 with read_reg > - fixed state change messages on multi reg fetches > - made use of caching functions strongly encouraged :) > - added important get_stat_output method to actually know what the stat is > telling > your HVAC system to do > - get_stat_model function > > > Dan Arnold May 2009 > =================== > Added state processing > > NOTE: State changes will not take effect until all registers have been > cached > -> no more -- merlin > All of the states that may be set: > all_registers_cached: All of the registers have been read into the cache, > you can read any register without penalty > filter_reminder: Filter reminder has expired > -> obsolete -- merlin > temp_change: Inside temperature changed > (call get_temp() to get value) > heat_sp_change: Heat setpoint was changed > (call get_heat_sp() to get value). > cool_sp_change: Cool setpoint was changed > (call get_cool_sp() to get value). > mode_change: System mode changed > (call get_mode() to get value). > fan_mode_change: Fan mode changed > (call get_fan_mode() to get value). > > New/modified functions available: > mode(): > Sets system mode to argument: 'off', 'heat', 'cool', 'auto', > 'program_heat', 'program_cool', 'program_auto' (program_ implies > hold=off) > get_mode(): > Returns the last mode returned by poll_mode(). > fan(): > Sets fan to 'on' or 'auto' > get_fan_mode(): > Returns the current fan mode (fan_on or fan_auto) > cool_setpoint(): > Sets a new cool setpoint. > get_cool_sp(): > Returns the current cool setpoint. > heat_setpoint(): > Sets a new heat setpoint. > get_heat_sp(): > Returns the current heat setpoint. > get_temp(): > Returns the current temperature at the thermostat. > get_filter_reminder(): > Returns the number of days until the furnace filter needs to be > replaced > restore_setpoints(): > Returns the heat/cool setpoints to what they would have been if the > thermostat were running on schedule > > > Dan Arnold March 2009 > ====================== > Added caching of registers > > > Dan Arnold February 2009 > ========================= > Added function to set registers > Added time translation for thermostat programming (12h or 24h format based > on Omnistat_24hr config param) > Added ability to set outside temp to display on thermostat > Added the ability to translate to/from Celcius (depends on the > Omnistat_celcius config param) > Modified set procedures to use set_reg > Modified temp translation to use math rather than a lookup table (needed to > cover possible outside temps) > Fixed a bug in read_reg > > > Joel Davidson February 2009 > ========================= > Corrected bad syntax in mode comparison logic in read_group1. > > > Joel Davidson December 2005 > ========================= > Re-ordered routines to avoid run-time error from prototyped subroutines. > Modified comparison values in read_group1 tests to fix users problem with > incorrect compare results. Added additional comments. Added addressing > mods to support multiple thermostats. Removed calls to set_time and > display in serial_startup since they cause a funky runtime error. > > > Joel Davidson June 2004 > ========================= > Modified checksum() to return 8 bit checksum. Fixed set_time. > Added read_group1 to return register group 1 values (setpoints, > modes, current temperature). Added generic function to read any > specified register(s), read_register(address, [# of regs]). > Changed Omnistat_run_program config option to Omnistat_non_program. > Setting to a 1 disables thermostat internal program. Changed > Omnistat_show_clock to Omnistat_hide_clock. 1 hides clock and filter > display. > > > Kent Noonan Jan 2002 > ===================== > I have another module for misterhouse. But it is not finished. This is a > module for controling HAI Omnistat Communicating thermostats. It was > specifically written against the RC80 but as far as I can tell it should > work with any of them. There is a problem with it. I am not finished with > it. I started working on it, then moved to a house with an older heater > that the thermostat doesn't work with. It's going to be a couple of years > before we can upgrade the heater, so I thought I'd send this incase > somebody else wanted to continue where I left off before I can get back to > it again. Right now I cant even gaurantee that it works at all, but I > think it did.. > > > > > > ######################################################## > > Below is a list of registers for reference: > > # INTERNAL REGISTERS (RO = READ ONLY) > 0 (00) - Thermostat address (ro) (1 - 127) > 1 (01) - Communications mode (ro) (0, 1, 8 or 24) > 2 (02) - System options (ro) > 3 (03) - Display options > 4 (04) - Calibration offset (1 to 59, 30=no change - ½ C units) > 5 (05) - Cool setpoint low limit (Omnitemp units) > 6 (06) - Heat setpoint high limit (Omnitemp units) > 7 (07) - Reserved > 8 (08) - Reserved > 9 (09) - Cooling anticipator (0 to 30) (RC-80, -81, -90, -91 only) > 10 (0A) - Heating anticipator (0 to 30) (RC-80, -81, -90, -91 only), Stage > 2 differential (RC-112) > 11 (0B) - Cooling cycle time (2 - 30 minutes) > 12 (0C) - Heating cycle time (2 - 30 minutes) > 13 (0D) - Aux heat differential, (RC-100, -101, -112), Stage 2 differential > (RC-120, -121, -122) (Omnitemp units) > 14 (0E) - Clock adjust (seconds/day) 1=-29, 30=0, 59=+29 > 15 (0F) - Days remaining until filter reminder > 16 (10) - System run time, current week - hours > 17 (11) - System run time, last week - hours > > # Registers 18 - 20 are used only in models with real time pricing. > 18 (12) - Real time pricing setback - Mid (Omnitemp units) > 19 (13) - High > 20 (14) - Critical > > # Programming registers > 21 (15) - weekday morning time > 22 (16) - cool setpoint > 23 (17) - heat setpoint > 24 (18) - weekday day time > 25 (19) - cool setpoint > 26 (1A) - heat setpoint > 27 (1B) - weekday evening time > 28 (1C) - cool setpoint > 29 (1D) - heat setpoint > 30 (1E) - weekday night time > 31 (1F) - cool setpoint > 32 (20) - heat setpoint > 33 (21) - Saturday morning time > 34 (22) - cool setpoint > 35 (23) - heat setpoint > 36 (24) - Saturday day time > 37 (25) - cool setpoint > 38 (26) - heat setpoint > 39 (27) - Saturday evening time > 40 (28) - cool setpoint > 41 (29) - heat setpoint > 42 (2A) - Saturday night time > 43 (2B) - cool setpoint > 44 (2C) - heat setpoint > 45 (2D) - Sunday morning time > 46 (2E) - cool setpoint > 47 (2F) - heat setpoint > 48 (30) - Sunday day time > 49 (31) - cool setpoint > 50 (32) - heat setpoint > 51 (33) - Sunday evening time > 52 (34) - cool setpoint > 53 (35) - heat setpoint > 54 (36) - Sunday night time > 55 (37) - cool setpoint > 56 (38) - heat setpoint > 57 (39) - Reserved - do not write > > # this one is lost, it kind of belongs with 0x41 below > 58 (3A) - Day of week (0=Monday - 6=Sunday) > > # group1 data start > 59 (3B) - Cool setpoint (current) > 60 (3C) - Heat setpoint (current) > 61 (3D) - Thermostat mode (0=off, 1=heat, 2=cool, 3=auto) (4=Emerg heat: > RC-100, -101, -112 only) > 62 (3E) - Fan status (0=auto 1=on) > 63 (3F) - Hold (0=off 255=on) > 64 (40) - Actual temperature in Omni format > # group1 data stop. > # Would have been so very nice is 0x48 were in group1 since you typically > want to query that often too > # to know what commands your stat is sending to your HVAC system :-/ > > 65 (41) - Seconds 0 - 59 > 66 (42) - Minutes 0 - 59 > 67 (43) - Hours 0 - 23 > 68 (44) - Outside temperature (see below) > 69 (45) - Reserved > 70 (46) - Real time pricing mode (0=lo, 1=mid, 2=high, 3=critical) (RC-81, > -91, -101, -121 only) > 71 (47) - (ro) current mode (0=off 1=heat 2=cool) > 72 (48) - (ro) output status > 73 (49) - (ro) model of thermostat > > # 0x48: reflects the positions of the control relays on the thermostat. > bit 0: heat/cool bit - set for heat, clear for cool > bit 1: auxiliary heat bit - set for on, clear for off (RC-100, -101, -112 > only) > bit 2: stage 1 run bit - set for on, clear for off > bit 3: fan bit - set for on, clear for off > bit 4: stage 2 run bit: set for on, clear for off (RC-112, 120, 121, 122 > only) > > # 0x49: thermostat model > RC-80 0 > RC-81 1 > RC-90 8 > RC-91 9 > RC-100 16 > RC-101 17 > RC-112 34 > RC-120 48 > RC-121 49 > RC-122 50 > > Outside Temperature: writing to the outside temperature register will cause > the thermostat to display the > outside temperature every 4 seconds. The thermostat will stop displaying > the outside temperature if this > register is not refreshed at least every 5 minutes. > > Display Options: > bit 0: set for Fahrenheit, clear for Celsius > bit 1: set for 24 hour time display, clear for AM/PM > bit 2: set for non-programmable, clear for programmable (disables internal > programs in thermostat) > bit 3: set for real time pricing (RTP) mode, clear for no RTP (RC-81, -91, > -101, -121 only) > bit 4: set to hide clock, RTP and filter display, clear to show them. > > New Registers for Omnistat2 > 74 (4A) Current energy cost (0 – 254, 255=disabled) > > Programming Tuesday - Friday: > 75 (4B) Programming Tuesday morning time (15 minute increments) > 76 (4C) Programming Tuesday morning cool setpoint (in Omnitemp) > 77 (4D) Programming Tuesday morning heat setpoint (in Omnitemp) > 78 (4E) Programming Tuesday day time (15 minute increments) > 79 (4F) Programming Tuesday day cool setpoint (in Omnitemp) > 80 (50) Programming Tuesday day heat setpoint (in Omnitemp) > 81 (51) Programming Tuesday evening time (15 minute increments) > 82 (52) Programming Tuesday evening cool setpoint (in Omnitemp) > 83 (53) Programming Tuesday evening heat setpoint (in Omnitemp) > 84 (54) Programming Tuesday night time (15 minute increments) > 85 (55) Programming Tuesday night cool setpoint (in Omnitemp) > 86 (56) Programming Tuesday night heat setpoint (in Omnitemp) > 87 (57) Programming Wednesday morning time (15 minute increments) > 88 (58) Programming Wednesday morning cool setpoint (in Omnitemp) > 89 (59) Programming Wednesday morning heat setpoint (in Omnitemp) > 90 (5A) Programming Wednesday day time (15 minute increments) > 91 (5B) Programming Wednesday day cool setpoint (in Omnitemp) > 92 (5C) Programming Wednesday day heat setpoint (in Omnitemp) > 93 (5D) Programming Wednesday evening time (15 minute increments) > 94 (5E) Programming Wednesday evening cool setpoint (in Omnitemp) > 95 (5F) Programming Wednesday evening heat setpoint (in Omnitemp) > 96 (60) Programming Wednesday night time (15 minute increments) > 97 (61) Programming Wednesday night cool setpoint (in Omnitemp) > 98 (62) Programming Wednesday night heat setpoint (in Omnitemp) > 99 (63) Programming Thursday morning time (15 minute increments) > 100 (64) Programming Thursday morning cool setpoint (in Omnitemp) > 101 (65) Programming Thursday morning heat setpoint (in Omnitemp) > 102 (66) Programming Thursday day time (15 minute increments) > 103 (67) Programming Thursday day cool setpoint (in Omnitemp) > 104 (68) Programming Thursday day heat setpoint (in Omnitemp) > 105 (69) Programming Thursday evening time (15 minute increments) > 106 (6A) Programming Thursday evening cool setpoint (in Omnitemp) > 107 (6B) Programming Thursday evening heat setpoint (in Omnitemp) > 108 (6C) Programming Thursday night time (15 minute increments) > 109 (6D) Programming Thursday night cool setpoint (in Omnitemp) > 110 (6E) Programming Thursday night heat setpoint (in Omnitemp) > 111 (6F) Programming Friday morning time (15 minute increments) > 112 (70) Programming Friday morning cool setpoint (in Omnitemp) > 113 (71) Programming Friday morning heat setpoint (in Omnitemp) > 114 (72) Programming Friday day time (15 minute increments) > 115 (73) Programming Friday day cool setpoint (in Omnitemp) > 116 (74) Programming Friday day heat setpoint (in Omnitemp) > 117 (75) Programming Friday evening time (15 minute increments) > 118 (76) Programming Friday evening cool setpoint (in Omnitemp) > 119 (77) Programming Friday evening heat setpoint (in Omnitemp) > 120 (78) Programming Friday night time (15 minute increments) > 121 (79) Programming Friday night cool setpoint (in Omnitemp) > 122 (7A) Programming Friday night heat setpoint (in Omnitemp) > > Programming Occupancy: > 123 (7B) Programming Day Cool setpoint (in Omnitemp) > 124 (7C) Programming Day Heat setpoint (in Omnitemp) > 125 (7D) Programming Night Cool setpoint (in Omnitemp) > 126 (7E) Programming Night Heat setpoint (in Omnitemp) > 127 (7F) Programming Away Cool setpoint (in Omnitemp) > 128 (80) Programming Away Heat setpoint (in Omnitemp) > 129 (81) Programming Vacation Cool setpoint (in Omnitemp) > 130 (82) Programming Vacation Heat setpoint (in Omnitemp) > > Setup: > 131 (83) Program mode (0=None, 1=Schedule, 2=Occupancy) > 132 (84) Expansion baud (0=300, 1=100, 42=1200, 54=2400, 126=9600) > 133 (85) Days until filter reminder appears > 134 (86) Humidity Setpoint > 135 (87) Dehumidify Setpoint > 136 (88) Dehumidifier output options (0=Not used, 1=Standalone, 2= variable > speed fan) > 137 (89) Humidifier output (0=Not used, 1=Standalone) > 138 (8A) Minutes out of 20 that fan is on during cycle (1-19) > 139 (8B) Backlight settings (0=Off, 1=On, 2=Auto) > 140 (8C) Backlight color (0-100) > 141 (8D) Backlight intensity (1-10) > 142 (8E) Selective message enable/disable > 143 (8F) Minimum on time for cool (2-30) > 144 (90) Minimum off time for cool (2-30) > 145 (91) Minimum on time for heat (2-30) > 146 (92) Minimum off time for heat (2-30) > 147 (93) System type (0=Heat Pump, 1=Conventional, 2=Dual Fuel) > 148 (94) Reserved > 149 (95) End of vacation date: day > 150 (96) > 151 (97) End of vacation date: hour > 152 (98) Hours HVAC used in Week 0 > 153 (99) Hours HVAC used in Week 1 > 154 (9A) Hours HVAC used in Week 2 > 155 (9B) Hours HVAC used in Week 3 > 156 (9C) Reserved > 157 (9D) Reserved > 158 (9E) Enable/disable individual temp sensors > 159 (9F) Number of cool stages > 160 (A0) Number of heat stages > 161 (A1) Current occupancy mode (0=Day, 1=Night, 2=Away, 3=Vacation) > 162 (A2) Current indoor humidity > 163 (A3) Cool setpoint for vacation mode (51-91) > 164 (A4) Heat setpoint for vacation mode (51-91) > > Energy: > 165 (A5) Displayed price of energy with medium level energy > 166 (A6) Displayed price of energy with high level energy > 167 (A7) Displayed price of energy with critical level energy > 168 (A8) Sensitivity setting for proximity sensor (0-99) > 169 (A9) Energy level as set by the meter > 170 (AA) Current energy total cost, upper byte > 171 (AB) Current energy total cost, lower byte > 172 (AC) STRING ASCII display for first load control module > 173 (AD) STRING ASCII display for second load control module > 174 (AE) STRING ASCII display for third load control module > 175 (AF) STRING ASCII display for Energy message > 176 (B0) STRING ASCII display for emergency broadcast message (not > implemented) > 177 (B1) STRING ASCII display for custom message (not implemented) > 178 (B2) STRING ASCII display for energy graph title bar > 179 (B3) STRING ASCII display for energy graph x axis > 180 (B4) STRING ASCII display for energy graph y axis > 181 (B5) STRING ASCII display for long messages (not implemented) > 182 (B6) graph bar max height, upper byte > 183 (B7) graph bar max height, lower byte > 184 (B8) graph bar one value, upper byte > 185 (B9) graph bar one value, lower byte > 186 (BA) graph bar two value, upper byte > 187 (BB) graph bar two value, lower byte > 188 (BC) graph bar three value, upper byte > 189 (BD) graph bar three value, lower byte > 190 (BE) graph bar four value, upper byte > 191 (BF) graph bar four value, lower byte > 192 (C0) Status and enable/disable of each load control module > > Sensors: > 200 (C8) Current temperature of sensor 3 > 201 (C9) Current temperature of sensor 4 > 202 (CA) Reserved > > Wireless: > 224 (E0) Wireless MAC address byte 1 > 225 (E1) Wireless MAC address byte 2 > 226 (E2) Wireless MAC address byte 3 > 227 (E3) Wireless MAC address byte 4 > 228 (E4) Wireless MAC address byte 5 > 229 (E5) Wireless MAC address byte 6 > 230 (E6) Wireless MAC address byte 7 > 231 (E7) Wireless MAC address byte 8 > 232 (E8) Wireless firmware version integer place > 233 (E9) Wireless firmware version decimal place > 234 (EA) Wireless strength (0-100) > 235 (EB) Wireless buzzer enable or disable > 236 (EC) Wireless IP address byte 1 > 237 (ED) Wireless IP address byte 2 > 238 (EE) Wireless IP address byte 3 > 239 (EF) Wireless IP address byte 4 > 253 Reserved > 254 Reserved > =cut > > use strict; > > package Omnistat; > > sub omnistat_debug { > my ($mesg) = @_; > > print "$::Time_Date: $mesg\n" if $::Debug{omnistat}; > } > > # Load Time::HiRes if it's available > use vars qw($USLEEP); > eval { require Time::HiRes }; > if (not $@) { > Time::HiRes->import( qw(usleep) ); > omnistat_debug("Omnistat found Time::Hires, will use usleep in > omni_sleep"); > $USLEEP=1; > } else { > omnistat_debug("Omnistat did NOT find Time::Hires, will NOT use usleep in > omni_sleep"); > warn("Omnistat works much better with Time::HiRes, install it if you > can"); > $USLEEP=0; > } > > # -------------------------------------------------------------- > # -------------------- START OF SUBROUTINES -------------------- > # -------------------------------------------------------------- > > @Omnistat::ISA = ('Serial_Item'); > > sub omni_sleep() { > $USLEEP ? usleep($_[0]) : sleep(int($_[0] + 0.99999)); > } > > > # My guess is that most people would want to have temperature logging, but > you can turn it off with > # Omnistat_stat_log=0 in mh.private.ini -- merlin > sub omnistat_log { > my ($mesg, $level) = @_; > my $loglevel = $main::config_parms{Omnistat_stat_log}; > > $loglevel = 1 if (not defined $loglevel); > $level = 1 if (not defined $level); > > &::print_log("log=logs/thermostat.log $mesg") if ($level <= $loglevel); > } > > > sub is_hex { > # make sure we got proper hex and not a number or some other error > return ($_[0] =~ /^0x[0-9a-fA-F][0-9a-fA-F]$/); > } > > # ******************************************************** > # * Get address for this thermostat from the argument. > # * Address defaults to 1 if no argument. > # ******************************************************** > sub new { > my ( $class, $address ) = @_; > $address = 1 unless $address; > my $self = {}; > $$self{address} = $address; > $$self{cache} = {}; > # when the cache was last updated > $$self{cache_updatetime} = {}; > # per register override of how long we want to cache > $$self{cache_agelimit} = {}; > > # **************************** IMPORTANT ***************************** > # Cache values can be increased by up to 10% at runtime to avoid having > # a bunch of variables have their cache expire at the same time and cause > # hangs due to synchronized reads. -- merlin > # **************************** IMPORTANT ***************************** > > # by default registers are cached 1h # CACHE_TIMEOUT_DEFAULT > $$self{cache_defaultagelimit} = 3600; > # These are important registers for which we don't want to cache data 1 > hour > # or registers that never change and we cache longer > # (the rest default to $$self{cache_defaultlifetime}) > > # filter reminder, or type of thermostat, and all the programming > setpoints is good enough once a day > foreach my $reg (0x15..0x38, 0x0f, 0x49) { > $$self{cache_agelimit}{$reg} = 3600 * 24; # CACHE_TIMEOUT_DAILY > } > # setpoints and modes are cached 53 secs so that they are pretty much > # guaranteed to be updated once a minute even with the random +10% offset > foreach my $reg (0x3b .. 0x3f) { > $$self{cache_agelimit}{$reg} = 54; # CACHE_TIMEOUT_SHORT > } > # temperatures and what the stat outputs, we only cache 10 seconds > foreach my $reg (0x40, 0x44, 0x48) { > $$self{cache_agelimit}{$reg} = 10; # CACHE_TIMEOUT_VERYSHORT > } > > #The next line is an experiment with http_server.pm to allow other > objects to show up in the web interface > $$self{html_text} = "<a href=/hai/omnistat_web.pl>Set > Thermostat</a>"; > > omnistat_debug("[HVAC] Omnistat[$$self{address}] object created"); > bless $self,$class; > > # This is to work around a timing bug where the first query doesn't get a > proper ack > # we just "prime" the device and connection by making one query to it > where we ignore the answer > # no idea why this is needed, but it works for me and things are stable > afterwards -- merlin > $self->{'PRIME'}=1; > $self->send_cmd("0x01 0x20 0x40 0x01 0x62", 1); > $self->{'PRIME'}=0; > > return $self; > } > > # ************************************* > # * Add the checksum to the cmd array. > # ************************************* > sub add_checksum { > my (@array) = @_; > my @modarr = @array; > my $value = 0; > foreach (@modarr) { > s/^0x//g; > $_ = hex($_); > $value = $value + $_; > } > $value = $value % 256; > $array[ $#array + 1 ] = sprintf( "0x%02x", $value ); > return @array; > } > > # ************************************** > # * Send the command to the thermostat. > # ************************************** > # I added very basic support of acknowledgments by just checking that we > get one byte back that contains 0x80. It is totally > # incomplete, but better than nothing -- merlin > # FIXME?: the spec says we're supposed to listen to the reply, and resend > messages after an inter message timeout > # of 1.25s, that said it seems to work ok with the current timings and > should work without resends unless your > # serial cable wires are crap (use CAT-5) and/or very long -- merlin > # > # FIXME, 2-3 times, I had this bug right after starting mh: > # 25/07/2009 10:15:30 : Omnistat[2]->read_cached_reg: reg=0x3b not cached, > fetching > # 25/07/2009 10:15:30 : Omnistat->send_cmd string=0x02 0x20 0x3b 0x0e 0x6b > (with 833325,us reply delay (14 char(s) to read back)) > # 25/07/2009 10:15:30 : Omnistat->send_cmd got reply "0xff 0x82 0xf2 0x3b > 0x8b 0x64 0x03 0x00 0x00 0x78 0x1e 0x0f 0x0a 0x60 0x00 0x00 0x02 0x00 0xb2 " > # The 0xff in the reply didn't belong. No idea where it came from, > especially because it was the first command and reply. > # for now it makes the code die, the command fail and then things restart > and continue -- merlin > sub send_cmd { > # if you want to default to a full 2sec wait, pass '-1' as reply_count > my ($self, $reply_count, @string) = @_; > my $addr = $$self{address}; > my $cmd = ''; > # We try to calculate how long we wait for the reply, or default to 2 sec > (2M usec) > my $reply_wait = 2000000; > > # some experimentation shows on my system that we need to wait 0.3sec + > 0.1sec for each 3 registers returned --merlin > # 300bps is 30cps, which does equate to 0.0333333s per character. From > experimentation, one needs to wait an extra > # 11 characters in addition to the payload you're expecting back to get > reliable replies (10 almost works but causes > # occasional corruption due to timings). -- merlin > # (12+ chars wait instead of 11 might be needed for you. Please increase & > let me know if this is too short for you). > my $REPLY_BASE_DELAY = 14; > > # While we don't get as many bytes as we sent if we were to set several > registers in a row (which is not currently > # supported in this code), the spec says to wait 30ms per register set, so > the wait time ends up being able the same > # when setting data than when polling it. > $reply_wait = (33333*($reply_count + $REPLY_BASE_DELAY)) if ($reply_count > > -1); > > omnistat_debug("Omnistat[$$self{address}]->send_cmd string=@string (with > $reply_wait,us reply delay ($reply_count char(s) to read back))"); > foreach my $byte (@string) { > $byte =~ s/0x//; # strip off the 0x > $cmd = $cmd . pack "H2", $byte; # pack it into 8 bits > } > > # send it to thermostat > #omnistat_debug("Omnistat->send_cmd will write $cmd"); > $main::Serial_Ports{Omnistat}{object}->write($cmd); > > # need to wait a bit for the reply > # FIXME: sleep is bad, especially if you're not using usleep from > Time::Hires, the only proper way to do this would be to > # have a request queue where one command gets processed every 1-2 seconds, > but that would be a big rewrite -- merlin > &Omnistat::omni_sleep($reply_wait); > > # read response > &main::check_for_generic_serial_data('Omnistat'); > my $temp = $main::Serial_Ports{Omnistat}{data}; > $main::Serial_Ports{Omnistat}{data} = ''; > my $len = length($temp); > $temp = unpack( "H*", $temp ); > my ($i); > my $rcvd = ''; > my $ack_byte = 0x80 + $addr; > for ( $i = 0 ; $i < $len ; $i++ ) { > $rcvd = $rcvd . sprintf( "0x%s ", substr( $temp, $i * 2, 2 ) ); > } > my $rcvd_ack = hex(substr($rcvd, 0 , 4)); > > if ($self->{'PRIME'}) > { > omnistat_debug("Omnistat[$$self{address}]->send_cmd skipping error > check and return value during prime"); > return; > } > > # FIXME? Those two dies aren't ideal, but it happens that you get > corruption or bad data on a reply. > # Expected for 01 20 3b 0e 6a is something like > # 0x81 0xf2 0x3b 0x7c 0x71 0x03 0x00 0x00 0x7d 0x07 0x1e 0x0f 0x00 0x00 > 0x00 0x02 0x0c 0x5d > # but I have seen replies like > # 0x03 0xf2 0x3b 0x7c 0x71 0x03 0x00 0x00 0x7d 0x00 0x1c 0x0f 0x00 0x00 > 0x00 0x02 0x0c 0x54 (0x81 ack byte is wrong) > # or sync issues like > # 0x64 0x82 0xf2 0x3b 0x8e 0x64 0x00 0x00 0x00 0x7d 0x20 0x29 0x0f 0x60 > 0x00 0x00 0x00 0x00 0xd6 (0x64 shouldn't be here) > # On my system, this error only seems to happen soon after startup and > doesn't seem to happen later -- merlin > die "$::Time_Date: Omnistat[$$self{address}]->send_cmd did not get ack > reply to command @string ($rcvd)" unless (length($rcvd) > 3); > die "$::Time_Date: Omnistat[$$self{address}]->send_cmd did not get > expected first byte (".sprintf("0x%02x",$ack_byte).") in ack reply to > command @string (got ".sprintf("0x%02x", $rcvd_ack)." in $rcvd)" unless > ($rcvd_ack eq $ack_byte); > omnistat_debug("Omnistat[$$self{address}]->send_cmd got reply \"$rcvd\""); > > return $rcvd; > } > > # ****************************** > # * check for returned data. > # ****************************** > sub check_for_data { > &main::check_for_generic_serial_data('Omnistat'); > } > > # ************************************************* > # * Set the thermostat clock to the current time. > # ************************************************* > sub set_time { > my ($self) = @_; > my $wday; > my $addr = $$self{address}; > > omnistat_debug("Omnistat[$$self{address}] -> Setting time/day of week"); > my @cmd = qw(0x01 0x41 0x41); > > #set the time > $cmd[0] = sprintf( "0x%02x", $addr ); > $cmd[3] = sprintf( "0x%02x", $::Second ); > $cmd[4] = sprintf( "0x%02x", $::Minute ); > $cmd[5] = sprintf( "0x%02x", $::Hour ); > @cmd = add_checksum(@cmd); > $self->send_cmd(0, @cmd); > > #set the weekday > $wday = $::Wday ? $::Wday - 1 : 6; > $self->set_reg( "0x3a", sprintf( "0x%02x", $wday) ); > } > > # ******************************************* > # * Set the display mode of the thermostat. > # ******************************************* > sub display { > my ($self) = @_; > my $addr = $$self{address}; > > #$main::config_parms{Omnistat_serial_port} > my $DISPLAY_BITS; > # Bit 0 > if ( $main::config_parms{Omnistat_celcius} ) { > $DISPLAY_BITS = 0; > } else { > $DISPLAY_BITS = 1; > } > # Bit 1 > if ( $main::config_parms{Omnistat_24hr} ) { > $DISPLAY_BITS = $DISPLAY_BITS + 2; > } > # Bit 2 > if ( $main::config_parms{Omnistat_non_program} ) { > $DISPLAY_BITS = $DISPLAY_BITS + 4; > } > # Bit 3 > if ( $main::config_parms{Omnistat_rtp_mode} ) { > $DISPLAY_BITS = $DISPLAY_BITS + 8; > } > # Bit 4 > if ( $main::config_parms{Omnistat_hide_clock} ) { > $DISPLAY_BITS = $DISPLAY_BITS + 16; > } > > $self->set_reg( "0x03", sprintf( "0x%02x", $DISPLAY_BITS ) ); > } > > # ********************************************* > # * Create the Omnistat device on serial port. > # ********************************************* > sub serial_startup { > &main::serial_port_create( 'Omnistat', > $main::config_parms{Omnistat_serial_port}, 300, 'none', 'raw' ); > &::MainLoop_pre_add_hook( \&Omnistat::check_for_data, 1 ); > } > > # ******************************** > # * Set the hold mode on or off. > # ******************************** > sub hold { > my ( $self, $state ) = @_; > my $new_hold; > my $cur_hold = $self->read_cached_reg("0x3f",1); > $state = lc($state); > > if ( $state eq "off" ) { > $new_hold = "0x00"; > } elsif ( $state eq "on" ) { > $new_hold = "0xff"; > } else { > print "$::Time_Date: Omnistat[$$self{address}]: Invalid Hold state: > $state\n"; > return; > } > > # obviously there is a small race condition here, if hold was changed in > the last minute from the panel, > # we could fail to set it when it needs to be, but that should be quite > rare, and avoiding all the repeated > # hold set to off before changing other values is worth it -- merlin > if ($cur_hold ne $new_hold) { > $self->set_reg( "0x3f", $new_hold ); > omnistat_debug("Omnistat[$$self{address}]->hold: Hold set to $state"); > } else { > omnistat_debug("Omnistat[$$self{address}]->hold: Hold stays at $state"); > } > } > > # ************************************************************* > # * Translate Temperature between Fahrenheit/Celcius and Omni values. > # ************************************************************* > sub translate_temp { > my ($settemp) = @_; > my ($omnitemp); > > # this is a good place to catch a 14 reg read that happens in read_group1 > extended, being off by one character, or returning > # bogus 0's. > die "$::Time_Date: Omnistat->translate_temp got an input temperature of 0 > = -40F/C, this typically means serial port corruption, bad... You may want > to increase REPLY_BASE_DELAY" if (not $settemp or $settemp eq "0x00"); > > # Calculate conversion mathematically rather than using a table so all > temps will work (needed for outside temperature) > if ( substr( $settemp, 0, 2 ) eq '0x' ) > { # if it starts with 0x, reverse xlate > $omnitemp = hex($settemp); > $omnitemp = -40 + .5 * $omnitemp; #degrees Celcius > if ( !( $main::config_parms{Omnistat_celcius} ) ) { > $omnitemp = 32 + 1.8 * $omnitemp; # degrees Fahrenheit > $omnitemp = int( $omnitemp + .5 * ( $omnitemp <=> 0 ) ); #round > } > } else { # xlate from Fahrenheit/Celcius > if ( !( $main::config_parms{Omnistat_celcius} ) ) { > $omnitemp = ( $settemp - 32 ) / 1.8; #Fahrenheit to Celcius > } > $omnitemp = ( $omnitemp + 40 ) / .5; #omnistat > degrees > $omnitemp = int( $omnitemp + .5 * ( $omnitemp <=> 0 ) ); #round > $omnitemp = sprintf( "0x%02x", $omnitemp ); > } > > omnistat_debug("Omnistat: Converted $settemp to $omnitemp"); > return $omnitemp; > } > > # ************************************************************* > # * Translate Time between readable and Omni values. > # ************************************************************* > sub translate_time { > my ($settime) = @_; > my ( $hours, $minutes, $ampm ); > my ($omnitime); > > if ( substr( $settime, 0, 2 ) eq '0x' ) { #Translate omnitime to readable > time > if ( $settime eq '0x60' ) > { #if it's set to 24hrs past midnight, time is blank > $omnitime = ''; > } else { > $minutes = > hex($settime) * > 15; #Omnistat is stored as 15 minute time periods pas midnight > $hours = int( $minutes / 60 ); > $minutes = $minutes % 60; #minutes past hour > if ( $main::config_parms{Omnistat_24hr} ) { > > #Translate to 24hr time > $omnitime = sprintf( '%02s:%02s', $hours, $minutes ); > } else { > > #Translate omni to AM/PM > if ( $hours == 0 ) { > $hours = 12; > $ampm = 'PM'; > } elsif ( $hours > 12 ) { > $ampm = 'PM'; > $hours -= 12; > } else { > $ampm = 'AM'; > } > $omnitime = sprintf( '%02s:%02s %s', $hours, $minutes, $ampm ); > } > } > } else { #Translate readable to omnistat time > if ( $settime eq '0' ) { #set to 0 to clear time, or 24:00 if using 24h > time > $omnitime = '0x60'; > } elsif ( $main::config_parms{Omnistat_24hr} ) { > #convert 24h time > if ( $settime =~ /^([0-1][0-9]|[2][0-4]):([0-5][0-9])$/ ) { > > #valid time > $hours = $1; > $minutes = $2; > $minutes = $minutes + $hours * 60; > $omnitime = $minutes / 15; > $omnitime = sprintf( "0x%02x", $omnitime ); > } else { > #invalid time > $omnitime = ''; > } > } else { > #convert am/pm time > if ( $settime =~ /^(1[0-2]|0?[1-9]):([0-5][0-9]) *(AM|PM)$/ ) { > > #valid time > $hours = $1; > $minutes = $2; > $ampm = $3; > > #PM we may need to add 12 hours (unless it's midnight), AM is > already right > if ( $ampm eq 'PM' ) { > if ( $hours == 12 ) { > $hours = 0; > } else { > $hours = $hours + 12; > } > } > > $minutes = $minutes + $hours * 60; > $omnitime = $minutes / 15; > > $omnitime = sprintf( "0x%02x", $omnitime ); > } else { > #invalid time > $omnitime = ''; > } > } > } > > return $omnitime; > } > > # ***************************************************** > # * Read and convert the bits in reg 0x48 (HVAC output) > # ***************************************************** > sub translate_stat_output { > my ( $self, $reg48 ) = @_; > > die "Omnistat::translate_stat_output got non hex value in $reg48" unless > (is_hex($reg48)); > # see reg 0x48 / output register at the top of this file > my $output = "off"; > $output = "fan" if (hex($reg48) & 8); > > # if stage 1 and stage 2 heat/cool are off, return here > #&::print_log("pass1: reg48: $reg48, $output"); > return $output if (not hex($reg48) & (4+16)); > > $output .= "/auxheat" if (hex($reg48) & 2); > > $output .= (hex($reg48) & 1) ? "/heat" : "/cool"; > $output .= "/stage2" if (hex($reg48) & 16); > #&::print_log("pass2: reg48: $reg48, $output"); > return $output; > } > > > # ***************************************************************** > # * Change the mode of the thermostat between off/auto/heat/cool. > # ***************************************************************** > sub mode { > my ( $self, $state ) = @_; > $state = lc($state); > > #TODO: Should heat/cool/auto turn on hold? > > omnistat_debug("Omnistat[$$self{address}] -> Mode $state"); > my $addr = $$self{address}; > my @cmd; > if ( $state eq "off" ) { > $self->set_reg( "0x3d", "0x00" ); > } elsif ( $state eq "heat" ) { > $self->set_reg( "0x3d", "0x01" ); > } elsif ( $state eq "cool" ) { > $self->set_reg( "0x3d", "0x02" ); > } elsif ( $state eq "auto" ) { > $self->set_reg( "0x3d", "0x03" ); > } elsif ( $state eq "program_heat" ) { > $self->set_reg( "0x3d", "0x03" ); > $self->set_reg( "0x3f", "0x00" ); > } elsif ( $state eq "program_cool" ) { > $self->set_reg( "0x3d", "0x03" ); > $self->set_reg( "0x3f", "0x00" ); > } elsif ( $state eq "program_auto" ) { > $self->set_reg( "0x3d", "0x03" ); > $self->set_reg( "0x3f", "0x00" ); > } else { > print "$::Time_Date: Omnistat: Invalid Mode state: $state\n"; > } > print "$::Time_Date: Omnistat: Invalid Mode state: $state\n"; > } > > # ************************** > # * Restore the heat and cool setpoints to what they would be if the > schedule were in effect > # ************************** > sub restore_setpoints { > my ($self) = @_; > my $point = 0; > my $time; > my $day; > my $register; > my $setpointnum; > my $daynum; > > # this does not work if the stat is in hold mode, and we assume that > calling this means we want > # to un-hold > $self->hold("off"); > > # This touches a lot of registers, so it's quicker to cache them all once. > # Unfortunately, we can only read 14 registers at a time, so we'll read > 3x12 > # We don't store the result, this is just to prime the cache in case it > wasn't. > $self->read_cached_reg("0x15", 12); > $self->read_cached_reg("0x21", 12); > $self->read_cached_reg("0x2d", 12); > > #Determine the day (weekday,sat,sun) > $day = $::Wday ? $::Wday - 1 : 6; > > #Determine the time > $time = > ( $::Hour * 4 ) + ( $::Minute / 15 ) + ( $::Second / 60 ); #Omnistat > time > > #Determine the day > if ( $day == 6 ) { # Sunday > $register = 0x36; # Sunday night time > } elsif ( $day == 5 ) { # Saturday > $register = 0x2a; # Saturday night time > } else { # Weekday > $register = 0x1e; # Weekday night time > } > > # Check for setpoints for that day, need to consider what time it is > for ( $setpointnum = 0 ; $setpointnum < 4 ; $setpointnum++ ) { > # FIXME: make sure I didn't break this (test it) and remove my FIXME -- > merlin > if ( hex($self->read_cached_reg( sprintf( "0x%02x", $register - 3 * > $setpointnum))) < $time ) { > $point = $register - 3 * $setpointnum; > last; > } > } > > #Check for setpoints on previous days, don't need to consider the time, > any setpoint will do > if ( $point == 0 ) { > > #Loop days > for ( $daynum = 0 ; $daynum < 3 ; $daynum++ ) { > if ($day > 0 && $day < 5 && $daynum == 0) { > #Weekday, previous day is also a weekday for first loop > } > else { > #Get the previous day > $register = $register - 12; > } > > if ( $register < 30 ) { $register = 54; } #Previous to weekday is > sunday > > #Loop setpoints > for ( $setpointnum = 0 ; $setpointnum < 4 ; $setpointnum++ ) { > # FIXME: make sure I didn't break this (test it) and remove my FIXME > -- merlin > if ( hex($self->read_cached_reg( sprintf( "0x%02x", $register - 3 * > $setpointnum))) != 96 ) > { > #If the setpoint has a time set, use the setpoint > $point = $register - 3 * $setpointnum; > last; > } > } > } > } > > if ( $point != 0 ) { > my $heat_sp = $self->read_cached_reg( sprintf( "0x%02x", $point + 2)); > my $cool_sp = $self->read_cached_reg( sprintf( "0x%02x", $point + 1)); > > # FIXME: make sure I didn't break this (test it) and remove my FIXME -- > merlin > # Set the setpoints (setting the registers avoids converting the temp > only to convert it back) > print "$::Time_Date: Omnistat: Heat Set to " . > &Omnistat::translate_temp($heat_sp) . "\n"; > print "$::Time_Date: Omnistat: Cool Set to " . > &Omnistat::translate_temp($cool_sp) . "\n"; > $self->set_reg( "0x3c", $heat_sp ); > $self->set_reg( "0x3b", $cool_sp ); > } > } > > > > # ************************************ > # * Set the fan mode to on/off/auto. > # ************************************ > sub fan { > my ( $self, $state ) = @_; > $state = lc($state); > my $addr = $$self{address}; > my @cmd; > > omnistat_debug("Omnistat[$$self{address}] -> Fan $state"); > if ( $state eq "on" ) { > $self->set_reg( "0x3e", "0x01" ); > } elsif ( $state eq "auto" ) { > $self->set_reg( "0x3e", "0x00" ); > } elsif ( $state eq "cycle" ) { > $self->set_reg( "0x3e", "0x02" ); > } else { > print "$::Time_Date: Omnistat: Invalid Fan state: $state\n"; > } > } > > # ************************************** > # * Set the occupancy mode > # ************************************** > sub set_occupancy_mode { > my ( $self, $state ) = @_; > $state = lc($state); > my $addr = $$self{address}; > my @cmd; > > omnistat_debug("Omnistat[$$self{address}] -> occupancy $state"); > if ( $state eq "day" ) { > $self->set_reg( "0xa1", "0x00" ); > } elsif ( $state eq "night" ) { > $self->set_reg( "0xa1", "0x01" ); > } elsif ( $state eq "away" ) { > $self->set_reg( "0xa1", "0x02" ); > } elsif ( $state eq "vacation" ) { > $self->set_reg( "0xa1", "0x03" ); > } else { > print "$::Time_Date: Omnistat: Invalid Occupancy state: $state\n"; > } > } > > # ************************************** > # * Set the program mode > # ************************************** > sub set_program_mode { > my ( $self, $state ) = @_; > $state = lc($state); > my $addr = $$self{address}; > my @cmd; > > omnistat_debug("Omnistat[$$self{address}] -> program $state"); > if ( $state eq "none" ) { > $self->set_reg( "0x83", "0x00" ); > } elsif ( $state eq "schedule" ) { > $self->set_reg( "0x83", "0x01" ); > } elsif ( $state eq "occupancy" ) { > $self->set_reg( "0x83", "0x02" ); > } else { > print "$::Time_Date: Omnistat: Invalid Program state: $state\n"; > } > } > > # ************************** > # * Set the cool setpoint. > # ************************** > sub cool_setpoint { > my ( $self, $settemp ) = @_; > # hold has to be removed for this command to go through. > $self->hold('off'); > $self->set_reg( "0x3b", &Omnistat::translate_temp($settemp) ); > $self->hold('on') unless $main::config_parms{Omnistat_set_does_not_hold}; > } > > # ************************** > # * Set the heat setpoint. > # ************************** > sub heat_setpoint { > my ( $self, $settemp ) = @_; > # hold has to be removed for this command to go through. > $self->hold('off'); > $self->set_reg( "0x3c", &Omnistat::translate_temp($settemp) ); > $self->hold('on') unless $main::config_parms{Omnistat_set_does_not_hold}; > } > > # ************************** > # * Set the day cool setpoint. > # ************************** > sub day_cool_setpoint { > my ( $self, $settemp ) = @_; > # hold has to be removed for this command to go through. > #$self->hold('off'); > $self->set_reg( "0x7b", &Omnistat::translate_temp($settemp) ); > #$self->hold('on') unless $main::config_parms{Omnistat_set_does_not_hold}; > } > > # ************************** > # * Set the day heat setpoint. > # ************************** > sub day_heat_setpoint { > my ( $self, $settemp ) = @_; > # hold has to be removed for this command to go through. > $self->hold('off'); > $self->set_reg( "0x7c", &Omnistat::translate_temp($settemp) ); > $self->hold('on') unless $main::config_parms{Omnistat_set_does_not_hold}; > } > > # ************************** > # * Set the night cool setpoint. > # ************************** > sub night_cool_setpoint { > my ( $self, $settemp ) = @_; > # hold has to be removed for this command to go through. > $self->hold('off'); > $self->set_reg( "0x7d", &Omnistat::translate_temp($settemp) ); > $self->hold('on') unless $main::config_parms{Omnistat_set_does_not_hold}; > } > > # ************************** > # * Set the night heat setpoint. > # ************************** > sub night_heat_setpoint { > my ( $self, $settemp ) = @_; > # hold has to be removed for this command to go through. > $self->hold('off'); > $self->set_reg( "0x7e", &Omnistat::translate_temp($settemp) ); > $self->hold('on') unless $main::config_parms{Omnistat_set_does_not_hold}; > } > > # ************************** > # * Set the away cool setpoint. > # ************************** > sub away_cool_setpoint { > my ( $self, $settemp ) = @_; > # hold has to be removed for this command to go through. > $self->hold('off'); > $self->set_reg( "0x7f", &Omnistat::translate_temp($settemp) ); > $self->hold('on') unless $main::config_parms{Omnistat_set_does_not_hold}; > } > > # ************************** > # * Set the away heat setpoint. > # ************************** > sub away_heat_setpoint { > my ( $self, $settemp ) = @_; > # hold has to be removed for this com... [truncated message content] |
From: Mickey A. <AO...@mi...> - 2010-12-05 22:33:11
|
Once I got the wiring right, there were no problems with communication. I also put a sniffer on the line when I was troubleshooting. If I recall, the wiring diagrams on the web were wrong...two wires were reversed and there was only a note on a bulletin board that mentioned it. Sorry that I didn't document all this when I did it...it was right around the time when there were questions about conflict of interest/intellectual property rights with my employer due to areas we are starting to get into. On 12/5/10 9:39 AM, Karl Suchy wrote: > Mickey, > > Thanks for sending that out. I will work that in once I get the > OmniStat Communicating with the computer. Do you recall having to do > anything special with the OmniStat 2 to get it to talk? My version > does not have a communication jumper on the pcb, instead there was a > wire jumper between the "N/C" and "black" pins as labeled on the pcb. > In settings it is set up for serial at 300 baud. I have tried using > Realterm to send commands to the Stat with no response back. > > Thanks > Karl > > On Sat, Dec 4, 2010 at 10:15 PM, Mickey Argo <mh...@ar... > <mailto:mh...@ar...>> wrote: > > I didn't exactly code this for wide distribution...there are > complications with my employer and areas they might be working on > in the near future. If you want to clean this up for inclusion > into the trunk code, please feel free and take the credit! > Compare the attached Omnistat.pm with the trunk version. > omnistat2.pl <http://omnistat2.pl> is in my code directory. I > have the following in my private ini: > > Omnistat_serial_port=COM1 > Omnistat_24hr=1 > # disable internal program > #Omnistat_non_program=[0,1] > Omnistat_no_stat_log=3 > Omnistat_stat_log=$config_parms{data_dir}/omnistatlog.txt > omnistat_allowed_errors = 999999999999 > hvac_allowed_errors = 999999999999 > show_all_errors = yes > > > Mickey Argo wrote: >> Karl, >> >> I have this working on my setup...I'll try to send out the code >> changes later today. I did add some new stuff, like changing the >> background color based on the outside air temperature. >> >> Mickey Argo >> >> On 12/3/10 12:11 PM, Karl Suchy wrote: >>> Thanks for the feed back. The Omnistat is in the correct >>> communication mode. I think I will start messing around with >>> Omnistat.pm and trying to send simple get and set commands and >>> see what I get back. >>> >>> Karl >>> >>> On Fri, Dec 3, 2010 at 12:46 PM, Marc MERLIN >>> <ma...@me... <mailto:ma...@me...>> wrote: >>> >>> On Fri, Dec 03, 2010 at 09:45:18AM -0800, Marc MERLIN wrote: >>> > On Fri, Dec 03, 2010 at 12:35:27PM -0500, Karl Suchy wrote: >>> > > I am trying to get a single Omnistat 2 functioning with >>> MisterHouse. Any >>> > > help will be greatly appreciated. >>> > > >>> > > When opening omnistat_setup_web.pl >>> <http://omnistat_setup_web.pl> I get the following error >>> message "http >>> > > error in http eval of omnistat_setup_web.pl >>> <http://omnistat_setup_web.pl>: 12/03/10 11:40:36 AM: >>> > > Omnistat[1]->send_cmd did not get ack reply to command >>> 01 20 3b 0e 6a () at >>> > > /opt/misterhouse/mh/bin/../lib/Omnistat.pm line 554, >>> line 216." >>> >>> I should add that '()' shows that no reply at all was >>> received from the >>> omnistat, so you may indeed have a basic communication problem. >>> >>> Marc >>> -- >>> "A mouse is a device used to point at the xterm you want to >>> type in" - A.S.R. >>> Microsoft is to operating systems & security .... >>> .... what McDonalds is >>> to gourmet cooking >>> Home page: http://marc.merlins.org/ >>> >>> ------------------------------------------------------------------------------ >>> Increase Visibility of Your 3D Game App & Earn a Chance To >>> Win $500! >>> Tap into the largest installed PC base & get more eyes on >>> your game by >>> optimizing for Intel(R) Graphics Technology. Get started >>> today with the >>> Intel(R) Software Partner Program. Five $500 cash prizes are >>> up for grabs. >>> http://p.sf.net/sfu/intelisp-dev2dev >>> ________________________________________________________ >>> To unsubscribe from this list, go to: >>> http://sourceforge.net/mail/?group_id=1365 >>> >>> >>> >>> ------------------------------------------------------------------------------ >>> Increase Visibility of Your 3D Game App& Earn a Chance To Win $500! >>> Tap into the largest installed PC base& get more eyes on your game by >>> optimizing for Intel(R) Graphics Technology. Get started today with the >>> Intel(R) Software Partner Program. Five $500 cash prizes are up for grabs. >>> http://p.sf.net/sfu/intelisp-dev2dev >>> >>> >>> ________________________________________________________ >>> To unsubscribe from this list, go to:http://sourceforge.net/mail/?group_id=1365 >>> >>> >> ------------------------------------------------------------------------ >> >> ------------------------------------------------------------------------------ >> What happens now with your Lotus Notes apps - do you make another costly >> upgrade, or settle for being marooned without product support? Time to move >> off Lotus Notes and onto the cloud with Force.com, apps are easier to build, >> use, and manage than apps on traditional platforms. Sign up for the Lotus >> Notes Migration Kit to learn more.http://p.sf.net/sfu/salesforce-d2d >> ------------------------------------------------------------------------ >> ________________________________________________________ To >> unsubscribe from this list, go to: >> http://sourceforge.net/mail/?group_id=1365 > > # Category=HVAC > > =begin comment > > Mickey Argo July 2010 > > This code is to run the Omnistat2 thermostats. I used code > credited to the following people; > Kent Noonan, Joel Davidson, Dan Arnold, Marc MERLIN > > Not done in this module; > Handle multiple thermostats > > > =cut > > > $omnistat2=new Omnistat; > > my $saved_setpoint_cool = 0; > my $saved_setpoint_heat = 0; > my $saved_program_mode = ''; > my $saved_HVAC_command = ''; > my $saved_indoor_temp = 0; > my $thermo_model = ''; > my $program_mode = ''; > my $hvac_return_data = ''; > #one time settings > if ($Reload or $Reread) { > #$omnistat2->cooling_anticipator('10'); Not used in two stage > #$omnistat2->heating_anticipator('10'); Not used in 2 stage > save_HVAC ("[PROGRAM] System had restarted"); > $omnistat2->cooling_cycle_time('8'); > $omnistat2->heating_cycle_time('8'); > $omnistat2->day_cool_setpoint('80'); > $omnistat2->day_heat_setpoint('68'); > $omnistat2->night_cool_setpoint('81'); > $omnistat2->night_heat_setpoint('66'); > $omnistat2->away_cool_setpoint('82'); > $omnistat2->away_heat_setpoint('65'); > $omnistat2->vaca_cool_setpoint('84'); > $omnistat2->vaca_heat_setpoint('63'); > $omnistat2->outdoor_temp($Weather{TempOutdoor}); > $hvac_return_data = $omnistat2->get_stat_type; > save_HVAC ("[PROGRAM] Thermostat Model is $hvac_return_data"); > $hvac_return_data = $omnistat2->get_program_mode; > save_HVAC ("[PROGRAM] Program mode is $hvac_return_data"); > $hvac_return_data = $omnistat2->get_occupancy_mode; > save_HVAC ("[PROGRAM] Occupancy is set to $hvac_return_data"); > $hvac_return_data = $omnistat2->get_fan_mode; > save_HVAC ("[PROGRAM] Fan is set to $hvac_return_data"); > } > > if ($New_Day) { > $omnistat2->set_time; > } > > > > $v_omnistat_fan=new Voice_Cmd('Set Thermostat fan [on,auto,cycle]'); > if ($state = said $v_omnistat_fan) { > $omnistat2->fan($state); > speak("app=weather Setting HVAC fan to $state"); > } > $v_omnistat_hold=new Voice_Cmd('Set Thermostat hold [on,off]'); > if ($state = said $v_omnistat_hold) { > $omnistat2->hold($state); > } > $v_omnistat_mode=new Voice_Cmd('Set Thermostat mode > [off,heat,cool,auto]'); > if ($state = said $v_omnistat_mode) { > $omnistat2->mode($state); > speak("app=weather Setting HVAC mode to $state"); > } > $v_omnistat_cool_sp=new Voice_Cmd("Set Thermostat cool setpoint to > [74,76,78,79,80,81,82,83]"); > if ($state = said $v_omnistat_cool_sp) { > $omnistat2->cool_setpoint($state); > speak("app=weather Setting HVAC cool set point to $state > degrees"); > } > $v_omnistat_heat_sp=new Voice_Cmd("Set Thermostat heat setpoint to > [65,66,67,68,69,70,71,72]"); > if ($state = said $v_omnistat_heat_sp) { > $omnistat2->heat_setpoint($state); > speak("app=weather Setting HVAC heat set point to $state > degrees"); > } > $v_omnistat_get_occupancy=new Voice_Cmd("What is the occupancy mode"); > if ($state = said $v_omnistat_get_occupancy) { > $hvac_return_data = $omnistat2->get_occupancy_mode; > speak("app=weather The occupancy mode is $hvac_return_data"); > } > $v_omnistat_set_occupancy=new Voice_Cmd("Set occupancy mode to > [day,night,away,vacation]"); > if ($state = said $v_omnistat_set_occupancy) { > $omnistat2->set_occupancy_mode($state); > save_HVAC ("[PROGRAM] System occupancy mode set to $state"); > speak("app=weather Setting occupancy mode to $state"); > } > $v_omnistat_set_program_mode=new Voice_Cmd("Set program mode to > [none,schedule,occupancy]"); > if ($state = said $v_omnistat_set_program_mode) { > $omnistat2->set_program_mode($state); > save_HVAC ("[PROGRAM] System program mode set to $state"); > speak("app=weather Setting program mode to $state"); > } > > $v_omnistat_background=new Voice_Cmd("Set Thermostat background to > [Blue,Green,Purple,Red,Orange,Yellow]"); > if ($state = said $v_omnistat_background) { > my $background_hex = "0x00"; > if ($state = 'Blue'){ > $background_hex = "0x44"; > } elsif ($state = 'Green'){ > $background_hex = "0x25"; > } elsif ($state = 'Purple'){ > $background_hex = "0x5a"; > } elsif ($state = 'Red'){ > $background_hex = "0x01"; > } elsif ($state = 'Orange'){ > $background_hex = "0x03"; > } elsif ($state = 'Yellow'){ > $background_hex = "0x05"; > } > $omnistat2->set_reg("0x8c", $background_hex); > } > > > # update data once a minute, log changes only. > if ($New_Minute) { > # we make the extended group1 call that also retreives the > stat's output status > my ($cool_sp, $heat_sp, $mode, $fan, $hold, $temp, $output) > = $omnistat2->read_group1("true"); > > my $stat_type = $omnistat2->get_stat_type; > # This mashes $hold and $mode together from registers > cached in the group1 call and outputs a combined string > $mode = $omnistat2->get_mode; > > #Save temps every minute > save_HVAC ("[TEMP-IN] $temp"); > # save_HVAC ("[TEMP-OUT] $Weather{TempOutdoor}"); > > #Update the thermostat data > $omnistat2->outdoor_temp($Weather{TempOutdoor}); > > > #check and log changed data > if ($saved_setpoint_cool ne $cool_sp){ > save_HVAC ("[SETPOINT] Cool Setpoint has been > changed to $cool_sp"); > $saved_setpoint_cool = $cool_sp; > } > if ($saved_setpoint_heat ne $heat_sp){ > save_HVAC ("[SETPOINT] Heat Setpoint has been > changed to $heat_sp"); > $saved_setpoint_heat = $heat_sp; > } > if ($saved_HVAC_command ne $output){ > save_HVAC ("[HVAC] HVAC Command is now $output"); > $saved_HVAC_command = $output; > } > if ($saved_program_mode ne $mode){ > save_HVAC ("[HVAC] HVAC Mode is now $mode"); > $saved_program_mode = $mode; > } > } > > #Change the backlight > if (new_minute 15) { > my $background_color = "0x00"; > if ($Weather{TempOutdoor} >= 95){ > $background_color = "0x01"; #Red > } > elsif ($Weather{TempOutdoor} >= 85){ > $background_color = "0x05"; #Yellow > } > elsif ($Weather{TempOutdoor} >= 65){ > $background_color = "0x25"; #Green > } > elsif ($Weather{TempOutdoor} >= 55){ > $background_color = "0x5a"; #Purple > } > elsif ($Weather{TempOutdoor} < 55){ > $background_color = "0x44"; #Blue > } > else{ > $background_color = "0x03"; #Orange > } > $omnistat2->set_reg("0x8c", $background_color); > } > > sub normal_HVAC_control > { > $omnistat2->set_occupancy_mode('day'); > print_log "[HVAC] Setting HVAC to day setbacks" ; > save_HVAC ("[PROGRAM] Occupancy is set to day"); > } > > sub setback_HVAC_control { > $omnistat2->set_occupancy_mode('away'); > print_log "[HVAC] Setting HVAC to away setbacks"; > save_HVAC ("[PROGRAM] Occupancy is set to away"); > } > > sub setback_night_HVAC_control { > $omnistat2->set_occupancy_mode('night'); > print_log "[HVAC] Setting HVAC to night setbacks"; > save_HVAC ("[PROGRAM] Occupancy is set to night"); > } > > =begin comment > > # 132 columns max > 123456789112345678921234567893123456789412345678951234567896123456789712345678981234567899123456789012345678911234567892123456789312 > > # this file, if distributed separately, replaces > misterhouse/lib/Omnistat.pm > > Module for HAI RC-Series Electronic Communicating Thermostats > (Omnistat) > Specifically written with/for RC-80 but should work with any of them. > http://www.homeauto.com/Products/HAIAccessories/Omnistat/rc80.htm > > Newer Omnistat2 thermostats have a slightly different protocol and > may need > some work. They look nicer, but they are pricier (vs $50 for an > RC-80 on ebay) > and don't offer functionality that's useful to most people -- merlin > > > ################### > > Use these mh.ini parameters to enable this code: > Omnistat_serial_port=/dev/ttyUSB0 > > There are optional settings for the Omnistat for mh.private.ini: > If these settings aren't in mh.private.ini the default is 0 (false) > > # use celcius for temperatures > Omnistat_celcius=[0,1] > # use 24hour clock for times > Omnistat_24hr=[0,1] > # disable internal program > Omnistat_non_program=[0,1] > # Real Time Pricing mode > Omnistat_rtp_mode=[0,1] > # hide clock on thermostat > Omnistat_hide_clock=[0,1] > # You can set how much gets logged > Omnistat_no_stat_log=[0,1,2,3] > > # For debugging, add omnistat to debug in mh.private.ini, as in > debug=insteon,omnistat > # instead of debug=insteon > > This module is used 2 ways > 1) from a web interface > 2) from mh/code/public/omnistat.pl <http://omnistat.pl> which you > need to install in your code directory > > > ================================================================================ > TODOs > ================================================================================ > > TODO: Add hooks for caching instead of relying on pl file > TODO: ini parameter for range of registers to read (default to temp) > TODO: Adjust clock speed? Not sure if possible (reg 14), may need > to be done in pl > TODO: Modify set_reg to accept muliple registers (hasn't been > really needed so far) > TODO: Ini Parameter to turn on/off set outdoor temp (may be in pl) > TODO: The sleep situation has been much improved, but if someone > smart could replace > the sleep with a proper callback so as not to stall mh, that > would rule > > ================================================================================ > Changelog > ================================================================================ > > 2009/08/03 - Marc MERLIN > ======================== > - send_cmd is now a method too so that we can compare the return > value against $$self{addr} > - improved command ack parsing failure error reporting > - oops, got omnistat_log function to actually respect log_level > - added omnistat_debug function > - hold function now only sets hold if it's different from cached > value, this is because we get > frequent calls to hold off and want to avoid actually sending > them if hold was already off > - restore|cool|heat_setpoints now unhold the that before > programming it (or it won't work) and > then put it back on hold depending on the > Omnistat_set_does_not_hold setting in mh.private.ini > > > 2009/07/25 - Marc MERLIN > ======================== > - optimized sleep/wait in send_cmd to be as little as needed. It > is now as cheap to > read 2 registers separately as 10 registers in a row: 0.666s > (before, reading 2 registers separately took 4 seconds) > - NOTE: if you were calling send_cmd, you need to change your call > to prepend the number > of characters you expect back (this is needed by the timing > improvements) > - fixed get_stat_type method > - A lot more error handling and status reporting, including making > sure that you get all > the data back that you're supposed to get, and that set_reg > actually gets some kind of ack > - omnistat.pl <http://omnistat.pl> allows for temporarily changing > setpoints until the next schedule change with > Omnistat_set_does_not_hold=1 in mh.private.ini > - omnistat_log allows for logging stat data with > Omnistat_no_stat_log which defaults to 1 > - fixed an insidious bug in set_time that sent an integer for the > week instead of an hex string > perl helpfully converted that and then tried to H2 pack "5" which > is invalid and sent a short command > - added checks for set_reg and friends to make sure registers and > values are hex strings > - auto convertions in perl are too helpful and can bite you in the > butt, so all register values are now > required to be passed as 0xXX whether they might have worked > before, or not > > > 2009/07/22 - Marc MERLIN > ======================== > - ripped out old one register update per minute cache > - added on demand cache and cache prefetching for setpoints and > temperature > - fixed bugs / cleanups > - merged read_group1 with read_reg > - fixed state change messages on multi reg fetches > - made use of caching functions strongly encouraged :) > - added important get_stat_output method to actually know what the > stat is telling > your HVAC system to do > - get_stat_model function > > > Dan Arnold May 2009 > =================== > Added state processing > > NOTE: State changes will not take effect until all registers have > been cached > -> no more -- merlin > All of the states that may be set: > all_registers_cached: All of the registers have been read into > the cache, you can read any register without penalty > filter_reminder: Filter reminder has expired > -> obsolete -- merlin > temp_change: Inside temperature changed > (call get_temp() to get value) > heat_sp_change: Heat setpoint was changed > (call get_heat_sp() to get value). > cool_sp_change: Cool setpoint was changed > (call get_cool_sp() to get value). > mode_change: System mode changed > (call get_mode() to get value). > fan_mode_change: Fan mode changed > (call get_fan_mode() to get value). > > New/modified functions available: > mode(): > Sets system mode to argument: 'off', 'heat', 'cool', 'auto', > 'program_heat', 'program_cool', 'program_auto' (program_ > implies hold=off) > get_mode(): > Returns the last mode returned by poll_mode(). > fan(): > Sets fan to 'on' or 'auto' > get_fan_mode(): > Returns the current fan mode (fan_on or fan_auto) > cool_setpoint(): > Sets a new cool setpoint. > get_cool_sp(): > Returns the current cool setpoint. > heat_setpoint(): > Sets a new heat setpoint. > get_heat_sp(): > Returns the current heat setpoint. > get_temp(): > Returns the current temperature at the thermostat. > get_filter_reminder(): > Returns the number of days until the furnace filter needs to > be replaced > restore_setpoints(): > Returns the heat/cool setpoints to what they would have been > if the thermostat were running on schedule > > > Dan Arnold March 2009 > ====================== > Added caching of registers > > > Dan Arnold February 2009 > ========================= > Added function to set registers > Added time translation for thermostat programming (12h or 24h > format based on Omnistat_24hr config param) > Added ability to set outside temp to display on thermostat > Added the ability to translate to/from Celcius (depends on the > Omnistat_celcius config param) > Modified set procedures to use set_reg > Modified temp translation to use math rather than a lookup table > (needed to cover possible outside temps) > Fixed a bug in read_reg > > > Joel Davidson February 2009 > ========================= > Corrected bad syntax in mode comparison logic in read_group1. > > > Joel Davidson December 2005 > ========================= > Re-ordered routines to avoid run-time error from prototyped > subroutines. > Modified comparison values in read_group1 tests to fix users > problem with > incorrect compare results. Added additional comments. Added > addressing > mods to support multiple thermostats. Removed calls to set_time and > display in serial_startup since they cause a funky runtime error. > > > Joel Davidson June 2004 > ========================= > Modified checksum() to return 8 bit checksum. Fixed set_time. > Added read_group1 to return register group 1 values (setpoints, > modes, current temperature). Added generic function to read any > specified register(s), read_register(address, [# of regs]). > Changed Omnistat_run_program config option to Omnistat_non_program. > Setting to a 1 disables thermostat internal program. Changed > Omnistat_show_clock to Omnistat_hide_clock. 1 hides clock and filter > display. > > > Kent Noonan Jan 2002 > ===================== > I have another module for misterhouse. But it is not finished. > This is a > module for controling HAI Omnistat Communicating thermostats. It was > specifically written against the RC80 but as far as I can tell it > should > work with any of them. There is a problem with it. I am not > finished with > it. I started working on it, then moved to a house with an older > heater > that the thermostat doesn't work with. It's going to be a couple > of years > before we can upgrade the heater, so I thought I'd send this incase > somebody else wanted to continue where I left off before I can get > back to > it again. Right now I cant even gaurantee that it works at all, but I > think it did.. > > > > > > ######################################################## > > Below is a list of registers for reference: > > # INTERNAL REGISTERS (RO = READ ONLY) > 0 (00) - Thermostat address (ro) (1 - 127) > 1 (01) - Communications mode (ro) (0, 1, 8 or 24) > 2 (02) - System options (ro) > 3 (03) - Display options > 4 (04) - Calibration offset (1 to 59, 30=no change - ½ C units) > 5 (05) - Cool setpoint low limit (Omnitemp units) > 6 (06) - Heat setpoint high limit (Omnitemp units) > 7 (07) - Reserved > 8 (08) - Reserved > 9 (09) - Cooling anticipator (0 to 30) (RC-80, -81, -90, -91 only) > 10 (0A) - Heating anticipator (0 to 30) (RC-80, -81, -90, -91 > only), Stage 2 differential (RC-112) > 11 (0B) - Cooling cycle time (2 - 30 minutes) > 12 (0C) - Heating cycle time (2 - 30 minutes) > 13 (0D) - Aux heat differential, (RC-100, -101, -112), Stage 2 > differential (RC-120, -121, -122) (Omnitemp units) > 14 (0E) - Clock adjust (seconds/day) 1=-29, 30=0, 59=+29 > 15 (0F) - Days remaining until filter reminder > 16 (10) - System run time, current week - hours > 17 (11) - System run time, last week - hours > > # Registers 18 - 20 are used only in models with real time pricing. > 18 (12) - Real time pricing setback - Mid (Omnitemp units) > 19 (13) - High > 20 (14) - Critical > > # Programming registers > 21 (15) - weekday morning time > 22 (16) - cool setpoint > 23 (17) - heat setpoint > 24 (18) - weekday day time > 25 (19) - cool setpoint > 26 (1A) - heat setpoint > 27 (1B) - weekday evening time > 28 (1C) - cool setpoint > 29 (1D) - heat setpoint > 30 (1E) - weekday night time > 31 (1F) - cool setpoint > 32 (20) - heat setpoint > 33 (21) - Saturday morning time > 34 (22) - cool setpoint > 35 (23) - heat setpoint > 36 (24) - Saturday day time > 37 (25) - cool setpoint > 38 (26) - heat setpoint > 39 (27) - Saturday evening time > 40 (28) - cool setpoint > 41 (29) - heat setpoint > 42 (2A) - Saturday night time > 43 (2B) - cool setpoint > 44 (2C) - heat setpoint > 45 (2D) - Sunday morning time > 46 (2E) - cool setpoint > 47 (2F) - heat setpoint > 48 (30) - Sunday day time > 49 (31) - cool setpoint > 50 (32) - heat setpoint > 51 (33) - Sunday evening time > 52 (34) - cool setpoint > 53 (35) - heat setpoint > 54 (36) - Sunday night time > 55 (37) - cool setpoint > 56 (38) - heat setpoint > 57 (39) - Reserved - do not write > > # this one is lost, it kind of belongs with 0x41 below > 58 (3A) - Day of week (0=Monday - 6=Sunday) > > # group1 data start > 59 (3B) - Cool setpoint (current) > 60 (3C) - Heat setpoint (current) > 61 (3D) - Thermostat mode (0=off, 1=heat, 2=cool, 3=auto) (4=Emerg > heat: RC-100, -101, -112 only) > 62 (3E) - Fan status (0=auto 1=on) > 63 (3F) - Hold (0=off 255=on) > 64 (40) - Actual temperature in Omni format > # group1 data stop. > # Would have been so very nice is 0x48 were in group1 since you > typically want to query that often too > # to know what commands your stat is sending to your HVAC system :-/ > > 65 (41) - Seconds 0 - 59 > 66 (42) - Minutes 0 - 59 > 67 (43) - Hours 0 - 23 > 68 (44) - Outside temperature (see below) > 69 (45) - Reserved > 70 (46) - Real time pricing mode (0=lo, 1=mid, 2=high, 3=critical) > (RC-81, -91, -101, -121 only) > 71 (47) - (ro) current mode (0=off 1=heat 2=cool) > 72 (48) - (ro) output status > 73 (49) - (ro) model of thermostat > > # 0x48: reflects the positions of the control relays on the > thermostat. > bit 0: heat/cool bit - set for heat, clear for cool > bit 1: auxiliary heat bit - set for on, clear for off (RC-100, > -101, -112 only) > bit 2: stage 1 run bit - set for on, clear for off > bit 3: fan bit - set for on, clear for off > bit 4: stage 2 run bit: set for on, clear for off (RC-112, 120, > 121, 122 only) > > # 0x49: thermostat model > RC-80 0 > RC-81 1 > RC-90 8 > RC-91 9 > RC-100 16 > RC-101 17 > RC-112 34 > RC-120 48 > RC-121 49 > RC-122 50 > > Outside Temperature: writing to the outside temperature register > will cause the thermostat to display the > outside temperature every 4 seconds. The thermostat will stop > displaying the outside temperature if this > register is not refreshed at least every 5 minutes. > > Display Options: > bit 0: set for Fahrenheit, clear for Celsius > bit 1: set for 24 hour time display, clear for AM/PM > bit 2: set for non-programmable, clear for programmable (disables > internal programs in thermostat) > bit 3: set for real time pricing (RTP) mode, clear for no RTP > (RC-81, -91, -101, -121 only) > bit 4: set to hide clock, RTP and filter display, clear to show them. > > New Registers for Omnistat2 > 74 (4A) Current energy cost (0 – 254, 255=disabled) > > Programming Tuesday - Friday: > 75 (4B) Programming Tuesday morning time (15 minute increments) > 76 (4C) Programming Tuesday morning cool setpoint (in Omnitemp) > 77 (4D) Programming Tuesday morning heat setpoint (in Omnitemp) > 78 (4E) Programming Tuesday day time (15 minute increments) > 79 (4F) Programming Tuesday day cool setpoint (in Omnitemp) > 80 (50) Programming Tuesday day heat setpoint (in Omnitemp) > 81 (51) Programming Tuesday evening time (15 minute increments) > 82 (52) Programming Tuesday evening cool setpoint (in Omnitemp) > 83 (53) Programming Tuesday evening heat setpoint (in Omnitemp) > 84 (54) Programming Tuesday night time (15 minute increments) > 85 (55) Programming Tuesday night cool setpoint (in Omnitemp) > 86 (56) Programming Tuesday night heat setpoint (in Omnitemp) > 87 (57) Programming Wednesday morning time (15 minute increments) > 88 (58) Programming Wednesday morning cool setpoint (in Omnitemp) > 89 (59) Programming Wednesday morning heat setpoint (in Omnitemp) > 90 (5A) Programming Wednesday day time (15 minute increments) > 91 (5B) Programming Wednesday day cool setpoint (in Omnitemp) > 92 (5C) Programming Wednesday day heat setpoint (in Omnitemp) > 93 (5D) Programming Wednesday evening time (15 minute increments) > 94 (5E) Programming Wednesday evening cool setpoint (in Omnitemp) > 95 (5F) Programming Wednesday evening heat setpoint (in Omnitemp) > 96 (60) Programming Wednesday night time (15 minute increments) > 97 (61) Programming Wednesday night cool setpoint (in Omnitemp) > 98 (62) Programming Wednesday night heat setpoint (in Omnitemp) > 99 (63) Programming Thursday morning time (15 minute increments) > 100 (64) Programming Thursday morning cool setpoint (in Omnitemp) > 101 (65) Programming Thursday morning heat setpoint (in Omnitemp) > 102 (66) Programming Thursday day time (15 minute increments) > 103 (67) Programming Thursday day cool setpoint (in Omnitemp) > 104 (68) Programming Thursday day heat setpoint (in Omnitemp) > 105 (69) Programming Thursday evening time (15 minute increments) > 106 (6A) Programming Thursday evening cool setpoint (in Omnitemp) > 107 (6B) Programming Thursday evening heat setpoint (in Omnitemp) > 108 (6C) Programming Thursday night time (15 minute increments) > 109 (6D) Programming Thursday night cool setpoint (in Omnitemp) > 110 (6E) Programming Thursday night heat setpoint (in Omnitemp) > 111 (6F) Programming Friday morning time (15 minute increments) > 112 (70) Programming Friday morning cool setpoint (in Omnitemp) > 113 (71) Programming Friday morning heat setpoint (in Omnitemp) > 114 (72) Programming Friday day time (15 minute increments) > 115 (73) Programming Friday day cool setpoint (in Omnitemp) > 116 (74) Programming Friday day heat setpoint (in Omnitemp) > 117 (75) Programming Friday evening time (15 minute increments) > 118 (76) Programming Friday evening cool setpoint (in Omnitemp) > 119 (77) Programming Friday evening heat setpoint (in Omnitemp) > 120 (78) Programming Friday night time (15 minute increments) > 121 (79) Programming Friday night cool setpoint (in Omnitemp) > 122 (7A) Programming Friday night heat setpoint (in Omnitemp) > > Programming Occupancy: > 123 (7B) Programming Day Cool setpoint (in Omnitemp) > 124 (7C) Programming Day Heat setpoint (in Omnitemp) > 125 (7D) Programming Night Cool setpoint (in Omnitemp) > 126 (7E) Programming Night Heat setpoint (in Omnitemp) > 127 (7F) Programming Away Cool setpoint (in Omnitemp) > 128 (80) Programming Away Heat setpoint (in Omnitemp) > 129 (81) Programming Vacation Cool setpoint (in Omnitemp) > 130 (82) Programming Vacation Heat setpoint (in Omnitemp) > > Setup: > 131 (83) Program mode (0=None, 1=Schedule, 2=Occupancy) > 132 (84) Expansion baud (0=300, 1=100, 42=1200, 54=2400, 126=9600) > 133 (85) Days until filter reminder appears > 134 (86) Humidity Setpoint > 135 (87) Dehumidify Setpoint > 136 (88) Dehumidifier output options (0=Not used, 1=Standalone, 2= > variable speed fan) > 137 (89) Humidifier output (0=Not used, 1=Standalone) > 138 (8A) Minutes out of 20 that fan is on during cycle (1-19) > 139 (8B) Backlight settings (0=Off, 1=On, 2=Auto) > 140 (8C) Backlight color (0-100) > 141 (8D) Backlight intensity (1-10) > 142 (8E) Selective message enable/disable > 143 (8F) Minimum on time for cool (2-30) > 144 (90) Minimum off time for cool (2-30) > 145 (91) Minimum on time for heat (2-30) > 146 (92) Minimum off time for heat (2-30) > 147 (93) System type (0=Heat Pump, 1=Conventional, 2=Dual Fuel) > 148 (94) Reserved > 149 (95) End of vacation date: day > 150 (96) > 151 (97) End of vacation date: hour > 152 (98) Hours HVAC used in Week 0 > 153 (99) Hours HVAC used in Week 1 > 154 (9A) Hours HVAC used in Week 2 > 155 (9B) Hours HVAC used in Week 3 > 156 (9C) Reserved > 157 (9D) Reserved > 158 (9E) Enable/disable individual temp sensors > 159 (9F) Number of cool stages > 160 (A0) Number of heat stages > 161 (A1) Current occupancy mode (0=Day, 1=Night, 2=Away, 3=Vacation) > 162 (A2) Current indoor humidity > 163 (A3) Cool setpoint for vacation mode (51-91) > 164 (A4) Heat setpoint for vacation mode (51-91) > > Energy: > 165 (A5) Displayed price of energy with medium level energy > 166 (A6) Displayed price of energy with high level energy > 167 (A7) Displayed price of energy with critical level energy > 168 (A8) Sensitivity setting for proximity sensor (0-99) > 169 (A9) Energy level as set by the meter > 170 (AA) Current energy total cost, upper byte > 171 (AB) Current energy total cost, lower byte > 172 (AC) STRING ASCII display for first load control module > 173 (AD) STRING ASCII display for second load control module > 174 (AE) STRING ASCII display for third load control module > 175 (AF) STRING ASCII display for Energy message > 176 (B0) STRING ASCII display for emergency broadcast message (not > implemented) > 177 (B1) STRING ASCII display for custom message (not implemented) > 178 (B2) STRING ASCII display for energy graph title bar > 179 (B3) STRING ASCII display for energy graph x axis > 180 (B4) STRING ASCII display for energy graph y axis > 181 (B5) STRING ASCII display for long messages (not implemented) > 182 (B6) graph bar max height, upper byte > 183 (B7) graph bar max height, lower byte > 184 (B8) graph bar one value, upper byte > 185 (B9) graph bar one value, lower byte > 186 (BA) graph bar two value, upper byte > 187 (BB) graph bar two value, lower byte > 188 (BC) graph bar three value, upper byte > 189 (BD) graph bar three value, lower byte > 190 (BE) graph bar four value, upper byte > 191 (BF) graph bar four value, lower byte > 192 (C0) Status and enable/disable of each load control module > > Sensors: > 200 (C8) Current temperature of sensor 3 > 201 (C9) Current temperature of sensor 4 > 202 (CA) Reserved > > Wireless: > 224 (E0) Wireless MAC address byte 1 > 225 (E1) Wireless MAC address byte 2 > 226 (E2) Wireless MAC address byte 3 > 227 (E3) Wireless MAC address byte 4 > 228 (E4) Wireless MAC address byte 5 > 229 (E5) Wireless MAC address byte 6 > 230 (E6) Wireless MAC address byte 7 > 231 (E7) Wireless MAC address byte 8 > 232 (E8) Wireless firmware version integer place > 233 (E9) Wireless firmware version decimal place > 234 (EA) Wireless strength (0-100) > 235 (EB) Wireless buzzer enable or disable > 236 (EC) Wireless IP address byte 1 > 237 (ED) Wireless IP address byte 2 > 238 (EE) Wireless IP address byte 3 > 239 (EF) Wireless IP address byte 4 > 253 Reserved > 254 Reserved > =cut > > use strict; > > package Omnistat; > > sub omnistat_debug { > my ($mesg) = @_; > > print "$::Time_Date: $mesg\n" if $::Debug{omnistat}; > } > > # Load Time::HiRes if it's available > use vars qw($USLEEP); > eval { require Time::HiRes }; > if (not $@) { > Time::HiRes->import( qw(usleep) ); > omnistat_debug("Omnistat found Time::Hires, will use usleep in > omni_sleep"); > $USLEEP=1; > } else { > omnistat_debug("Omnistat did NOT find Time::Hires, will NOT use > usleep in omni_sleep"); > warn("Omnistat works much better with Time::HiRes, install it if > you can"); > $USLEEP=0; > } > > # -------------------------------------------------------------- > # -------------------- START OF SUBROUTINES -------------------- > # -------------------------------------------------------------- > > @Omnistat::ISA = ('Serial_Item'); > > sub omni_sleep() { > $USLEEP ? usleep($_[0]) : sleep(int($_[0] + 0.99999)); > } > > > # My guess is that most people would want to have temperature > logging, but you can turn it off with > # Omnistat_stat_log=0 in mh.private.ini -- merlin > sub omnistat_log { > my ($mesg, $level) = @_; > my $loglevel = $main::config_parms{Omnistat_stat_log}; > > $loglevel = 1 if (not defined $loglevel); > $level = 1 if (not defined $level); > > &::print_log("log=logs/thermostat.log $mesg") if ($level <= > $loglevel); > } > > > sub is_hex { > # make sure we got proper hex and not a number or some other error > return ($_[0] =~ /^0x[0-9a-fA-F][0-9a-fA-F]$/); > } > > # ******************************************************** > # * Get address for this thermostat from the argument. > # * Address defaults to 1 if no argument. > # ******************************************************** > sub new { > my ( $class, $address ) = @_; > $address = 1 unless $address; > my $self = {}; > $$self{address} = $address; > $$self{cache} = {}; > # when the cache was last updated > $$self{cache_updatetime} = {}; > # per register override of how long we want to cache > $$self{cache_agelimit} = {}; > > # **************************** IMPORTANT > ***************************** > # Cache values can be increased by up to 10% at runtime to avoid > having > # a bunch of variables have their cache expire at the same time > and cause > # hangs due to synchronized reads. -- merlin > # **************************** IMPORTANT > ***************************** > > # by default registers are cached 1h # CACHE_TIMEOUT_DEFAULT > $$self{cache_defaultagelimit} = 3600; > # These are important registers for which we don't want to cache > data 1 hour > # or registers that never change and we cache longer > # (the rest default to $$self{cache_defaultlifetime}) > > # filter reminder, or type of thermostat, and all the programming > setpoints is good enough once a day > foreach my $reg (0x15..0x38, 0x0f, 0x49) { > $$self{cache_agelimit}{$reg} = 3600 * 24; # CACHE_TIMEOUT_DAILY > } > # setpoints and modes are cached 53 secs so that they are pretty much > # guaranteed to be updated once a minute even with the random > +10% offset > foreach my $reg (0x3b .. 0x3f) { > $$self{cache_agelimit}{$reg} = 54; # CACHE_TIMEOUT_SHORT > } > # temperatures and what the stat outputs, we only cache 10 seconds > foreach my $reg (0x40, 0x44, 0x48) { > $$self{cache_agelimit}{$reg} = 10; # CACHE_TIMEOUT_VERYSHORT > } > > #The next line is an experiment with http_server.pm > <http://http_server.pm> to allow other objects to show up in the > web interface > $$self{html_text} = "<a href=/hai/omnistat_web.pl > <http://omnistat_web.pl>>Set Thermostat</a>"; > > omnistat_debug("[HVAC] Omnistat[$$self{address}] object created"); > bless $self,$class; > > # This is to work around a timing bug where the first query > doesn't get a proper ack > # we just "prime" the device and connection by making one query > to it where we ignore the answer > # no idea why this is needed, but it works for me and things are > stable afterwards -- merlin > $self->{'PRIME'}=1; > $self->send_cmd("0x01 0x20 0x40 0x01 0x62", 1); > $self->{'PRIME'}=0; > > return $self; > } > > # ************************************* > # * Add the checksum to the cmd array. > # ************************************* > sub add_checksum { > my (@array) = @_; > my @modarr = @array; > my $value = 0; > foreach (@modarr) { > s/^0x//g; > $_ = hex($_); > $value = $value + $_; > } > $value = $value % 256; > $array[ $#array + 1 ] = sprintf( "0x%02x", $value ); > return @array; > } > > # ************************************** > # * Send the command to the thermostat. > # ************************************** > # I added very basic support of acknowledgments by just checking > that we get one byte back that contains 0x80. It is totally > # incomplete, but better than nothing -- merlin > # FIXME?: the spec says we're supposed to listen to the reply, and > resend messages after an inter message timeout > # of 1.25s, that said it seems to work ok with the current timings > and should work without resends unless your > # serial cable wires are crap (use CAT-5) and/or very long -- merlin > # > # FIXME, 2-3 times, I had this bug right after starting mh: > # 25/07/2009 10:15:30 : Omnistat[2]->read_cached_reg: reg=0x3b not > cached, fetching > # 25/07/2009 10:15:30 : Omnistat->send_cmd string=0x02 0x20 0x3b > 0x0e 0x6b (with 833325,us reply delay (14 char(s) to read back)) > # 25/07/2009 10:15:30 : Omnistat->send_cmd got reply "0xff 0x82 > 0xf2 0x3b 0x8b 0x64 0x03 0x00 0x00 0x78 0x1e 0x0f 0x0a 0x60 0x00 > 0x00 0x02 0x00 0xb2 " > # The 0xff in the reply didn't belong. No idea where it came from, > especially because it was the first command and reply. > # for now it makes the code die, the command fail and then things > restart and continue -- merlin > sub send_cmd { > # if you want to default to a full 2sec wait, pass '-1' as > reply_count > my ($self, $reply_count, @string) = @_; > my $addr = $$self{address}; > my $cmd = ''; > # We try to calculate how long we wait for the reply, or default > to 2 sec (2M usec) > my $reply_wait = 2000000; > > # some experimentation shows on my system that we need to wait > 0.3sec + 0.1sec for each 3 registers returned --merlin > # 300bps is 30cps, which does equate to 0.0333333s per character. > From experimentation, one needs to wait an extra > # 11 characters in addition to the payload you're expecting back > to get reliable replies (10 almost works but causes > # occasional corruption due to timings). -- merlin > # (12+ chars wait instead of 11 might be needed for you. Please > increase & let me know if this is too short for you). > my $REPLY_BASE_DELAY = 14; > > # While we don't get as many bytes as we sent if we were to set > several registers in a row (which is not currently > # supported in this code), the spec says to wait 30ms per > register set, so the wait time ends up being able the same > # when setting data than when polling it. > $reply_wait = (33333*($reply_count + $REPLY_BASE_DELAY)) if > ($reply_count > -1); > > omnistat_debug("Omnistat[$$self{address}]->send_cmd > string=@string (with $reply_wait,us reply delay ($reply_count > char(s) to read back))"); > foreach my $byte (@string) { > $byte =~ s/0x//; # strip off the 0x > $cmd = $cmd . pack "H2", $byte; # pack it into 8 bits > } > > # send it to thermostat > #omnistat_debug("Omnistat->send_cmd will write $cmd"); > $main::Serial_Ports{Omnistat}{object}->write($cmd); > > # need to wait a bit for the reply > # FIXME: sleep is bad, especially if you're not using usleep from > Time::Hires, the only proper way to do this would be to > # have a request queue where one command gets processed every 1-2 > seconds, but that would be a big rewrite -- merlin > &Omnistat::omni_sleep($reply_wait); > > # read response > &main::check_for_generic_serial_data('Omnistat'); > my $temp = $main::Serial_Ports{Omnistat}{data}; > $main::Serial_Ports{Omnistat}{data} = ''; > my $len = length($temp); > $temp = unpack( "H*", $temp ); > my ($i); > my $rcvd = ''; > my $ack_byte = 0x80 + $addr; > for ( $i = 0 ; $i < $len ; $i++ ) { > $rcvd = $rcvd . sprintf( "0x%s ", substr( $temp, $i * 2, 2 ) ); > } > my $rcvd_ack = hex(substr($rcvd, 0 , 4)); > > if ($self->{'PRIME'}) > { > omnistat_debug("Omnistat[$$self{address}]->send_cmd skipping > error check and return value during prime"); > return; > } > > # FIXME? Those two dies aren't ideal, but it happens that you get > corruption or bad data on a reply. > # Expected for 01 20 3b 0e 6a is something like > # 0x81 0xf2 0x3b 0x7c 0x71 0x03 0x00 0x00 0x7d 0x07 0x1e 0x0f > 0x00 0x00 0x00 0x02 0x0c 0x5d > # but I have seen replies like > # 0x03 0xf2 0x3b 0x7c 0x71 0x03 0x00 0x00 0x7d 0x00 0x1c 0x0f > 0x00 0x00 0x00 0x02 0x0c 0x54 (0x81 ack byte is wrong) > # or sync issues like > # 0x64 0x82 0xf2 0x3b 0x8e 0x64 0x00 0x00 0x00 0x7d 0x20 0x29 > 0x0f 0x60 0x00 0x00 0x00 0x00 0xd6 (0x64 shouldn't be here) > # On my system, this error only seems to happen soon after > startup and doesn't seem to happen later -- merlin > die "$::Time_Date: Omnistat[$$self{address}]->send_cmd did not > get ack reply to command @string ($rcvd)" unless (length($rcvd) > 3); > die "$::Time_Date: Omnistat[$$self{address}]->send_cmd did not > get expected first byte (".sprintf("0x%02x",$ack_byte).") in ack > reply to command @string (got ".sprintf("0x%02x", $rcvd_ack)." in > $rcvd)" unless ($rcvd_ack eq $ack_byte); > omnistat_debug("Omnistat[$$self{address}]->send_cmd got reply > \"$rcvd\""); > > return $rcvd; > } > > # ****************************** > # * check for returned data. > # ****************************** > sub check_for_data { > &main::check_for_generic_serial_data('Omnistat'); > } > > # ************************************************* > # * Set the thermostat clock to the current time. > # ************************************************* > sub set_time { > my ($self) = @_; > my $wday; > my $addr = $$self{address}; > > omnistat_debug("Omnistat[$$self{address}] -> Setting time/day of > week"); > my @cmd = qw(0x01 0x41 0x41); > > #set the time > $cmd[0] = sprintf( "0x%02x", $addr ); > $cmd[3] = sprintf( "0x%02x", $::Second ); > $cmd[4] = sprintf( "0x%02x", $::Minute ); > $cmd[5] = sprintf( "0x%02x", $::Hour ); > @cmd = add_checksum(@cmd); > $self->send_cmd(0, @cmd); > > #set the weekday > $wday = $::Wday ? $::Wday - 1 : 6; > $self->set_reg( "0x3a", sprintf( "0x%02x", $wday) ); > } > > # ******************************************* > # * Set the display mode of the thermostat. > # ******************************************* > sub display { > my ($self) = @_; > my $addr = $$self{address}; > > #$main::config_parms{Omnistat_serial_port} > my $DISPLAY_BITS; > # Bit 0 > if ( $main::config_parms{Omnistat_celcius} ) { > $DISPLAY_BITS = 0; > } else { > $DISPLAY_BITS = 1; > } > # Bit 1 > if ( $main::config_parms{Omnistat_24hr} ) { > $DISPLAY_BITS = $DISPLAY_BITS + 2; > } > # Bit 2 > if ( $main::config_parms{Omnistat_non_program} ) { > $DISPLAY_BITS = $DISPLAY_BITS + 4; > } > # Bit 3 > if ( $main::config_parms{Omnistat_rtp_mode} ) { > $DISPLAY_BITS = $DISPLAY_BITS + 8; > } > # Bit 4 > if ( $main::config_parms{Omnistat_hide_clock} ) { > $DISPLAY_BITS = $DISPLAY_BITS + 16; > } > > $self->set_reg( "0x03", sprintf( "0x%02x", $DISPLAY_BITS ) ); > } > > # ********************************************* > # * Create the Omnistat device on serial port. > # ********************************************* > sub serial_startup { > &main::serial_port_create( 'Omnistat', > $main::config_parms{Omnistat_serial_port}, 300, 'none', 'raw' ); > &::MainLoop_pre_add_hook( \&Omnistat::check_for_data, 1 ); > } > > # ******************************** > # * Set the hold mode on or off. > # ******************************** > sub hold { > my ( $self, $state ) = @_; > my $new_hold; > my $cur_hold = $self->read_cached_reg("0x3f",1); > $state = lc($state); > > if ( $state eq "off" ) { > $new_hold = "0x00"; > } elsif ( $state eq "on" ) { > $new_hold = "0xff"; > } else { > print "$::Time_Date: Omnistat[$$self{address}]: Invalid Hold > state: $state\n"; > return; > } > > # obviously there is a small race condition here, if hold was > changed in the last minute from the panel, > # we could fail to set it when it needs to be, but that should be > quite rare, and avoiding all the repeated > # hold set to off before changing other values is worth it -- merlin > if ($cur_hold ne $new_hold) { > $self->set_reg( "0x3f", $new_hold ); > omnistat_debug("Omnistat[$$self{address}]->hold: Hold set to > $state"); > } else { > omnistat_debug("Omnistat[$$self{address}]->hold: Hold stays at > $state"); > } > } > > # ************************************************************* > # * Translate Temperature between Fahrenheit/Celcius and Omni values. > # ************************************************************* > sub translate_temp { > my ($settemp) = @_; > my ($omnitemp); > > # this is a good place to catch a 14 reg read that happens in > read_group1 extended, being off by one character, or returning > # bogus 0's. > die "$::Time_Date: Omnistat->translate_temp got an input > temperature of 0 = -40F/C, this typically means serial port > corruption, bad... You may want to increase REPLY_BASE_DELAY" if > (not $settemp or $settemp eq "0x00"); > > # Calculate conversion mathematically rather than using a table > so all temps will work (needed for outside temperature) > if ( substr( $settemp, 0, 2 ) eq '0x' ) > { # if it starts with 0x, reverse xlate > $omnitemp = hex($settemp); > $omnitemp = -40 + .5 * $omnitemp; #degrees Celcius > if ( !( $main::config_parms{Omnistat_celcius} ) ) { > $omnitemp = 32 + 1.8 * $omnitemp; # degrees Fahrenheit > $omnitemp = int( $omnitemp + .5 * ( $omnitemp <=> 0 ) ); > #round > } > } else { # xlate from Fahrenheit/Celcius > if ( !( $main::config_parms{Omnistat_celcius} ) ) { > $omnitemp = ( $settemp - 32 ) / 1.8; #Fahrenheit to Celcius > } > $omnitemp = ( $omnitemp + 40 ) / .5; > #omnistat degrees > $omnitemp = int( $omnitemp + .5 * ( $omnitemp <=> 0 ) ); #round > $omnitemp = sprintf( "0x%02x", $omnitemp ); > } > > omnistat_debug("Omnistat: Converted $settemp to $omnitemp"); > return $omnitemp; > } > > # ************************************************************* > # * Translate Time between readable and Omni values. > # ************************************************************* > sub translate_time { > my ($settime) = @_; > my ( $hours, $minutes, $ampm ); > my ($omnitime); > > if ( substr( $settime, 0, 2 ) eq '0x' ) { #Translate omnitime to > readable time > if ( $settime eq '0x60' ) > { #if it's set to 24hrs past midnight, time is blank > $omnitime = ''; > } else { > $minutes = > hex($settime) * > 15; #Omnistat is stored as 15 minute time periods pas > midnight > $hours = int( $minutes / 60 ); > $minutes = $minutes % 60; #minutes past hour > if ( $main::config_parms{Omnistat_24hr} ) { > > #Translate to 24hr time > $omnitime = sprintf( '%02s:%02s', $hours, $minutes ); > } else { > > #Translate omni to AM/PM > if ( $hours == 0 ) { > $hours = 12; > $ampm = 'PM'; > } elsif ( $hours > 12 ) { > $ampm = 'PM'; > $hours -= 12; > } else { > $ampm = 'AM'; > } > $omnitime = sprintf( '%02s:%02s %s', $hours, $minutes, $ampm ); > } > } > } else { #Translate readable to omnistat time > if ( $settime eq '0' ) { #set to 0 to clear time, or 24:00 if > using 24h time > $omnitime = '0x60'; > } elsif ( $main::config_parms{Omnistat_24hr} ) { > #convert 24h time > if ( $settime =~ /^([0-1][0-9]|[2][0-4]):([0-5][0-9])$/ ) { > > #valid time > $hours = $1; > $minutes = $2; > $minutes = $minutes + $hours * 60; > $omnitime = $minutes / 15; > $omnitime = sprintf( "0x%02x", $omnitime ); > } else { > #invalid time > $omnitime = ''; > } > } else { > #convert am/pm time > if ( $settime =~ /^(1[0-2]|0?[1-9]):([0-5][0-9]) *(AM|PM)$/ ) { > > #valid time > $hours = $1; > $minutes = $2; > $ampm = $3; > > #PM we may need to add 12 hours (unless it's midnight), AM > is already right > if ( $ampm eq 'PM' ) { > if ( $hours == 12 ) { > $hours = 0; > } else { > $hours = $hours + 12; > } > } > > $minutes = $minutes + $hours * 60; > $omnitime = $minutes / 15; > > $omnitime = sprintf( "0x%02x", $omnitime ); > } else { > #invalid time > $omnitime = ''; > } > } > } > > return $omnitime; > } > > # ***************************************************** > # * Read and convert the bits in reg 0x48 (HVAC output) > # ***************************************************** > sub translate_stat_output { > my ( $self, $reg48 ) = @_; > > die "Omnistat::translate_stat_output got non hex value in $reg48" > unless (is_hex($reg48)); > # see reg 0x48 / output register at the top of this file > my $output = "off"; > $output = "fan" if (hex($reg48) & 8); > > # if stage 1 and stage 2 heat/cool are off, return here > #&::print_log("pass1: reg48: $reg48, $output"); > return $output if (not hex($reg48) & (4+16)); > > $output .= "/auxheat" if (hex($reg48) & 2); > > $output .= (hex($reg48) & 1) ? "/heat" : "/cool"; > $output .= "/stage2" if (hex($reg48) & 16); > #&::print_log("pass2: reg48: $reg48, $output"); > return $output; > } > > > # ***************************************************************** > # * Change the mode of the thermostat between off/auto/heat/cool. > # ***************************************************************** > sub mode { > my ( $self, $state ) = @_; > $state = lc($state); > > #TODO: Should heat/cool/auto turn on hold? > > omnistat_debug("Omnistat[$$self{address}] -> Mode $state"); > my $addr = $$self{address}; > my @cmd; > if ( $state eq "off" ) { > $self->set_reg( "0x3d", "0x00" ); > } elsif ( $state eq "heat" ) { > $self->set_reg( "0x3d", "0x01" ); > } elsif ( $state eq "cool" ) { > $self->set_reg( "0x3d", "0x02" ); > } elsif ( $state eq "auto" ) { > $self->set_reg( "0x3d", "0x03" ); > } elsif ( $state eq "program_heat" ) { > $self->set_reg( "0x3d", "0x03" ); > $self->set_reg( "0x3f", "0x00" ); > } elsif ( $state eq "program_cool" ) { > $self->set_reg( "0x3d", "0x03" ); > $self->set_reg( "0x3f", "0x00" ); > } elsif ( $state eq "program_auto" ) { > $self->set_reg( "0x3d", "0x03" ); > $self->set_reg( "0x3f", "0x00" ); > } else { > print "$::Time_Date: Omnistat: Invalid Mode state: $state\n"; > } > print "$::Time_Date: Omnistat: Invalid Mode state: $state\n"; > } > > # ************************** > # * Restore the heat and cool setpoints to what they would be if > the schedule were in effect > # ************************** > sub restore_setpoints { > my ($self) = @_; > my $point = 0; > my $time; > my $day; > my $register; > my $setpointnum; > my $daynum; > > # this does not work if the stat is in hold mode, and we assume > that calling this means we want > # to un-hold > $self->hold("off"); > > # This touches a lot of registers, so it's quicker to cache them > all once. > # Unfortunately, we can only read 14 registers at a time, so > we'll read 3x12 > # We don't store the result, this is just to prime the cache in > case it wasn't. > $self->read_cached_reg("0x15", 12); > $self->read_cached_reg("0x21", 12); > $self->read_cached_reg("0x2d", 12); > > #Determine the day (weekday,sat,sun) > $day = $::Wday ? $::Wday - 1 : 6; > > #Determine the time > $time = > ( $::Hour * 4 ) + ( $::Minute / 15 ) + ( $::Second / 60 ); > #Omnistat time > > #Determine the day > if ( $day == 6 ) { # Sunday > $register = 0x36; # Sunday night time > } elsif ( $day == 5 ) { # Saturday > $register = 0x2a; # Saturday night time > } else { # Weekday > $register = 0x1e; # Weekday night time > } > > # Check for setpoints for that day, need to consider what time it is > for ( $setpointnum = 0 ; $setpointnum < 4 ; $setpointnum++ ) { > # FIXME: make sure I didn't break this (test it) and remove my > FIXME -- merlin > if ( hex($self->read_cached_reg( sprintf( "0x%02x", $register - > 3 * $setpointnum))) < $time ) { > $point = $regis... [truncated message content] |
From: Marc M. <ma...@me...> - 2010-12-05 19:00:59
|
On Sun, Dec 05, 2010 at 09:56:09AM -0600, Joel Davidson wrote: > Karl- > > What type of serial port are you using to communicate with the Omnistat? > As I recall the Omnistat serial i/f gets it power from one of the host > port status lines, and some serial ports cheat and don't drive their > status lines to the +/-12 or so volts in the rs-232 spec. I used to use > a Moxa pci 8 port serial board with my rc-80 and it worked fine. Now > I'm using a ByteRunner usb 8 port serial adapter and it too works ok. > For starters you might try a different serial port if you can. Joel is right, I had the same problem with one of my USB 8 port hubs: it did not provide enough power and the communication did not work. I switched to a single USB to serial adapter, and things worked after that. Marc -- "A mouse is a device used to point at the xterm you want to type in" - A.S.R. Microsoft is to operating systems & security .... .... what McDonalds is to gourmet cooking Home page: http://marc.merlins.org/ |
From: Joel D. <jr...@io...> - 2010-12-05 19:17:03
|
On Sun, 5 Dec 2010, it would appear that Marc MERLIN wrote: > On Sun, Dec 05, 2010 at 09:56:09AM -0600, Joel Davidson wrote: >> Karl- >> >> What type of serial port are you using to communicate with the Omnistat? >> As I recall the Omnistat serial i/f gets it power from one of the host >> port status lines, and some serial ports cheat and don't drive their >> status lines to the +/-12 or so volts in the rs-232 spec. I used to use >> a Moxa pci 8 port serial board with my rc-80 and it worked fine. Now >> I'm using a ByteRunner usb 8 port serial adapter and it too works ok. >> For starters you might try a different serial port if you can. > > Joel is right, I had the same problem with one of my USB 8 port hubs: it did > not provide enough power and the communication did not work. > I switched to a single USB to serial adapter, and things worked after that. > > Marc Speaking of which, if anyone is interested in the Moxa 8 port serial card, let me know. I'd be willing to sell it for $50 including postage in the US. It's a C168H/PCI. Works great with linux (and of course windoze), and all the drivers/ manuals/etc are available on their website: http://www.moxa.com/product/C168Hpci.htm Joel -- Joel Davidson Austin, TX |
From: Joel D. <jr...@io...> - 2010-12-17 22:16:30
|
Karl- Glad to know you had a good outcome. It's nice to know there are still companies out there that provide good customer service. Joel On Fri, 17 Dec 2010, it would appear that Karl Suchy wrote: > Just wanted to give everyone an update on the Omnistat 2. Sent it back to > HAI for a replacement. They were very courteous and speedy with the > process. Got the new one today, plugged it in and had is working in no time > with the current version of omnistat.pm. > > Marc- > I plan on working the changes/updates for the Omnistat2 into the current > code in the next few days and will send it your way. > > And a thank you to everyone that helped me figure out what was going on. > > Karl > > On Mon, Dec 6, 2010 at 6:51 PM, Mickey Argo <mh...@ar...> wrote: > >> Check http://www.cortexa.com/knowledgebase/multiplethermostats >> >> >> Mickey Argo >> >> Karl Suchy wrote: >> >> Mickey, >> >> Thanks for sending that out. I will work that in once I get the OmniStat >> Communicating with the computer. Do you recall having to do anything >> special with the OmniStat 2 to get it to talk? My version does not have a >> communication jumper on the pcb, instead there was a wire jumper between the >> "N/C" and "black" pins as labeled on the pcb. In settings it is set up for >> serial at 300 baud. I have tried using Realterm to send commands to the >> Stat with no response back. >> >> Thanks >> Karl >> >> On Sat, Dec 4, 2010 at 10:15 PM, Mickey Argo <mh...@ar...> wrote: >> >>> I didn't exactly code this for wide distribution...there are complications >>> with my employer and areas they might be working on in the near future. If >>> you want to clean this up for inclusion into the trunk code, please feel >>> free and take the credit! Compare the attached Omnistat.pm with the trunk >>> version. omnistat2.pl is in my code directory. I have the following in >>> my private ini: >>> >>> Omnistat_serial_port=COM1 >>> Omnistat_24hr=1 >>> # disable internal program >>> #Omnistat_non_program=[0,1] >>> Omnistat_no_stat_log=3 >>> Omnistat_stat_log=$config_parms{data_dir}/omnistatlog.txt >>> omnistat_allowed_errors = 999999999999 >>> hvac_allowed_errors = 999999999999 >>> show_all_errors = yes >>> >>> >>> Mickey Argo wrote: >>> >>> Karl, >>> >>> I have this working on my setup...I'll try to send out the code changes >>> later today. I did add some new stuff, like changing the background color >>> based on the outside air temperature. >>> >>> Mickey Argo >>> >>> On 12/3/10 12:11 PM, Karl Suchy wrote: >>> >>> Thanks for the feed back. The Omnistat is in the correct communication >>> mode. I think I will start messing around with Omnistat.pm and trying to >>> send simple get and set commands and see what I get back. >>> >>> Karl >>> >>> On Fri, Dec 3, 2010 at 12:46 PM, Marc MERLIN <ma...@me...> wrote: >>> >>>> On Fri, Dec 03, 2010 at 09:45:18AM -0800, Marc MERLIN wrote: >>>>> On Fri, Dec 03, 2010 at 12:35:27PM -0500, Karl Suchy wrote: >>>>>> I am trying to get a single Omnistat 2 functioning with MisterHouse. >>>> Any >>>>>> help will be greatly appreciated. >>>>>> >>>>>> When opening omnistat_setup_web.pl I get the following error message >>>> "http >>>>>> error in http eval of omnistat_setup_web.pl: 12/03/10 11:40:36 AM: >>>>>> Omnistat[1]->send_cmd did not get ack reply to command 01 20 3b 0e 6a >>>> () at >>>>>> /opt/misterhouse/mh/bin/../lib/Omnistat.pm line 554, line 216." >>>> >>>> I should add that '()' shows that no reply at all was received from the >>>> omnistat, so you may indeed have a basic communication problem. >>>> >>>> Marc >>>> -- >>>> "A mouse is a device used to point at the xterm you want to type in" - >>>> A.S.R. >>>> Microsoft is to operating systems & security .... >>>> .... what McDonalds is to gourmet >>>> cooking >>>> Home page: http://marc.merlins.org/ >>>> >>>> |
From: Eloy P. <pe...@ch...> - 2010-12-17 22:59:22
|
On 12/17/2010 05:16 PM, Joel Davidson wrote: > Karl- > > Glad to know you had a good outcome. It's nice to know there are still > companies out there that provide good customer service. Indeed. And it also nice to know that the MisterHouse code for this particular device is working ;-) Cheers, Eloy Paris.- |
From: Joel D. <jr...@io...> - 2010-12-05 15:56:18
|
Karl- What type of serial port are you using to communicate with the Omnistat? As I recall the Omnistat serial i/f gets it power from one of the host port status lines, and some serial ports cheat and don't drive their status lines to the +/-12 or so volts in the rs-232 spec. I used to use a Moxa pci 8 port serial board with my rc-80 and it worked fine. Now I'm using a ByteRunner usb 8 port serial adapter and it too works ok. For starters you might try a different serial port if you can. Joel -- Joel Davidson Austin, TX On Sun, 5 Dec 2010, it would appear that Karl Suchy wrote: > Mickey, > > Thanks for sending that out. I will work that in once I get the OmniStat > Communicating with the computer. Do you recall having to do anything > special with the OmniStat 2 to get it to talk? My version does not have a > communication jumper on the pcb, instead there was a wire jumper between the > "N/C" and "black" pins as labeled on the pcb. In settings it is set up for > serial at 300 baud. I have tried using Realterm to send commands to the > Stat with no response back. > > Thanks > Karl > > On Sat, Dec 4, 2010 at 10:15 PM, Mickey Argo <mh...@ar...> wrote: > >> I didn't exactly code this for wide distribution...there are complications >> with my employer and areas they might be working on in the near future. If >> you want to clean this up for inclusion into the trunk code, please feel >> free and take the credit! Compare the attached Omnistat.pm with the trunk >> version. omnistat2.pl is in my code directory. I have the following in >> my private ini: >> >> Omnistat_serial_port=COM1 >> Omnistat_24hr=1 >> # disable internal program >> #Omnistat_non_program=[0,1] >> Omnistat_no_stat_log=3 >> Omnistat_stat_log=$config_parms{data_dir}/omnistatlog.txt >> omnistat_allowed_errors = 999999999999 >> hvac_allowed_errors = 999999999999 >> show_all_errors = yes >> >> >> Mickey Argo wrote: >> >> Karl, >> >> I have this working on my setup...I'll try to send out the code changes >> later today. I did add some new stuff, like changing the background color >> based on the outside air temperature. >> >> Mickey Argo >> >> On 12/3/10 12:11 PM, Karl Suchy wrote: >> >> Thanks for the feed back. The Omnistat is in the correct communication >> mode. I think I will start messing around with Omnistat.pm and trying to >> send simple get and set commands and see what I get back. >> >> Karl >> >> On Fri, Dec 3, 2010 at 12:46 PM, Marc MERLIN <ma...@me...> wrote: >> >>> On Fri, Dec 03, 2010 at 09:45:18AM -0800, Marc MERLIN wrote: >>>> On Fri, Dec 03, 2010 at 12:35:27PM -0500, Karl Suchy wrote: >>>>> I am trying to get a single Omnistat 2 functioning with MisterHouse. >>> Any >>>>> help will be greatly appreciated. >>>>> >>>>> When opening omnistat_setup_web.pl I get the following error message >>> "http >>>>> error in http eval of omnistat_setup_web.pl: 12/03/10 11:40:36 AM: >>>>> Omnistat[1]->send_cmd did not get ack reply to command 01 20 3b 0e 6a >>> () at >>>>> /opt/misterhouse/mh/bin/../lib/Omnistat.pm line 554, line 216." >>> >>> I should add that '()' shows that no reply at all was received from the >>> omnistat, so you may indeed have a basic communication problem. >>> >>> Marc >>> -- >>> "A mouse is a device used to point at the xterm you want to type in" - >>> A.S.R. >>> Microsoft is to operating systems & security .... >>> .... what McDonalds is to gourmet >>> cooking >>> Home page: http://marc.merlins.org/ >>> >>> ...snip... |
From: Neil C. <nc...@li...> - 2010-12-05 19:22:08
|
On 12/05/2010 02:00 PM, Marc MERLIN wrote: > On Sun, Dec 05, 2010 at 09:56:09AM -0600, Joel Davidson wrote: >> As I recall the Omnistat serial i/f gets it power from one of the host >> port status lines, and some serial ports cheat and don't drive their >> status lines to the +/-12 or so volts in the rs-232 spec. I used to use >> a Moxa pci 8 port serial board with my rc-80 and it worked fine. Now >> I'm using a ByteRunner usb 8 port serial adapter and it too works ok. >> For starters you might try a different serial port if you can. > Joel is right, I had the same problem with one of my USB 8 port hubs: it did > not provide enough power and the communication did not work. > I switched to a single USB to serial adapter, and things worked after that. _Sometimes_ a powered USB hub can help but I wouldn't totally trust it. Power stealing can be a lot of trouble. -- Linux Home Automation Neil Cherry nc...@li... http://www.linuxha.com/ Main site http://linuxha.blogspot.com/ My HA Blog Author of: Linux Smart Homes For Dummies |
From: Joel D. <jr...@io...> - 2010-12-05 19:25:50
|
On Sun, 5 Dec 2010, it would appear that Neil Cherry wrote: > On 12/05/2010 02:00 PM, Marc MERLIN wrote: >> On Sun, Dec 05, 2010 at 09:56:09AM -0600, Joel Davidson wrote: > >>> As I recall the Omnistat serial i/f gets it power from one of the host >>> port status lines, and some serial ports cheat and don't drive their >>> status lines to the +/-12 or so volts in the rs-232 spec. I used to use >>> a Moxa pci 8 port serial board with my rc-80 and it worked fine. Now >>> I'm using a ByteRunner usb 8 port serial adapter and it too works ok. >>> For starters you might try a different serial port if you can. > >> Joel is right, I had the same problem with one of my USB 8 port hubs: it did >> not provide enough power and the communication did not work. >> I switched to a single USB to serial adapter, and things worked after that. > > _Sometimes_ a powered USB hub can help but I wouldn't totally trust > it. Power stealing can be a lot of trouble. True. The 8 port usb-serial adapter is powered and it solved a number of problems with power-stealing serial devices. |
From: Karl S. <kar...@gm...> - 2010-12-05 23:25:14
|
I have tried it using both on board serial ports from a ASUS A7N8X-E and I also have tried using a usb to serial on both my server and off my laptop with and without a powered usb hub. On Sun, Dec 5, 2010 at 2:25 PM, Joel Davidson <jr...@io...> wrote: > On Sun, 5 Dec 2010, it would appear that Neil Cherry wrote: > > > On 12/05/2010 02:00 PM, Marc MERLIN wrote: > >> On Sun, Dec 05, 2010 at 09:56:09AM -0600, Joel Davidson wrote: > > > >>> As I recall the Omnistat serial i/f gets it power from one of the host > >>> port status lines, and some serial ports cheat and don't drive their > >>> status lines to the +/-12 or so volts in the rs-232 spec. I used to > use > >>> a Moxa pci 8 port serial board with my rc-80 and it worked fine. Now > >>> I'm using a ByteRunner usb 8 port serial adapter and it too works ok. > >>> For starters you might try a different serial port if you can. > > > >> Joel is right, I had the same problem with one of my USB 8 port hubs: it > did > >> not provide enough power and the communication did not work. > >> I switched to a single USB to serial adapter, and things worked after > that. > > > > _Sometimes_ a powered USB hub can help but I wouldn't totally trust > > it. Power stealing can be a lot of trouble. > > True. The 8 port usb-serial adapter is powered and it solved a number > of problems with power-stealing serial devices. > > > > ------------------------------------------------------------------------------ > What happens now with your Lotus Notes apps - do you make another costly > upgrade, or settle for being marooned without product support? Time to move > off Lotus Notes and onto the cloud with Force.com, apps are easier to > build, > use, and manage than apps on traditional platforms. Sign up for the Lotus > Notes Migration Kit to learn more. http://p.sf.net/sfu/salesforce-d2d > ________________________________________________________ > To unsubscribe from this list, go to: > http://sourceforge.net/mail/?group_id=1365 > > |
From: Joel D. <jr...@io...> - 2010-12-06 01:05:03
|
Have you double checked your cable? Sounds like you might have a connectivity problem. Here's an old document from HAI that has some useful info: www.io.com/~jrd/ha/ThermostatCommCables.pdf Joel On Sun, 5 Dec 2010, it would appear that Karl Suchy wrote: > I have tried it using both on board serial ports from a ASUS A7N8X-E and I > also have tried using a usb to serial on both my server and off my laptop > with and without a powered usb hub. > > On Sun, Dec 5, 2010 at 2:25 PM, Joel Davidson <jr...@io...> wrote: > >> On Sun, 5 Dec 2010, it would appear that Neil Cherry wrote: >> >>> On 12/05/2010 02:00 PM, Marc MERLIN wrote: >>>> On Sun, Dec 05, 2010 at 09:56:09AM -0600, Joel Davidson wrote: >>> >>>>> As I recall the Omnistat serial i/f gets it power from one of the host >>>>> port status lines, and some serial ports cheat and don't drive their >>>>> status lines to the +/-12 or so volts in the rs-232 spec. I used to >> use >>>>> a Moxa pci 8 port serial board with my rc-80 and it worked fine. Now >>>>> I'm using a ByteRunner usb 8 port serial adapter and it too works ok. >>>>> For starters you might try a different serial port if you can. >>> >>>> Joel is right, I had the same problem with one of my USB 8 port hubs: it >> did >>>> not provide enough power and the communication did not work. >>>> I switched to a single USB to serial adapter, and things worked after >> that. >>> >>> _Sometimes_ a powered USB hub can help but I wouldn't totally trust >>> it. Power stealing can be a lot of trouble. >> >> True. The 8 port usb-serial adapter is powered and it solved a number >> of problems with power-stealing serial devices. >> |
From: Karl S. <kar...@gm...> - 2010-12-06 18:46:53
|
Definetly have checked the cable. Using RealTerm I have verified DCD, DTR, and DSR are tied together and going to the correct pin on the RC-1000, that RTS and CTS are tied together. I have also verified that TXD, RXD, and GND end up on the correct pins on the RC-1000. HAI also pointed me to Serial Device Tester, http://www.hackconsulting.com/register.html, from H.A.C.K Consulting. Both RealTerm and SD Tester have come in useful in trying to get a response from the OmniStat. I am starting to think that the serial communications on the RC-1000 are dead. Thanks for all the advice. Karl On Sun, Dec 5, 2010 at 8:04 PM, Joel Davidson <jr...@io...> wrote: > Have you double checked your cable? Sounds like you might have a > connectivity problem. > > Here's an old document from HAI that has some useful info: > www.io.com/~jrd/ha/ThermostatCommCables.pdf<http://www.io.com/%7Ejrd/ha/ThermostatCommCables.pdf> > > Joel > > On Sun, 5 Dec 2010, it would appear that Karl Suchy wrote: > > > I have tried it using both on board serial ports from a ASUS A7N8X-E and > I > > also have tried using a usb to serial on both my server and off my laptop > > with and without a powered usb hub. > > > > On Sun, Dec 5, 2010 at 2:25 PM, Joel Davidson <jr...@io...> wrote: > > > >> On Sun, 5 Dec 2010, it would appear that Neil Cherry wrote: > >> > >>> On 12/05/2010 02:00 PM, Marc MERLIN wrote: > >>>> On Sun, Dec 05, 2010 at 09:56:09AM -0600, Joel Davidson wrote: > >>> > >>>>> As I recall the Omnistat serial i/f gets it power from one of the > host > >>>>> port status lines, and some serial ports cheat and don't drive their > >>>>> status lines to the +/-12 or so volts in the rs-232 spec. I used to > >> use > >>>>> a Moxa pci 8 port serial board with my rc-80 and it worked fine. Now > >>>>> I'm using a ByteRunner usb 8 port serial adapter and it too works ok. > >>>>> For starters you might try a different serial port if you can. > >>> > >>>> Joel is right, I had the same problem with one of my USB 8 port hubs: > it > >> did > >>>> not provide enough power and the communication did not work. > >>>> I switched to a single USB to serial adapter, and things worked after > >> that. > >>> > >>> _Sometimes_ a powered USB hub can help but I wouldn't totally trust > >>> it. Power stealing can be a lot of trouble. > >> > >> True. The 8 port usb-serial adapter is powered and it solved a > number > >> of problems with power-stealing serial devices. > >> > > > ------------------------------------------------------------------------------ > What happens now with your Lotus Notes apps - do you make another costly > upgrade, or settle for being marooned without product support? Time to move > off Lotus Notes and onto the cloud with Force.com, apps are easier to > build, > use, and manage than apps on traditional platforms. Sign up for the Lotus > Notes Migration Kit to learn more. http://p.sf.net/sfu/salesforce-d2d > ________________________________________________________ > To unsubscribe from this list, go to: > http://sourceforge.net/mail/?group_id=1365 > > |
From: Joel D. <jr...@io...> - 2010-12-06 18:50:01
|
Hmm, is the 'stat under warranty? Will HAI test/repair/replace it for you? Joel On Mon, 6 Dec 2010, it would appear that Karl Suchy wrote: > Definetly have checked the cable. Using RealTerm I have verified DCD, DTR, > and DSR are tied together and going to the correct pin on the RC-1000, that > RTS and CTS are tied together. I have also verified that TXD, RXD, and GND > end up on the correct pins on the RC-1000. HAI also pointed me to Serial > Device Tester, http://www.hackconsulting.com/register.html, from H.A.C.K > Consulting. Both RealTerm and SD Tester have come in useful in trying to > get a response from the OmniStat. I am starting to think that the serial > communications on the RC-1000 are dead. Thanks for all the advice. > > Karl > > On Sun, Dec 5, 2010 at 8:04 PM, Joel Davidson <jr...@io...> wrote: > >> Have you double checked your cable? Sounds like you might have a >> connectivity problem. >> >> Here's an old document from HAI that has some useful info: >> www.io.com/~jrd/ha/ThermostatCommCables.pdf<http://www.io.com/%7Ejrd/ha/ThermostatCommCables.pdf> >> >> Joel >> >> On Sun, 5 Dec 2010, it would appear that Karl Suchy wrote: >> >>> I have tried it using both on board serial ports from a ASUS A7N8X-E and >> I >>> also have tried using a usb to serial on both my server and off my laptop >>> with and without a powered usb hub. >>> >>> On Sun, Dec 5, 2010 at 2:25 PM, Joel Davidson <jr...@io...> wrote: >>> >>>> On Sun, 5 Dec 2010, it would appear that Neil Cherry wrote: >>>> >>>>> On 12/05/2010 02:00 PM, Marc MERLIN wrote: >>>>>> On Sun, Dec 05, 2010 at 09:56:09AM -0600, Joel Davidson wrote: >>>>> >>>>>>> As I recall the Omnistat serial i/f gets it power from one of the >> host >>>>>>> port status lines, and some serial ports cheat and don't drive their >>>>>>> status lines to the +/-12 or so volts in the rs-232 spec. I used to >>>> use >>>>>>> a Moxa pci 8 port serial board with my rc-80 and it worked fine. Now >>>>>>> I'm using a ByteRunner usb 8 port serial adapter and it too works ok. >>>>>>> For starters you might try a different serial port if you can. >>>>> >>>>>> Joel is right, I had the same problem with one of my USB 8 port hubs: >> it >>>> did >>>>>> not provide enough power and the communication did not work. >>>>>> I switched to a single USB to serial adapter, and things worked after >>>> that. >>>>> >>>>> _Sometimes_ a powered USB hub can help but I wouldn't totally trust >>>>> it. Power stealing can be a lot of trouble. >>>> >>>> True. The 8 port usb-serial adapter is powered and it solved a >> number >>>> of problems with power-stealing serial devices. >>>> |
From: Karl S. <kar...@gm...> - 2010-12-17 21:54:03
|
Just wanted to give everyone an update on the Omnistat 2. Sent it back to HAI for a replacement. They were very courteous and speedy with the process. Got the new one today, plugged it in and had is working in no time with the current version of omnistat.pm. Marc- I plan on working the changes/updates for the Omnistat2 into the current code in the next few days and will send it your way. And a thank you to everyone that helped me figure out what was going on. Karl On Mon, Dec 6, 2010 at 6:51 PM, Mickey Argo <mh...@ar...> wrote: > Check http://www.cortexa.com/knowledgebase/multiplethermostats > > > Mickey Argo > > Karl Suchy wrote: > > Mickey, > > Thanks for sending that out. I will work that in once I get the OmniStat > Communicating with the computer. Do you recall having to do anything > special with the OmniStat 2 to get it to talk? My version does not have a > communication jumper on the pcb, instead there was a wire jumper between the > "N/C" and "black" pins as labeled on the pcb. In settings it is set up for > serial at 300 baud. I have tried using Realterm to send commands to the > Stat with no response back. > > Thanks > Karl > > On Sat, Dec 4, 2010 at 10:15 PM, Mickey Argo <mh...@ar...> wrote: > >> I didn't exactly code this for wide distribution...there are complications >> with my employer and areas they might be working on in the near future. If >> you want to clean this up for inclusion into the trunk code, please feel >> free and take the credit! Compare the attached Omnistat.pm with the trunk >> version. omnistat2.pl is in my code directory. I have the following in >> my private ini: >> >> Omnistat_serial_port=COM1 >> Omnistat_24hr=1 >> # disable internal program >> #Omnistat_non_program=[0,1] >> Omnistat_no_stat_log=3 >> Omnistat_stat_log=$config_parms{data_dir}/omnistatlog.txt >> omnistat_allowed_errors = 999999999999 >> hvac_allowed_errors = 999999999999 >> show_all_errors = yes >> >> >> Mickey Argo wrote: >> >> Karl, >> >> I have this working on my setup...I'll try to send out the code changes >> later today. I did add some new stuff, like changing the background color >> based on the outside air temperature. >> >> Mickey Argo >> >> On 12/3/10 12:11 PM, Karl Suchy wrote: >> >> Thanks for the feed back. The Omnistat is in the correct communication >> mode. I think I will start messing around with Omnistat.pm and trying to >> send simple get and set commands and see what I get back. >> >> Karl >> >> On Fri, Dec 3, 2010 at 12:46 PM, Marc MERLIN <ma...@me...> wrote: >> >>> On Fri, Dec 03, 2010 at 09:45:18AM -0800, Marc MERLIN wrote: >>> > On Fri, Dec 03, 2010 at 12:35:27PM -0500, Karl Suchy wrote: >>> > > I am trying to get a single Omnistat 2 functioning with MisterHouse. >>> Any >>> > > help will be greatly appreciated. >>> > > >>> > > When opening omnistat_setup_web.pl I get the following error message >>> "http >>> > > error in http eval of omnistat_setup_web.pl: 12/03/10 11:40:36 AM: >>> > > Omnistat[1]->send_cmd did not get ack reply to command 01 20 3b 0e 6a >>> () at >>> > > /opt/misterhouse/mh/bin/../lib/Omnistat.pm line 554, line 216." >>> >>> I should add that '()' shows that no reply at all was received from the >>> omnistat, so you may indeed have a basic communication problem. >>> >>> Marc >>> -- >>> "A mouse is a device used to point at the xterm you want to type in" - >>> A.S.R. >>> Microsoft is to operating systems & security .... >>> .... what McDonalds is to gourmet >>> cooking >>> Home page: http://marc.merlins.org/ >>> >>> >>> ------------------------------------------------------------------------------ >>> Increase Visibility of Your 3D Game App & Earn a Chance To Win $500! >>> Tap into the largest installed PC base & get more eyes on your game by >>> optimizing for Intel(R) Graphics Technology. Get started today with the >>> Intel(R) Software Partner Program. Five $500 cash prizes are up for >>> grabs. >>> http://p.sf.net/sfu/intelisp-dev2dev >>> ________________________________________________________ >>> To unsubscribe from this list, go to: >>> http://sourceforge.net/mail/?group_id=1365 >>> >>> >> >> ------------------------------------------------------------------------------ >> Increase Visibility of Your 3D Game App & Earn a Chance To Win $500! >> Tap into the largest installed PC base & get more eyes on your game by >> optimizing for Intel(R) Graphics Technology. Get started today with the >> Intel(R) Software Partner Program. Five $500 cash prizes are up for grabs.http://p.sf.net/sfu/intelisp-dev2dev >> >> >> ________________________________________________________ >> To unsubscribe from this list, go to: http://sourceforge.net/mail/?group_id=1365 >> >> ------------------------------ >> >> ------------------------------------------------------------------------------ >> What happens now with your Lotus Notes apps - do you make another costly >> upgrade, or settle for being marooned without product support? Time to move >> off Lotus Notes and onto the cloud with Force.com, apps are easier to build, >> use, and manage than apps on traditional platforms. Sign up for the Lotus >> Notes Migration Kit to learn more. http://p.sf.net/sfu/salesforce-d2d >> >> ------------------------------ >> >> ________________________________________________________ >> To unsubscribe from this list, go to: http://sourceforge.net/mail/?group_id=1365 >> >> >> # Category=HVAC >> >> =begin comment >> >> Mickey Argo July 2010 >> >> This code is to run the Omnistat2 thermostats. I used code credited to >> the following people; >> Kent Noonan, Joel Davidson, Dan Arnold, Marc MERLIN >> >> Not done in this module; >> Handle multiple thermostats >> >> >> =cut >> >> >> $omnistat2=new Omnistat; >> >> my $saved_setpoint_cool = 0; >> my $saved_setpoint_heat = 0; >> my $saved_program_mode = ''; >> my $saved_HVAC_command = ''; >> my $saved_indoor_temp = 0; >> my $thermo_model = ''; >> my $program_mode = ''; >> my $hvac_return_data = ''; >> #one time settings >> if ($Reload or $Reread) { >> #$omnistat2->cooling_anticipator('10'); Not used in two stage >> #$omnistat2->heating_anticipator('10'); Not used in 2 stage >> save_HVAC ("[PROGRAM] System had restarted"); >> $omnistat2->cooling_cycle_time('8'); >> $omnistat2->heating_cycle_time('8'); >> $omnistat2->day_cool_setpoint('80'); >> $omnistat2->day_heat_setpoint('68'); >> $omnistat2->night_cool_setpoint('81'); >> $omnistat2->night_heat_setpoint('66'); >> $omnistat2->away_cool_setpoint('82'); >> $omnistat2->away_heat_setpoint('65'); >> $omnistat2->vaca_cool_setpoint('84'); >> $omnistat2->vaca_heat_setpoint('63'); >> $omnistat2->outdoor_temp($Weather{TempOutdoor}); >> $hvac_return_data = $omnistat2->get_stat_type; >> save_HVAC ("[PROGRAM] Thermostat Model is $hvac_return_data"); >> $hvac_return_data = $omnistat2->get_program_mode; >> save_HVAC ("[PROGRAM] Program mode is $hvac_return_data"); >> $hvac_return_data = $omnistat2->get_occupancy_mode; >> save_HVAC ("[PROGRAM] Occupancy is set to $hvac_return_data"); >> $hvac_return_data = $omnistat2->get_fan_mode; >> save_HVAC ("[PROGRAM] Fan is set to $hvac_return_data"); >> } >> >> if ($New_Day) { >> $omnistat2->set_time; >> } >> >> >> >> $v_omnistat_fan=new Voice_Cmd('Set Thermostat fan [on,auto,cycle]'); >> if ($state = said $v_omnistat_fan) { >> $omnistat2->fan($state); >> speak("app=weather Setting HVAC fan to $state"); >> } >> $v_omnistat_hold=new Voice_Cmd('Set Thermostat hold [on,off]'); >> if ($state = said $v_omnistat_hold) { >> $omnistat2->hold($state); >> } >> $v_omnistat_mode=new Voice_Cmd('Set Thermostat mode >> [off,heat,cool,auto]'); >> if ($state = said $v_omnistat_mode) { >> $omnistat2->mode($state); >> speak("app=weather Setting HVAC mode to $state"); >> } >> $v_omnistat_cool_sp=new Voice_Cmd("Set Thermostat cool setpoint to >> [74,76,78,79,80,81,82,83]"); >> if ($state = said $v_omnistat_cool_sp) { >> $omnistat2->cool_setpoint($state); >> speak("app=weather Setting HVAC cool set point to $state degrees"); >> } >> $v_omnistat_heat_sp=new Voice_Cmd("Set Thermostat heat setpoint to >> [65,66,67,68,69,70,71,72]"); >> if ($state = said $v_omnistat_heat_sp) { >> $omnistat2->heat_setpoint($state); >> speak("app=weather Setting HVAC heat set point to $state degrees"); >> } >> $v_omnistat_get_occupancy=new Voice_Cmd("What is the occupancy mode"); >> if ($state = said $v_omnistat_get_occupancy) { >> $hvac_return_data = $omnistat2->get_occupancy_mode; >> speak("app=weather The occupancy mode is $hvac_return_data"); >> } >> $v_omnistat_set_occupancy=new Voice_Cmd("Set occupancy mode to >> [day,night,away,vacation]"); >> if ($state = said $v_omnistat_set_occupancy) { >> $omnistat2->set_occupancy_mode($state); >> save_HVAC ("[PROGRAM] System occupancy mode set to $state"); >> speak("app=weather Setting occupancy mode to $state"); >> } >> $v_omnistat_set_program_mode=new Voice_Cmd("Set program mode to >> [none,schedule,occupancy]"); >> if ($state = said $v_omnistat_set_program_mode) { >> $omnistat2->set_program_mode($state); >> save_HVAC ("[PROGRAM] System program mode set to $state"); >> speak("app=weather Setting program mode to $state"); >> } >> >> $v_omnistat_background=new Voice_Cmd("Set Thermostat background to >> [Blue,Green,Purple,Red,Orange,Yellow]"); >> if ($state = said $v_omnistat_background) { >> my $background_hex = "0x00"; >> if ($state = 'Blue'){ >> $background_hex = "0x44"; >> } elsif ($state = 'Green'){ >> $background_hex = "0x25"; >> } elsif ($state = 'Purple'){ >> $background_hex = "0x5a"; >> } elsif ($state = 'Red'){ >> $background_hex = "0x01"; >> } elsif ($state = 'Orange'){ >> $background_hex = "0x03"; >> } elsif ($state = 'Yellow'){ >> $background_hex = "0x05"; >> } >> $omnistat2->set_reg("0x8c", $background_hex); >> } >> >> >> # update data once a minute, log changes only. >> if ($New_Minute) { >> # we make the extended group1 call that also retreives the stat's >> output status >> my ($cool_sp, $heat_sp, $mode, $fan, $hold, $temp, $output) = >> $omnistat2->read_group1("true"); >> >> my $stat_type = $omnistat2->get_stat_type; >> # This mashes $hold and $mode together from registers cached in the >> group1 call and outputs a combined string >> $mode = $omnistat2->get_mode; >> >> #Save temps every minute >> save_HVAC ("[TEMP-IN] $temp"); >> # save_HVAC ("[TEMP-OUT] $Weather{TempOutdoor}"); >> >> #Update the thermostat data >> $omnistat2->outdoor_temp($Weather{TempOutdoor}); >> >> >> #check and log changed data >> if ($saved_setpoint_cool ne $cool_sp){ >> save_HVAC ("[SETPOINT] Cool Setpoint has been changed to >> $cool_sp"); >> $saved_setpoint_cool = $cool_sp; >> } >> if ($saved_setpoint_heat ne $heat_sp){ >> save_HVAC ("[SETPOINT] Heat Setpoint has been changed to >> $heat_sp"); >> $saved_setpoint_heat = $heat_sp; >> } >> if ($saved_HVAC_command ne $output){ >> save_HVAC ("[HVAC] HVAC Command is now $output"); >> $saved_HVAC_command = $output; >> } >> if ($saved_program_mode ne $mode){ >> save_HVAC ("[HVAC] HVAC Mode is now $mode"); >> $saved_program_mode = $mode; >> } >> } >> >> #Change the backlight >> if (new_minute 15) { >> my $background_color = "0x00"; >> if ($Weather{TempOutdoor} >= 95){ >> $background_color = "0x01"; #Red >> } >> elsif ($Weather{TempOutdoor} >= 85){ >> $background_color = "0x05"; #Yellow >> } >> elsif ($Weather{TempOutdoor} >= 65){ >> $background_color = "0x25"; #Green >> } >> elsif ($Weather{TempOutdoor} >= 55){ >> $background_color = "0x5a"; #Purple >> } >> elsif ($Weather{TempOutdoor} < 55){ >> $background_color = "0x44"; #Blue >> } >> else{ >> $background_color = "0x03"; #Orange >> } >> $omnistat2->set_reg("0x8c", $background_color); >> } >> >> sub normal_HVAC_control >> { >> $omnistat2->set_occupancy_mode('day'); >> print_log "[HVAC] Setting HVAC to day setbacks" ; >> save_HVAC ("[PROGRAM] Occupancy is set to day"); >> } >> >> sub setback_HVAC_control { >> $omnistat2->set_occupancy_mode('away'); >> print_log "[HVAC] Setting HVAC to away setbacks"; >> save_HVAC ("[PROGRAM] Occupancy is set to away"); >> } >> >> sub setback_night_HVAC_control { >> $omnistat2->set_occupancy_mode('night'); >> print_log "[HVAC] Setting HVAC to night setbacks"; >> save_HVAC ("[PROGRAM] Occupancy is set to night"); >> } >> >> =begin comment >> >> # 132 columns max >> >> 123456789112345678921234567893123456789412345678951234567896123456789712345678981234567899123456789012345678911234567892123456789312 >> >> # this file, if distributed separately, replaces >> misterhouse/lib/Omnistat.pm >> >> Module for HAI RC-Series Electronic Communicating Thermostats (Omnistat) >> Specifically written with/for RC-80 but should work with any of them. >> http://www.homeauto.com/Products/HAIAccessories/Omnistat/rc80.htm >> >> Newer Omnistat2 thermostats have a slightly different protocol and may >> need >> some work. They look nicer, but they are pricier (vs $50 for an RC-80 on >> ebay) >> and don't offer functionality that's useful to most people -- merlin >> >> >> ################### >> >> Use these mh.ini parameters to enable this code: >> Omnistat_serial_port=/dev/ttyUSB0 >> >> There are optional settings for the Omnistat for mh.private.ini: >> If these settings aren't in mh.private.ini the default is 0 (false) >> >> # use celcius for temperatures >> Omnistat_celcius=[0,1] >> # use 24hour clock for times >> Omnistat_24hr=[0,1] >> # disable internal program >> Omnistat_non_program=[0,1] >> # Real Time Pricing mode >> Omnistat_rtp_mode=[0,1] >> # hide clock on thermostat >> Omnistat_hide_clock=[0,1] >> # You can set how much gets logged >> Omnistat_no_stat_log=[0,1,2,3] >> >> # For debugging, add omnistat to debug in mh.private.ini, as in >> debug=insteon,omnistat >> # instead of debug=insteon >> >> This module is used 2 ways >> 1) from a web interface >> 2) from mh/code/public/omnistat.pl which you need to install in your code >> directory >> >> >> >> ================================================================================ >> TODOs >> >> ================================================================================ >> >> TODO: Add hooks for caching instead of relying on pl file >> TODO: ini parameter for range of registers to read (default to temp) >> TODO: Adjust clock speed? Not sure if possible (reg 14), may need to be >> done in pl >> TODO: Modify set_reg to accept muliple registers (hasn't been really >> needed so far) >> TODO: Ini Parameter to turn on/off set outdoor temp (may be in pl) >> TODO: The sleep situation has been much improved, but if someone smart >> could replace >> the sleep with a proper callback so as not to stall mh, that would >> rule >> >> >> ================================================================================ >> Changelog >> >> ================================================================================ >> >> 2009/08/03 - Marc MERLIN >> ======================== >> - send_cmd is now a method too so that we can compare the return value >> against $$self{addr} >> - improved command ack parsing failure error reporting >> - oops, got omnistat_log function to actually respect log_level >> - added omnistat_debug function >> - hold function now only sets hold if it's different from cached value, >> this is because we get >> frequent calls to hold off and want to avoid actually sending them if >> hold was already off >> - restore|cool|heat_setpoints now unhold the that before programming it >> (or it won't work) and >> then put it back on hold depending on the Omnistat_set_does_not_hold >> setting in mh.private.ini >> >> >> 2009/07/25 - Marc MERLIN >> ======================== >> - optimized sleep/wait in send_cmd to be as little as needed. It is now as >> cheap to >> read 2 registers separately as 10 registers in a row: 0.666s >> (before, reading 2 registers separately took 4 seconds) >> - NOTE: if you were calling send_cmd, you need to change your call to >> prepend the number >> of characters you expect back (this is needed by the timing improvements) >> - fixed get_stat_type method >> - A lot more error handling and status reporting, including making sure >> that you get all >> the data back that you're supposed to get, and that set_reg actually gets >> some kind of ack >> - omnistat.pl allows for temporarily changing setpoints until the next >> schedule change with >> Omnistat_set_does_not_hold=1 in mh.private.ini >> - omnistat_log allows for logging stat data with Omnistat_no_stat_log >> which defaults to 1 >> - fixed an insidious bug in set_time that sent an integer for the week >> instead of an hex string >> perl helpfully converted that and then tried to H2 pack "5" which is >> invalid and sent a short command >> - added checks for set_reg and friends to make sure registers and values >> are hex strings >> - auto convertions in perl are too helpful and can bite you in the butt, >> so all register values are now >> required to be passed as 0xXX whether they might have worked before, or >> not >> >> >> 2009/07/22 - Marc MERLIN >> ======================== >> - ripped out old one register update per minute cache >> - added on demand cache and cache prefetching for setpoints and >> temperature >> - fixed bugs / cleanups >> - merged read_group1 with read_reg >> - fixed state change messages on multi reg fetches >> - made use of caching functions strongly encouraged :) >> - added important get_stat_output method to actually know what the stat is >> telling >> your HVAC system to do >> - get_stat_model function >> >> >> Dan Arnold May 2009 >> =================== >> Added state processing >> >> NOTE: State changes will not take effect until all registers have been >> cached >> -> no more -- merlin >> All of the states that may be set: >> all_registers_cached: All of the registers have been read into the >> cache, you can read any register without penalty >> filter_reminder: Filter reminder has expired >> -> obsolete -- merlin >> temp_change: Inside temperature changed >> (call get_temp() to get value) >> heat_sp_change: Heat setpoint was changed >> (call get_heat_sp() to get value). >> cool_sp_change: Cool setpoint was changed >> (call get_cool_sp() to get value). >> mode_change: System mode changed >> (call get_mode() to get value). >> fan_mode_change: Fan mode changed >> (call get_fan_mode() to get value). >> >> New/modified functions available: >> mode(): >> Sets system mode to argument: 'off', 'heat', 'cool', 'auto', >> 'program_heat', 'program_cool', 'program_auto' (program_ implies >> hold=off) >> get_mode(): >> Returns the last mode returned by poll_mode(). >> fan(): >> Sets fan to 'on' or 'auto' >> get_fan_mode(): >> Returns the current fan mode (fan_on or fan_auto) >> cool_setpoint(): >> Sets a new cool setpoint. >> get_cool_sp(): >> Returns the current cool setpoint. >> heat_setpoint(): >> Sets a new heat setpoint. >> get_heat_sp(): >> Returns the current heat setpoint. >> get_temp(): >> Returns the current temperature at the thermostat. >> get_filter_reminder(): >> Returns the number of days until the furnace filter needs to be >> replaced >> restore_setpoints(): >> Returns the heat/cool setpoints to what they would have been if the >> thermostat were running on schedule >> >> >> Dan Arnold March 2009 >> ====================== >> Added caching of registers >> >> >> Dan Arnold February 2009 >> ========================= >> Added function to set registers >> Added time translation for thermostat programming (12h or 24h format based >> on Omnistat_24hr config param) >> Added ability to set outside temp to display on thermostat >> Added the ability to translate to/from Celcius (depends on the >> Omnistat_celcius config param) >> Modified set procedures to use set_reg >> Modified temp translation to use math rather than a lookup table (needed >> to cover possible outside temps) >> Fixed a bug in read_reg >> >> >> Joel Davidson February 2009 >> ========================= >> Corrected bad syntax in mode comparison logic in read_group1. >> >> >> Joel Davidson December 2005 >> ========================= >> Re-ordered routines to avoid run-time error from prototyped subroutines. >> Modified comparison values in read_group1 tests to fix users problem with >> incorrect compare results. Added additional comments. Added addressing >> mods to support multiple thermostats. Removed calls to set_time and >> display in serial_startup since they cause a funky runtime error. >> >> >> Joel Davidson June 2004 >> ========================= >> Modified checksum() to return 8 bit checksum. Fixed set_time. >> Added read_group1 to return register group 1 values (setpoints, >> modes, current temperature). Added generic function to read any >> specified register(s), read_register(address, [# of regs]). >> Changed Omnistat_run_program config option to Omnistat_non_program. >> Setting to a 1 disables thermostat internal program. Changed >> Omnistat_show_clock to Omnistat_hide_clock. 1 hides clock and filter >> display. >> >> >> Kent Noonan Jan 2002 >> ===================== >> I have another module for misterhouse. But it is not finished. This is a >> module for controling HAI Omnistat Communicating thermostats. It was >> specifically written against the RC80 but as far as I can tell it should >> work with any of them. There is a problem with it. I am not finished with >> it. I started working on it, then moved to a house with an older heater >> that the thermostat doesn't work with. It's going to be a couple of years >> before we can upgrade the heater, so I thought I'd send this incase >> somebody else wanted to continue where I left off before I can get back to >> it again. Right now I cant even gaurantee that it works at all, but I >> think it did.. >> >> >> >> >> >> ######################################################## >> >> Below is a list of registers for reference: >> >> # INTERNAL REGISTERS (RO = READ ONLY) >> 0 (00) - Thermostat address (ro) (1 - 127) >> 1 (01) - Communications mode (ro) (0, 1, 8 or 24) >> 2 (02) - System options (ro) >> 3 (03) - Display options >> 4 (04) - Calibration offset (1 to 59, 30=no change - ½ C units) >> 5 (05) - Cool setpoint low limit (Omnitemp units) >> 6 (06) - Heat setpoint high limit (Omnitemp units) >> 7 (07) - Reserved >> 8 (08) - Reserved >> 9 (09) - Cooling anticipator (0 to 30) (RC-80, -81, -90, -91 only) >> 10 (0A) - Heating anticipator (0 to 30) (RC-80, -81, -90, -91 only), Stage >> 2 differential (RC-112) >> 11 (0B) - Cooling cycle time (2 - 30 minutes) >> 12 (0C) - Heating cycle time (2 - 30 minutes) >> 13 (0D) - Aux heat differential, (RC-100, -101, -112), Stage 2 >> differential (RC-120, -121, -122) (Omnitemp units) >> 14 (0E) - Clock adjust (seconds/day) 1=-29, 30=0, 59=+29 >> 15 (0F) - Days remaining until filter reminder >> 16 (10) - System run time, current week - hours >> 17 (11) - System run time, last week - hours >> >> # Registers 18 - 20 are used only in models with real time pricing. >> 18 (12) - Real time pricing setback - Mid (Omnitemp units) >> 19 (13) - High >> 20 (14) - Critical >> >> # Programming registers >> 21 (15) - weekday morning time >> 22 (16) - cool setpoint >> 23 (17) - heat setpoint >> 24 (18) - weekday day time >> 25 (19) - cool setpoint >> 26 (1A) - heat setpoint >> 27 (1B) - weekday evening time >> 28 (1C) - cool setpoint >> 29 (1D) - heat setpoint >> 30 (1E) - weekday night time >> 31 (1F) - cool setpoint >> 32 (20) - heat setpoint >> 33 (21) - Saturday morning time >> 34 (22) - cool setpoint >> 35 (23) - heat setpoint >> 36 (24) - Saturday day time >> 37 (25) - cool setpoint >> 38 (26) - heat setpoint >> 39 (27) - Saturday evening time >> 40 (28) - cool setpoint >> 41 (29) - heat setpoint >> 42 (2A) - Saturday night time >> 43 (2B) - cool setpoint >> 44 (2C) - heat setpoint >> 45 (2D) - Sunday morning time >> 46 (2E) - cool setpoint >> 47 (2F) - heat setpoint >> 48 (30) - Sunday day time >> 49 (31) - cool setpoint >> 50 (32) - heat setpoint >> 51 (33) - Sunday evening time >> 52 (34) - cool setpoint >> 53 (35) - heat setpoint >> 54 (36) - Sunday night time >> 55 (37) - cool setpoint >> 56 (38) - heat setpoint >> 57 (39) - Reserved - do not write >> >> # this one is lost, it kind of belongs with 0x41 below >> 58 (3A) - Day of week (0=Monday - 6=Sunday) >> >> # group1 data start >> 59 (3B) - Cool setpoint (current) >> 60 (3C) - Heat setpoint (current) >> 61 (3D) - Thermostat mode (0=off, 1=heat, 2=cool, 3=auto) (4=Emerg heat: >> RC-100, -101, -112 only) >> 62 (3E) - Fan status (0=auto 1=on) >> 63 (3F) - Hold (0=off 255=on) >> 64 (40) - Actual temperature in Omni format >> # group1 data stop. >> # Would have been so very nice is 0x48 were in group1 since you typically >> want to query that often too >> # to know what commands your stat is sending to your HVAC system :-/ >> >> 65 (41) - Seconds 0 - 59 >> 66 (42) - Minutes 0 - 59 >> 67 (43) - Hours 0 - 23 >> 68 (44) - Outside temperature (see below) >> 69 (45) - Reserved >> 70 (46) - Real time pricing mode (0=lo, 1=mid, 2=high, 3=critical) (RC-81, >> -91, -101, -121 only) >> 71 (47) - (ro) current mode (0=off 1=heat 2=cool) >> 72 (48) - (ro) output status >> 73 (49) - (ro) model of thermostat >> >> # 0x48: reflects the positions of the control relays on the thermostat. >> bit 0: heat/cool bit - set for heat, clear for cool >> bit 1: auxiliary heat bit - set for on, clear for off (RC-100, -101, -112 >> only) >> bit 2: stage 1 run bit - set for on, clear for off >> bit 3: fan bit - set for on, clear for off >> bit 4: stage 2 run bit: set for on, clear for off (RC-112, 120, 121, 122 >> only) >> >> # 0x49: thermostat model >> RC-80 0 >> RC-81 1 >> RC-90 8 >> RC-91 9 >> RC-100 16 >> RC-101 17 >> RC-112 34 >> RC-120 48 >> RC-121 49 >> RC-122 50 >> >> Outside Temperature: writing to the outside temperature register will >> cause the thermostat to display the >> outside temperature every 4 seconds. The thermostat will stop displaying >> the outside temperature if this >> register is not refreshed at least every 5 minutes. >> >> Display Options: >> bit 0: set for Fahrenheit, clear for Celsius >> bit 1: set for 24 hour time display, clear for AM/PM >> bit 2: set for non-programmable, clear for programmable (disables internal >> programs in thermostat) >> bit 3: set for real time pricing (RTP) mode, clear for no RTP (RC-81, -91, >> -101, -121 only) >> bit 4: set to hide clock, RTP and filter display, clear to show them. >> >> New Registers for Omnistat2 >> 74 (4A) Current energy cost (0 – 254, 255=disabled) >> >> Programming Tuesday - Friday: >> 75 (4B) Programming Tuesday morning time (15 minute increments) >> 76 (4C) Programming Tuesday morning cool setpoint (in Omnitemp) >> 77 (4D) Programming Tuesday morning heat setpoint (in Omnitemp) >> 78 (4E) Programming Tuesday day time (15 minute increments) >> 79 (4F) Programming Tuesday day cool setpoint (in Omnitemp) >> 80 (50) Programming Tuesday day heat setpoint (in Omnitemp) >> 81 (51) Programming Tuesday evening time (15 minute increments) >> 82 (52) Programming Tuesday evening cool setpoint (in Omnitemp) >> 83 (53) Programming Tuesday evening heat setpoint (in Omnitemp) >> 84 (54) Programming Tuesday night time (15 minute increments) >> 85 (55) Programming Tuesday night cool setpoint (in Omnitemp) >> 86 (56) Programming Tuesday night heat setpoint (in Omnitemp) >> 87 (57) Programming Wednesday morning time (15 minute increments) >> 88 (58) Programming Wednesday morning cool setpoint (in Omnitemp) >> 89 (59) Programming Wednesday morning heat setpoint (in Omnitemp) >> 90 (5A) Programming Wednesday day time (15 minute increments) >> 91 (5B) Programming Wednesday day cool setpoint (in Omnitemp) >> 92 (5C) Programming Wednesday day heat setpoint (in Omnitemp) >> 93 (5D) Programming Wednesday evening time (15 minute increments) >> 94 (5E) Programming Wednesday evening cool setpoint (in Omnitemp) >> 95 (5F) Programming Wednesday evening heat setpoint (in Omnitemp) >> 96 (60) Programming Wednesday night time (15 minute increments) >> 97 (61) Programming Wednesday night cool setpoint (in Omnitemp) >> 98 (62) Programming Wednesday night heat setpoint (in Omnitemp) >> 99 (63) Programming Thursday morning time (15 minute increments) >> 100 (64) Programming Thursday morning cool setpoint (in Omnitemp) >> 101 (65) Programming Thursday morning heat setpoint (in Omnitemp) >> 102 (66) Programming Thursday day time (15 minute increments) >> 103 (67) Programming Thursday day cool setpoint (in Omnitemp) >> 104 (68) Programming Thursday day heat setpoint (in Omnitemp) >> 105 (69) Programming Thursday evening time (15 minute increments) >> 106 (6A) Programming Thursday evening cool setpoint (in Omnitemp) >> 107 (6B) Programming Thursday evening heat setpoint (in Omnitemp) >> 108 (6C) Programming Thursday night time (15 minute increments) >> 109 (6D) Programming Thursday night cool setpoint (in Omnitemp) >> 110 (6E) Programming Thursday night heat setpoint (in Omnitemp) >> 111 (6F) Programming Friday morning time (15 minute increments) >> 112 (70) Programming Friday morning cool setpoint (in Omnitemp) >> 113 (71) Programming Friday morning heat setpoint (in Omnitemp) >> 114 (72) Programming Friday day time (15 minute increments) >> 115 (73) Programming Friday day cool setpoint (in Omnitemp) >> 116 (74) Programming Friday day heat setpoint (in Omnitemp) >> 117 (75) Programming Friday evening time (15 minute increments) >> 118 (76) Programming Friday evening cool setpoint (in Omnitemp) >> 119 (77) Programming Friday evening heat setpoint (in Omnitemp) >> 120 (78) Programming Friday night time (15 minute increments) >> 121 (79) Programming Friday night cool setpoint (in Omnitemp) >> 122 (7A) Programming Friday night heat setpoint (in Omnitemp) >> >> Programming Occupancy: >> 123 (7B) Programming Day Cool setpoint (in Omnitemp) >> 124 (7C) Programming Day Heat setpoint (in Omnitemp) >> 125 (7D) Programming Night Cool setpoint (in Omnitemp) >> 126 (7E) Programming Night Heat setpoint (in Omnitemp) >> 127 (7F) Programming Away Cool setpoint (in Omnitemp) >> 128 (80) Programming Away Heat setpoint (in Omnitemp) >> 129 (81) Programming Vacation Cool setpoint (in Omnitemp) >> 130 (82) Programming Vacation Heat setpoint (in Omnitemp) >> >> Setup: >> 131 (83) Program mode (0=None, 1=Schedule, 2=Occupancy) >> 132 (84) Expansion baud (0=300, 1=100, 42=1200, 54=2400, 126=9600) >> 133 (85) Days until filter reminder appears >> 134 (86) Humidity Setpoint >> 135 (87) Dehumidify Setpoint >> 136 (88) Dehumidifier output options (0=Not used, 1=Standalone, 2= >> variable speed fan) >> 137 (89) Humidifier output (0=Not used, 1=Standalone) >> 138 (8A) Minutes out of 20 that fan is on during cycle (1-19) >> 139 (8B) Backlight settings (0=Off, 1=On, 2=Auto) >> 140 (8C) Backlight color (0-100) >> 141 (8D) Backlight intensity (1-10) >> 142 (8E) Selective message enable/disable >> 143 (8F) Minimum on time for cool (2-30) >> 144 (90) Minimum off time for cool (2-30) >> 145 (91) Minimum on time for heat (2-30) >> 146 (92) Minimum off time for heat (2-30) >> 147 (93) System type (0=Heat Pump, 1=Conventional, 2=Dual Fuel) >> 148 (94) Reserved >> 149 (95) End of vacation date: day >> 150 (96) >> 151 (97) End of vacation date: hour >> 152 (98) Hours HVAC used in Week 0 >> 153 (99) Hours HVAC used in Week 1 >> 154 (9A) Hours HVAC used in Week 2 >> 155 (9B) Hours HVAC used in Week 3 >> 156 (9C) Reserved >> 157 (9D) Reserved >> 158 (9E) Enable/disable individual temp sensors >> 159 (9F) Number of cool stages >> 160 (A0) Number of heat stages >> 161 (A1) Current occupancy mode (0=Day, 1=Night, 2=Away, 3=Vacation) >> 162 (A2) Current indoor humidity >> 163 (A3) Cool setpoint for vacation mode (51-91) >> 164 (A4) Heat setpoint for vacation mode (51-91) >> >> Energy: >> 165 (A5) Displayed price of energy with medium level energy >> 166 (A6) Displayed price of energy with high level energy >> 167 (A7) Displayed price of energy with critical level energy >> 168 (A8) Sensitivity setting for proximity sensor (0-99) >> 169 (A9) Energy level as set by the meter >> 170 (AA) Current energy total cost, upper byte >> 171 (AB) Current energy total cost, lower byte >> 172 (AC) STRING ASCII display for first load control module >> 173 (AD) STRING ASCII display for second load control module >> 174 (AE) STRING ASCII display for third load control module >> 175 (AF) STRING ASCII display for Energy message >> 176 (B0) STRING ASCII display for emergency broadcast message (not >> implemented) >> 177 (B1) STRING ASCII display for custom message (not implemented) >> 178 (B2) STRING ASCII display for energy graph title bar >> 179 (B3) STRING ASCII display for energy graph x axis >> 180 (B4) STRING ASCII display for energy graph y axis >> 181 (B5) STRING ASCII display for long messages (not implemented) >> 182 (B6) graph bar max height, upper byte >> 183 (B7) graph bar max height, lower byte >> 184 (B8) graph bar one value, upper byte >> 185 (B9) graph bar one value, lower byte >> 186 (BA) graph bar two value, upper byte >> 187 (BB) graph bar two value, lower byte >> 188 (BC) graph bar three value, upper byte >> 189 (BD) graph bar three value, lower byte >> 190 (BE) graph bar four value, upper byte >> 191 (BF) graph bar four value, lower byte >> 192 (C0) Status and enable/disable of each load control module >> >> Sensors: >> 200 (C8) Current temperature of sensor 3 >> 201 (C9) Current temperature of sensor 4 >> 202 (CA) Reserved >> >> Wireless: >> 224 (E0) Wireless MAC address byte 1 >> 225 (E1) Wireless MAC address byte 2 >> 226 (E2) Wireless MAC address byte 3 >> 227 (E3) Wireless MAC address byte 4 >> 228 (E4) Wireless MAC address byte 5 >> 229 (E5) Wireless MAC address byte 6 >> 230 (E6) Wireless MAC address byte 7 >> 231 (E7) Wireless MAC address byte 8 >> 232 (E8) Wireless firmware version integer place >> 233 (E9) Wireless firmware version decimal place >> 234 (EA) Wireless strength (0-100) >> 235 (EB) Wireless buzzer enable or disable >> 236 (EC) Wireless IP address byte 1 >> 237 (ED) Wireless IP address byte 2 >> 238 (EE) Wireless IP address byte 3 >> 239 (EF) Wireless IP address byte 4 >> 253 Reserved >> 254 Reserved >> =cut >> >> use strict; >> >> package Omnistat; >> >> sub omnistat_debug { >> my ($mesg) = @_; >> >> print "$::Time_Date: $mesg\n" if $::Debug{omnistat}; >> } >> >> # Load Time::HiRes if it's available >> use vars qw($USLEEP); >> eval { require Time::HiRes }; >> if (not $@) { >> Time::HiRes->import( qw(usleep) ); >> omnistat_debug("Omnistat found Time::Hires, will use usleep in >> omni_sleep"); >> $USLEEP=1; >> } else { >> omnistat_debug("Omnistat did NOT find Time::Hires, will NOT use usleep in >> omni_sleep"); >> warn("Omnistat works much better with Time::HiRes, install it if you >> can"); >> $USLEEP=0; >> } >> >> # -------------------------------------------------------------- >> # -------------------- START OF SUBROUTINES -------------------- >> # -------------------------------------------------------------- >> >> @Omnistat::ISA = ('Serial_Item'); >> >> sub omni_sleep() { >> $USLEEP ? usleep($_[0]) : sleep(int($_[0] + 0.99999)); >> } >> >> >> # My guess is that most people would want to have temperature logging, but >> you can turn it off with >> # Omnistat_stat_log=0 in mh.private.ini -- merlin >> sub omnistat_log { >> my ($mesg, $level) = @_; >> my $loglevel = $main::config_parms{Omnistat_stat_log}; >> >> $loglevel = 1 if (not defined $loglevel); >> $level = 1 if (not defined $level); >> >> &::print_log("log=logs/thermostat.log $mesg") if ($level <= $loglevel); >> } >> >> >> sub is_hex { >> # make sure we got proper hex and not a number or some other error >> return ($_[0] =~ /^0x[0-9a-fA-F][0-9a-fA-F]$/); >> } >> >> # ******************************************************** >> # * Get address for this thermostat from the argument. >> # * Address defaults to 1 if no argument. >> # ******************************************************** >> sub new { >> my ( $class, $address ) = @_; >> $address = 1 unless $address; >> my $self = {}; >> $$self{address} = $address; >> $$self{cache} = {}; >> # when the cache was last updated >> $$self{cache_updatetime} = {}; >> # per register override of how long we want to cache >> $$self{cache_agelimit} = {}; >> >> # **************************** IMPORTANT ***************************** >> # Cache values can be increased by up to 10% at runtime to avoid having >> # a bunch of variables have their cache expire at the same time and cause >> # hangs due to synchronized reads. -- merlin >> # **************************** IMPORTANT ***************************** >> >> # by default registers are cached 1h # CACHE_TIMEOUT_DEFAULT >> $$self{cache_defaultagelimit} = 3600; >> # These are important registers for which we don't want to cache data 1 >> hour >> # or registers that never change and we cache longer >> # (the rest default to $$self{cache_defaultlifetime}) >> >> # filter reminder, or type of thermostat, and all the programming >> setpoints is good enough once a day >> foreach my $reg (0x15..0x38, 0x0f, 0x49) { >> $$self{cache_agelimit}{$reg} = 3600 * 24; # CACHE_TIMEOUT_DAILY >> } >> # setpoints and modes are cached 53 secs so that they are pretty much >> # guaranteed to be updated once a minute even with the random +10% offset >> foreach my $reg (0x3b .. 0x3f) { >> $$self{cache_agelimit}{$reg} = 54; # CACHE_TIMEOUT_SHORT >> } >> # temperatures and what the stat outputs, we only cache 10 seconds >> foreach my $reg (0x40, 0x44, 0x48) { >> $$self{cache_agelimit}{$reg} = 10; # CACHE_TIMEOUT_VERYSHORT >> } >> >> #The next line is an experiment with http_server.pm to allow other >> objects to show up in the web interface >> $$self{html_text} = "<a href=/hai/omnistat_web.pl>Set >> Thermostat</a>"; >> >> omnistat_debug("[HVAC] Omnistat[$$self{address}] object created"); >> bless $self,$class; >> >> # This is to work around a timing bug where the first query doesn't get a >> proper ack >> # we just "prime" the device and connection by making one query to it >> where we ignore the answer >> # no idea why this is needed, but it works for me and things are stable >> afterwards -- merlin >> $self->{'PRIME'}=1; >> $self->send_cmd("0x01 0x20 0x40 0x01 0x62", 1); >> $self->{'PRIME'}=0; >> >> return $self; >> } >> >> # ************************************* >> # * Add the checksum to the cmd array. >> # ************************************* >> sub add_checksum { >> my (@array) = @_; >> my @modarr = @array; >> my $value = 0; >> foreach (@modarr) { >> s/^0x//g; >> $_ = hex($_); >> $value = $value + $_; >> } >> $value = $value % 256; >> $array[ $#array + 1 ] = sprintf( "0x%02x", $value ); >> return @array; >> } >> >> # ************************************** >> # * Send the command to the thermostat. >> # ************************************** >> # I added very basic support of acknowledgments by just checking that we >> get one byte back that contains 0x80. It is totally >> # incomplete, but better than nothing -- merlin >> # FIXME?: the spec says we're supposed to listen to the reply, and resend >> messages after an inter message timeout >> # of 1.25s, that said it seems to work ok with the current timings and >> should work without resends unless your >> # serial cable wires are crap (use CAT-5) and/or very long -- merlin >> # >> # FIXME, 2-3 times, I had this bug right after starting mh: >> # 25/07/2009 10:15:30 : Omnistat[2]->read_cached_reg: reg=0x3b not cached, >> fetching >> # 25/07/2009 10:15:30 : Omnistat->send_cmd string=0x02 0x20 0x3b 0x0e 0x6b >> (with 833325,us reply delay (14 char(s) to read back)) >> # 25/07/2009 10:15:30 : Omnistat->send_cmd got reply "0xff 0x82 0xf2 0x3b >> 0x8b 0x64 0x03 0x00 0x00 0x78 0x1e 0x0f 0x0a 0x60 0x00 0x00 0x02 0x00 0xb2 " >> # The 0xff in the reply didn't belong. No idea where it came from, >> especially because it was the first command and reply. >> # for now it makes the code die, the command fail and then things restart >> and continue -- merlin >> sub send_cmd { >> # if you want to default to a full 2sec wait, pass '-1' as reply_count >> my ($self, $reply_count, @string) = @_; >> my $addr = $$self{address}; >> my $cmd = ''; >> # We try to calculate how long we wait for the reply, or default to 2 sec >> (2M usec) >> my $reply_wait = 2000000; >> >> # some experimentation shows on my system that we need to wait 0.3sec + >> 0.1sec for each 3 registers returned --merlin >> # 300bps is 30cps, which does equate to 0.0333333s per character. From >> experimentation, one needs to wait an extra >> # 11 characters in addition to the payload you're expecting back to get >> reliable replies (10 almost works but causes >> # occasional corruption due to timings). -- merlin >> # (12+ chars wait instead of 11 might be needed for you. Please increase >> & let me know if this is too short for you). >> my $REPLY_BASE_DELAY = 14; >> >> # While we don't get as many bytes as we sent if we were to set several >> registers in a row (which is not currently >> # supported in this code), the spec says to wait 30ms per register set, >> so the wait time ends up being able the same >> # when setting data than when polling it. >> $reply_wait = (33333*($reply_count + $REPLY_BASE_DELAY)) if ($reply_count >> > -1); >> >> omnistat_debug("Omnistat[$$self{address}]->send_cmd string=@string (with >> $reply_wait,us reply delay ($reply_count char(s) to read back))"); >> foreach my $byte (@string) { >> $byte =~ s/0x//; # strip off the 0x >> $cmd = $cmd . pack "H2", $byte; # pack it into 8 bits >> } >> >> # send it to thermostat >> #omnistat_debug("Omnistat->send_cmd will write $cmd"); >> $main::Serial_Ports{Omnistat}{object}->write($cmd); >> >> # need to wait a bit for the reply >> # FIXME: sleep is bad, especially if you're not using usleep from >> Time::Hires, the only proper way to do this would be to >> # have a request queue where one command gets processed every 1-2 >> seconds, but that would be a big rewrite -- merlin >> &Omnistat::omni_sleep($reply_wait); >> >> # read response >> &main::check_for_generic_serial_data('Omnistat'); >> my $temp = $main::Serial_Ports{Omnistat}{data}; >> $main::Serial_Ports{Omnistat}{data} = ''; >> my $len = length($temp); >> $temp = unpack( "H*", $temp ); >> my ($i); >> my $rcvd = ''; >> my $ack_byte = 0x80 + $addr; >> for ( $i = 0 ; $i < $len ; $i++ ) { >> $rcvd = $rcvd . sprintf( "0x%s ", substr( $temp, $i * 2, 2 ) ); >> } >> my $rcvd_ack = hex(substr($rcvd, 0 , 4)); >> >> if ($self->{'PRIME'}) >> { >> omnistat_debug("Omnistat[$$self{address}]->send_cmd skipping error >> check and return value during prime"); >> return; >> } >> >> # FIXME? Those two dies aren't ideal, but it happens that you get >> corruption or bad data on a reply. >> # Expected for 01 20 3b 0e 6a is something like >> # 0x81 0xf2 0x3b 0x7c 0x71 0x03 0x00 0x00 0x7d 0x07 0x1e 0x0f 0x00 0x00 >> 0x00 0x02 0x0c 0x5d >> # but I have seen replies like >> # 0x03 0xf2 0x3b 0x7c 0x71 0x03 0x00 0x00 0x7d 0x00 0x1c 0x0f 0x00 0x00 >> 0x00 0x02 0x0c 0x54 (0x81 ack byte is wrong) >> # or sync issues like >> # 0x64 0x82 0xf2 0x3b 0x8e 0x64 0x00 0x00 0x00 0x7d 0x20 0x29 0x0f 0x60 >> 0x00 0x00 0x00 0x00 0xd6 (0x64 shouldn't be here) >> # On my system, this error only seems to happen soon after startup and >> doesn't seem to happen later -- merlin >> die "$::Time_Date: Omnistat[$$self{address}]->send_cmd did not get ack >> reply to command @string ($rcvd)" unless (length($rcvd) > 3); >> die "$::Time_Date: Omnistat[$$self{address}]->send_cmd did not get >> expected first byte (".sprintf("0x%02x",$ack_byte).") in ack reply to >> command @string (got ".sprintf("0x%02x", $rcvd_ack)." in $rcvd)" unless >> ($rcvd_ack eq $ack_byte); >> omnistat_debug("Omnistat[$$self{address}]->send_cmd got reply >> \"$rcvd\""); >> >> return $rcvd; >> } >> >> # ****************************** >> # * check for returned data. >> # ****************************** >> sub check_for_data { >> &main::check_for_generic_serial_data('Omnistat'); >> } >> >> # ************************************************* >> # * Set the thermostat clock to the current time. >> # ************************************************* >> sub set_time { >> my ($self) = @_; >> my $wday; >> my $addr = $$self{address}; >> >> omnistat_debug("Omnistat[$$self{address}] -> Setting time/day of week"); >> my @cmd = qw(0x01 0x41 0x41); >> >> #set the time >> $cmd[0] = sprintf( "0x%02x", $addr ); >> $cmd[3] = sprintf( "0x%02x", $::Second ); >> $cmd[4] = sprintf( "0x%02x", $::Minute ); >> $cmd[5] = sprintf( "0x%02x", $::Hour ); >> @cmd = add_checksum(@cmd); >> $self->send_cmd(0, @cmd); >> >> #set the weekday >> $wday = $::Wday ? $::Wday - 1 : 6; >> $self->set_reg( "0x3a", sprintf( "0x%02x", $wday) ); >> } >> >> # ******************************************* >> # * Set the display mode of the thermostat. >> # ******************************************* >> sub display { >> my ($self) = @_; >> my $addr = $$self{address}; >> >> #$main::config_parms{Omnistat_serial_port} >> my $DISPLAY_BITS; >> # Bit 0 >> if ( $main::config_parms{Omnistat_celcius} ) { >> $DISPLAY_BITS = 0; >> } else { >> $DISPLAY_BITS = 1; >> } >> # Bit 1 >> if ( $main::config_parms{Omnistat_24hr} ) { >> $DISPLAY_BITS = $DISPLAY_BITS + 2; >> } >> # Bit 2 >> if ( $main::config_parms{Omnistat_non_program} ) { >> $DISPLAY_BITS = $DISPLAY_BITS + 4; >> } >> # Bit 3 >> if ( $main::config_parms{Omnistat_rtp_mode} ) { >> $DISPLAY_BITS = $DISPLAY_BITS + 8; >> } >> # Bit 4 >> if ( $main::config_parms{Omnistat_hide_clock} ) { >> $DISPLAY_BITS = $DISPLAY_BITS + 16; >> } >> >> $self->set_reg( "0x03", sprintf( "0x%02x", $DISPLAY_BITS ) ); >> } >> >> # ********************************************* >> # * Create the Omnistat device on serial port. >> # ********************************************* >> sub serial_startup { >> &main::serial_port_create( 'Omnistat', >> $main::config_parms{Omnistat_serial_port}, 300, 'none', 'raw' ); >> &::MainLoop_pre_add_hook( \&Omnistat::check_for_data, 1 ); >> } >> >> # ******************************** >> # * Set the hold mode on or off. >> # ******************************** >> sub hold { >> my ( $self, $state ) = @_; >> my $new_hold; >> my $cur_hold = $self->read_cached_reg("0x3f",1); >> $state = lc($state); >> >> if ( $state eq "off" ) { >> $new_hold = "0x00"; >> } elsif ( $state eq "on" ) { >> $new_hold = "0xff"; >> } else { >> print "$::Time_Date: Omnistat[$$self{address}]: Invalid Hold state: >> $state\n"; >> return; >> } >> >> # obviously there is a small race condition here, if hold was changed in >> the last minute from the panel, >> # we could fail to set it when it needs to be, but that should be quite >> rare, and avoiding all the repeated >> # hold set to off before changing other values is worth it -- merlin >> if ($cur_hold ne $new_hold) { >> $self->set_reg( "0x3f", $new_hold ); >> omnistat_debug("Omnistat[$$self{address}]->hold: Hold set to $state"); >> } else { >> omnistat_debug("Omnistat[$$self{address}]->hold: Hold stays at >> $state"); >> } >> } >> >> # ************************************************************* >> # * Translate Temperature between Fahrenheit/Celcius and Omni values. >> # ************************************************************* >> sub translate_temp { >> my ($settemp) = @_; >> my ($omnitemp); >> >> # this is a good place to catch a 14 reg read that happens in read_group1 >> extended, being off by one character, or returning >> # bogus 0's. >> die "$::Time_Date: Omnistat->translate_temp got an input temperature of 0 >> = -40F/C, this typically means serial port corruption, bad... You may want >> to increase REPLY_BASE_DELAY" if (not $settemp or $settemp eq "0x00"); >> >> # Calculate conversion mathematically rather than using a table so all >> temps will work (needed for outside temperature) >> if ( substr( $settemp, 0, 2 ) eq '0x' ) >> { # if it starts with 0x, reverse xlate >> $omnitemp = hex($settemp); >> $omnitemp = -40 + .5 * $omnitemp; #degrees Celcius >> if ( !( $main::config_parms{Omnistat_celcius} ) ) { >> $omnitemp = 32 + 1.8 * $omnitemp; # degrees Fahrenheit >> $omnitemp = int( $omnitemp + .5 * ( $omnitemp <=> 0 ) ); #round >> } >> } else { # xlate from Fahrenheit/Celcius >> if ( !( $main::config_parms{Omnistat_celcius} ) ) { >> $omnitemp = ( $settemp - 32 ) / 1.8; #Fahrenheit to Celcius >> } >> $omnitemp = ( $omnitemp + 40 ) / .5; #omnistat >> degrees >> $omnitemp = int( $omnitemp + .5 * ( $omnitemp <=> 0 ) ); #round >> $omnitemp = sprintf( "0x%02x", $omnitemp ); >> } >> >> omnistat_debug("Omnistat: Converted $settemp to $omnitemp"); >> return $omnitemp; >> } >> >> # ************************************************************* >> # * Translate Time between readable and Omni values. >> # ************************************************************* >> sub translate_time { >> my ($settime) = @_; >> my ( $hours, $minutes, $ampm ); >> my ($omnitime); >> >> if ( substr( $settime, 0, 2 ) eq '0x' ) { #Translate omnitime to readable >> time >> if ( $settime eq '0x60' ) >> { #if it's set to 24hrs past midnight, time is blank >> $omnitime = ''; >> } else { >> $minutes = >> hex($settime) * >> 15; #Omnistat is stored as 15 minute time periods pas midnight >> $hours = int( $minutes / 60 ); >> $minutes = $minutes % 60; #minutes past hour >> if ( $main::config_parms{Omnistat_24hr} ) { >> >> #Translate to 24hr time >> $omnitime = sprintf( '%02s:%02s', $hours, $minutes ); >> } else { >> >> #Translate omni to AM/PM >> if ( $hours == 0 ) { >> $hours = 12; >> $ampm = 'PM'; >> } elsif ( $hours > 12 ) { >> $ampm = 'PM'; >> $hours -= 12; >> } else { >> $ampm = 'AM'; >> } >> $omnitime = sprintf( '%02s:%02s %s', $hours, $minutes, $ampm ); >> } >> } >> } else { #Translate readable to omnistat time >> if ( $settime eq '0' ) { #set to 0 to clear time, or 24:00 if using 24h >> time >> $omnitime = '0x60'; >> } elsif ( $main::config_parms{Omnistat_24hr} ) { >> #convert 24h time >> if ( $settime =~ /^([0-1][0-9]|[2][0-4]):([0-5][0-9])$/ ) { >> >> #valid time >> $hours = $1; >> $minutes = $2; >> $minutes = $minutes + $hours * 60; >> $omnitime = $minutes / 15; >> $omnitime = sprintf( "0x%02x", $omnitime ); >> } else { >> #invalid time >> $omnitime = ''; >> } >> } else { >> #convert am/pm time >> if ( $settime =~ /^(1[0-2]|0?[1-9]):([0-5][0-9]) *(AM|PM)$/ ) { >> >> #valid time >> $hours = $1; >> $minutes = $2; >> $ampm = $3; >> >> #PM we may need to add 12 hours (unless it's midnight), AM is >> already right >> if ( $ampm eq 'PM' ) { >> if ( $hours == 12 ) { >> $hours = 0; >> } else { >> $hours = $hours + 12; >> } >> } >> >> $minutes = $minutes + $hours * 60; >> $omnitime = $minutes / 15; >> >> $omnitime = sprintf( "0x%02x", $omnitime ); >> } else { >> #invalid time >> $omnitime = ''; >> } >> } >> } >> >> return $omnitime; >> } >> >> # ***************************************************** >> # * Read and convert the bits in reg 0x48 (HVAC output) >> # ***************************************************** >> sub translate_stat_output { >> my ( $self, $reg48 ) = @_; >> >> die "Omnistat::translate_stat_output got non hex value in $reg48" unless >> (is_hex($reg48)); >> # see reg 0x48 / output register at the top of this file >> my $output = "off"; >> $output = "fan" if (hex($reg48) & 8); >> >> # if stage 1 and stage 2 heat/cool are off, return here >> #&::print_log("pass1: reg48: $reg48, $output"); >> return $output if (not hex($reg48) & (4+16)); >> >> $output .= "/auxheat" if (hex($reg48) & 2); >> >> $output .= (hex($reg48) & 1) ? "/heat" : "/cool"; >> $output .= "/stage2" if (hex($reg48) & 16); >> #&::print_log("pass2: reg48: $reg48, $output"); >> return $output; >> } >> >> >> # ***************************************************************** >> # * Change the mode of the thermostat between off/auto/heat/cool. >> # ***************************************************************** >> sub mode { >> my ( $self, $state ) = @_; >> $state = lc($state); >> >> #TODO: Should heat/cool/auto turn on hold? >> >> omnistat_debug("Omnistat[$$self{address}] -> Mode $state"); >> my $addr = $$self{address}; >> my @cmd; >> if ( $state eq "off" ) { >> $self->set_reg( "0x3d", "0x00" ); >> } elsif ( $state eq "heat" ) { >> $self->set_reg( "0x3d", "0x01" ); >> } elsif ( $state eq "cool" ) { >> $self->set_reg( "0x3d", "0x02" ); >> } elsif ( $state eq "auto" ) { >> $self->set_reg( "0x3d", "0x03" ); >> } elsif ( $state eq "program_heat" ) { >> $self->set_reg( "0x3d", "0x03" ); >> $self->set_reg( "0x3f", "0x00" ); >> } elsif ( $state eq "program_cool" ) { >> $self->set_reg( "0x3d", "0x03" ); >> $self->set_reg( "0x3f", "0x00" ); >> } elsif ( $state eq "program_auto" ) { >> $self->set_reg( "0x3d", "0x03" ); >> $self->set_reg( "0x3f", "0x00" ); >> } else { >> print "$::Time_Date: Omnistat: Invalid Mode state: $state\n"; >> } >> print "$::Time_Date: Omnistat: Invalid Mode state: $state\n"; >> } >> >> # ************************** >> # * Restore the heat and cool setpoints to what they would be if the >> schedule were in effect >> # ************************** >> sub restore_setpoints { >> my ($self) = @_; >> my $point = 0; >> my $time; >> my $day; >> my $register; >> my $setpointnum; >> my $daynum; >> >> # this does not work if the stat is in hold mode, and we assume that >> calling this means we want >> # to un-hold >> $self->hold("off"); >> >> # This touches a lot of registers, so it's quicker to cache them all >> once. >> # Unfortunately, we can only read 14 registers at a time, so we'll read >> 3x12 >> # We don't store the result, this is just to prime the cache in case it >> wasn't. >> $self->read_cached_reg("0x15", 12); >> $self->read_cached_reg("0x21", 12); >> $self->read_cached_reg("0x2d", 12); >> >> #Determine the day (weekday,sat,sun) >> $day = $::Wday ? $::Wday - 1 : 6; >> >> #Determine the time >> $time = >> ( $::Hour * 4 ) + ( $::Minute / 15 ) + ( $::Second / 60 ); #Omnistat >> time >> >> #Determine the day >> if ( $day == 6 ) { # Sunday >> $register = 0x36; # Sunday night time >> } elsif ( $day == 5 ) { # Saturday >> $register = 0x2a; # Saturday night time >> } else { # Weekday >> $register = 0x1e; # Weekday night time >> } >> >> # Check for setpoints for that day, need to consider what time it is >> for ( $setpointnum = 0 ; $setpointnum < 4 ; $setpointnum++ ) { >> # FIXME: make sure I didn't break this (test it) and remove my FIXME -- >> merlin >> if ( hex($self->read_cached_reg( sprintf( "0x%02x", $register - 3 * >> $setpointnum))) < $time ) { >> $point = $register - 3 * $setpointnum; >> last; >> } >> } >> >> #Check for setpoints on previous days, don't need to consider the time, >> any setpoint will do >> if ( $point == 0 ) { >> >> #Loop days >> for ( $daynum = 0 ; $daynum < 3 ; $daynum++ ) { >> if ($day > 0 && $day < 5 && $daynum == 0) { >> #Weekday, previous day is also a weekday for first loop >> } >> else { >> #Get the previous day >> $register = $register - 12; >> } >> >> if ( $register < 30 ) { $register = 54; } #Previous to weekday is >> sunday >> >> #Loop setpoints >> for ( $setpointnum = 0 ; $setpointnum < 4 ; $setpointnum++ ) { >> # FIXME: make sure I didn't break this (test it) and remove my >> FIXME -- merlin >> if ( hex($self->read_cached_reg( sprintf( "0x%02x", $register - 3 * >> $setpointnum))) != 96 ) >> { >> #If the setpoint has a time set, use the setpoint >> $point = $register - 3 * $setpointnum; >> last; >> } >> } >> } >> } >> >> if ( $point != 0 ) { >> my $heat_sp = $self->read_cached_reg( sprintf( "0x%02x", $point + 2)); >> my $cool_sp = $self->read_cached_reg( sprintf( "0x%02x", $point + 1)); >> >> # FIXME: make sure I didn't break this (test it) and remove my FIXME -- >> merlin >> # Set the setpoints (setting the registers avoids converting the temp >> only to convert it back) >> print "$::Time_Date: Omnistat: Heat Set to " . >> &Omnistat::translate_temp($heat_sp) . "\n"; >> print "$::Time_Date: Omnistat: Cool Set to " . >> &Omnistat::translate_temp($cool_sp) . "\n"; >> $self->set_reg( "0x3c", $heat_sp ); >> $self->set_reg( "0x3b", $cool_sp ); >> } >> } >> >> >> >> # ************************************ >> # * Set the fan mode to on/off/auto. >> # ************************************ >> sub fan { >> my ( $self, $state ) = @_; >> $state = lc($state); >> my $addr = $$self{address}; >> my @cmd; >> >> omnistat_debug("Omnistat[$$self{address}] -> Fan $state"); >> if ( $state eq "on" ) { >> $self->set_reg( "0x3e", "0x01" ); >> } elsif ( $state eq "auto" ) { >> $self->set_reg( "0x3e", "0x00" ); >> } elsif ( $state eq "cycle" ) { >> $self->set_reg( "0x3e", "0x02" ); >> } else { >> print "$::Time_Date: Omnistat: Invalid Fan state: $state\n"; >> } >> } >> >> # ************************************** >> # * Set the occupancy mode >> # ************************************** >> sub set_occupancy_mode { >> my ( $self, $state ) = @_; >> $state = lc($state); >> my $addr = $$self{address}; >> my @cmd; >> >> omnistat_debug("Omnistat[$$self{address}] -> occupancy $state"); >> if ( $state eq "day" ) { >> $self->set_reg( "0xa1", "0x00" ); >> } elsif ( $state eq "night" ) { >> $self->set_reg( "0xa1", "0x01" ); >> } elsif ( $state eq "away" ) { >> $self->set_reg( "0xa1", "0x02" ); >> } elsif ( $state eq "vacation" ) { >> $self->set_reg( "0xa1", "0x03" ); >> } else { >> print "$::Time_Date: Omnistat: Invalid Occupancy state: $state\n"; >> } >> } >> >> # ************************************** >> # * Set the program mode >> # ************************************** >> sub set_program_mode { >> my ( $self, $state ) = @_; >> $state = lc($state); >> my $addr = $$self{address}; >> my @cmd; >> >> omnistat_debug("Omnistat[$$self{address}] -> program $state"); >> if ( $state eq "none" ) { >> $self->set_reg( "0x83", "0x00" ); >> } elsif ( $state eq "schedule" ) { >> $self->set_reg( "0x83", "0x01" ); >> } elsif ( $state eq "occupancy" ) { >> $self->set_reg( "0x83", "0x02" ); >> } else { >> print "$::Time_Date: Omnistat: Invalid Program state: $state\n"; >> } >> } >> >> # ************************** >> # * Set the cool setpoint. >> # ************************** >> sub cool_setpoint { >> my ( $self, $settemp ) = @_; >> # hold has to be removed for this command to go through. >> $self->hold('off'); >> $self->set_reg( "0x3b", &Omnistat::translate_temp($settemp) ); >> $self->hold('on') unless $main::config_parms{Omnistat_set_does_not_hold}; >> } >> >> # ************************** >> # * Set the heat setpoint. >> # ************************** >> sub heat_setpoint { >> my ( $self, $settemp ) = @_; >> # hold has to be removed for this command to go through. >> $self->hold(... [truncated message content] |