Alarm

Building a custom home alarm system.

CONSIDERATIONS

It all started in the summer of 2011 where my basement was flooded during a heavy rain storm. No great damage was done that time, but what about next time? It initiated the idea of building an alarm system that could monitor events in the house and send notifications of manual incidents as well a timed updates. But living in an old house presents a lot challenges in terms of wiring and modifications if you want to install a surveillance system.

The scope for me was to create a system that could give some kind of feed-back from unusual events happening when the family was away. This could be water leaks, smoke and human activity etc. For a start a simple e-mail reminder could be useful so neighbours could be contacted in case something unusual is happening while travelling. Later on an SMS service was added.

Standard alarm systems are easy to operate and install but they often miss features that can combine motion activity with other useful inputs and outputs from a private house. The choice could therefore be to install some of the expensive home automation systems with a lot of standard interface boxes and a nice programming user interface.

Instead I decided to try out the Raspberry Pi (RPi). It is a cheap device which has a nice and rare combination of programming features and pins that can be connected to different sensors/buttons. The device operates in an open source environment and there are some standard programs that can be used for surveillance purposes. But if you want a true custom installation you have to build everything yourself.

Using the RPi presents a lot of technical challenges so some tech knowledge is necessary in order to succeed.  But being systematic and dividing the project up in to smaller chunks helps a lot for understanding, designing, installing and operating a system based on the RPi. A custom and wired installation also benefits from a central control with one power source for all devices saving you from maintenance and battery changes around the installation.

The Raspberry forum has a lot of many nice examples that can be picked up for inspiration so benefitting from this shared environment also encouraged me to make this documentation in order to share my experiences with other newcomers.

DESIGN

Designing the connection of peripherals to the RPi is the best way to start. During this process a lot of considerations and decisions are made in terms of functionality and which components to use. And its easy to add new features when they pop up (and they will do).

Once made the design is a great help when wiring the house  and purchasing electronics and components for building the interface board for the RPi. All devices that were previously installed in the house were also connected to the system (door bell, PIR etc.)

The design can be seen here

Colored cables are used in the installation and electronics for easy testing and error checking.

INTERFACING

There are several options for interfacing the RPi to peripherals such as the Slice of Pi and Humble Pi boards on which you can solder your electronic components. These are an excellent solution if you want a compact installation as they can be fitted on top of the RPi in one of the standard RPi cases.

Another option is to buy a bread board which is good for prototyping and the use this in the installation. But it takes up some space.

For this project a prototype interface board was built on a print test board with copper strips. It is easy to cut the strips eg if you mount a resistor on top of them. This also gives the ability of making “bus-lines” eg for Ground or Power distribution.

Next consideration is connectors to the board. If you go for a compact installation there is no space for eg the Molex 2,54mm connectors on the interface board if you need to keep everything  inside the standard casing. You can decide to build a break-out box and connect the two with a standard 26p flat cable. This enables you to use large connectors to your different peripherals and protect the RPi via resistors. The advantage is that the space gives a good freedom during testing (and reconstruction).

In my home installation I have an ethernet patch box equipped with RJ-45 connectors which lead to the different outlets in the house. Using some of the spare connectors in this box enables me to tie the RPi  to the box via CAT5 cables soldered directly to the interface board which is mounted on top of the RPi. Cutting some slices in the hood of the RPi case makes it possible to close and secure the unit after testing has been made making it a truly compact device in my installation.

The final unit with cables, modified hood and buzzer mounted on top of the print board. Ready for power supply before it is up and running.

From the patch box CAT5 cables go to the different peripherals and terminals.

SENSORS

The basement has two 220V PIR motion-sensors installed – one at each end of the staircase. Activating one of the sensors turns on the light so it stays on for five minutes. Using PIR instead of a standard power switch was a choice made during a renovation project as it is easy to forget to switch off the light when you leave the rooms downstairs as using a standard 220V switch the light can potentially stay on for a long time before the next person enters. The alarm system should thus connect to the PIR’s in a simple way. Dealing with 220 volts is not easy and it is not recommendable to connect the RPi directly to the power installation. Instead a simple spare power supply was connected to an extra 220V outlet for producing a 9 V DC voltage each time the light is switched on. By using a resistor and an opto coupler at the RPi interface board a safe power bridge is made between the two installations.

An extra low voltage PIR was installed in the entrance hall. This unit also has a “Tamper” switch in case someone tries to break the unit.

As smoke sensor the old fire alarm was used. It has two terminals inside which are used for connecting a series of alarms together, so if one alarm smells smoke then all alarms will sound. The terminal outputs around 5-6V when active and this is used for triggering the RPi via another opto coupler. Mounting the smoke alarm next to the PIR made it easy to take the 9V power supply and loop it from the PIR to the fire alarm so batteries are avoided.

A water leak sensor was a high priority as the basement was flooded during a rain storm in 2011. After the renovation the probability of a new event is much reduced but it will be an extra security to add this feature. The idea was to construct a primitive sensor based on some wires and a piece of kitchen roll. Adding some resistors together with a transistor at the RPi board then picks up the small current that would walk between the wires when the paper gets wet. Simple and cheap – but efficient!

ELECTRONICS

The opto coupler used for interfacing with the 220V PIR is the IL74 but there are a lot of other alternatives on the market if you can’t get this one.

Two transistors are used for driving the water leak input and the buzzer output respectively. Using transistors gives a good protection of the RPi and it also loops current away from the GPIO pins. The transistors used here are the BC 546C

Resistors are all standard carbon 5-10% resistors.

Piezo buzzers were selected for sound signalling as they are a very efficient when it comes to sound versus current consumption. Two buzzers are mounted in parallel – one at the board in the basement and one in the hall above the front door.

This board is too small if all GPIO ports needs to be used but for the initial use it worked fine. After six months it was decided to build an expansion board and connect the two boards with a 8-wire Molex connector. This open up for extended weather logging and triggers.

FUNCTIONALITY

The system had to be simple to operate and a high WAF is a prerequisite for daily use. So external keypads was quickly abandoned in favour of a simple on/off switch mounted inside beside the front door. The switch should turn on  alarm function for the movement sensors (PIR) in the entrance hall and in the basement. All other sensors should be armed all times.

When leaving the house, the switch is set to “on” position which starts a series of short beeps. After 30 seconds the beeps get longer and finally after 40 seconds a long beep is heard in order to show that the alarm is now armed eg if you wait outside the door. The system is now armed and a service message will be sent by mail.

Entering the house through the main door will almost immediately trigger the PIR sensor which invokes a loop of 30 seconds with a series of short beeps that should remind the incoming person to switch off the system. Doing so stops the loop immediately.

If an intruder enters the house through the basement the system will switch to alarm mode when the light turns on through the PIR sensors. If entering directly in to the ground floor the intruder will be caught by the PIR in the hall which starts the warning loop. Assuming that he/she will not find the Off switch as it looks like a normal light switch the system will switch to alarm mode after 30 seconds. If the house is entered from the first floor there is no PIR up there to catch the intruder. Obviously he will descend some time and then be caught by the PIR in the Hall.

In Alarm mode a series of “SOS” beeps will be sound at the buzzers installed and a “Alarm” warning message will be sent by mail with information about which sensor that was activated. Same thing will happen if one of the other sensors (smoke, water etc) are triggered while system is disarmed (and armed).

A function sends a mail every day in order to show that the system is alive. This feature is nice to have when leaving the house for longer vacations.

All mails can also be converted to SMS if you want a direct and quick information about an event.

PROGRAMMING

Python was selected as the programming language as it is recommended for RPi usage. I had no previous experience with Python and was only familiar with Basic and Visual Basic. It seemed to be a good back-ground as there are a lot of similarities. However Python is not so helpful with debugging as VB and especially the sensivity to correct casing causes some extra debugging in comparison to what I am used to.

There are some general differences you have to observe when walking from VB to Python:
<ul>
<li>Be aware of casing. Sometimes you have to start with a capital letter (“True”, “False” etc.) and sometimes not (“if”, “while”, “def” etc.).</li>
<li>def is the same function as a SUB routine</li>
<li>Using variables in the different modules (def’s) is fine as long as they are declared Global in the def.</li>
<li>Use threads if you want a def to run independently of the rest of your program</li>
</ul>
In general two resistors are used for the IN functions as I doubted that the internal resistors could provide full protection if I did something wrong. The input circuitry pulls the level down to ground by default and up to 3,3 volts of an event happens. The two PIR outputs from the hall are both closed by default so the programming has to be opposite for those.

Assuming that PIN 3 and 5 could be set-up for this kind of inputs was wrong. They can only be triggered if the input level is taken down as the internal pull up resistor can’t be disabled (at least I couldn’t find a way). This is also the reason for using BCM mode instead of BOARD mode as I saw someone recommending this for solving the PIN 3+5 problems. It didn’t help but I let the programming stay in BCM mode (for now). I will recommend BOARD mode for other projects as BCM doesn’t make sense to me as it is a source of unnecessary distraction to change pin numbers to something that isn’t logical even though that BCM should be compatible to other types of hardware. Instead of changing wiring so PINs 3+5 could be ground connected I decided to leave them free as they can be used if you want to expand GPIO’s at a later time (see links at bottom of the document).

All programming should be made directly at the RPi for quick testing of the GPIO functionality. Your old TFT with analog input can get a revival if you buy a HDMI-&gt;VGA <a href=”http://www.newegg.com/Product/Product.aspx?Item=N82E16812400359″ target=”_blank”>adapter</a>. It works in contradiction to what is (was?) posted somewhere at the RPi site.

It is also possible to install Python at your PC and do the coding there but then there is no way of testing the GPIO’s unless the code is transferred to the RPi. This can be done via FTP if you know the unit’s IP-address but it is not advisable to follow this procedure before you know that the electronics is working and you have gained some knowledge about Python. The RPi makes it possible to overwrite the program while it is running in contradiction to Windows. So you can do some adjustments on your PC’s Python program and the transfer this to the RPi that just has to be rebooted for the changes to take effect. With the remote terminal program PuTTy you can also test your new version on top of the old version.

There’s no GUI or displays for this installation. The only feed-back I get is the audible beeps when the system boots and alarm is armed together with a blinking LED outside the front door. If a monitor is connected to the RPi there will however be some “print events” displayed but no interactivity.

When the program is ready it is logical to start the alarm program every time the unit boots. This is done by editing the  /etc/rc.local file. You have to do this in command mode, that is starting the terminal where you type “sudo leafpad”. Then insert this line: (sleep 5; sudo python  /home/pi/projects/alarm.py) and make sure the last line is “exit 0″.

The code is <a title=”Code” href=”https://jesperbonde.dk/rpi/alarm/code/”>here</a>

I’ve made a page <a title=”SMS” href=”https://jesperbonde.dk/rpi/alarm/sms/”>here </a>which describes how an SMS function was added to the project.

TESTING

For testing the electronics and programming (and learning) I decided to build a “dummy” of the house interfaces so I could sit down next to my work station in order to use it for research and debugging. As the installation is made with CAT5 and RJ-45 connectors, three cables were made with some spare components that can be quickly connected when the RPi is removed from the basement installation.

INSTALLATION

It was decided to use CAT5 cables for wiring as they were “on stock” already. Two new cables were drawn from hall to the RJ45 patch bay in the basement and extra cable trays mounted in the hall. A standard 220V switch was mounted next to the front door for easy Alarm control. And finally the new PIR sensor was mounted in the hall.

In the basement three new RJ-45 connectors were mounted in the patch bay. One of them leading to a terminal where inputs from power supplies and water leak resistor could be connected. The other two connects to the cables to the hall.

So this is how it looks like now: The basic network installation with the RPi added top right and the terminal break-out bottom left. Other components are ADSL router, switchers and landline-to-wireless phone.

 

CONCLUSION

This project has taken app 60-70 hours from the first idea to final use. The installation is easy to operate and is therefore used when leaving the house which is the most important prerequisite for functionality.

There are a lot of cheap and easy standard solutions out on the market but they don’t give as much fun, versatility and knowledge as with this technology. It is great to see that the functionality is exactly what our family need for daily use and to know that new features can easily be added in the future. The experience gained is highly useful for other projects at home and professional use as the RPi is highly adaptable to all purposes.

RAIN SENSOR

A digital/optical rain sensor has now been included in the set-up. More details <a title=”Rain sensor” href=”https://jesperbonde.dk/rpi/alarm/rain-sensor/”>here</a>

TEMPERATURE

Temperature sensors are now added to the system. A specification can be seen here (soon)

THE FUTURE

Plans for extra functionality and features are
<ul>
<li>Extending the “Tamper” circuit to include windows and outside doors</li>
<li>Connecting more water sensors in parallel</li>
<li>Temperature sensors</li>
<li>External light sensor for conditional programming (daylight sensor)</li>
<li>Heating control – lower temperature in house when alarm is armed.</li>
<li>Camera functionality</li>
<li>Perhaps some GUI stuff or touch display but at this time I don’t see that it is useful.</li>
</ul>
<span style=”line-height: 1.714285714; font-size: 1rem;”>LINKS</span>

<a style=”line-height: 1.714285714; font-size: 1rem;” href=”http://www.raspberrypi.org/” target=”_blank”>The Raspberry Site</a>

<a href=”https://docs.python.org/2/” target=”_blank”>Python programming</a>

<a href=”http://elinux.org/RPi_Low-level_peripherals” target=”_blank”>Details about the GPIO</a>

The TI expander chip <a href=”http://www.ti.com.cn/cn/lit/ds/symlink/pcf8574a.pdf” target=”_blank”>PCF8574A</a> can be used if you need extra GPIO’s via pins 3+5

DISCLAIMER

Please respect that all the above information, guidelines, pictures and diagrams are published as a courtesy without warranty and responsibility and should be used at own risk.

&nbsp;

CODE

[pastacode lang=”python” manual=”%23alarm%20version%20without%20gdata%0Aimport%20RPi.GPIO%20as%20gpio%0Aimport%20string%0Aimport%20smtplib%0Afrom%20time%20import%20sleep%0Afrom%20datetime%20import%20datetime%0Aimport%20threading%0Aimport%20time%0Aimport%20os%0Aimport%20subprocess%0Aimport%20glob%2C%20sys%0A%23import%20gdata.spreadsheet.service%0Aimport%20commands%0Afrom%20urllib%20import%20urlopen%0Aimport%20re%0Afrom%20ftplib%20import%20FTP%0Aimport%20ftplib%0A%0A%23GOOGLE%3A%0A%23account%20details%0Agmail%20%3D%20’you%40gmail.com’%0Agpass%20%3D%20’yourpass’%0Aghost%20%3D%20’smtp.gmail.com%3A587’%0A%0A%23FTP%20information%0AFTPhost%20%3D%20’ftpserver.zz’%0AFTPdomain%20%3D%20’ftpdomain.zz’%0AFTPpass%20%3D%20’yourpass’%0A%0A%23%20ID’s%20of%20the%20different%20Google%20spreadsheets%20used%20for%20storing%20data%0Aspr_key_event%20%3D%20’1QNmkzUWZKyZ6BYDu0B7UBJCcdiC9bLrg_vxXxBZIhtk’%20%23Event%20log%0Aspr_key_rainy_day%20%3D%20’1sidW4BTGui7frjRxehZlySCPe4oo_lSshNmGQDP0v3k’%20%23%20Rain%20last%2024H%20in%2010%20min%20intervals%0Aspr_key_rainy_week%20%3D%20’1EFfRVuUIYqD9QObG0M_AgSmYjHYMd3kv2yvmHUrNSZM’%20%23%20Rain%20last%202%20weeks%20in%201H%20intervals%0Aspr_key_rainy_year%20%3D%20’16R1nxxtNsrQNKULwiFXLzkvZUYaRc_N2XbgqcoVFnKE’%20%23Rain%20last%20year%20with%20once%20daily%20updates%0Aspr_key_temp_out%20%3D%20’1EflJUkzfxBI5vPTSAMh5Ds8Y01O63RGd5hCg2IFM50w’%20%23Outside%20temperature%20the%20last%2024h%20(Temp%20out)%0Aspr_key_temp_now%20%3D%20’1iyAU5eHBeYHG3XHisUlPSWFEFM4dnc8_RkOR7AU108g’%20%23Outside%20temperature%20right%20now%20(Temp%20now)%0Aspr_key_temp_all%20%3D%20’1srQPRg9ZUfFLY_5z8m6DLgbz-Cz833S2HRz2alsWrRo’%20%23All%20temperature%20sensors%20(Temp%20all)%20for%20internal%20use%0Aspr_key_temp_extremes%20%3D%20’1Oe3DlM4uobJMgSfJw8Fucyd4qgDt7WgXn0BVFZRuFRw’%20%23Outside%20temperature%20extremes%0A%0Aworksheet_id%20%3D%20’od6’%20%23%20worksheet%20%231%20by%20default%20always%20has%20a%20value%20of%20’od6’%20if%20necessary%0A%0A%23%20Mail%20default%20data%0ATO%20%3D%20%22zz%40zz.zz%22%0AFROM%20%3D%20%22zz%40gmail.com%22%0ATEXT%20%3D%20%22Alarm%20event%20registered%20%22%0Asubject%20%3D%20%22Default%20subject%22%0ASUBJECT_before%20%3D%20%22Before%20subject%20to%20send%20only%20one%20mail%20or%20SMS%20each%20time%22%0Aevent_value%20%3D%20%22something%22%0Aurl%20%3D%20’http%3A%2F%2Fcheckip.dyndns.org’%20%23%20Check%20current%20IP%20address%0Aiplookup%20%3D%20’this%20has%20to%20be%20exchanged%20with%20the%20full%20ip%20information’%0Amyip%20%3D%20’123.456.789.000’%0A%0A%23Declare%20rain%20variables%0ARAIN_10MIN%20%3D%200.0%20%23Accumulated%20rain%20mm%20in%2010%20minutes%20steps%0ARAIN_30MIN%20%3D%200.0%20%23Accumulated%20rain%20mm%20in%2030%20minutes%20steps%0ARAIN_HOUR%20%3D%200.0%20%23Accumulated%20rain%20the%20last%20whole%20hour%0ARAIN_DAY%20%3D%200.0%20%23%22Accumulated%20rain%20for%20the%20day%20-%20reset%20every%20midnight%22%0ARAIN_LO%20%3D%202.0%20%23Value%20for%20Heavy%20Rain%20mm%2F30%20min%0ARAIN_MID%20%3D%202.0%20%23Value%20for%20Heavy%20Rain%20mm%2F30%20min%0ARAIN_HI%20%3D%2010.0%20%23Value%20for%20Skyfall%20mm%2F30%20min%20(definition%20is%2015mm%2F30min)%0ARAIN10A%20%3D%200.0%20%23Newest%20rain%20data%20for%2010min%20interval%0ARAIN10B%20%3D%200.0%20%23Last%20rain%20data%20for%2010min%20interval%20-%20before%20A%0ARAIN10C%20%3D%200.0%20%23Last%20rain%20data%20for%2010min%20interval%20-%20before%20B%0A%0A%23Check%20if%20rain%20updates%20has%20been%20sent%3A%0ARAINDAY_UPDATE%20%3D%20True%20%0ARAINWEEK_UPDATE%20%3D%20True%20%0ARAINYEAR_UPDATE%20%3D%20True%20%0A%0A%23temperature%20settings%2C%20load%20the%20kernel%20modules%20needed%20so%20directories%20are%20created%20for%20each%20sensor%0Aos.system(‘modprobe%20w1-gpio’)%0Aos.system(‘modprobe%20w1-therm’)%0A%0A%23Declare%20temperature%20variables%20and%20states%0ATEMP_OUT_READ%20%3D%20True%0ATEMP_OUT_UPDATE%20%3D%20True%0ATEMP_NOW_UPDATE%20%3D%20True%0ATEMP_ALL_UPDATE%20%3D%20True%0ATEMP_OUT%20%3D%200.0%0ATEMP_MAX%20%3D%20-20.0%20%23Max%20outdoor%20temperatue%20the%20last%2024%20hours%20(supposed%20to%20be%20higher%20than%20this)%0ATEMP_MIN%20%3D%2060.0%20%23Min%20outdoor%20temperatue%20the%20last%2024%20hours%20(supposed%20to%20be%20lower%20than%20this)%0Atemperature%20%3D%20100%0At_check%20%3D%20-100%0At_last%20%3D%2099%20%23store%20measurement%20from%2010%20mins%20ago%20for%20comparison%0A%0A%23Array%20with%20information%20about%20all%20tested%20sensors%20incl.%20color%20code%2C%20sensor%20ID%2C%20dict%20name%2C%20function%2C%20cell%20number%20and%20cable%20marker%20in%20tank%0ATempIDs%20%3D%20%5B%22BLACK%22%2C%20%2228-000005b16567%22%2C%20%22out%22%2C%20%22Outside%20temperature%22%2C%20%2216%22%2C%20%22X%22%5D%2C%5C%0A%20%20%20%20%20%20%20%20%20%20%5B%22BROWN%22%2C%20%2228-000005b226ae%22%2C%20%22water%22%2C%20%22Water%20tank%20temp%22%2C%20%2217%22%2C%20%22A%22%5D%2C%20%5C%0A%20%20%20%20%20%20%20%20%20%20%5B%22RED%22%2C%20%2228-000005b217e2%22%2C%20%22radf%22%2C%20%22Radiators%20forward%20temp%22%2C%20%2218%22%2C%20%22B%22%5D%2C%5C%0A%20%20%20%20%20%20%20%20%20%20%5B%22ORANGE%22%2C%20%2228-000005b23020%22%2C%20%22radr%22%2C%20%22Radiators%20return%20temp%22%2C%20%2219%22%2C%20%22C%22%5D%2C%5C%0A%20%20%20%20%20%20%20%20%20%20%5B%22YELLOW%22%2C%20%2228-000005b21d0d%22%2C%20%22heatf%22%2C%20%22Received%20power%20heating%20temp%22%2C%20%2220%22%2C%20%22D%22%5D%2C%20%5C%0A%20%20%20%20%20%20%20%20%20%20%5B%22GREEN%22%2C%20%2228-000005b1b646%22%2C%20%22heatr%22%2C%20%22Returned%20power%20heating%20temp%22%2C%20%2221%22%2C%20%22E%22%20%5D%2C%5C%0A%20%20%20%20%20%20%20%20%20%20%5B%22BLUE%22%2C%20%2228-000005b1d70e%22%2C%20%22floorf%22%2C%20%22Floor%20heating%20forward%20temp%22%2C%20%2222%22%2C%20%22F%22%5D%2C%20%5C%0A%20%20%20%20%20%20%20%20%20%20%5B%22PURPLE%22%2C%20%2228-000005b217eb%22%2C%20%22floorr%22%2C%20%22Floor%20heating%20return%20temp%22%2C%20%2223%22%2C%20%22G%22%5D%2C%5C%0A%20%20%20%20%20%20%20%20%20%20%5B%22BLACK%20-%20LOST%20BROWN%22%2C%20%2228-000005b1a9e9%22%2C%20%22xx%22%2C%20%22xx%22%2C%20%2224%22%2C%20%22X%22%5D%2C%5C%0A%20%20%20%20%20%20%20%20%20%20%5B%22BROWN%22%2C%20%2228-000005bxxxxx%22%2C%20%22XX%22%2C%20%22xx%22%2C%20%2225%22%2C%20%22Y%22%5D%2C%5C%0A%20%20%20%20%20%20%20%20%20%20%5B%22RED%22%2C%20%2228-000005b22544%22%2C%20%22dining%22%2C%20%22Dining%20Room%22%2C%20%2226%22%2C%20%22Z%22%5D%2C%5C%0A%20%20%20%20%20%20%20%20%20%20%5B%22ORANGE%22%2C%20%2228-000005b1725b%22%2C%20%22living%22%2C%20%22Living%20Room%22%2C%20%2227%22%2C%20%22Q%22%5D%2C%5C%0A%20%20%20%20%20%20%20%20%20%20%0A%23Event%20notices%0ASEND_SMS%20%3D%20False%20%23Used%20for%20disabling%20too%20many%20SMS%20messages%20for%20some%20of%20the%20events%0ASEND_MAIL%20%3D%20True%20%23Used%20for%20disabling%20too%20many%20mails%20for%20some%20of%20the%20events%0ASEND_EVENT%20%3D%20True%20%23Used%20for%20disabling%20too%20many%20event%20updates%0A%0A%23Set%20the%20inputs%20and%20outputs%0A%23gpio%20no.%204%20is%20used%20for%20the%20DS18B20%20temperature%20sensors%20with%20a%204K7%20pull%20up%20resistor%0Agpio.setmode(gpio.BCM)%0Agpio.setup(3%2C%20gpio.OUT%2C%20pull_up_down%3Dgpio.PUD_OFF)%20%23Bell%20LED%0A%23%20gpio%20no.%207%20is%20reserved%20as%20an%20external%20trigger%20trigger%20pin%20OUT%0Agpio.setup(8%2C%20gpio.IN)%20%20%23External%20event%20trigger%20and%20test%20button%20switch%20IN%0Agpio.setup(9%2C%20gpio.IN)%20%20%23PIR%20Basement%20sensor%0Agpio.setup(10%2C%20gpio.IN)%20%23PIR%20Hall%20alarm%0Agpio.setup(11%2C%20gpio.IN)%20%23Floating%20sewer%20switch%0Agpio.setup(14%2C%20gpio.IN)%20%23Power%20failure%20relay%0Agpio.setup(17%2C%20gpio.OUT)%20%23Piezo%20buzzer%0Agpio.setup(18%2C%20gpio.IN)%20%23Bell%20push%20button%20in%20the%20entrance%20hall%0Agpio.setup(22%2C%20gpio.IN)%20%23PIR%20Hall%20tamper%0Agpio.setup(23%2C%20gpio.IN)%20%23Main%20switch%20arming%20the%20alarm%20in%20the%20entrance%20hall%0Agpio.setup(24%2C%20gpio.IN)%20%23Rain%20input%20from%20Hydreon%20sensor%20(50%20ms%20bursts)%0Agpio.setup(25%2C%20gpio.IN)%20%23Water%20Leak%20Basement%20sensor%0Agpio.setup(27%2C%20gpio.IN)%20%23Smoke%20alarm%0A%0AAlarmBellRunning%20%3D%20False%20%23Only%20start%20one%20bell%20if%20someone%20sits%20at%20the%20button%0AAlarmBlinkRunning%20%3D%20False%20%23Tells%20if%20the%20blink%2Fbuzz%20warning%20should%20be%20used%0AAlarmBuzzRunning%20%3D%20False%20%23Tells%20if%20the%20buzzer%20is%20sounding%20SOS%0AAlarmDelayRunning%20%3D%20False%20%23Tells%20if%20the%20Arm-timer%20is%20running%0AAlarmEvents%20%3D%20False%20%23If%20events%20under%20the%20Alarm-Arm-Switch%20should%20run%0A%0A%23%22before%22%20values%20are%20used%20to%20compare%20current%20status%20to%20past%20in%20order%20to%20avoid%20mail%20loops%0AUpdate_before%20%3D%20gpio.input(8)%0APIR_basement_before%20%3D%20gpio.input(9)%0APIR_hall_before%20%3D%20gpio.input(10)%0AFloat_before%20%3D%20gpio.input(11)%0ABell_before%20%3D%20gpio.input(18)%0ATamper_before%20%3D%20gpio.input(22)%0AArmed_before%20%3D%20gpio.input(23)%0ARain_before%20%3D%20gpio.input(24)%0AWater_before%20%3D%20gpio.input(25)%0ASmoke_before%20%3D%20gpio.input(27)%0A%0A%23Miscellaneous%0ASpeedCount%20%3D%200%20%23%20Checking%20CPU%20performance%0ASpeedSample%20%3D%200%20%23Sample%20rate%20in%20Hz%0ASpeedTime%20%3D%200%20%23Mean%20time%20of%20one%20’While%20Cycle’%0APowerFailure%20%3D%20gpio.input(14)%20%23Relay%20from%20UPS%20goes%20on%20when%20the%20PowerBank%20is%20looped%20through%0ASPR_FAIL%20%3D%20False%20%23%20Set%20to%20True%20if%20anything%20went%20wrong%20uploading%20data%20to%20Google%20sheet%0A%0A%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%20%20%20FUNCTION%20DEFINITIONS%20%20%20%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%0A%0Adef%20AlarmBell%20()%3A%0A%20%20%20%20%20%20%20%20global%20AlarmBellRunning%0A%20%20%20%20%20%20%20%20global%20SEND_SMS%0A%0A%20%20%20%20%20%20%20%20if%20Bell%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20AlarmBellRunning%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20EventNotice%20(%22Bell%3A%20Someone%20at%20the%20door%20%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23print%20%22Bell%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20True)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(3%2C%20True)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(2)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20False)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(3%2C%20False)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20AlarmBellRunning%20%3D%20False%0A%0Adef%20AlarmBlink%20()%3A%0A%20%20%20%20%20%20%20%20global%20AlarmBlinkRunning%0A%0A%20%20%20%20%20%20%20%20AlarmBlinkRunning%20%3D%20True%0A%20%20%20%20%20%20%20%20gpio.output(3%2C%20True)%0A%20%20%20%20%20%20%20%20sleep(2)%0A%20%20%20%20%20%20%20%20gpio.output(3%2C%20False)%0A%20%20%20%20%20%20%20%20sleep(1)%0A%20%20%20%20%20%20%20%20AlarmBlinkRunning%20%3D%20False%0A%0Adef%20AlarmBuzz%20()%3A%20%23ALARM%20sound%20signal!%0A%20%20%20%20%20%20%20%20global%20AlarmBuzzRunning%0A%0A%20%20%20%20%20%20%20%20AlarmBuzzRunning%20%3D%20True%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20for%20i%20in%20range%20(10)%3A%20%23SOS%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20n%20in%20range%20(3)%3A%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20True)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(0.1)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20False)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(0.1)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20n%20in%20range%20(3)%3A%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20True)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(0.3)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20False)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(0.3)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20n%20in%20range%20(3)%3A%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20True)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(0.1)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20False)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(0.1)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(1)%0A%20%20%20%20%20%20%20%20AlarmBuzzRunning%20%3D%20False%20%20%20%20%20%20%20%20%0A%0Adef%20AlarmDelay%20()%3A%20%23Function%20to%20delay%20the%20alarm%20sensors%20in%20order%20to%20get%20out%20out%20the%20house.%20Beeps%20are%20warning%20meanwhile.%0A%20%20%20%20%20%20%20%20global%20SEND_SMS%0A%20%20%20%20%20%20%20%20global%20AlarmEvents%20%23passes%20value%20of%20this%20variable%20along%20to%20other%20modules%0A%20%20%20%20%20%20%20%20global%20AlarmDelayRunning%0A%20%20%20%20%20%20%20%20AlarmDelayRunning%20%3D%20True%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20i%20in%20range%20(100)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20Armed%20%3D%3D%20True%3A%20%23Checks%20that%20the%20Arm%20switch%20is%20still%20on%20for%20each%20loop%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20True)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(0.1)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20False)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(2)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20i%20in%20range%20(100)%3A%20%23Faster%20beeps%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20Armed%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20True)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(0.2)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20False)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(0.1)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20Armed%20%3D%3D%20True%3A%20%23Checks%20that%20the%20Arm%20switch%20is%20still%20on%20(no%20regrets)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(0.2)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20True)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(5)%20%23Long%20beep%20to%20indicate%20that%20the%20alarm%20is%20now%20armed%20if%20you%20are%20listening%20outside%20the%20front%20door%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20False)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20EventNotice%20(%22Alarm%3A%20Armed%20%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23print%20%22Alarm%20armed%20after%20delay%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20AlarmEvents%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20AlarmDelayRunning%20%3D%20False%0A%20%20%20%20%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20LogFile%20(%22%20AlarmDelay%20%22%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20AlarmDelayRunning%20%3D%20False%0A%20%20%20%20%20%20%20%20AlarmDelayRunning%20%3D%20False%0A%0Adef%20AlarmPIRhall%20()%3A%20%23Runs%20if%20PIR%20hall%20is%20activated%20while%20Armed%0A%20%20%20%20%20%20%20%20global%20SEND_SMS%0A%20%20%20%20%20%20%20%20if%20Armed%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20n%20in%20range%20(15)%3A%20%23Delay%20so%20you%20can%20get%20in%20and%20disarm%20before%20alarm%20goes%20on%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20Armed%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20True)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(0.3)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gpio.output(17%2C%20False)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(1.5)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20Armed%20%3D%3D%20True%3A%20%23Next%20block%20runs%20if%20alarm%20hasn’t%20been%20disarmed%20after%20entrance%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20EventNotice%20(%22Alarm%3A%20PIR%20hall%20%22)%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23print%20%22PIR%20hall%20alarm%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20AlarmBuzzRunning%20%3D%3D%20False%20%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20s%3Dthreading.Thread(target%3DAlarmBuzz)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20s.start()%0A%0Adef%20AlarmSmoke%20()%3A%0A%20%20%20%20%20%20%20%20global%20SEND_SMS%0A%20%20%20%20%20%20%20%20sleep%20(0.3)%20%23prevent%20false%20alarms%0A%20%20%20%20%20%20%20%20if%20Smoke%20%3D%3D%20True%3A%20%23%20Fire%20alarm%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%20Smoke_before%20!%3D%20Smoke%3A%20%23%20Ensures%20that%20only%20one%20event%20is%20triggered%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20EventNotice%20(%22Alarm%3A%20FIRE!%20%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23print%20%22FIRE!%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20AlarmBuzzRunning%20%3D%3D%20False%20%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20s%3Dthreading.Thread(target%3DAlarmBuzz)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20s.start()%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%0Adef%20AlarmTamper%20()%3A%0A%20%20%20%20%20%20%20%20global%20SEND_SMS%0A%20%20%20%20%20%20%20%20sleep%20(0.3)%20%23Double%20check%20to%20avoid%20false%20alarms%0A%20%20%20%20%20%20%20%20if%20Tamper%20%3D%3D%20False%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%20Tamper_before%20!%3D%20Tamper%3A%20%23%20Ensures%20that%20only%20one%20event%20is%20triggered%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20EventNotice%20(%22Alarm%3A%20Tampering%20PIR%20in%20the%20hall%20%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23print%20%22Tamper%22%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20AlarmBuzzRunning%20%3D%3D%20False%20%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20s%3Dthreading.Thread(target%3DAlarmBuzz)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20s.start()%0A%0Adef%20EventNotice%20(subject)%3A%20%23Function%20to%20distribute%20info%20about%20each%20registered%20event%0A%20%20%20%20%20%20%20%20global%20SUBJECT%0A%20%20%20%20%20%20%20%20global%20SUBJECT_before%0A%20%20%20%20%20%20%20%20global%20SEND_SMS%0A%20%20%20%20%20%20%20%20global%20SEND_MAIL%0A%20%20%20%20%20%20%20%20global%20SEND_EVENT%0A%20%20%20%20%20%20%20%20global%20RAIN_10MIN%2C%20RAIN_30MIN%2C%20RAIN_HOUR%2C%20RAIN_DAY%0A%20%20%20%20%20%20%20%20global%20TEMP_OUT%2C%20TEMP_MAX%2C%20TEMP_MIN%0A%20%20%20%20%20%20%20%20global%20SpeedCount%2C%20SpeedSample%2C%20SpeedTime%20%23Meantime%20of%20While%20cycles%0A%20%20%20%20%20%20%20%20global%20PowerFailure%0A%20%20%20%20%20%20%20%20global%20myip%0A%0A%20%20%20%20%20%20%20%20%23%20Prepare%20the%20dictionary%20to%20write%0A%20%20%20%20%20%20%20%20dict%20%3D%20%7B%7D%0A%20%20%20%20%20%20%20%20dict%5B’text’%5D%20%3D%20subject%0A%20%20%20%20%20%20%20%20dict%5B’date’%5D%20%3D%20time.strftime(‘%25d%2F%25m%2F%25Y’)%0A%20%20%20%20%20%20%20%20dict%5B’time’%5D%20%3D%20time.strftime(‘%25H%3A%25M%3A%25S’)%0A%20%20%20%20%20%20%20%20dict%5B’mm10’%5D%20%3D%20str(RAIN_10MIN)%0A%20%20%20%20%20%20%20%20dict%5B’mm30’%5D%20%3D%20str(RAIN_30MIN)%0A%20%20%20%20%20%20%20%20dict%5B’mmhour’%5D%20%3D%20str(RAIN_HOUR)%0A%20%20%20%20%20%20%20%20dict%5B’mmday’%5D%20%3D%20str(RAIN_DAY)%0A%20%20%20%20%20%20%20%20dict%5B’tamper’%5D%20%3D%20str(gpio.input(22))%0A%20%20%20%20%20%20%20%20dict%5B’armed’%5D%20%3D%20str(gpio.input(23))%0A%20%20%20%20%20%20%20%20dict%5B’water’%5D%20%3D%20str(gpio.input(25))%0A%20%20%20%20%20%20%20%20dict%5B’smoke’%5D%20%3D%20str(gpio.input(27))%0A%20%20%20%20%20%20%20%20dict%5B’hall’%5D%20%3D%20str(gpio.input(10))%0A%20%20%20%20%20%20%20%20dict%5B’base’%5D%20%3D%20str(gpio.input(9))%0A%20%20%20%20%20%20%20%20dict%5B’tout’%5D%20%3D%20str(TEMP_OUT)%0A%20%20%20%20%20%20%20%20dict%5B’tmax’%5D%20%3D%20str(TEMP_MAX)%0A%20%20%20%20%20%20%20%20dict%5B’tmin’%5D%20%3D%20str(TEMP_MIN)%0A%20%20%20%20%20%20%20%20dict%5B’sample’%5D%20%3D%20str(SpeedSample)%0A%20%20%20%20%20%20%20%20dict%5B’powerfail’%5D%20%3D%20str(PowerFailure)%0A%20%20%20%20%20%20%20%20dict%5B’myip’%5D%20%3D%20str(myip)%0A%20%20%20%20%20%20%20%20d%20%3D%20open(‘%2Fhome%2Fpi%2FDesktop%2Ftxt%2Fdict.txt’%2C%20’w’)%0A%20%20%20%20%20%20%20%20d.write%20(str(dict))%0A%20%20%20%20%20%20%20%20d.close()%0A%20%20%20%20%20%20%20%20Upload(‘dict’%2C%20’txt’)%0A%0A%20%20%20%20%20%20%20%20if%20SUBJECT_before%20!%3D%20subject%3A%20%23Ensures%20that%20only%20one%20update%20%20will%20be%20made%20per%20event%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20SEND_SMS%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SMS%20%3D%20%22echo%20%22%20%2B%20chr(34)%20%2B%20str(subject)%20%2B%20%22%20%22%20%2B%20chr(34)%2B%20%22%20%7C%20%2Fusr%2Fbin%2Fgnokii%20–sendsms%20%2B1234567890%20–smsc%20%2B1234567890%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20os.popen(SMS)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20SEND_MAIL%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20BODY%20%3D%20string.join((%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22From%3A%20%25s%22%20%25%20FROM%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22To%3A%20%25s%22%20%25%20TO%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Subject%3A%20%25s%22%20%25%20subject%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TEXT%20%2B%20time.strftime(%22%25c%22)%2C%20str(dict))%2C%20%22%5Cr%5Cn%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20server%20%3D%20smtplib.SMTP(ghost)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20server.ehlo()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20server.starttls()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20server.ehlo()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20server.login(gmail%2C%20gpass)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20server.sendmail(FROM%2C%20TO%2C%20BODY)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20server.quit()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20SEND_EVENT%20%3D%3D%20True%3A%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SprUpdate%20(dict%2C%20spr_key_event)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20LogFile%20(%22%20EventNotice%20%22%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SUBJECT_before%20%3D%20subject%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_MAIL%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_EVENT%20%3D%20True%0A%0Adef%20LogFile%20(func)%3A%20%23Write%20to%20logfile%20if%20spreadsheet%20updates%20fail%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20f%20%3D%20open(‘%2Fhome%2Fpi%2FDesktop%2Falarm.txt’%2C%20’a’)%0A%20%20%20%20%20%20%20%20f.write(%22%20Event%20registered%20for%20%22%20%2B%20func%20%2B%20%22%20at%20%22%20%2B%20str(time.strftime(‘%25d%2F%25m%2F%25Y%20%25H%3A%25M%3A%25S’)))%0A%20%20%20%20%20%20%20%20f.close()%0A%0Adef%20RainAlerts%20()%3A%20%23Calculate%20and%20warn%20for%20severe%20rain%20incidents%0A%20%20%20%20%20%20%20%20global%20RAIN10A%0A%20%20%20%20%20%20%20%20global%20RAIN10B%0A%20%20%20%20%20%20%20%20global%20RAIN10C%0A%20%20%20%20%20%20%20%20global%20RAIN_10MIN%0A%20%20%20%20%20%20%20%20global%20RAIN_30MIN%0A%20%20%20%20%20%20%20%20global%20RAIN_LO%0A%20%20%20%20%20%20%20%20global%20RAIN_MID%0A%20%20%20%20%20%20%20%20global%20RAIN_HI%0A%0A%20%20%20%20%20%20%20%20RAIN10C%20%3D%20RAIN10B%0A%20%20%20%20%20%20%20%20RAIN10B%20%3D%20RAIN10A%0A%20%20%20%20%20%20%20%20RAIN10A%20%3D%20RAIN_10MIN%0A%20%20%20%20%20%20%20%20RAIN_30MIN%20%3D%20RAIN10A%20%2B%20RAIN10B%20%2B%20RAIN10C%0A%20%20%20%20%20%20%20%20if%20RAIN_30MIN%20%3E%3D%20RAIN_LO%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20RAIN_30MIN%20%3C%20RAIN_MID%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20EventNotice%20(%22Heavy%20Rain%20%22)%0A%20%20%20%20%20%20%20%20if%20RAIN_30MIN%20%3E%3D%20RAIN_MID%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20RAIN_30MIN%20%3C%20RAIN_HI%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20EventNotice%20(%22Severe%20Rain%20%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20False%0A%20%20%20%20%20%20%20%20if%20RAIN_30MIN%20%3E%3D%20RAIN_HI%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20EventNotice%20(%22Skyfall%20%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20False%20%0A%0Adef%20RainAppend%20(spr_key%2C%20rain_value)%3A%20%23%20Function%20to%20update%20any%20of%20the%203%20Rain%20sheets%0A%20%20%20%20%20%20%20%20global%20SPR_FAIL%0A%20%20%20%20%20%20%20%20%23%20Prepare%20the%20dictionary%20to%20write%0A%20%20%20%20%20%20%20%20dict%20%3D%20%7B%7D%0A%20%20%20%20%20%20%20%20dict%5B’date’%5D%20%3D%20time.strftime(‘%25d%2F%25m%2F%25Y%20%25H%3A%25M’)%0A%20%20%20%20%20%20%20%20dict%5B’mm’%5D%20%3D%20str(rain_value)%0A%20%20%20%20%20%20%20%20r%3Dthreading.Thread(target%3DSprUpdate%20(dict%2C%20spr_key))%0A%20%20%20%20%20%20%20%20r.start()%0A%20%20%20%20%20%20%20%20sleep%20(2)%0A%20%20%20%20%20%20%20%20if%20SPR_FAIL%20%3D%3D%20True%3A%20%23Check%20if%20update%20was%20unsuccessful%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(10)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r%3Dthreading.Thread(target%3DSprUpdate%20(dict%2C%20spr_key))%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.start()%20%23Try%20again%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SPR_FAIL%20%3D%20False%0A%0Adef%20SprUpdate%20(dict%2C%20spr_key)%3A%20%23Generic%20function%20for%20updating%20all%20spreadsheets%20with%20’dict’%0A%20%20%20%20%20%20%20%20%23DISABLED%20DUE%20TO%20GOOGLE%20PARANOIA%20AND%20THEIR%20OAUTH%20SYSTEM%0A%20%20%20%20%20%20%20%20global%20SprDelete%0A%20%20%20%20%20%20%20%20global%20SPR_FAIL%0A%23%20%20%20%20%20%20%20try%3A%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20entry%20%3D%20spr_client.InsertRow(dict%2C%20spr_key)%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20SprDelete%20%3D%3D%20True%3A%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(0.2)%20%23wait%20for%20the%20update%20before%20deleting%20the%20first%20line%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20delfeed%20%3D%20spr_client.GetListFeed(spr_key)%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20spr_client.DeleteRow(delfeed.entry%5B0%5D)%0A%23%20%20%20%20%20%20%20except%3A%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SPR_FAIL%20%3D%20True%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20LogFile%20(%22%20SprUpdate%20%22)%0A%0A%20%20%20%20%20%20%20%20SprDelete%20%3D%20True%0A%0Adef%20SprUpdateCell%20(value%2C%20cellno%2C%20spr_key)%3A%20%23Function%20to%20update%20single%20cells%20(temp%2C%20cell%20number%20and%20spreadsheet)%0A%20%20%20%20%20%20%20%20sleep(1)%23has%20to%20be%20something%20here%20instead%20…..%0A%20%20%20%20%20%20%20%20%0A%23%20%20%20%20%20%20%20try%3A%20%20%20%20%20%20%20%20%20%20%20%20%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20query%20%3D%20gdata.spreadsheet.service.CellQuery()%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20query.return_empty%20%3D%20%22true%22%20%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cells%20%3D%20spr_client.GetCellsFeed(spr_key%2C%20query%3Dquery)%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20batchRequest%20%3D%20gdata.spreadsheet.SpreadsheetsCellsFeed()%20%20%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cells.entry%5Bint(str(cellno))%5D.cell.inputValue%20%3D%20str(value)%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20batchRequest.AddUpdate(cells.entry%5Bint(str(cellno))%5D)%20%20%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20updated%20%3D%20spr_client.ExecuteBatch(batchRequest%2C%20cells.GetBatchLink().href)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%23%20%20%20%20%20%20%20except%3A%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20LogFile%20(%22%20SprUpdateCell%20had%20a%20problem.%20Temperature%3A%20%22%20%2B%20str(value)%20%2B%20%22%20Cell%3A%20%22%20%2B%20str(cellno)%20%2B%20%22%20Key%3A%20%22%20%2B%20str(spr_key)%20%2B%20%22%20%22)%0A%0Adef%20TempAll(spr_key)%3A%20%23Compare%20the%20list%20of%20sensors%20with%20online%20sensors%20in%20order%20to%20know%20which%20ones%20to%20use%0A%20%20%20%20%20%20%20%20global%20TEMP_MAX%2C%20TEMP_MIN%2C%20spr_key_temp_all%2C%20temperature%2C%20TempIDs%0A%20%20%20%20%20%20%20%20OnlineIDs%20%3D%20commands.getoutput(‘%20ls%20%2Fsys%2Fbus%2Fw1%2Fdevices%2F%20%7C%20grep%20-e%20%2228-%22’).splitlines()%20%20%23%20Ids%20for%20sensors%20online%0A%0A%20%20%20%20%20%20%20%20dict%20%3D%20%7B%7D%20%23Prepare%20data%20for%20spreadsheet%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%23Open%20the%20CSV%20and%20write%20the%20header%20instead%20of%20old%20content%0A%20%20%20%20%20%20%20%20r%20%3D%20open(‘%2Fhome%2Fpi%2FDesktop%2Fcsv%2Ftemp.csv’%2C%20’w’)%0A%20%20%20%20%20%20%20%20r.write%20(time.strftime(‘Date%20%25d-%25m-%25Y%20Time%20%25H%3A%25M%3A%25S’)%20%2B%20%22%2C%22%20%2B%20%22Degrees%20(C)%22%20%2B%20’%5Cr%5Cn’)%0A%20%20%20%20%20%20%20%20r.close()%0A%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20i%3D0%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t%3D0%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20u%3D0%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20i%20in%20range(len(OnlineIDs))%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20a%3DOnlineIDs%5Bi%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20t%20in%20range(len(TempIDs))%3A%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20b%3DTempIDs%5Bt%5D%5B1%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20a%3D%3Db%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TempRead%20(str(TempIDs%5Bt%5D%5B1%5D))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20temperature%20%3D%3D-0.062%3A%20%23Check%20for%20error%20readings%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TempRead%20(str(TempIDs%5Bt%5D%5B1%5D))%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23u%3Dint((TempIDs%5Bt%5D%5B2%5D)-16%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23print%20str(TempIDs%5Bt%5D%5B0%5D)%20%2B%20%22%20is%20%22%20%2B%20str(temperature)%20%2B%20%22%20deg%20C%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dict%5Bstr(TempIDs%5Bt%5D%5B2%5D)%5D%20%3D%20str(temperature)%20%23Build%20the%20list%20for%20each%20run%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r%20%3D%20open(‘%2Fhome%2Fpi%2FDesktop%2Fcsv%2Ftemp.csv’%2C%20’a’)%20%23Append%20the%20readings%20to%20the%20CSV%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.write%20(str(TempIDs%5Bt%5D%5B3%5D)%20%2B%20%22%2C%22%20%2B%20str(temperature)%20%2B%20’%5Cr%5Cn’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.close()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23SprUpdateCell%20(str(TempIDs%5Bt%5D%5B2%5D)%2C%20str(u)%2C%20spr_key)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SprUpdateCell%20(temperature%2C%20str(TempIDs%5Bt%5D%5B4%5D)%2C%20spr_key)%0A%0A%20%20%20%20%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20LogFile%20(%22%20TempAll%20%22)%0A%20%20%20%20%20%20%20%20Upload%20(‘temp’%2C’csv’)%0A%0Adef%20TempAppend%20(spr_key%2C%20tempmax%2C%20tempmin)%3A%20%23%20Update%20the%20max%20and%20min%20temp%20sheet%0A%20%20%20%20%20%20%20%20global%20SprDelete%0A%20%20%20%20%20%20%20%20global%20SPR_FAIL%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20SprDelete%20%3D%20False%0A%20%20%20%20%20%20%20%20%23Prepare%20the%20dictionary%20to%20write%0A%20%20%20%20%20%20%20%20dict%20%3D%20%7B%7D%0A%20%20%20%20%20%20%20%20dict%5B’date’%5D%20%3D%20time.strftime(‘%25d%2F%25m%2F%25Y’)%0A%20%20%20%20%20%20%20%20dict%5B’tmax’%5D%20%3D%20str(tempmax)%0A%20%20%20%20%20%20%20%20dict%5B’tmin’%5D%20%3D%20str(tempmin)%0A%20%20%20%20%20%20%20%20SprUpdate%20(dict%2C%20spr_key)%0A%20%20%20%20%20%20%20%20sleep%20(2)%0A%23%20%20%20%20%20%20%20if%20SPR_FAIL%20%3D%3D%20True%3A%20%23Check%20if%20temp%20update%20was%20unsuccessful%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(10)%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SprUpdate%20(dict%2C%20spr_key)%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SPR_FAIL%20%3D%20False%0A%20%20%20%20%20%20%20%20%0A%0Adef%20TempNow%20()%3A%20%23Update%20the%20’Temp%20now’%20Gauge%20spreadsheet%20every%2010%20mins%0A%20%20%20%20%20%20%20%20global%20TEMP_OUT%2C%20TEMP_MAX%2C%20TEMP_MIN%2C%20spr_key_temp_now%2C%20i%2C%20t%0A%20%20%20%20%20%20%20%20TempNowVal%20%3D%20%5BTEMP_OUT%2C%20TEMP_MAX%2C%20TEMP_MIN%5D%20%23Array%20with%20the%203%20outside%20temperatures%0A%20%20%20%20%20%20%20%20i%3D0%0A%20%20%20%20%20%20%20%20t%3D0%20%20%20%20%20%0A%20%20%20%20%20%20%20%20try%3A%20%20%20%20%23Update%20the%203%20outside%20temperatures%20in%20the%20Temp%20Now%20spreadsheet%20by%20cells%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20t%20in%20range(len(TempNowVal))%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20i%3Dt%2Blen(TempNowVal)%20%23First%20cell%20number%20is%200%20so%20second%20row%20starts%20with%20length%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SprUpdateCell%20(str(TempNowVal%5Bt%5D)%2C%20i%2C%20spr_key_temp_now)%0A%20%20%20%20%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20LogFile%20(%22%20TempNow%20%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0Adef%20TempOut%20()%3A%20%23Update%20the%20’Temp%20out’%20spreadsheet%20every%2010%20mins%20%0A%20%20%20%20%20%20%20%20global%20TEMP_OUT%0A%20%20%20%20%20%20%20%20global%20TEMP_MAX%0A%20%20%20%20%20%20%20%20global%20TEMP_MIN%0A%20%20%20%20%20%20%20%20global%20spr_key_temp_out%0A%20%20%20%20%20%20%20%20global%20temperature%0A%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TempRead(%2228-000005b16567%22)%20%23ID%20of%20outside%20sensor%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20temperature%20%3D%3D-0.062%3A%20%23Check%20for%20errors%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TempRead%20(%2228-000005b16567%22)%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TEMP_OUT%20%3D%20temperature%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TEMP_MAX%20%3D%20var_read(%22TEMP_MAX%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep(0.2)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TEMP_MIN%20%3D%20var_read(%22TEMP_MIN%22)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20TEMP_OUT%20%3E%20TEMP_MAX%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TEMP_MAX%20%3D%20TEMP_OUT%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var_write(%22TEMP_MAX%22%2C%20TEMP_MAX)%20%23saves%20new%20value%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20TEMP_OUT%20%3C%20TEMP_MIN%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TEMP_MIN%20%3D%20TEMP_OUT%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var_write(%22TEMP_MIN%22%2C%20TEMP_MIN)%20%23saves%20new%20value%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23Write%20the%203%20values%20to%20a%20CSV%20file%20that%20can%20be%20uploaded%20and%20used%20for%20online%20charts%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t%20%3D%20open(‘%2Fhome%2Fpi%2FDesktop%2Fcsv%2Ftout.csv’%2C%20’w’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.write%20(time.strftime(‘Date%20%25d-%25m-%25Y%20Time%20%25H%3A%25M%3A%25S’)%20%2B%20%22%2C%22%20%2B%20%22Degrees%20(C)%22%20%2B%20’%5Cr%5Cn’)%20%23%20The%20’%5Cr%5Cn’%20is%20needed%20for%20correct%20linefeed%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.write(%22Temp%20max%2C%22%20%2B%20str(TEMP_MAX)%20%2B%20’%5Cr%5Cn’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.write(%22Temp%20now%2C%22%20%2B%20str(TEMP_OUT)%20%2B%20’%5Cr%5Cn’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.write(%22Temp%20min%2C%22%20%2B%20str(TEMP_MIN)%20%2B%20’%5Cr%5Cn’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.close()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Upload(‘tout’%2C’csv’)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23Append%20the%20current%20outside%20temperature%20to%20the%20daily%20temp%20CSV%20file%20that%20can%20be%20uploaded%20and%20used%20for%20online%20charts%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t%20%3D%20open(‘%2Fhome%2Fpi%2FDesktop%2Fcsv%2FTEMP_DAY.csv’%2C%20’a’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.write%20(time.strftime(‘%25H%3A%25M%3A%25S’)%20%2B%20%22%2C%22%20%2B%20str(TEMP_OUT)%20%2B%20’%5Cr%5Cn’)%20%23%20The%20’%5Cr%5Cn’%20is%20needed%20for%20correct%20linefeed%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.close()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Upload(‘TEMP_DAY’%2C’csv’)%0A%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Prepare%20the%20dictionary%20to%20write%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dict%20%3D%20%7B%7D%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dict%5B’date’%5D%20%3D%20time.strftime(‘%25d%2F%25m%2F%25Y%20%25H%3A%25M’)%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dict%5B’tout’%5D%20%3D%20str(TEMP_OUT)%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SprUpdate%20(dict%2C%20spr_key_temp_out)%0A%0A%20%20%20%20%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20LogFile%20(%22%20TempOut%20%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0Adef%20TempRead%20(tID)%3A%20%23Read%20the%20temperature%20of%20a%20specific%20sensor%20by%20ID%20and%20return%20as%20’temperature’%0A%20%20%20%20%20%20%20%20global%20temperature%2C%20t_check%0A%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20file%20%3D%20open(%22%2Fsys%2Fbus%2Fw1%2Fdevices%2F%22%20%2B%20tID%20%2B%20%22%2Fw1_slave%22)%20%23%20Path%20of%20the%20sensor%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Read%20all%20of%20the%20text%20in%20the%20file.%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20text%20%3D%20file.read()%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Close%20the%20file%20now%20that%20the%20text%20has%20been%20read.%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20file.close()%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Split%20the%20text%20with%20new%20lines%20(%5Cn)%20and%20select%20the%20second%20line.%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20secondline%20%3D%20text.split(%22%5Cn%22)%5B1%5D%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Split%20the%20line%20into%20words%2C%20referring%20to%20the%20spaces%2C%20and%20select%20the%2010th%20word%20(counting%20from%200).%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20temperaturedata%20%3D%20secondline.split(%22%20%22)%5B9%5D%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20The%20first%20two%20characters%20are%20%22t%3D%22%2C%20so%20get%20rid%20of%20those%20and%20convert%20the%20temperature%20from%20a%20string%20to%20a%20number.%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20temperature%20%3D%20float(temperaturedata%5B2%3A%5D)%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Put%20the%20decimal%20point%20in%20the%20right%20place%20and%20display%20it.%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20temperature%20%3D%20temperature%20%2F%201000%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(0.5)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23Run%20the%20above%20one%20more%20time%20in%20order%20to%20compare%20for%20errors%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t_check%20%3D%20temperature%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20file%20%3D%20open(%22%2Fsys%2Fbus%2Fw1%2Fdevices%2F%22%20%2B%20tID%20%2B%20%22%2Fw1_slave%22)%20%23%20Path%20of%20the%20sensor%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20text%20%3D%20file.read()%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20file.close()%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20secondline%20%3D%20text.split(%22%5Cn%22)%5B1%5D%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20temperaturedata%20%3D%20secondline.split(%22%20%22)%5B9%5D%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20temperature%20%3D%20float(temperaturedata%5B2%3A%5D)%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20temperature%20%3D%20temperature%20%2F%201000%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20temperature%20!%3D%20t_check%3A%20%23make%20another%20measure%20if%20the%20first%20two%20were%20different%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20file%20%3D%20open(%22%2Fsys%2Fbus%2Fw1%2Fdevices%2F%22%20%2B%20tID%20%2B%20%22%2Fw1_slave%22)%20%23%20Path%20of%20the%20sensor%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20text%20%3D%20file.read()%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20file.close()%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20secondline%20%3D%20text.split(%22%5Cn%22)%5B1%5D%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20temperaturedata%20%3D%20secondline.split(%22%20%22)%5B9%5D%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20temperature%20%3D%20float(temperaturedata%5B2%3A%5D)%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20temperature%20%3D%20temperature%20%2F%201000%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20t_last%20!%3D%2099%3A%20%23Don’t%20do%20this%20at%20first%20run%20after%20reboot%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20temperature-t_last%3E10%3A%20%23skip%20if%20unrealistic%20high%20difference%20in%20measurement%20since%20last%20one%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20temperature%3Dt_last%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t_last%20%3D%20temperature%0A%0A%20%20%20%20%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20LogFile%20(%22%20Temp%20Read%20failure%20%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0Adef%20Upload%20(filename%2C%20ext)%3A%20%23Uploads%20the%20specified%20CSV%20file%20to%20the%20domain%0A%20%20%20%20%20%20%20%20global%20FTPhost%2C%20FTPdomain%2C%20FTPpass%0A%20%20%20%20%20%20%20%20fileup%20%3D%20’%2Fhome%2Fpi%2FDesktop%2F’%20%2B%20str(ext)%20%2B%20’%2F’%20%2B%20str(filename)%20%2B%20′.’%20%2B%20str(ext)%0A%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20session%20%3D%20ftplib.FTP(str(FTPhost)%2C%20str(FTPdomain)%2C%20str(FTPpass))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20file%20%3D%20open(str(fileup)%2C’rb’)%20%20%23%20file%20to%20send%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20session.cwd(‘%2Fwp-content%2Fuploads%2F’)%20%23change%20directory%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20session.storbinary(‘STOR%20’%20%2B%20str(filename)%20%2B%20%22.%22%20%2B%20str(ext)%2C%20file)%20%23%20send%20the%20file%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20file.close()%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20close%20file%20and%20FTP%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20session.quit()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print%20%22FTP%20upload%20failed%20%22%0A%0A%23Two%20functions%20to%20read%20and%20write%20variables%20to%20disk%20in%20order%20to%20save%20values%20and%20re-use%20after%20reboot.%0Adef%20var_read(var)%3A%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t%20%3D%20open(‘%2Fhome%2Fpi%2FDesktop%2Fvar%2F’%20%2B%20str(var)%20%2B%20′.txt’%2C%20’r’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20float(t.read())%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.close()%0A%20%20%20%20%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20LogFile%20(%22%20File%20read%20problem%20%22%20)%0A%0A%0Adef%20var_write(var%2C%20value)%3A%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t%20%3D%20open(‘%2Fhome%2Fpi%2FDesktop%2Fvar%2F’%20%2B%20str(var)%20%2B%20′.txt’%2C%20’w’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.write%20(str(value))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.close()%0A%20%20%20%20%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20LogFile%20(%22%20File%20write%20problem%20%22%20)%0A%0Adef%20checkmyip()%3A%0A%20%20%20%20%20%20%20%20global%20url%2C%20iplookup%2C%20myip%0A%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20iplookup%20%3D%20urlopen(url).read().decode(‘utf-8’)%20%23%20Open%20up%20the%20url%2C%20then%20read%20the%20contents%2C%20and%20take%20away%20to%20IP%20address%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20myip%20%3D%20re.findall(%22%5Cd%7B1%2C3%7D%5C.%5Cd%7B1%2C3%7D%5C.%5Cd%7B1%2C3%7D%5C.%5Cd%7B1%2C3%7D%22%2C%20iplookup)%20%23%20Extract%20the%20IP%20address%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20myip%20%3D%20str(myip)%0A%20%20%20%20%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20LogFile%20(%22%20my%20ip%20problem%20%22%20)%0A%0A%0A%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%20%20%20BOOT%20PROCESS%20%20%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%0A%0Aprint%20%22*************%20%20Booting%20Alarm%20system%20%20**************%22%0A%0Aif%20PowerFailure%20%3D%3D%20True%3A%20%23Send%20an%20SMS%20if%20the%20power%20failure%20pin%20is%20high%0A%20%20%20%20%20%20%20%20SMS%20%3D%20%22echo%20%22%20%2B%20chr(34)%20%2B%20%22Power%20failure%20%22%20%2B%20chr(34)%2B%20%22%20%7C%20%2Fusr%2Fbin%2Fgnokii%20–sendsms%20%2B4521214544%20–smsc%20%2B4571781877%22%0A%20%20%20%20%20%20%20%20os.popen(SMS)%0A%20%20%20%20%20%20%20%20sleep(40)%20%23Take%20a%20nap%20-%20the%20rest%20might%20not%20work%20as%20the%20network%20is%20also%20down.%0A%0A%23Read%20stored%20variables%0ARAIN_DAY%20%3D%20var_read(%22RAIN_DAY%22)%0ATEMP_MAX%20%3D%20var_read(%22TEMP_MAX%22)%0ATEMP_MIN%20%3D%20var_read(%22TEMP_MIN%22)%0A%0Am%3Dthreading.Thread(target%3Dcheckmyip)%0Am.start()%0Asleep%20(5)%20%23wait%20for%20the%20IP%20to%20be%20retrieved%0A%0Aif%20PowerFailure%20%3D%3D%20False%3A%0A%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20True%0A%20%20%20%20%20%20%20%20%23Initializing%20spreadsheet%20service%0A%23%20%20%20%20%20%20%20spr_client%20%3D%20gdata.spreadsheet.service.SpreadsheetsService()%0A%23%20%20%20%20%20%20%20spr_client.email%20%3D%20gmail%0A%23%20%20%20%20%20%20%20spr_client.password%20%3D%20gpass%0A%23%20%20%20%20%20%20%20spr_client.source%20%3D%20’Example%20Spreadsheet%20Writing%20Application’%0A%23%20%20%20%20%20%20%20spr_client.ProgrammaticLogin()%0A%23%20%20%20%20%20%20%20SprDelete%20%3D%20True%20%23If%20false%20then%20first%20line%20is%20not%20deleted%20after%20appending%20a%20new%20line%0A%20%20%20%20%20%20%20%20EventNotice%20(%22Alarm%20was%20booted%20%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0Aif%20Armed_before%20%3D%3D%20True%3A%20%23Check%20if%20reboot%20is%20made%20while%20house%20is%20left%20-%20eg%20after%20power%20failure%0A%20%20%20%20%20%20%20%20Armed_before%20%3D%20False%0A%0A%23Read%20sensors%20that%20are%20online%20%0AOnlineIDs%20%3D%20commands.getoutput(‘%20ls%20%2Fsys%2Fbus%2Fw1%2Fdevices%2F%20%7C%20grep%20-e%20%2228-%22’).splitlines()%20%20%23%20Get%20IDs%20for%20sensors%20online%0ATempAll%20(spr_key_temp_all)%20%23Update%20the%20Temp%20All%20spreadsheet%0Asleep%20(2)%0A%0A%23print%20%22TempAll%20updated%22%0Aprint%20%22Total%20amount%20of%20installed%20temp%20sensors%3A%20%22%20%2B%20str(len(TempIDs))%0Aprint%20%22Amount%20of%20online%20temp%20sensors%3A%20%22%20%2B%20str(len(OnlineIDs))%0Aprint%20%22ID%20of%20online%20sensors%3A%20%22%20%2B%20str(OnlineIDs)%0A%0ATempNow%20()%20%23Update%20the%20Temp%20Now%20spreadsheet%0Aprint%20%22TempNow%20updated%22%0A%0ALogFile%20(%22%20Booooted%20%22)%0Aprint%20%22LogFile%20updated%22%0A%0Aprint%20%22The%20current%20IP%20address%20is%3A%20%22%20%2B%20str(myip)%0A%0Afor%20i%20in%20range%20(2)%3A%20%23Two%20beeps%2Fblink%20to%20tell%20that%20the%20boot%20process%20has%20ended%0A%20%20%20%20%20%20%20%20gpio.output(17%2C%20True)%0A%20%20%20%20%20%20%20%20gpio.output(3%2C%20True)%0A%20%20%20%20%20%20%20%20sleep%20(0.2)%0A%20%20%20%20%20%20%20%20gpio.output(17%2C%20False)%0A%20%20%20%20%20%20%20%20gpio.output(3%2C%20False)%0A%20%20%20%20%20%20%20%20sleep%20(0.2)%0A%0Aprint%20%22*************%20%20Boot%20process%20finished%20%20**************%22%0A%0A%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%20END%20OF%20BOOT%20PROCESS%20%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%0A%0Awhile%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20Update%20%3D%20gpio.input(8)%0A%20%20%20%20%20%20%20%20PIR_basement%20%3D%20gpio.input(9)%0A%20%20%20%20%20%20%20%20PIR_hall%20%3D%20gpio.input(10)%0A%20%20%20%20%20%20%20%20Float%20%3D%20gpio.input(11)%0A%20%20%20%20%20%20%20%20Bell%20%3D%20gpio.input(18)%0A%20%20%20%20%20%20%20%20Tamper%20%3D%20gpio.input(22)%0A%20%20%20%20%20%20%20%20Armed%20%3D%20gpio.input(23)%0A%20%20%20%20%20%20%20%20Rain%20%3D%20gpio.input(24)%0A%20%20%20%20%20%20%20%20Water%20%3D%20gpio.input(25)%0A%20%20%20%20%20%20%20%20Smoke%20%3D%20gpio.input(27)%0A%20%20%20%20%20%20%20%20minw%20%3D%20str(time.strftime%20(%22%25M%22))%23%20Watching%20minutes%0A%20%20%20%20%20%20%20%20%0A%0A%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%20%20%20MANUAL%20EVENTS%20%20%20%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%0A%0A%20%20%20%20%20%20%20%20%23Section%20for%20events%20that%20should%20always%20run%20when%20system%20is%20powered%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20if%20Bell%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%20Bell_before%20!%3D%20Bell%3A%20%23%20Ensures%20that%20only%20one%20event%20is%20triggered%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20AlarmBellRunning%20%3D%3D%20False%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20b%3Dthreading.Thread(target%3DAlarmBell)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20b.start()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20if%20Update%20%3D%3D%20True%3A%20%23Request%20%20for%20status%20update%20via%20SSH%20on%20pin%208%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20Update%20!%3D%20Update_before%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20EventNotice%20(%22Status%20requested%20%22)%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20if%20Update%20%3D%3D%20False%3A%20%23prepare%20for%20another%20update%20request%20if%20required%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20Update%20!%3D%20Update_before%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SUBJECT_before%20%3D%20%22Status%20pin%20is%20low%20again%20%22%0A%0A%20%20%20%20%20%20%20%20if%20Rain%20%3D%3D%20True%3A%20%23Rain%20detected%20(0%2C2%20mm)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%20Rain_before%20!%3D%20Rain%3A%20%23%20Ensures%20that%20only%20one%20event%20is%20triggered%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20RAIN_10MIN%20%3D%20RAIN_10MIN%20%2B%200.2%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20RAIN_DAY%20%3D%20var_read(%22RAIN_DAY%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep(0.2)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20RAIN_DAY%20%3D%20RAIN_DAY%20%2B%200.2%20%23summarizing%20rain%20for%20all%20day%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var_write(%22RAIN_DAY%22%2C%20RAIN_DAY)%20%23saves%20values%20after%20reboot%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20RAIN_HOUR%20%3D%20RAIN_HOUR%20%2B%200.2%20%23accumulated%20rain%20for%20current%20hour%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SUBJECT_before%20%3D%22Is%20it%20still%20raining%3F%20%22%20%23subsequently%20send%20mails%20with%20rain%20info%20if%20nothing%20else%20happened%20since%20last%20mm%20count%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%0A%20%20%20%20%20%20%20%20if%20Smoke%20%3D%3D%20True%3A%20%23%20Fire%20alarm%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%20Smoke_before%20!%3D%20Smoke%3A%20%23%20Ensures%20that%20only%20one%20event%20is%20triggered%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20s%3Dthreading.Thread(target%3DAlarmSmoke)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20s.start()%0A%0A%20%20%20%20%20%20%20%20if%20Float%20%3D%3D%20True%3A%20%23%20Sewer%20flooded%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%20Float_before%20!%3D%20Float%3A%20%23%20Ensures%20that%20only%20one%20event%20is%20triggered%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20EventNotice%20(%22Alarm%3A%20FLOAT%20%22)%0A%0A%20%20%20%20%20%20%20%20if%20Tamper%20%3D%3D%20False%3A%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%20Tamper_before%20!%3D%20Tamper%3A%20%23%20Ensures%20that%20only%20one%20event%20is%20triggered%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t%3Dthreading.Thread(target%3DAlarmTamper)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.start()%0A%0A%20%20%20%20%20%20%20%20%23if%20Water%20%3D%3D%20True%3A%20%23Water%20leak%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23if%20%20Water_before%20!%3D%20Water%3A%20%23%20Ensures%20that%20only%20one%20event%20is%20triggered%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23SEND_SMS%20%3D%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23EventNotice%20(%22Alarm%3A%20WATER!%20%22)%20%20%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print%20%22Water%22%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20AlarmBuzzRunning%20%3D%3D%20False%20%3A%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20s%3Dthreading.Thread(target%3DAlarmBuzz)%0A%23%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20s.start()%0A%0A%0A%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%20%20%20TIMED%20EVENTS%20%20%20%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%0A%0A%20%20%20%20%20%20%20%20%23Section%20for%20events%20that%20are%20activated%20around%20the%20clock%2024-7(mainly%20Google%20updates)%0A%0A%0A%20%20%20%20%20%20%20%20if%20minw%5B-1%3A%5D%20%3D%3D%20%222%22%3A%20%23Update%20the%20outside%20thermometer%20every%2010%20min%20and%20make%20new%20Max%20and%20Min’s%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20TEMP_OUT_UPDATE%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t%3Dthreading.Thread(target%3DTempOut())%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.start()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TEMP_OUT_UPDATE%20%3D%20False%0A%0A%20%20%20%20%20%20%20%20if%20minw%5B-1%3A%5D%20%3D%3D%20%223%22%3A%20%23Update%20the%20thermometer%20gauge%20spreadsheet%20every%2010%20min%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20TEMP_NOW_UPDATE%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20n%3Dthreading.Thread(target%3DTempNow())%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20n.start()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TEMP_NOW_UPDATE%20%3D%20False%0A%0A%20%20%20%20%20%20%20%20if%20minw%5B-1%3A%5D%20%3D%3D%20%224%22%3A%20%23Update%20the%20sensor%20spreadsheet%20every%2010%20min%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20TEMP_ALL_UPDATE%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20a%3Dthreading.Thread(target%3DTempAll(spr_key_temp_all))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20a.start()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TEMP_ALL_UPDATE%20%3D%20False%0A%0A%20%20%20%20%20%20%20%20if%20minw%5B-1%3A%5D%20%3D%3D%20%225%22%3A%20%23Update%20the%20%20Rainy%20DAY%20sheet%20every%2010%20min%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20RAINDAY_UPDATE%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r%3Dthreading.Thread(target%3DRainAppend(spr_key_rainy_day%2C%20RAIN_10MIN))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.start()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23print%20%22Starting%20next%2010%20min%20loop%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r%20%3D%20open(‘%2Fhome%2Fpi%2FDesktop%2Fcsv%2FRAIN_DAY.csv’%2C%20’a’)%20%23add%20values%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.write%20(time.strftime%20(%22%25H%3A%25M%22)%20%2B%20%22%3A00%2C%22%20%2B%20str(RAIN_10MIN)%20%2B%20’%5Cr%5Cn’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.close()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Upload(‘RAIN_DAY’%2C’csv’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20a%20%3D%20open(‘%2Fhome%2Fpi%2FDesktop%2Fcsv%2FRAIN_ACC.csv’%2C%20’a’)%20%23add%20values%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20a.write%20(time.strftime%20(%22%25H%3A%25M%3A%25S%22)%2B%20%22%2C%22%20%2B%20str(RAIN_DAY)%20%2B%20’%5Cr%5Cn’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20a.close()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Upload(‘RAIN_ACC’%2C’csv’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20RAINDAY_UPDATE%20%3D%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20RAIN_10MIN%20%3D%200%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20if%20minw%5B-1%3A%5D%20%3D%3D%20%226%22%3A%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23Prepare%20for%20next%20Update%20of%20the%20rain-%20and%20temp%20sheets%20(10%20min)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20RAINDAY_UPDATE%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TEMP_OUT_READ%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TEMP_OUT_UPDATE%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TEMP_NOW_UPDATE%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TEMP_ALL_UPDATE%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23print%20%22Ready%20for%20next%2010%20min%20loop%22%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20RAINDAY_UPDATE%20%3D%3D%20False%3A%20%23Check%20for%20heavy%20rain%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20h%3Dthreading.Thread(target%3DRainAlerts())%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20h.start()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20RAINDAY_UPDATE%20%3D%20True%0A%0A%23%20%20%20%20%20%20%20if%20minw%5B-1%3A%5D%20%3D%3D%20%227%22%3A%20%0A%0A%20%20%20%20%20%20%20%20if%20time.strftime%20(%22%25M%22)%20%3D%3D%20%2257%22%3A%20%23Prepare%20for%20next%20Update%20of%20the%20WEEK%20sheet%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20RAINWEEK_UPDATE%20%3D%3D%20False%3A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20RAINWEEK_UPDATE%20%3D%20True%0A%0A%20%20%20%20%20%20%20%20if%20time.strftime%20(%22%25M%22)%20%3D%3D%20%2200%22%3A%20%23Update%20the%20Rainy%20WEEK%20sheet%20every%20hour%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20RAINWEEK_UPDATE%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20w%3Dthreading.Thread(target%3DRainAppend(spr_key_rainy_week%2C%20RAIN_HOUR))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20w.start()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20RAIN_HOUR%3D0%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20RAINWEEK_UPDATE%20%3D%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23print%20%22starting%20next%201%20hour%20loop%22%0A%0A%20%20%20%20%20%20%20%20if%20time.strftime%20(%22%25H%3A%25M%3A%25S%22)%20%3D%3D%20%2223%3A57%3A10%22%3A%20%23Prepare%20speed%20counting%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SpeedCount%3D0%0A%0A%20%20%20%20%20%20%20%20if%20time.strftime%20(%22%25H%3A%25M%3A%25S%22)%20%3D%3D%20%2223%3A57%3A20%22%3A%20%23Speed%20count%20test%20through%201%20second%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SpeedCount%20%3D%20SpeedCount%2B1%0A%0A%20%20%20%20%20%20%20%20if%20time.strftime%20(%22%25H%3A%25M%3A%25S%22)%20%3D%3D%20%2223%3A57%3A30%22%3A%20%23Prepare%20daily%20update%20of%20the%20Rainy%20YEAR%20sheet%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SpeedSample%20%3D%20SpeedCount%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SpeedTime%20%3D%201000%2FSpeedSample%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20RAINYEAR_UPDATE%20%3D%3D%20False%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20RAINYEAR_UPDATE%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20if%20time.strftime%20(%22%25H%3A%25M%3A%25S%22)%20%3D%3D%20%2223%3A57%3A40%22%3A%20%23Daily%20update%20of%20the%20Rainy%20YEAR%20and%20TEMP%20sheets%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20RAINYEAR_UPDATE%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SprDelete%20%3D%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r%3Dthreading.Thread(target%3DRainAppend(spr_key_rainy_year%2C%20RAIN_DAY))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.start()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_MAIL%20%3D%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r%20%3D%20open(‘%2Fhome%2Fpi%2FDesktop%2Fcsv%2FRAIN_YEAR.csv’%2C%20’a’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.write%20(time.strftime(‘%25d%2F%25m%2F%25Y’)%20%2B%20%22%2C%22%20%2B%20str(RAIN_DAY)%20%2B%20’%5Cr%5Cn’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.close()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep(5)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Upload%20(‘RAIN_YEAR’%2C’csv’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20EventNotice%20(%22Resetting%20rain%20counters.%20%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep(5)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20RAIN_DAY%20%3D%200.0%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var_write(%22RAIN_DAY%22%2C%20RAIN_DAY)%20%23reset%20file%20as%20well%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep(5)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23Store%20and%20reset%20outside%20temp%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t%3Dthreading.Thread(target%3DTempAppend(spr_key_temp_extremes%2C%20TEMP_MAX%2C%20TEMP_MIN))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.start()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep(2)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r%20%3D%20open(‘%2Fhome%2Fpi%2FDesktop%2Fcsv%2FTEMP_YEAR.csv’%2C%20’a’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.write%20(time.strftime(‘%25d%2F%25m%2F%25Y’)%20%2B%20%22%2C%22%20%2B%20str(TEMP_MIN)%20%2B%20%22%2C%22%20%2B%20str(TEMP_MAX)%20%2B%20’%5Cr%5Cn’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.close()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep(2)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Upload%20(‘TEMP_YEAR’%2C’csv’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep(2)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TEMP_MAX%20%3D%20-20.0%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var_write(%22TEMP_MAX%22%2C%20TEMP_MAX)%20%23reset%20max%20temperature%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep(2)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TEMP_MIN%20%3D%2060.0%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var_write(%22TEMP_MIN%22%2C%20TEMP_MIN)%20%23reset%20min%20temperature%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20RAINYEAR_UPDATE%20%3D%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%22starting%20next%2024%20hour%20loop%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r%20%3D%20open(‘%2Fhome%2Fpi%2FDesktop%2Fcsv%2FRAIN_DAY.csv’%2C%20’w’)%20%23prepare%20a%20new%20daily%20rain%20file%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.write%20(%22time%2Cmm%22%20%2B%20’%5Cr%5Cn’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.close()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t%20%3D%20open(‘%2Fhome%2Fpi%2FDesktop%2Fcsv%2FTEMP_DAY.csv’%2C%20’w’)%20%23prepare%20a%20new%20daily%20temperature%20file%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.write%20(%22time%2Cdeg%22%20%2B%20’%5Cr%5Cn’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.close()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20a%20%3D%20open(‘%2Fhome%2Fpi%2FDesktop%2Fcsv%2FRAIN_ACC.csv’%2C%20’w’)%20%23prepare%20a%20new%20daily%20accumulated%20rain%20file%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20a.write%20(%22time%2Cmm%22%20%2B%20’%5Cr%5Cn’)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20a.close()%0A%0A%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%20%20%20ARMED%20ALARM%20EVENTS%20%20%20%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%23Section%20for%20events%20that%20should%20run%20only%20when%20house%20is%20left%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20if%20Armed%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%20Armed_before%20!%3D%20Armed%3A%20%23%20Ensures%20that%20only%20one%20delay%20is%20executed%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20AlarmDelayRunning%20%3D%3D%20False%3A%20%23Activates%20Arm%20delay%20for%20house%20exit%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t%3Dthreading.Thread(target%3DAlarmDelay)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20t.start()%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20AlarmBlinkRunning%20%3D%3D%20False%3A%20%23Blink%20sequence%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sleep%20(0.01)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20AlarmBlinkRunning%20%3D%3D%20False%3A%20%23Avoid%20double%20loops%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20b%3Dthreading.Thread(target%3DAlarmBlink)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20b.start()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20time.strftime%20(%22%25H%3A%25M%3A%25S%22)%20%3D%3D%20%2212%3A00%3A00%22%3A%20%23Once%20daily%20notification%20without%20SMS%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23print%20%22Still%20Alive!%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20False%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_MAIL%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20n%3Dthreading.Thread(target%3DEventNotice%20(%22Alarm%20is%20still%20alive%2012.00%20%22))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20n.start()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20time.strftime%20(%22%25H%3A%25M%3A%25S%22)%20%3D%3D%20%2217%3A00%3A00%22%3A%20%23Once%20daily%20notification%20with%20SMS%20during%20vacations%20etc%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23print%20%22Still%20Alive!%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_MAIL%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20s%3Dthreading.Thread(target%3DEventNotice%20(%22Alarm%20is%20still%20alive%2017.00%20%22))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20s.start()%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20AlarmEvents%20%3D%3D%20True%3A%20%23Events%20that%20will%20run%20only%20after%20the%20Armed%20delay%20is%20over%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Ensure%20that%20only%20one%20mail%20is%20sent%20and%20that%20it%20doesn’t%20fire%20if%20active%20when%20alarm%20is%20armed%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%20PIR_hall_before%20!%3D%20PIR_hall%3A%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20PIR_hall%20%3D%3D%20False%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20a%3Dthreading.Thread(target%3DAlarmPIRhall)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20a.start()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%20PIR_basement_before%20!%3D%20PIR_basement%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20PIR_basement%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20EventNotice%20(%22Alarm%3A%20PIR%20basement%20%22)%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23print%20%22PIR%20basement%20alarm%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20AlarmBuzzRunning%20%3D%3D%20False%20%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20s%3Dthreading.Thread(target%3DAlarmBuzz)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20s.start()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20LogFile%20(%22%20Arming%20failed%20%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%0A%20%20%20%20%20%20%20%20if%20Armed%20%3D%3D%20False%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20AlarmEvents%20%3D%3D%20True%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%20Armed_before%20!%3D%20Armed%3A%20%23%20Ensures%20that%20only%20one%20mail%20is%20sent%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23print%20%22Alarm%20disarmed%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20SEND_SMS%20%3D%20True%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20EventNotice%20(%22Alarm%3A%20Disarmed%20%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20AlarmEvents%20%3D%20False%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%20%20%20RESET%20%20%20%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%23%0A%0A%20%20%20%20%20%20%20%20%23Reset%20all%20variables%20once%20every%20cycle%0A%0A%20%20%20%20%20%20%20%20PIR_basement_before%20%3D%20PIR_basement%0A%20%20%20%20%20%20%20%20PIR_hall_before%20%3D%20PIR_hall%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20Armed_before%20%3D%20Armed%0A%20%20%20%20%20%20%20%20Water_before%20%3D%20Water%0A%20%20%20%20%20%20%20%20Rain_before%20%3D%20Rain%0A%20%20%20%20%20%20%20%20Smoke_before%20%3D%20Smoke%0A%20%20%20%20%20%20%20%20Tamper_before%20%3D%20Tamper%0A%20%20%20%20%20%20%20%20Bell_before%20%3D%20Bell%0A%20%20%20%20%20%20%20%20Update_before%20%3D%20Update%0A%20%20%20%20%20%20%20%20%0Agpio.cleanup()%0A%0A%23THIS%20IS%20THE%20END%0A” message=”” highlight=”” provider=”manual”/]