<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://www.thinkwiki.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=T33nkp4d</id>
	<title>ThinkWiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://www.thinkwiki.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=T33nkp4d"/>
	<link rel="alternate" type="text/html" href="https://www.thinkwiki.org/wiki/Special:Contributions/T33nkp4d"/>
	<updated>2026-05-16T16:00:16Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.31.12</generator>
	<entry>
		<id>https://www.thinkwiki.org/w/index.php?title=Script_for_theft_alarm_using_HDAPS&amp;diff=50062</id>
		<title>Script for theft alarm using HDAPS</title>
		<link rel="alternate" type="text/html" href="https://www.thinkwiki.org/w/index.php?title=Script_for_theft_alarm_using_HDAPS&amp;diff=50062"/>
		<updated>2010-11-20T13:05:14Z</updated>

		<summary type="html">&lt;p&gt;T33nkp4d: /* Activation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==General==&lt;br /&gt;
&lt;br /&gt;
Recent ThinkPad models include a built-in two-axis accelerometer, as part of the [[HDAPS]] feature. This accelerometer can be put to another use: as a laptop theft deterrent. The following scripts detect when the laptop is moved, and emits a loud audio alarm. &lt;br /&gt;
&lt;br /&gt;
This alarm can be an effective deterrent against a casual laptop-snatcher in a populated environment (e.g., typical office space). It's also useful when you're across the room from the laptop and want to know if someone fiddles with it.&lt;br /&gt;
&lt;br /&gt;
Note that the alarm is disabled when the laptop is suspended or powered off. You can buy external (hardware) motion detector alarms to handle those cases.&lt;br /&gt;
&lt;br /&gt;
{{WARN|The audio alarm is played at a very high volume. Never enable the alarm while wearing headphones connected to the laptop's speaker output or when the laptop is connected to a high-power amplifier.}}&lt;br /&gt;
&lt;br /&gt;
==A comprehensive script==&lt;br /&gt;
&lt;br /&gt;
This Perl script periodically samples the tilt data reported by the accelerometer, computes the variance over recent samples, and triggers the alarm when the variance exceeds a given threshold.&lt;br /&gt;
&lt;br /&gt;
On a ThinkPad with [[Active Protection System]] running a modern Linux installation with the [[HDAPS|hdaps]] kernel module loaded, the script should work as is. Just run {{cmdroot|tp-theft --arm}} and see (or rather, hear) what happens when you tilt your laptop. &lt;br /&gt;
&lt;br /&gt;
The volume and alarm sound can be adjusted at the top of the script. On a ThinkPad {{T43}}, the synthetic siren at &amp;lt;tt&amp;gt;$alarm_volume=100&amp;lt;/tt&amp;gt; (up from the default 70) is quite ear-splitting, and combined with &amp;lt;tt&amp;gt;$acpi_volume=15&amp;lt;/tt&amp;gt; it is dangerously loud.&lt;br /&gt;
&lt;br /&gt;
The script is designed to run continuously in the background, so by default the alarm will be activated only when the KDE screen saver is locked. If you you open the laptop lid (or press the lid button) shortly before or after the beginning of movement, the alarm will be suspended (except for a brief warning) and you will get a few seconds of grace to unlock the screen saver (preferably, [[How to enable the fingerprint reader|using the integrated fingerprint reader]]!). You can disable this functionality by passing the &amp;lt;tt&amp;gt;--arm&amp;lt;/tt&amp;gt; parameter, by setting &amp;lt;tt&amp;gt;$use_kde=0&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;$use_lid=0&amp;lt;/tt&amp;gt;, or by using the simpler script below.&lt;br /&gt;
&lt;br /&gt;
There is also an option to track a BlueTooth device (e.g., a mobile phone). In this case, the alarm is activated (and optionally, the KDE desktop is locked) whenever the device is turned off or too distant for a given period, and deactivated when the BlueTooth device is nearby. You need to provide the device's BD address. If both KDE screen saver and BlueTooth checking are enabled, then the alarm will be activated when *either* the screensaver is enabled or the BlueTooth device is amiss.&lt;br /&gt;
&lt;br /&gt;
===Prerequisites===&lt;br /&gt;
&lt;br /&gt;
* ThinkPad with [[Active Protection System]]&lt;br /&gt;
* [[HDAPS|hdaps]] kernel module loaded (included in kernel 2.6.14 and later)&lt;br /&gt;
* Optional: [[ibm-acpi|ibm_acpi]] module loaded with the &amp;lt;tt&amp;gt;experimental=1&amp;lt;/tt&amp;gt; parameter (included in kernel 2.6.14 and later; needed only for full volume control)&lt;br /&gt;
The following are included in all modern Linux distributions:&lt;br /&gt;
* ALSA sound system, &amp;lt;tt&amp;gt;alsactl&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;aplay&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;amixer&amp;lt;/tt&amp;gt; )&lt;br /&gt;
* &amp;lt;tt&amp;gt;sox&amp;lt;/tt&amp;gt; (SOund eXchange) sound utility&lt;br /&gt;
&lt;br /&gt;
===The script===&lt;br /&gt;
{{CodeRef|tp-theft}}&lt;br /&gt;
&lt;br /&gt;
==A basic script==&lt;br /&gt;
&lt;br /&gt;
This is a simpler version of the above script, which omits the fancier functionality such as KDE screensaver detection, lid detection and state machine.&lt;br /&gt;
&lt;br /&gt;
===Prerequisites===&lt;br /&gt;
&lt;br /&gt;
* ThinkPad with [[Active Protection System]]&lt;br /&gt;
* [[HDAPS|hdaps]] kernel module loaded (included in kernel 2.6.14 and later)&lt;br /&gt;
* &amp;lt;tt&amp;gt;aumix&amp;lt;/tt&amp;gt; mixer control utility (included in all modern Linux distributions)&lt;br /&gt;
* &amp;lt;tt&amp;gt;sox&amp;lt;/tt&amp;gt; (SOund eXchange) sound utility (included in all modern Linux distributions, e.g. packages &amp;quot;sox&amp;quot; and &amp;quot;libsox-fmt-oss&amp;quot; in Ubuntu)&lt;br /&gt;
* in newer Versions of sox (e.g. shipped with current Ubuntu), the used parameters are no longer supported. You have to replace &amp;quot;-t nul /dev/null&amp;quot; with &amp;quot;-n&amp;quot; in line 19.&lt;br /&gt;
&lt;br /&gt;
===The script===&lt;br /&gt;
&lt;br /&gt;
{{CodeRef|tp-theft-basic}}&lt;br /&gt;
&lt;br /&gt;
== LCARS edition ==&lt;br /&gt;
The simple script modified to show a [http://memory-alpha.org/wiki/LCARS LCARS] red alert flash animation from Star Trek Voyager instead of a beep.&lt;br /&gt;
&lt;br /&gt;
===Prerequisites===&lt;br /&gt;
* ThinkPad with [[Active Protection System]]&lt;br /&gt;
* [[HDAPS|hdaps]] kernel module loaded (included in kernel 2.6.14 and later)&lt;br /&gt;
* &amp;lt;tt&amp;gt;gtk-gnash&amp;lt;/tt&amp;gt; [http://www.gnashdev.org/ Gnash] a GNU SWF movie player&lt;br /&gt;
* internet connection to get the swf from http://lcars.org.uk (you may download the file and adjust the path)&lt;br /&gt;
&lt;br /&gt;
===The script===&lt;br /&gt;
&lt;br /&gt;
{{CodeRef|tp-theft-lcars}}&lt;br /&gt;
&lt;br /&gt;
==Ideas for improvement==&lt;br /&gt;
Features awaiting contribution:&lt;br /&gt;
&lt;br /&gt;
===Activation===&lt;br /&gt;
* Gnome and xscreensaver support (similarly to [http://fdd.com/software/radeon/lightwatch.pl lightwatch.pl]?)&lt;br /&gt;
* Monitor X server presence  and make noise on sudden X server shutdown (i.e. catch {{key|ctrl}}{{key|alt}}{{key|backspace}} events).&lt;br /&gt;
Already done !* Monitor AC power and take it into account for alarm activation -- thieves seldom carry a UPS.&lt;br /&gt;
* Don't arm the alarm if movement of similar magnitude was happening also before the screensaver was auto-locked (the owner might be in a moving vehicle, etc.).&lt;br /&gt;
* Disarm the alarm (or hold it off for a few seconds, as already implemented for lid open) based on voice/sound recognition using the built-in microphone.&lt;br /&gt;
* Use fingerprint reader to disarm the alarm.&lt;br /&gt;
&lt;br /&gt;
===Precaution===&lt;br /&gt;
* Disable the alarm when headphones are plugged in -- it may cause hearing damage (if the user ignores the initial warning), and won't be effective anyway. '''Can we detect whether the something is plugged into the headphones/line-out socket?'''&lt;br /&gt;
* Theft attempts may be accompanied by rough handling, especially when the siren kicks in. So when starting an alarm also park the disk heads. Release the parking when a key is pressed (according {{path|/sys/bus/platform/drivers/hdaps/hdaps/keyboard_activity}}) so that the login prompt can start up. This requires kernel support for disk head parking and queue freezing, currently developed for the (original) HDAPS functionality.&lt;br /&gt;
&lt;br /&gt;
===Indication===&lt;br /&gt;
* Use XOSD to ivisually ndicate state changes.&lt;br /&gt;
* Use TrayIcon to indicate state changes.&lt;br /&gt;
* Use Notifications to indicate state changes.&lt;br /&gt;
&lt;br /&gt;
===Lockdown===&lt;br /&gt;
* Disable the volume buttons when the script is running so that a thief can't just turn the volume down. (Not an issue when &amp;lt;tt&amp;gt;ibm_acpi&amp;lt;/tt&amp;gt; volume control is available - see [[#Prerequisites|Prerequisites]].)&lt;br /&gt;
* Disable suspend and powersaving measures when armed.&lt;br /&gt;
* Disable poweroff (to the extent possible) when armed - or at least use the two seconds the power button acpi function comes active before poweroff to make as much noise as possible (it may take a while for a casual thief to actully remove the battery).&lt;br /&gt;
&lt;br /&gt;
===Alert===&lt;br /&gt;
* Start out quietly, and increase siren duration and volume if movement persists. Reset after a period of no movement.&lt;br /&gt;
* Report theft via network (if you get a chance to):&lt;br /&gt;
**Check for presence of wired or open wireless network and connect if not already connected.&lt;br /&gt;
**Send eMail to email to sms gateway or use an online sms service.&lt;br /&gt;
**If builtin webcam is present, take shots and upload them to a server or send via email. (This could be activated over a reboot, so that even if the thief gets away with the laptop, it would still be somewhat traceable.)&lt;br /&gt;
**If WAN of GPS devices are present, use it to detect position to a web server.&lt;br /&gt;
* When the alarm is triggered, also show a visual warning on the display. Override screensaver/powersaving if necessary. I.e., inform the thief that the notebook has a power on password and is useless without it. (This can be done by selecting a dedicated screensaver for that purpose.)&lt;br /&gt;
* Eject optical drive to irritate and hence slow down the thief.&lt;br /&gt;
&lt;br /&gt;
===Other===&lt;br /&gt;
* Implement this functionality in the embedded controller, so that the alarm will work even when the laptop is suspended. It may be possible to do so without IBM/Lenovo's involvement, using the [http://forum.thinkpads.com/viewtopic.php?t=20958 embedded controller disassembly].&lt;br /&gt;
* On Ubuntu, some of the paths don't work. alsactl is in /sbin, not /usr/sbin, and pidof is in /bin, not /sbin. These probably shouldn't be hardcoded paths. (Fix: create symlinks)&lt;br /&gt;
&lt;br /&gt;
==Pitfalls (and solutions?)==&lt;br /&gt;
* The audible alarm can always be suppressed by plugging earphones into the audio-jack (could be dealt with if software override for the audio-jack diversion is possible).&lt;br /&gt;
* The power button can be held to hard poweroff the notebook (can be avoided by using usb/bluetooth detection, closing the lid and sounding alarm if lid is opened before the usb/bluetooth device is present).&lt;br /&gt;
* The thief can unplug the battery to hard poweroff the notebook (can't do anything about it, but to set the alarm settings so that it goes off quickly - i.e., when using bluetooth detection, disable grace-period if activated manually)&lt;br /&gt;
&lt;br /&gt;
==Another Script (plugin-based)==&lt;br /&gt;
there's another script with the same intention available at http://www.informatik.hu-berlin.de/~pilop/HOWTO_Gentoo_T43/#TheftAlarm&lt;br /&gt;
&lt;br /&gt;
it uses a plugin-architecture for different checks (HDAPS, ethernet, power, lid, ...)&lt;br /&gt;
&lt;br /&gt;
==Yet another script (python/gtk based)==&lt;br /&gt;
You can find yet another version of this script at&lt;br /&gt;
&lt;br /&gt;
http://r3blog.nl/index.php/thinkpad-theft&lt;br /&gt;
(source at https://bitbucket.org/trbs/thinkpad-theft/overview/)&lt;br /&gt;
&lt;br /&gt;
It has almost the same features as the comprehensive script above, with a few improvements. It uses dbus to query the screensaver status and gconf for storing configuration value. To improve the delay before the alarm sounds, it has a built-in wav player, and it opens the file-descriptor of the wav at startup time (thereby removing the need to spawn an application to play the alarm; imagine someone stealing your laptop while you're doing heavy disk io). Furthermore, it has a trayicon allowing you to manipulate most settings stored in gconf aswell as showing you the current status of the alarm. The 0.2 release features activation on missing presence of a bluetooth or usb device.&lt;br /&gt;
&lt;br /&gt;
There is a [https://bitbucket.org/trbs/thinkpad-theft/overview new repository] at BitBucket that has seen some patches to fix problems with the unstable dbus-screensaver api and more.&lt;br /&gt;
&lt;br /&gt;
==Conceptional thoughts==&lt;br /&gt;
The above feature improvement suggestions partly require the theft protection software to be running as root (ACPI and hardware management), partly to be running as user (DBus SessionBus communications). Hence the following approach would be the one opening for the most coverage:&lt;br /&gt;
*theft-protection-daemon; run as root by init; controlling config, system related activation, lockdown and alarm&lt;br /&gt;
*theft-protection-trayicon; run in user-session; reporting desktop related activation criteria to the daemon and giving visual user feedback on the desktop&lt;br /&gt;
*theft-protection-properties; runnable as user; reporting configuration changes to daemon&lt;br /&gt;
&lt;br /&gt;
Ideally, they would provide a plugin-system, with plugins consisting of a functional and a gui part.&lt;br /&gt;
With this scheme, the theft protection can't be circumvented i.e. by pressing {{key|ctrl}}{{key|alt}}{{key|backspace}}.&lt;br /&gt;
&lt;br /&gt;
[[Category:Scripts]]&lt;/div&gt;</summary>
		<author><name>T33nkp4d</name></author>
		
	</entry>
	<entry>
		<id>https://www.thinkwiki.org/w/index.php?title=Code/tp-theft&amp;diff=50061</id>
		<title>Code/tp-theft</title>
		<link rel="alternate" type="text/html" href="https://www.thinkwiki.org/w/index.php?title=Code/tp-theft&amp;diff=50061"/>
		<updated>2010-11-20T12:05:24Z</updated>

		<summary type="html">&lt;p&gt;T33nkp4d: Aded checking and using power cord ac state for alarming&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/usr/bin/perl&lt;br /&gt;
#&lt;br /&gt;
# tp-theft v0.5.1&lt;br /&gt;
# (http://thinkwiki.org/wiki/Script_for_theft_alarm_using_HDAPS)&lt;br /&gt;
&lt;br /&gt;
# Provided under the GNU General Public License version 2 or later or&lt;br /&gt;
# the GNU Free Documentation License version 1.2 or later, at your option.&lt;br /&gt;
# See http://www.gnu.org/copyleft/gpl.html for the Warranty Disclaimer.&lt;br /&gt;
&lt;br /&gt;
# This script uses the HDAPS accelerometer found on recent ThinkPad models&lt;br /&gt;
# to emit an audio alarm when the laptop is tilted. In sufficiently&lt;br /&gt;
# populated environments, it can be used as a laptop theft deterrent.&lt;br /&gt;
# Uses a state machine and some heuristics to reduce false alarms.&lt;br /&gt;
#&lt;br /&gt;
# By default the alarm will be activated only when the KDE screen saver is&lt;br /&gt;
# locked. If you you open the laptop lid (or press the lid button) shortly&lt;br /&gt;
# before or after the beginning of movement, the alarm will be suspended&lt;br /&gt;
# (except for a brief warning) and you will get a few seconds of grace to &lt;br /&gt;
# unlock the screen saver. You can disable this functionality by passing &lt;br /&gt;
# the &amp;quot;--arm&amp;quot; parameter, or by setting  $use_kde=0 and $use_lid=0.&lt;br /&gt;
#&lt;br /&gt;
# There is also an option to track a BlueTooth device (e.g., a mobile phone).&lt;br /&gt;
# In this case, the alarm is activated (and optionally, the KDE desktop is &lt;br /&gt;
# locked) whenever the device is turned off or too distant for a given period,&lt;br /&gt;
# and deactivated when the BlueTooth device is nearby. You need to provide the&lt;br /&gt;
# device's BD address. If both KDE screen saver and BlueTooth checking are&lt;br /&gt;
# enabled, then the alarm will be activated when *either* the screensaver&lt;br /&gt;
# is enabled or the BlueTooth device is amiss.&lt;br /&gt;
#&lt;br /&gt;
# To control the sound and blinkenlights, and adjust the alarm activation &lt;br /&gt;
# parameters, see the variables below.&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
use FileHandle;&lt;br /&gt;
use IO::Pipe;&lt;br /&gt;
use Time::HiRes qw(sleep time);&lt;br /&gt;
use POSIX qw(:errno_h :signal_h);&lt;br /&gt;
&lt;br /&gt;
##############################&lt;br /&gt;
# Siren volume and content&lt;br /&gt;
&lt;br /&gt;
# Alarm audio volume (0..100)&lt;br /&gt;
my $alarm_volume = 70;&lt;br /&gt;
# Alarm command (default: synthesize a siren for 1.0 seconds):&lt;br /&gt;
my $alarm_cmd = &amp;quot;sox -t nul /dev/null -t wav -s -w -c2 -r48000 -t raw - synth 1.0 sine 2000-4000 sine 4000-2000 | aplay -q -fS16_LE -c2 -r48000&amp;quot;;&lt;br /&gt;
# my $alarm_cmd = &amp;quot;aplay keep_your_hands_off_me.wav&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
# Warning audio volume (0..100)&lt;br /&gt;
my $warn_volume = 45;&lt;br /&gt;
# Alarm command (default: synthesize a biref siren):&lt;br /&gt;
my $warn_cmd = &amp;quot;sox -t nul /dev/null -t wav -s -w -c2 -r48000 -t raw - synth 0.10 sine 2000-4000 sine 4000-2000 | aplay -q -fS16_LE -c2 -r48000&amp;quot;;&lt;br /&gt;
# my $warn_cmd = &amp;quot;aplay warning.wav&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
# Set ibm_acpi volume (0..15), if ibm_acpi is loaded with &amp;quot;experimental=1&amp;quot;.&lt;br /&gt;
# Combining $acpi_volume=15 and $alarm_volume=100 makes the alarm &lt;br /&gt;
# dangerously loud.&lt;br /&gt;
my $acpi_volume = 10;&lt;br /&gt;
&lt;br /&gt;
# Blink system LEDs when alarm activated?&lt;br /&gt;
my $use_led = 'safe';  # 0=off, 'safe'=only LEDs whose state you can recover, 'all'=pretty blinkenlights!&lt;br /&gt;
&lt;br /&gt;
# Blink ThinkLight when alarm activated?&lt;br /&gt;
my $use_light = 0;  # 0=off, 1=on&lt;br /&gt;
&lt;br /&gt;
# Use AC state to monitor &lt;br /&gt;
my $use_ac_state = 1; # 0=off, 1=on&lt;br /&gt;
##############################&lt;br /&gt;
# Activation control&lt;br /&gt;
&lt;br /&gt;
# Tilt threshold (increase value to decrease sensitivity):&lt;br /&gt;
my $thresh = 0.20;&lt;br /&gt;
# Minimum movement duration between warning and alarm:&lt;br /&gt;
my $min_hold = 1.3;&lt;br /&gt;
# When armed, any movement triggers alarm. How long should it remain armed?&lt;br /&gt;
my $arm_persist = 6;&lt;br /&gt;
&lt;br /&gt;
# After this many seconds of no movement, will allow a grace period again:&lt;br /&gt;
my $grace_relax = 15;&lt;br /&gt;
&lt;br /&gt;
# Activate according to KDE screen saver? Otherwise, always active:&lt;br /&gt;
my $use_kde = 1;&lt;br /&gt;
# When screen saver locked, wait this long before activation:&lt;br /&gt;
my $kde_lock_delay =  8;&lt;br /&gt;
&lt;br /&gt;
# Provide grace period if laptop lid is opened?&lt;br /&gt;
my $use_lid = 1;&lt;br /&gt;
# Opening a lid will grant this many seconds of grace (once):&lt;br /&gt;
my $lid_grace = 7;&lt;br /&gt;
# Lid must to be opened within this time to hold/pause alarm:&lt;br /&gt;
my $lid_grace_window = 8;&lt;br /&gt;
# Alarm will hold off this long when grace is available:&lt;br /&gt;
my $lid_hold = 3;&lt;br /&gt;
&lt;br /&gt;
# Control arming according by presence of a BlueTooth token&lt;br /&gt;
my $use_bluetooth = 0;&lt;br /&gt;
# Lock KDE screen saver when BlueTootk is not present?&lt;br /&gt;
my $bluetooth_lock_kde = 1;&lt;br /&gt;
# BD address of BlueTooth token (use &amp;quot;hcitool scan&amp;quot; to find this)&lt;br /&gt;
my $bluetooth_token_addr = '00:00:00:00:00:00';&lt;br /&gt;
# Consider token amiss when its received signal leve is below this (see &amp;quot;hcitool rssi&amp;quot;)&lt;br /&gt;
my $bluetooth_min_rssi = -10;&lt;br /&gt;
# Activate if BlueTooth token not seen this long:&lt;br /&gt;
my $bluetooth_activate_period = 12;&lt;br /&gt;
# Disactivate if BlueTooth token seen this recently:&lt;br /&gt;
my $bluetooth_deactivate_period = 5;&lt;br /&gt;
# If BlueTooth detection activated KDE lock, don't do it again for this long&lt;br /&gt;
my $bluetooth_lock_kde_interval = 30;&lt;br /&gt;
# If BlueTooth wasn't polled for this long, disregard recent history&lt;br /&gt;
my $bluetooth_reset_period = 10; &lt;br /&gt;
&lt;br /&gt;
##############################&lt;br /&gt;
# Other setup vars&lt;br /&gt;
&lt;br /&gt;
my $interval = 0.1;  # sampling intervalm in seconds&lt;br /&gt;
my $depth = 10;      # number of recent samples to analyze&lt;br /&gt;
my $verbose = 2;     # 0=nothing, 1=alarms, 2=state transitions, 3=everything&lt;br /&gt;
my $kde_check_interval = 1.5; # KDE screen saver check is expensive&lt;br /&gt;
my $bluetooth_sleep = 1;       # Sleep interval in BlueTooth check loop&lt;br /&gt;
&lt;br /&gt;
my $pos_file = '/sys/devices/platform/hdaps/position';&lt;br /&gt;
my $lid_file = '/proc/acpi/button/lid/LID/state';&lt;br /&gt;
my $led_file = '/proc/acpi/ibm/led';&lt;br /&gt;
my $light_file = '/proc/acpi/ibm/light';&lt;br /&gt;
my $bay_file = '/proc/acpi/ibm/bay';&lt;br /&gt;
my $volume_file = '/proc/acpi/ibm/volume'; # load ibm_acpi with experimental=1&lt;br /&gt;
my $bluetooth_file = '/proc/acpi/ibm/bluetooth'; # load ibm_acpi with experimental=1&lt;br /&gt;
my $ac_state_file = '/proc/acpi/ac_adapter/AC/state'; # ac state &lt;br /&gt;
&lt;br /&gt;
my $alsactl = '/usr/sbin/alsactl';&lt;br /&gt;
my $amixer = 'amixer';&lt;br /&gt;
my $kdesktop_lock = '/usr/bin/kdesktop_lock';&lt;br /&gt;
my $hcitool = '/usr/bin/hcitool';&lt;br /&gt;
my $l2ping = '/usr/bin/l2ping';&lt;br /&gt;
&lt;br /&gt;
##############################&lt;br /&gt;
# Utility functions&lt;br /&gt;
&lt;br /&gt;
sub say {&lt;br /&gt;
    my ($verb, $what) = @_;&lt;br /&gt;
    print(gmtime().&amp;quot;: $what\n&amp;quot;) if $verb&amp;lt;=$verbose;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub slurp { # read whole file&lt;br /&gt;
    my ($filename) = @_;&lt;br /&gt;
    local $/;&lt;br /&gt;
    my $fh = new FileHandle($filename,&amp;quot;&amp;lt;&amp;quot;) or return;&lt;br /&gt;
    return &amp;lt;$fh&amp;gt;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub burp { # write whole file&lt;br /&gt;
    my ($filename) = shift;&lt;br /&gt;
    my $fh = new FileHandle($filename,&amp;quot;&amp;gt;&amp;quot;) or die &amp;quot;Can't open $filename for writing: $!&amp;quot;;&lt;br /&gt;
    print $fh @_ or die &amp;quot;Can't write to $filename: $!&amp;quot;;&lt;br /&gt;
    close $fh or die &amp;quot;Can't close $filename after writing: $!&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub stddev { # standard deviation of list&lt;br /&gt;
    my $sum=0;&lt;br /&gt;
    my $sumsq=0;&lt;br /&gt;
    my $n=$#_+1;&lt;br /&gt;
    for my $v (@_) {&lt;br /&gt;
        $sum += $v;&lt;br /&gt;
        $sumsq += $v*$v;&lt;br /&gt;
    }&lt;br /&gt;
    return sqrt($n*$sumsq - $sum*$sum)/($n*($n-1));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub frac {&lt;br /&gt;
    my ($x) = @_;&lt;br /&gt;
    return $x-int($x);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub max {&lt;br /&gt;
    return $_[0] &amp;gt; $_[1] ? $_[0] : $_[1];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
my $alarm_file; # flags ongoing alarm (and also stores saved mixer settings)&lt;br /&gt;
&lt;br /&gt;
sub sound_alarm {&lt;br /&gt;
    # Sound alarm. Forks bash code which sets given volumes, runs the given&lt;br /&gt;
    # command, and then restores the given volumes to their saved values.&lt;br /&gt;
    my ($name, $volume, $acpi_volume, $cmd) = @_;&lt;br /&gt;
    return if (defined($alarm_file) &amp;amp;&amp;amp; -f $alarm_file);&lt;br /&gt;
    say(1,$name);&lt;br /&gt;
    $alarm_file = `mktemp /tmp/tp-theft-tmp.XXXXXXXX` or die &amp;quot;mktemp: $?&amp;quot;;&lt;br /&gt;
    chomp($alarm_file);&lt;br /&gt;
    my ($acpi_vol_file, $acpi_vol_set, $acpi_vol_restore);&lt;br /&gt;
    if ($_=slurp($volume_file) and m/^level:\s+(\d+)\n/) {&lt;br /&gt;
        $acpi_vol_file = $volume_file; &lt;br /&gt;
        $acpi_vol_set = &amp;quot;level $acpi_volume&amp;quot;; &lt;br /&gt;
        $acpi_vol_restore = &amp;quot;level $1&amp;quot;; &lt;br /&gt;
        if (m/^mute:\s+on$/m) {&lt;br /&gt;
          $acpi_vol_set = &amp;quot;up,&amp;quot;.$acpi_vol_set; # unmute first&lt;br /&gt;
          $acpi_vol_restore .= &amp;quot;,mute&amp;quot;;         # mute last&lt;br /&gt;
        }&lt;br /&gt;
    } else {&lt;br /&gt;
        $acpi_vol_file='/dev/null'; $acpi_vol_set=''; $acpi_vol_restore='';&lt;br /&gt;
    }&lt;br /&gt;
    system('/bin/bash', '-c', &amp;lt;&amp;lt;&amp;quot;EOF&amp;quot;)==0 or die &amp;quot;Failed: $?&amp;quot;;&lt;br /&gt;
( trap \&amp;quot;echo '$acpi_vol_restore' &amp;gt; $acpi_vol_file; sleep 0.1;&lt;br /&gt;
         $alsactl -f $alarm_file restore;&lt;br /&gt;
         rm -f $alarm_file&lt;br /&gt;
       \&amp;quot; EXIT HUP QUIT TERM&lt;br /&gt;
  $alsactl -f $alarm_file store &amp;amp;&amp;amp;                         # store ALSA&lt;br /&gt;
  echo '$acpi_vol_set' &amp;gt; $acpi_vol_file &amp;amp;&amp;amp; sleep 0.1 &amp;amp;&amp;amp;    # set ACPI&lt;br /&gt;
  $amixer -q set Master $volume% unmute &amp;amp;&amp;amp;                 # set ALSA Master&lt;br /&gt;
  $amixer -q set PCM 100% unmute &amp;amp;&amp;amp;                        # set alsa PCM&lt;br /&gt;
  $cmd ) &amp;amp;                                                 # invoke command&lt;br /&gt;
EOF&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
##############################&lt;br /&gt;
# KDE screen saver lock check&lt;br /&gt;
&lt;br /&gt;
if ($use_kde) { # Basic sanity check&lt;br /&gt;
    `/sbin/pidof kdesktop`; $?==0 or die &amp;quot;Can't use KDE, it's not running.\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub kdesktop_lock_status {&lt;br /&gt;
    # See if kdesktop_lock is running and check its cmdline and automatic lock delay&lt;br /&gt;
    my $bin = $kdesktop_lock;&lt;br /&gt;
    my $pids = `/sbin/pidof $bin`;&lt;br /&gt;
    return 'off' unless $?==0;&lt;br /&gt;
    for my $pid (split(/\s+/,$pids)) {&lt;br /&gt;
        next unless $pid =~ m/^\d+$/;&lt;br /&gt;
        # Attached to display &amp;quot;:0&amp;quot; or &amp;quot;localhost:0&amp;quot;?&lt;br /&gt;
        my $environ = slurp(&amp;quot;/proc/$pid/environ&amp;quot;) or next;&lt;br /&gt;
        my $good=0; my $home;&lt;br /&gt;
        for (split(/\x00/,$environ)) { &lt;br /&gt;
            $good=1 if m/^DISPLAY=(localhost)?:0$/; &lt;br /&gt;
            $home=$1 if m/^HOME=(.+)$/;  # also remember its $HOME&lt;br /&gt;
        }&lt;br /&gt;
        next unless $good;&lt;br /&gt;
        # Check command line&lt;br /&gt;
        my $cmdline = slurp(&amp;quot;/proc/$pid/cmdline&amp;quot;) or next;&lt;br /&gt;
        $cmdline =~ m/^[^\x00]+\x00(?:([^\x00]+)\x00)?/ or die &amp;quot;Cannot parse $bin command line\n&amp;quot;;&lt;br /&gt;
        if (!defined($1)) {&lt;br /&gt;
            # Read KDE screensaver lock time&lt;br /&gt;
            defined($home) or die &amp;quot;Cannot find HOME in environment of $bin process&amp;quot;;&lt;br /&gt;
            my $rc_path = &amp;quot;$home/.kde/share/config/kdesktoprc&amp;quot;;&lt;br /&gt;
            my $rc = new FileHandle($rc_path,&amp;quot;&amp;lt;&amp;quot;) or die &amp;quot;Error opening $rc_path: $!&amp;quot;;&lt;br /&gt;
            while (&amp;lt;$rc&amp;gt;) { m/^LockGrace=(\d+)$/ and return ('auto', $1/1000.0); };&lt;br /&gt;
            die &amp;quot;Cannot parse $rc_path&amp;quot;;&lt;br /&gt;
        } elsif ($1 eq '--forcelock') {&lt;br /&gt;
            return &amp;quot;force&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return 'off';&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
my $last_kls_update = 0; # time of last update&lt;br /&gt;
my $last_kls = 'init';   # last state seen&lt;br /&gt;
my $last_kls_start;      # when that state started&lt;br /&gt;
my @last_kls_opwhy = ();&lt;br /&gt;
&lt;br /&gt;
sub check_kde_lock {&lt;br /&gt;
    # De/activate according to KDE screen saver:&lt;br /&gt;
    my $now=time();&lt;br /&gt;
    return @last_kls_opwhy if $now &amp;lt; $last_kls_update + $kde_check_interval;&lt;br /&gt;
    my ($kls, $auto_delay) = kdesktop_lock_status();&lt;br /&gt;
    $last_kls_update = time();&lt;br /&gt;
    if ($kls ne $last_kls) {&lt;br /&gt;
        $last_kls = $kls;&lt;br /&gt;
        $last_kls_start = $now;&lt;br /&gt;
    }&lt;br /&gt;
    if ($kls eq 'off') { # no screen saver&lt;br /&gt;
        @last_kls_opwhy = (0, 'KDE screen saver not locked');&lt;br /&gt;
    } elsif ($kls eq 'auto') { # screen saver with automatic lock&lt;br /&gt;
        if ($now &amp;gt;= $last_kls_start + $auto_delay + $kde_lock_delay) {&lt;br /&gt;
            @last_kls_opwhy = (1, 'KDE screen saver is auto-locked');&lt;br /&gt;
        }&lt;br /&gt;
    } elsif ($kls eq 'force') { # screen saver with forced lock&lt;br /&gt;
        if ($now &amp;gt;= $last_kls_start + $kde_lock_delay) {&lt;br /&gt;
            @last_kls_opwhy = (1, 'KDE screen saver is forced-locked');&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return @last_kls_opwhy;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Lock KDE desktop&lt;br /&gt;
sub force_kde_lock {&lt;br /&gt;
    return if ($last_kls eq 'auto' or $last_kls eq 'force');&lt;br /&gt;
    say(1, &amp;quot;Locking KDE desktop&amp;quot;);&lt;br /&gt;
    # system('dcop kdesktop KScreensaverIface lock') or die &amp;quot;Cannot lock KDE dekstop: $!&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    my %oldENV = %ENV;&lt;br /&gt;
    my $kde_pids = `/sbin/pidof -x /usr/bin/startkde`;&lt;br /&gt;
    for my $kde_pid (split(/\s+/,$kde_pids)) {&lt;br /&gt;
        my $kde_env = slurp(&amp;quot;/proc/$kde_pid/environ&amp;quot;) or next;&lt;br /&gt;
        %ENV = ();&lt;br /&gt;
        for (split(/\x00/,$kde_env)) {&lt;br /&gt;
            m/^(DISPLAY|HOME|XAUTHORITY|USER|DBUS_SESSION_BUS_ADDRESS)=(.+)$/&lt;br /&gt;
                and $ENV{$1} = $2;&lt;br /&gt;
        }&lt;br /&gt;
        next unless defined($ENV{DISPLAY}) and $ENV{DISPLAY} =~ m/^:/;&lt;br /&gt;
        system('dcop kdesktop KScreensaverIface lock') and die &amp;quot;Cannot lock KDE dekstop: $!&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
    my %ENV = %oldENV;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
##############################&lt;br /&gt;
# Lid checking&lt;br /&gt;
&lt;br /&gt;
if ($use_lid) { # sanity check&lt;br /&gt;
    slurp($lid_file) or die &amp;quot;Can't use lid via $lid_file: $!&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
my $last_lid_status = 'open';&lt;br /&gt;
my $last_lid_open = 0;&lt;br /&gt;
&lt;br /&gt;
sub check_lid {&lt;br /&gt;
    my $lid = slurp($lid_file) or return;&lt;br /&gt;
    if ($lid =~ m/state: *open$/) {&lt;br /&gt;
        $last_lid_open = time() if ($last_lid_status eq 'closed');&lt;br /&gt;
        $last_lid_status = 'open';&lt;br /&gt;
    } else {&lt;br /&gt;
        $last_lid_status = 'closed';&lt;br /&gt;
    }&lt;br /&gt;
    return $last_lid_status;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
##############################&lt;br /&gt;
AC state checking&lt;br /&gt;
my $ac_state;&lt;br /&gt;
sub read_ac_state {&lt;br /&gt;
        open F, $ac_state_file;&lt;br /&gt;
        if ((&amp;lt;F&amp;gt;) =~ /^state:\s*(on|off)-line$/)&lt;br /&gt;
                {&lt;br /&gt;
                    $ac_state=$1;&lt;br /&gt;
                }&lt;br /&gt;
                close F;&lt;br /&gt;
            return($1);&lt;br /&gt;
                  }&lt;br /&gt;
        read_ac_state;&lt;br /&gt;
  ### used for testing       print &amp;quot;AC: $ac_state\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
##############################&lt;br /&gt;
# LED blinking&lt;br /&gt;
&lt;br /&gt;
# Flash LEDs&lt;br /&gt;
sub led_activate {&lt;br /&gt;
    return if $use_led eq '0';&lt;br /&gt;
    my $ledf = new FileHandle($led_file,&amp;quot;&amp;gt;&amp;quot;);&lt;br /&gt;
    if (!defined($ledf)) {&lt;br /&gt;
        print &amp;quot;Cannot open $led_file, disabling LED indicator: $!\n&amp;quot;;&lt;br /&gt;
        $use_led = '0';&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    $ledf-&amp;gt;autoflush(1);&lt;br /&gt;
    my $base = time()*2.5;&lt;br /&gt;
    print $ledf '0 '.((frac($base)&amp;gt;0.7)?'on':'off').&amp;quot;\n&amp;quot;; # power&lt;br /&gt;
    if ($use_led eq 'all') { # battery -- we can't recover these&lt;br /&gt;
        print $ledf '1 '.((frac($base+0.50)&amp;gt;0.7)?'on':'off').&amp;quot;\n&amp;quot;; # battery, orange&lt;br /&gt;
        print $ledf '2 '.((frac($base+0.25)&amp;gt;0.7)?'on':'off').&amp;quot;\n&amp;quot;; # battery, yellow&lt;br /&gt;
    }&lt;br /&gt;
    print $ledf '4 '.((frac($base)&amp;gt;0.7)?'on':'off').&amp;quot;\n&amp;quot;; # bay&lt;br /&gt;
    print $ledf '7 '.((frac($base+0.725)&amp;gt;0.7)?'on':'off').&amp;quot;\n&amp;quot;; # standby&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Restore LEDs to normal state&lt;br /&gt;
sub led_restore {&lt;br /&gt;
    return if $use_led eq '0';&lt;br /&gt;
    my $ledf = new FileHandle($led_file,&amp;quot;&amp;gt;&amp;quot;) or die &amp;quot;Cannot open $led_file: $!\n&amp;quot;;&lt;br /&gt;
    $ledf-&amp;gt;autoflush(1);&lt;br /&gt;
    print $ledf &amp;quot;0 on\n&amp;quot;; # power=on&lt;br /&gt;
    if ($use_led eq 'all') { # battery -- we can't recover these&lt;br /&gt;
        print $ledf &amp;quot;1 on\n&amp;quot;;&lt;br /&gt;
        print $ledf &amp;quot;2 on\n&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    print $ledf &amp;quot;7 off\n&amp;quot;; # power=off&lt;br /&gt;
    if (my $baydata = slurp($bay_file)) {&lt;br /&gt;
        my $is_bay = ($baydata =~ m/^status:\s*occupied$/m)?'on':'off';&lt;br /&gt;
        print $ledf &amp;quot;4 $is_bay\n&amp;quot;; # reset to correct status&lt;br /&gt;
    } else {&lt;br /&gt;
        print $ledf &amp;quot;4 on\n&amp;quot;; # force bay on, we don't know correct status&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
##############################&lt;br /&gt;
# ThinkLight blinking&lt;br /&gt;
&lt;br /&gt;
sub light_activate {&lt;br /&gt;
    return if $use_light eq '0';&lt;br /&gt;
    my $lightf = new FileHandle($light_file,&amp;quot;&amp;gt;&amp;quot;);&lt;br /&gt;
    if (!defined($lightf)) {&lt;br /&gt;
        print &amp;quot;Cannot open $light_file, disabling ThinkLight indicator: $!\n&amp;quot;;&lt;br /&gt;
        $use_light = '0';&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    my $base = time()/2;&lt;br /&gt;
    print $lightf (check_lid() eq 'open' &amp;amp;&amp;amp; ((frac($base)&amp;lt;0.1)?'on':'off')).&amp;quot;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub light_restore {&lt;br /&gt;
    return if $use_light eq '0';&lt;br /&gt;
    my $lightf = new FileHandle($light_file,&amp;quot;&amp;gt;&amp;quot;) or die &amp;quot;Cannot open $light_file: $!\n&amp;quot;;&lt;br /&gt;
    print $lightf &amp;quot;off\n&amp;quot;; # ThinkLight&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
##############################&lt;br /&gt;
# BlueTooth token detection&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
    my $temporary_bt = 0;&lt;br /&gt;
    my $bt_pid;&lt;br /&gt;
    my $bt_pipe;&lt;br /&gt;
    my $bluetooth_last_seen = 0; # last time we saw the BlueTooth token&lt;br /&gt;
    my $bluetooth_last_read = 0;   # last time we polled the BlueTooth child process&lt;br /&gt;
    my $bluetooth_ignore_missing_until = time() + $bluetooth_reset_period; &lt;br /&gt;
&lt;br /&gt;
    if ($use_bluetooth) {&lt;br /&gt;
        # If BlueTooth is disabled, temporarily enable it&lt;br /&gt;
        slurp($bluetooth_file) or die &amp;quot;Can't control bluetooth via $bluetooth_file: $!&amp;quot;;&lt;br /&gt;
        my $bt_status = slurp($bluetooth_file);&lt;br /&gt;
        $temporary_bt = $bt_status =~ m/status:[\t]*disabled/;&lt;br /&gt;
        if ($temporary_bt) {&lt;br /&gt;
            say(1, 'BlueTooth was disabled, enabling');&lt;br /&gt;
            burp($bluetooth_file, &amp;quot;enable\n&amp;quot;);&lt;br /&gt;
            sleep(1);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    sub bluetooth_reset {  # disregard recent (negative) history&lt;br /&gt;
        $bluetooth_ignore_missing_until = max($bluetooth_ignore_missing_until, time() + $bluetooth_reset_period);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    sub check_bluetooth {&lt;br /&gt;
        if (!defined($bt_pid)) {&lt;br /&gt;
            # Create new child process, which will loop checking&lt;br /&gt;
            # for the token. Each time it sees the token, it writes&lt;br /&gt;
            # the current time to a pipe that's read by the main process.&lt;br /&gt;
            $bt_pipe = new IO::Pipe;&lt;br /&gt;
            $bt_pid = fork();&lt;br /&gt;
            die &amp;quot;Cannot fork BlueTooth check: $!&amp;quot; unless defined($bt_pid);&lt;br /&gt;
            if (!$bt_pid) {&lt;br /&gt;
                # Child&lt;br /&gt;
                $bt_pipe-&amp;gt;writer();&lt;br /&gt;
                $bt_pipe-&amp;gt;autoflush(1);&lt;br /&gt;
                open(STDOUT, &amp;quot;&amp;gt;/dev/null&amp;quot;);&lt;br /&gt;
                open(STDERR, &amp;quot;&amp;gt;/dev/null&amp;quot;);&lt;br /&gt;
                while(1) {&lt;br /&gt;
                    # Is the BlueTooth token reachable?&lt;br /&gt;
                    my $res = system($l2ping,'-c','1','-t',1,$bluetooth_token_addr);&lt;br /&gt;
                    die &amp;quot;Failed invoking l2ping: $!\n&amp;quot; if $res&amp;amp;0xFF;&lt;br /&gt;
                    if ($res==0) {&lt;br /&gt;
                       # Is the BlueTooth sufficiently close, as judged by signal strength?&lt;br /&gt;
                       $res = `$hcitool rssi $bluetooth_token_addr`;&lt;br /&gt;
                       if ($?==0 &amp;amp;&amp;amp; $res =~ m/^RSSI return value: (-?[0-9]+)$/) {&lt;br /&gt;
                           my $rssi = $1;&lt;br /&gt;
                           printf $bt_pipe &amp;quot;%d\n&amp;quot;, time() if ($rssi &amp;gt; $bluetooth_min_rssi);&lt;br /&gt;
                       }&lt;br /&gt;
                    }&lt;br /&gt;
                    sleep($bluetooth_sleep);&lt;br /&gt;
                }&lt;br /&gt;
             }&lt;br /&gt;
             $bt_pipe-&amp;gt;reader();&lt;br /&gt;
             $bt_pipe-&amp;gt;blocking(0);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        while (1) { &lt;br /&gt;
            my $res = &amp;lt;$bt_pipe&amp;gt;;&lt;br /&gt;
            last if ($!==POSIX::EAGAIN); # busy&lt;br /&gt;
            die &amp;quot;Error reading from BlueTooth check child process\n&amp;quot; unless defined($res);&lt;br /&gt;
            $bluetooth_last_seen = $res;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        my $now = time();&lt;br /&gt;
        bluetooth_reset() if ($now &amp;gt; $bluetooth_last_read + $bluetooth_reset_period);&lt;br /&gt;
        $bluetooth_last_read = time();&lt;br /&gt;
        if ($now &amp;gt; max($bluetooth_last_seen + $bluetooth_activate_period, $bluetooth_ignore_missing_until)) {&lt;br /&gt;
            if ($bluetooth_lock_kde) {&lt;br /&gt;
                force_kde_lock();&lt;br /&gt;
                $bluetooth_ignore_missing_until = max($bluetooth_ignore_missing_until, time() + $bluetooth_lock_kde_interval);&lt;br /&gt;
            }&lt;br /&gt;
            return (1, &amp;quot;BlueTooth token not seen for $bluetooth_activate_period seconds, activating.&amp;quot;);&lt;br /&gt;
        } elsif ($now &amp;lt; $bluetooth_last_seen + $bluetooth_deactivate_period) {&lt;br /&gt;
            return (0, &amp;quot;BlueTooth token seen during last $bluetooth_deactivate_period seconds, deactivating.&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        return ();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    sub bluetooth_restore {&lt;br /&gt;
        if ($temporary_bt) {&lt;br /&gt;
            say(1, 'Disabling BlueTooth');&lt;br /&gt;
            burp($bluetooth_file, &amp;quot;disable\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        if ($bt_pid) {&lt;br /&gt;
            kill(SIGTERM, $bt_pid);&lt;br /&gt;
            waitpid($bt_pid, 0);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
##############################&lt;br /&gt;
# Main code&lt;br /&gt;
&lt;br /&gt;
my $state;       # See state machine in main loop&lt;br /&gt;
my %state_names=(0  =&amp;gt;'disabled    ',&lt;br /&gt;
                 0.5=&amp;gt;'activating  ',&lt;br /&gt;
                 1  =&amp;gt;'active+grace',&lt;br /&gt;
                 2  =&amp;gt;'active      ',&lt;br /&gt;
                 3  =&amp;gt;'hold+grace  ',&lt;br /&gt;
                 4  =&amp;gt;'armed+grace ',&lt;br /&gt;
                 5  =&amp;gt;'hold        ',&lt;br /&gt;
                 6  =&amp;gt;'armed       ',&lt;br /&gt;
                 7  =&amp;gt;'armed-force '  );&lt;br /&gt;
my $state_end = 0; &lt;br /&gt;
my $last_tilt = 0;&lt;br /&gt;
my $arm_forced = 0;&lt;br /&gt;
my (@XHIST, @YHIST); # sensor history&lt;br /&gt;
&lt;br /&gt;
my $initpower=read_ac_state();  #check power state when script runs&lt;br /&gt;
&lt;br /&gt;
sub set_state {&lt;br /&gt;
    my ($st,  $why) = @_;&lt;br /&gt;
    say(2, &amp;quot;state=[&amp;quot;.$state_names{$st}.&amp;quot;]  ($why)&amp;quot;);&lt;br /&gt;
    (@XHIST, @YHIST) = () if $st==0.5;&lt;br /&gt;
    led_restore() and light_restore() if defined($state) &amp;amp;&amp;amp; $st==0;&lt;br /&gt;
    $state = $st;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub get_pos {&lt;br /&gt;
    my $pos = slurp($pos_file);&lt;br /&gt;
    return undef if $!{EBUSY};&lt;br /&gt;
    die &amp;quot;Can't open HDAPS file $pos_file: $!\n&amp;quot; if (!defined($pos) || $!);&lt;br /&gt;
    $pos =~ m/^\((-?\d+),(-?\d+)\)$/ or die &amp;quot;Can't parse $pos_file content\n&amp;quot;;&lt;br /&gt;
    return ($1,$2);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
for (@ARGV) {&lt;br /&gt;
    m/^--arm/ &amp;amp;&amp;amp; do { $arm_forced=1; $use_lid=0; $use_kde=0; last; };&lt;br /&gt;
    die &amp;quot;Unknown parameter\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
set_state( ($use_kde || $use_bluetooth)  ? 0 : 0.5, &amp;quot;init&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
eval {&lt;br /&gt;
&lt;br /&gt;
$SIG{'HUP'}=$SIG{'INT'}=$SIG{'ABRT'}=$SIG{'QUIT'}=$SIG{'SEGV'}=$SIG{'TERM'} = sub { die &amp;quot;signal\n&amp;quot; };&lt;br /&gt;
&lt;br /&gt;
while (1) {&lt;br /&gt;
    sleep(($state==0 &amp;amp;&amp;amp; $use_kde) ? $kde_check_interval : $interval);&lt;br /&gt;
&lt;br /&gt;
    check_lid() if $use_lid;&lt;br /&gt;
    # Check screensaver and BlueTooth. Activate if either says so,&lt;br /&gt;
    # otherwise deactivate if either says so.&lt;br /&gt;
    my ($op1, $why1); ($op1, $why1) = check_kde_lock() if $use_kde;&lt;br /&gt;
    my ($op2, $why2); ($op2, $why2) = check_bluetooth() if $use_bluetooth;&lt;br /&gt;
    my ($op, $why) = ( !defined($op1) || ( defined($op2) &amp;amp;&amp;amp; $op2&amp;gt;$op1 ) ) ? ($op2,$why2) : ($op1,$why1);&lt;br /&gt;
    if (defined($op)) {&lt;br /&gt;
        if ($op==1 &amp;amp;&amp;amp; $state==0) { set_state(0.5, $why); }&lt;br /&gt;
        if ($op==0 &amp;amp;&amp;amp; $state&amp;gt;0) { set_state(0, $why); bluetooth_reset(); }&lt;br /&gt;
    }&lt;br /&gt;
    next unless $state&amp;gt;0;&lt;br /&gt;
&lt;br /&gt;
    # Collect and analyze sensor data:&lt;br /&gt;
    my $now = time();&lt;br /&gt;
    my $tilted;&lt;br /&gt;
    my ($x,$y) = get_pos() or next; # Hopefully the error is transient&lt;br /&gt;
    push(@XHIST,$x); push(@YHIST,$y);&lt;br /&gt;
    if ($state&amp;gt;0.5) {&lt;br /&gt;
        shift(@XHIST); shift(@YHIST);&lt;br /&gt;
        my $xdev = stddev(@XHIST);&lt;br /&gt;
        my $ydev = stddev(@YHIST);&lt;br /&gt;
        say(3,&amp;quot;X: v=$xdev (&amp;quot;.join(',',@XHIST).&amp;quot;)  Y: v=$ydev (&amp;quot;.join(&amp;quot;,&amp;quot;,@YHIST).&amp;quot;)&amp;quot;);&lt;br /&gt;
        $tilted = ($xdev&amp;gt;$thresh || $ydev&amp;gt;$thresh);&lt;br /&gt;
        $last_tilt = $now if $tilted;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
# ac state check and set when system started on battery and later plugged power cord&lt;br /&gt;
    my $power = read_ac_state();&lt;br /&gt;
        if ($initpower eq 'off' &amp;amp;&amp;amp; $power eq 'on') {&lt;br /&gt;
        $initpower='on';&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    # Decide: state machine transitions&lt;br /&gt;
    if ($state==0.5) { # ACTIVATING  (collecting motion data will soon activate)&lt;br /&gt;
        if ($#XHIST &amp;gt;= $depth &amp;amp;&amp;amp; $#YHIST &amp;gt;= $depth) {&lt;br /&gt;
            set_state($arm_forced?7:$use_lid?1:2, &amp;quot;finished data collection&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    } elsif ($state==1) { # ACTIVE+GRACE  (quiet for a long time, awaiting movement)&lt;br /&gt;
        if ($tilted) {&lt;br /&gt;
            set_state(3, &amp;quot;motion detected, holding for $lid_hold seconds, open lid for grace&amp;quot;);&lt;br /&gt;
            $state_end = $now + $lid_hold;&lt;br /&gt;
            sound_alarm(&amp;quot;WARNING&amp;quot;, $warn_volume, $acpi_volume, $warn_cmd);&lt;br /&gt;
        }&lt;br /&gt;
    } elsif ($state==2) { # ACTIVE  (short for a shorter time, awaiting movement)&lt;br /&gt;
        if ($tilted) {&lt;br /&gt;
            set_state(5, &amp;quot;motion detected, holding for $min_hold seconds&amp;quot;);&lt;br /&gt;
            $state_end = $now + $min_hold;&lt;br /&gt;
            sound_alarm(&amp;quot;WARNING&amp;quot;, $warn_volume, $acpi_volume, $warn_cmd);&lt;br /&gt;
        } else {&lt;br /&gt;
            if ($use_lid &amp;amp;&amp;amp; ($now &amp;gt; $last_tilt + $grace_relax )) { &lt;br /&gt;
                set_state(1, &amp;quot;$grace_relax seconds since last motion, so allowing grace again&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    } elsif ($state==3) { # HOLD+GRACE  (recent movemvent, but still holding off alarm; after long quiet)&lt;br /&gt;
        if ($now &amp;lt; $last_lid_open + $lid_grace) {&lt;br /&gt;
            set_state(5, &amp;quot;lid opened, holding for $lid_grace seconds grace period&amp;quot;);&lt;br /&gt;
            $state_end = $now + $lid_grace;&lt;br /&gt;
        } elsif ($now &amp;gt;= $state_end) {&lt;br /&gt;
            my $delta = $lid_grace_window - $lid_hold;&lt;br /&gt;
            $state_end = $now + $delta;&lt;br /&gt;
            set_state(4, &amp;quot;hold ended, arming but allowing grace for $delta more seconds&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    } elsif ($state==4) { # ARMED+GRACE  (armed, but recently quiet so allow grace if lid opened)&lt;br /&gt;
        if ($now &amp;lt; $last_lid_open + $lid_grace) {&lt;br /&gt;
            set_state(5, &amp;quot;lid opened, holding for $lid_grace seconds grace period&amp;quot;);&lt;br /&gt;
            $state_end = $now + $lid_grace;&lt;br /&gt;
        } elsif ($now &amp;gt;= $state_end) {&lt;br /&gt;
            set_state(6, &amp;quot;grace window ended&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    } elsif ($state==5) { # HOLD  (recent movement, but still holding off alarm; there was recent action)&lt;br /&gt;
        if ($now &amp;gt;= $state_end) {&lt;br /&gt;
            set_state(6, &amp;quot;hold ended, arming&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    } elsif ($state==6) { # ARMED  (sound alarm on any movement)&lt;br /&gt;
        if ($now &amp;gt; $last_tilt + $arm_persist) {&lt;br /&gt;
            set_state(2, &amp;quot;no motion for $arm_persist seconds, unarming&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # LEDs:&lt;br /&gt;
    if ($state&amp;gt;0) {&lt;br /&gt;
        led_activate(); light_activate();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # Alarm: &lt;br /&gt;
     # included initpower state&lt;br /&gt;
    if (($state==4 || $state==6 || $state==7) &amp;amp;&amp;amp; ($tilted || ($power eq 'off') &amp;amp;&amp;amp; $initpower eq 'on')) {&lt;br /&gt;
        sound_alarm(&amp;quot;ALARM&amp;quot;, $alarm_volume, $acpi_volume, $alarm_cmd);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
print &amp;quot;Shutting down.\n&amp;quot; if $verbose&amp;gt;1;&lt;br /&gt;
led_restore() and light_restore() if ($state&amp;gt;0);&lt;br /&gt;
bluetooth_restore();&lt;br /&gt;
die &amp;quot;$@&amp;quot; if $@;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>T33nkp4d</name></author>
		
	</entry>
</feed>