#!/bin/bash
# lampe by André Klausnitzer, CC0
# interactive bash script to control your Philips Hue lights

version="1.1.11"

declare _SELECTED_LAMP=1

checkKey() {
	case $1 in
		# $'\e[A' |
		"w")
			_UP=1
		;;
		# $'\e[B' |
		"s")
			_DOWN=1
		;;
		# $'\e[C' |
		"d")
			_RIGHT=1
		;;
		# $'\e[D' |
		"a")
			_LEFT=1
		;;
		"q")
			# hue
			_Q=1
		;;
		"e")
			# hue
			_E=1
		;;
		"y")
			# on
			_Y=1
		;;
		"n")
			# off
			_N=1
		;;
		"Y")
			# on: all lights
			_YY=1
		;;
		"N")
			# off: all lights
			_NN=1
		;;
		"h")
			# help
			_H=1
		;;
		"i")
			# info: do not send new, but receive light state
			_I=1
		;;
		"I")
			# info: receive state of all lights
			_II=1
		;;
		"r")
			# random
			_R=1
		;;
		"b")
			# blink
			_B=1
		;;
		"A")
			# alert select
			_A=1
		;;
		"Q")
			# quit
			_QQ=1
		;;
		"S")
			# save current light-setting as new user default
			_S=1
		;;
		"l")
			# load light-setting from saved user default
			_L=1
		;;
		"m")
			# mood light
			_M=1
		;;
		"t")
			# redshift sequence
			_T=1
		;;
		"o")
			# noise sequence
			_O=1
		;;
	esac
}

readAKey() {
	# readKey
	read -s -n 1 _KEY

	case $_KEY in
		[1-9])
			# select light
			printMainScreen "$_KEY"
			_SELECTED_LAMP=$_KEY
			readLightNumber "$_KEY"
		;;
		*)
			checkKey "$_KEY"
		;;
	esac
}

readLightNumber() {
	local number=$_KEY

	# debug
	#echo "number = '$number'"

	read -s -n 1 _NEXT_KEY
	case $_NEXT_KEY in
		[0-9])
			number=$number$_NEXT_KEY
		;;
		*)
			checkKey "$_NEXT_KEY"
		;;
	esac

	_SELECTED_LAMP=$number

	# debug
	#echo "_SELECTED_LAMP = '$_SELECTED_LAMP'"
}

# init brightness
declare -a _BRIGHTNESS
for (( b=1 ; b<100 ; b=b+1 )) ; do
	_BRIGHTNESS[$b]=32
done

# init saturation
declare -a _SAT
for (( s=1 ; s<100 ; s=s+1 )) ; do
	_SAT[$s]=192
done

# init switch
declare -a _SWITCH # on or off
for (( s=1 ; s<100 ; s=s+1 )) ; do
	_SWITCH[$s]="true"
done

# init color
declare -a _COLOR
for (( s=1 ; s<100 ; s=s+1 )) ; do
	_COLOR[$s]=12000 # orange
done

# init model
declare -a _MODEL
for (( s=1 ; s<100 ; s=s+1 )) ; do
	_MODEL[$s]="\"LCT001\"" # standard Hue bulb
done

moodSequence() {
	while [[ -f "${cache_dir}lampeSequenceM${_SELECTED_LAMP}" ]] ; do
        local high_color_factor=45 # 16bit-color, 65536 colors devided through 1439 steps
        local time_value=$(( $( date '+%-M' )+$( date '+%-H' )*60 ))

        local hue_color=$(( 65536-time_value*high_color_factor ))

		local hue="\"hue\":$hue_color,\"transitiontime\":30"
        if [[ -v "mood_sat" ]] ; then
            hue="${hue},\"sat\":$mood_sat"
        fi
		curl -s -d "{$hue}" -X PUT "http://$bridgeip/api/$bridgeuser/lights/$_SELECTED_LAMP/state" -m 1 > /dev/null

		sleep 60
	done
}

redshiftSequence() {
	while [[ -f "${cache_dir}lampeSequenceT$_SELECTED_LAMP" ]] ; do
        # test fake berlin
        #redshift_options="-l 52.52:13.40"

        # test fake chicago
        #redshifts_options="-l 41:-87"

        # get new color temperature
        kelvin=$( redshift -op $redshift_options 2>/dev/null | grep -e ":*K$" | cut -f 2 -d ":" | cut -b 2-5 )
        mired=$(( 1000000 / kelvin ))

        # debug
        #echo "t/R:redshift_options = $redshift_options"
        #echo "t/R:kelvin = $kelvin"
        #echo "t/R:mired ?= old_mired => $mired ?= $old_mired"

		local ct="\"ct\":$mired,\"transitiontime\":600"
		curl -s -d "{$ct}" -X PUT "http://$bridgeip/api/$bridgeuser/lights/$_SELECTED_LAMP/state" -m 1 > /dev/null

        sleep 60 # 1 minute
    done
}

noiseSequence() {
    local transition=$(( noise_interval * 10 )) # transition time
    local ihue=${_COLOR[$_SELECTED_LAMP]}
    local isat=${_SAT[$_SELECTED_LAMP]}

    # debug
    #echo "current hue = ${_COLOR[$_SELECTED_LAMP]}"
    #echo "lamp = $_SELECTED_LAMP"
    #echo "ihue = $ihue"

    local min_hue=$(( ihue-noise_range ))
    local max_hue=$(( ihue+noise_range ))

    #echo "min_hue = $min_hue"
    #echo "max_hue = $max_hue"

    osat=$isat
    ohue=$ihue
	while [[ -f "${cache_dir}lampeSequenceO$_SELECTED_LAMP" ]] ; do
        local hue_step=$(( RANDOM*noise_step*2/32767-noise_step ))
        local nhue=$(( ohue+hue_step ))
        if [[ "$nhue" -gt "$max_hue" ]] ; then
            nhue=$max_hue
        fi
        if [[ "$nhue" -lt "$min_hue" ]] ; then
            nhue=$min_hue
        fi

        local nsat=$(( osat+(hue_step/500) ))
        if [[ "$nsat" -gt 254 ]] ; then
            nsat=254
        fi
        if [[ "$nsat" -lt 0 ]] ; then
            nsat=192
        fi

        local noise="\"hue\":$nhue,\"transitiontime\":$transition,\"sat\":$nsat"
		curl -s -d "{$noise}" -X PUT "http://$bridgeip/api/$bridgeuser/lights/$_SELECTED_LAMP/state" -m 1 > /dev/null
        sleep "$noise_interval"
        ohue=$nhue
        osat=$nsat

        # debug
        #echo "nsat = $nsat, osat = $osat"
        #echo "nhue = $nhue, ohue = $ohue"
    done
}

# GPN15 preview sequence
previewRandom() {
	# debug
	#echo "random called, '$LAMPE_R'"

	local on="true"
	while [[ -f "${cache_dir}lampeSequenceR$_SELECTED_LAMP" ]] ; do
		# debug
		#echo "do random, '$LAMPE_R'"

		local hue=$(( RANDOM*2 ))
		local color="\"hue\":$hue,\"bri\":${_BRIGHTNESS[$_SELECTED_LAMP]},\"sat\":${_SAT[$_SELECTED_LAMP]},\"transitiontime\":100"
		curl -s -d "{\"on\":$on,$color}" -X PUT "http://$bridgeip/api/$bridgeuser/lights/$_SELECTED_LAMP/state" -m 1 > /dev/null

		sleep 10
	done
}

# blink preview sequence
previewBlink() {
	local on="true"
	local bri="$1"
	while [[ -f "${cache_dir}lampeSequenceB$_SELECTED_LAMP" ]] ; do
		curl -s -d "{\"on\":$on,\"transitiontime\":0,\"bri\":$bri}" -X PUT "http://$bridgeip/api/$bridgeuser/lights/$_SELECTED_LAMP/state" -m 1 > /dev/null

		sleep 1
		if [[ "$on" == "true" ]] ; then
			on="false"
		else
			on="true"
		fi
	done
}

selectAlert() {
	curl -s -d "{\"alert\":\"select\"}" -X PUT "http://$bridgeip/api/$bridgeuser/lights/$_SELECTED_LAMP/state" -m 1 > /dev/null
}

setBrightness() {
	brightness=${_BRIGHTNESS[$_SELECTED_LAMP]}

	brightness=$((brightness+$1))
	if [ $brightness -lt 2 ] ; then
		brightness=1
	elif [ $brightness -gt 0 ] ; then
		if [ $brightness -gt 254 ] ; then
			brightness=254
		fi
	fi

	_BRIGHTNESS[$_SELECTED_LAMP]=$brightness

	# debug
	#echo -n "param = $1 "
	#echo "brightness = ${_BRIGHTNESS[$_SELECTED_LAMP]} "
}

setSat() {
	sat=${_SAT[$_SELECTED_LAMP]}

	sat=$((sat+$1))
	if [ $sat -lt 1 ] ; then
		sat=1
	elif [ $sat -gt 254 ] ; then
		sat=254
	fi

	_SAT[$_SELECTED_LAMP]=$sat
}

setColor() {
	hue=${_COLOR[$_SELECTED_LAMP]}

	hue=$((hue+$1))
	if [ $hue -lt 1 ] ; then
		hue=65280
	elif [ $hue -gt 65280 ] ; then
		hue=1
	fi

	# debug
	# echo " $hue "

	_COLOR[$_SELECTED_LAMP]=$hue
}

setSwitch() {
	# debug
	#echo -n $1

	if [[ "$1" == 1 ]] ; then
		_SWITCH[$_SELECTED_LAMP]="true"
	else
		_SWITCH[$_SELECTED_LAMP]="false"

		# disable sequences that would turn the light on again
		rm "${cache_dir}lampeSequenceB$_SELECTED_LAMP" 2> /dev/null
		rm "${cache_dir}lampeSequenceR$_SELECTED_LAMP" 2> /dev/null
	fi
}

# get colorEscapes from colors
lColor() {
	case $1 in
		"red")
			echo -en "\e[0;31m"
		;;
		"yellow")
			echo -en "\e[0;33m"
		;;
		"green")
			echo -en "\e[0;32m"
		;;
		"blue")
			echo -en "\e[0;34m"
		;;
		"magenta")
			echo -en "\e[0;35m"
		;;
		"cyan")
			echo -en "\e[0;36m"
		;;
		"white")
			echo -en "\e[0m"
		;;
		"ltred")
			echo -en "\e[1;31m"
		;;
		"ltyellow")
			echo -en "\e[1;33m"
		;;
		"ltgreen")
			echo -en "\e[1;32m"
		;;
		"ltblue")
			echo -en "\e[1;34m"
		;;
		"ltmagenta")
			echo -en "\e[1;35m"
		;;
		"ltcyan")
			echo -en "\e[1;36m"
		;;
		"ltwhite")
			echo -en "\e[1m"
		;;
		"off")
			echo -en "\e[0m"
		;;
	esac
}

getColorByValue() {
	hue=$1
	if [[ ! -n "$hue" ]] ; then
		hue=12000
	fi

	if [ "$hue" -lt 9001 ] ; then
		# red
		colorEscape="red"
	elif [ "$hue" -gt 9000 ] && [ "$hue" -lt 19001 ] ; then
		# orange, yellow
		colorEscape="yellow"
	elif [ "$hue" -gt 19000 ] && [ "$hue" -lt 31001 ] ; then
		# green
		colorEscape="green"
	elif [ "$hue" -gt 31000 ] && [ "$hue" -lt 35001 ] ; then
		# white, green-blue, cyan
		colorEscape="cyan"
	elif [ "$hue" -gt 35000 ] && [ "$hue" -lt 48001 ] ; then
		# blue
		colorEscape="blue"
	elif [ "$hue" -gt 48000 ] && [ "$hue" -lt 62280 ] ; then
		# purple
		colorEscape="magenta"
	elif [ "$hue" -gt 62279 ] ; then
		# red
		colorEscape="red"
	fi
	echo -en "$(lColor $colorEscape)"
}

# get print-color of selected or given light number
getColorOfLight() {
	if [[ -n "$1" ]] ; then
		local light=$1
	else
		local light=$_SELECTED_LAMP
	fi

	hue=${_COLOR[$light]}
	if [[ ! -n "$hue" ]] ; then
		hue=12000
	fi

	# debug
	#echo " getColor.hue = '$hue'"

	bold=""
	if [[ ${_SWITCH[$light]} == "true" ]] ; then
		bold="lt"
	fi
	if [[ "$hue" -lt 9001 ]] ; then
		# red
		colorEscape="red"
	elif [[ "$hue" -gt 9000 ]] && [[ "$hue" -lt 19001 ]] ; then
		# orange, yellow
		colorEscape="yellow"
	elif [[ "$hue" -gt 19000 ]] && [[ "$hue" -lt 31001 ]] ; then
		# green
		colorEscape="green"
	elif [[ "$hue" -gt 31000 ]] && [[ "$hue" -lt 35001 ]] ; then
		# white, green-blue, cyan
		colorEscape="cyan"
	elif [[ "$hue" -gt 35000 ]] && [[ "$hue" -lt 48001 ]] ; then
		# blue
		colorEscape="blue"
	elif [[ "$hue" -gt 48000 ]] && [[ "$hue" -lt 63280 ]] ; then
		# purple
		colorEscape="magenta"
	elif [[ "$hue" -gt 63279 ]] ; then
		# red
		colorEscape="red"
	fi
	echo -en "$(lColor $bold$colorEscape)"
}

printMainScreen() {
	local firstNumer=$1

	if [[ "$L_BLIND" == 0 ]] || [[ "$_I" -eq 1 ]] ; then
		currentBrightness=$((_BRIGHTNESS[_SELECTED_LAMP]/5))
		currentSat=$((_SAT[_SELECTED_LAMP]*currentBrightness/254))
		colorPosition=$((_COLOR[_SELECTED_LAMP]*currentBrightness/65279))
		if [ $colorPosition -gt 1 ] ; then
			colorPosition=$((colorPosition-1))
		fi

		# debug
		#echo "$colorPosition"
		#echo "firstNumer = '$firstNumer'"

		# bug#23 fixed: use carriage return instead of deleting chars
		# one by one with backspace
		echo -en "\r"
		if [ -n "$firstNumer" ] ; then
			echo -n "$firstNumer. "
		else
            # debug
            # echo "_SELECTED_LAMP = $_SELECTED_LAMP"

			if [ "$_SELECTED_LAMP" -lt 10 ] ; then
				echo -n " "
			fi
			echo -en "$(getColorOfLight)$_SELECTED_LAMP $(lColor ltwhite)"
		fi

		echo -en "$(lColor off)$(lColor ltwhite)[$(lColor off)"
		for (( ii=0 ; ii<currentBrightness ; ii=ii+1 )) ; do
			colorValue=$((65279*ii/currentBrightness))
			echo -en "$(getColorByValue $colorValue)"

			if [ $ii == $colorPosition ] ; then
				if [[ -f "${cache_dir}lampeSequenceR$_SELECTED_LAMP" ]] ; then
					echo -en "$(getColorOfLight)R$(lColor off)"
				elif [[ -f "${cache_dir}lampeSequenceB$_SELECTED_LAMP" ]] ; then
					echo -en "$(getColorOfLight)B$(lColor off)"
				elif [[ -f "${cache_dir}lampeSequenceM$_SELECTED_LAMP" ]] ; then
					echo -en "$(getColorOfLight)M$(lColor off)"
				elif [[ -f "${cache_dir}lampeSequenceT$_SELECTED_LAMP" ]] ; then
					echo -en "$(getColorOfLight)T$(lColor off)"
				elif [[ -f "${cache_dir}lampeSequenceO$_SELECTED_LAMP" ]] ; then
					echo -en "$(getColorOfLight)O$(lColor off)"
				else
					echo -en "$(getColorOfLight)C$(lColor off)"
				fi
			else
				if [ $ii -lt $currentSat ] ; then
					echo -n "="
				else
					echo -n "-"
				fi
			fi
		done
		for (( ii=0 ; ii<50-currentBrightness ; ii=ii+1 )) ; do
			echo -n " "
		done
		echo -en "$(lColor off)$(lColor ltwhite)]$(lColor off) "
		if [[ ${_SWITCH[$_SELECTED_LAMP]} != "true" ]] ; then
			echo -n "off "
		else
			echo -n "    "
		fi
	fi
}

toggleSequence() {
	local sequence=$1

	if [[ ! -f "${cache_dir}lampeSequence$sequence$_SELECTED_LAMP" ]] ; then
		# activate $sequence, deactivate others
		touch "${cache_dir}lampeSequence$sequence$_SELECTED_LAMP" 2> /dev/null
		if [[ "$sequence" == "R" ]] ; then
			rm "${cache_dir}lampeSequenceB$_SELECTED_LAMP" 2> /dev/null
			rm "${cache_dir}lampeSequenceM$_SELECTED_LAMP" 2> /dev/null
			rm "${cache_dir}lampeSequenceT$_SELECTED_LAMP" 2> /dev/null
			rm "${cache_dir}lampeSequenceO$_SELECTED_LAMP" 2> /dev/null
			_SWITCH[_SELECTED_LAMP]="true"
			( previewRandom ) &
		elif [[ "$sequence" == "B" ]] ; then
			rm "${cache_dir}lampeSequenceR$_SELECTED_LAMP" 2> /dev/null
			rm "${cache_dir}lampeSequenceM$_SELECTED_LAMP" 2> /dev/null
			rm "${cache_dir}lampeSequenceT$_SELECTED_LAMP" 2> /dev/null
			rm "${cache_dir}lampeSequenceO$_SELECTED_LAMP" 2> /dev/null
			_SWITCH[_SELECTED_LAMP]="true"
			( previewBlink "${_BRIGHTNESS[$_SELECTED_LAMP]}" ) &
		elif [[ "$sequence" == "M" ]] ; then
			rm "${cache_dir}lampeSequenceB$_SELECTED_LAMP" 2> /dev/null
			rm "${cache_dir}lampeSequenceR$_SELECTED_LAMP" 2> /dev/null
			rm "${cache_dir}lampeSequenceT$_SELECTED_LAMP" 2> /dev/null
			rm "${cache_dir}lampeSequenceO$_SELECTED_LAMP" 2> /dev/null
			( moodSequence ) &
		elif [[ "$sequence" == "T" ]] ; then
			rm "${cache_dir}lampeSequenceB$_SELECTED_LAMP" 2> /dev/null
			rm "${cache_dir}lampeSequenceR$_SELECTED_LAMP" 2> /dev/null
			rm "${cache_dir}lampeSequenceM$_SELECTED_LAMP" 2> /dev/null
			rm "${cache_dir}lampeSequenceO$_SELECTED_LAMP" 2> /dev/null
			( redshiftSequence ) &
		elif [[ "$sequence" == "O" ]] ; then
			rm "${cache_dir}lampeSequenceB$_SELECTED_LAMP" 2> /dev/null
			rm "${cache_dir}lampeSequenceR$_SELECTED_LAMP" 2> /dev/null
			rm "${cache_dir}lampeSequenceM$_SELECTED_LAMP" 2> /dev/null
			rm "${cache_dir}lampeSequenceT$_SELECTED_LAMP" 2> /dev/null
			( noiseSequence ) &
		fi
	else
		# deactivate $sequence
		rm "${cache_dir}lampeSequence$sequence$_SELECTED_LAMP" 2> /dev/null
	fi
}

printHelpScreen() {
	echo ""
	echo "    1..99    LIGHT         confirm with any key"
	echo "    w, s     BRIGHTNESS    -"
	echo "    a, d     SATURATION    ="
	echo "    q, e     COLOR         C"
	echo "    y, Y     ON            switch (all) light/s on"
	echo "    n, N     OFF           switch (all) light/s off"
	echo "    i, I     INFO          receive (all) light state/s"
	echo "    S, l     SETTINGS      save, load user defaults"
	echo "    F        FIND          find new lights (TODO)"
	echo "    r        RANDOM        R: random color sequence"
	echo "    b        BLINK         B: blinking sequence"
	echo "    A        ALERT"
	echo "    m        MOOD          M: color based on time"
    echo "    t        REDSHIFT      T: color temperature"
    echo "                              based on redshift"
    echo "    o        NOISE         O: noise color sequence"
	#        [--------------------------------------------------]
	echo "    Q        QUIT"
}

lampeQuit() {
	echo -en "\n    Do you really want to quit 'lampe'? [$(lColor ltwhite)y$(lColor off)/$(lColor ltred)n$(lColor off)] "
	read doQuit
	if [[ "$doQuit" == "y" ]] || [[ "$doQuit" == "Y" ]] || [[ "$doQuit" == "" ]] ; then
		echo " $(lColor ltgreen)!$(lColor off)  bye"

		# cleanup and quit
		rm ${cache_dir}lampeSequence* 2> /dev/null
		# killall lampe > /dev/null
		exit 0
	fi
}

initializeKeys() {
	_UP=0
	_DOWN=0
	_RIGHT=0
	_LEFT=0
	_Q=0
	_E=0
	_Y=0
	_N=0
	_H=0
	_I=0
	_II=0
	_A=0
	_QQ=0
	_S=0
	_L=0
	_R=0
	_B=0
	_M=0
	_YY=0
	_NN=0
    _T=0
    _O=0
}

handleKey() {
	if [[ "$_UP" == 1 ]] ; then
		setBrightness 5
		hbSendLightBri
	elif [[ "$_DOWN" == 1 ]] ; then
		setBrightness -5
		hbSendLightBri
	elif [[ "$_RIGHT" == 1 ]] ; then
		setSat 10
		hbSendLightSat
	elif [[ "$_LEFT" == 1 ]] ; then
		setSat -10
		hbSendLightSat
	elif [[ "$_Q" == 1 ]] ; then
		# hue
		setColor -1000
		hbSendLightColor
	elif [[ "$_E" == 1 ]] ; then
		# hue
		setColor 1000
		hbSendLightColor
	elif [[ "$_Y" == 1 ]] ; then
		setSwitch 1
		hbSendLightsState
	elif [[ "$_N" == 1 ]] ; then
		setSwitch 0
		hbSendLightSwitch
	elif [[ "$_YY" == 1 ]] ; then
		_GLOBAL_SWITCH="true"
		hbSendLightSwitches
	elif [[ "$_NN" == 1 ]] ; then
		_GLOBAL_SWITCH="false"
		hbSendLightSwitches
	elif [[ "$_H" == 1 ]] ; then
		printHelpScreen
	elif [[ "$_A" == 1 ]] ; then
		selectAlert
	elif [[ "$_QQ" == 1 ]] ; then
		lampeQuit
	elif [[ "$_S" == 1 ]] ; then
		saveLightSetting
		saveAllSettings
	elif [[ "$_L" == 1 ]] ; then
		loadLightSetting
		hbSendLightsState
	elif [[ "$_R" == 1 ]] ; then
		toggleSequence "R"
	elif [[ "$_B" == 1 ]] ; then
		toggleSequence "B"
	elif [[ "$_M" == 1 ]] ; then
		toggleSequence "M"
	elif [[ "$_T" == 1 ]] ; then
		toggleSequence "T"
	elif [[ "$_O" == 1 ]] ; then
		toggleSequence "O"
	elif [[ "$_I" == 1 ]] ; then
		# receive current light state
		hbReceiveLightState "$_SELECTED_LAMP"
	elif [[ "$_II" == 1 ]] ; then
		# receive all light states
		echo ""
		hbReceiveLightStates
	fi
}

lampeMain() {
    if [[ "$L_ONESHOT" == 1 ]] ; then
        # oneshot-mode
        initializeKeys
        _SELECTED_LAMP=1

        # debug
        #echo "1:=$1 2:=$2 3:=$3"

        if [[ ! -n "$2" ]] ; then
            checkKey "$1"
        else
	        _SELECTED_LAMP=$1
			hbReceiveLightState "$_SELECTED_LAMP"
            checkKey "$2"
        fi

        handleKey
        # receive light state of selected light again
		hbReceiveLightState "$_SELECTED_LAMP"
        printMainScreen
		echo -e "\n $(lColor ltgreen)!$(lColor off)  bye"
    else
    	# interactive-mode: initial screen
    	echo -en "   [$(lColor red)=$(lColor yellow)=$(lColor green)=$(lColor cyan)=$(lColor blue)=$(lColor magenta)=$(lColor off)$(lColor ltwhite)---LAMPE-${version}---PRESS-h-FOR-HELP-----------$(lColor off)]"

    	lastTime=$(date +%s%3N) # seconds + milliseconds
    	while true; do
    		# main loop
            initializeKeys
    		readAKey
            handleKey
    		printMainScreen
    	done
    fi
}

# send hue light state only
hbSendLightColor() {
	local color="\"hue\":${_COLOR[$_SELECTED_LAMP]}"

	local thisTime=$(date +%s%3N)
	local diffTime=$((thisTime-lastTime))
	if [ $diffTime -gt 100 ] ; then
		# only send new state every 100ms
		curl -s -d "{$color}" -X PUT "http://$bridgeip/api/$bridgeuser/lights/$_SELECTED_LAMP/state" -m 1 > /dev/null
		local lastTime=$(date +%s%3N)
	fi
}

# send on,off switch state only
hbSendLightSwitch() {
	local thisTime=$(date +%s%3N)
	local diffTime=$((thisTime-lastTime))
	if [ $diffTime -gt 100 ] ; then
		# only send new state every 100ms
		curl -s -d "{\"on\":${_SWITCH[$_SELECTED_LAMP]}}" -X PUT "http://$bridgeip/api/$bridgeuser/lights/$_SELECTED_LAMP/state" -m 1 > /dev/null
		local lastTime=$(date +%s%3N)
	fi
}

# send on,off switch state only (all lights)
hbSendLightSwitches() {
	local thisTime=$(date +%s%3N)
	local diffTime=$((thisTime-lastTime))
	if [ $diffTime -gt 100 ] ; then
		# only send new state every 100ms
		curl -s -d "{\"on\":${_GLOBAL_SWITCH}}" -X PUT "http://$bridgeip/api/$bridgeuser/groups/0/action" -m 1 > /dev/null
		local lastTime=$(date +%s%3N)
	fi
}

# send brightness and switch light state only
hbSendLightBri() {
	local color="\"bri\":${_BRIGHTNESS[$_SELECTED_LAMP]}"

	local thisTime=$(date +%s%3N)
	local diffTime=$((thisTime-lastTime))
	if [ $diffTime -gt 100 ] ; then
		# only send new state every 100ms
		curl -s -d "{$color}" -X PUT "http://$bridgeip/api/$bridgeuser/lights/$_SELECTED_LAMP/state" -m 1 > /dev/null
		local lastTime=$(date +%s%3N)
	fi
}

# send saturation light state only
hbSendLightSat() {
	local color="\"sat\":${_SAT[$_SELECTED_LAMP]}"

	local thisTime=$(date +%s%3N)
	local diffTime=$((thisTime-lastTime))
	if [ $diffTime -gt 100 ] ; then
		# only send new state every 100ms
		curl -s -d "{$color}" -X PUT "http://$bridgeip/api/$bridgeuser/lights/$_SELECTED_LAMP/state" -m 1 > /dev/null
		local lastTime=$(date +%s%3N)
	fi
}

# send light state (switch and color) to Hue Bridge
hbSendLightsState() {
	# debug
	#echo -e "\n    DEBUG hbSendLightsState().modelid = ${_MODEL[$_SELECTED_LAMP]}"

	if [[ "${_MODEL[$_SELECTED_LAMP]}" == "\"ZLL Light\"" ]] || [[ "${_MODEL[$_SELECTED_LAMP]}" == "\"LWB004\"" ]] || [[ "${_MODEL[$_SELECTED_LAMP]}" == "\"LWB010\"" ]] ; then
		# light models "ZLL", "LWB004" and "LWB010" do not support hue and sat
		local color="\"bri\":${_BRIGHTNESS[$_SELECTED_LAMP]}"
    elif [[ "${_MODEL[$_SELECTED_LAMP]}" == "\"LTW001\"" ]] ; then
		local color="\"bri\":${_BRIGHTNESS[$_SELECTED_LAMP]}"
            # TODO  implement "ct" (color temperature)
	else
		local color="\"hue\":${_COLOR[$_SELECTED_LAMP]},\"bri\":${_BRIGHTNESS[$_SELECTED_LAMP]},\"sat\":${_SAT[$_SELECTED_LAMP]}"
	fi

	local thisTime=$(date +%s%3N)
	local diffTime=$((thisTime-lastTime))
	if [[ "$diffTime" -gt 100 ]] ; then
		# only send new state every 100ms
		curl -s -d "{\"on\":${_SWITCH[$_SELECTED_LAMP]},$color}" -X PUT "http://$bridgeip/api/$bridgeuser/lights/$_SELECTED_LAMP/state" -m 1 > /dev/null
		local lastTime=$(date +%s%3N)
	fi
}

# checks wether there is a hue bridge at given IP
hbCheck() {
	hbDescription=$(curl "http://$1/description.xml" -m 3 2> /dev/null)
	hbModel=$(echo "$hbDescription" | grep "<modelDescription>Philips hue Personal Wireless Lighting</modelDescription>")
}

# try to detect the first Hue Bridge in the LAN (ipv4 class-C networks only, can detect only one bridge)
hbDetect() {
	_BRIDGE_FOUND=false

	# bug#4 fixed, all subnets will be searched, but discovery will stop at the first found hue-bridge
	tmpIPsFile="${cache_dir}lampe$$.tmp"
	L_ips=$(ip -f inet addr show | grep "/24" | sed -r "s/inet //" | cut -f 1-4 -d. | cut -f 1 -d"/")
		# TODO support subnets other than /24, maybe all that are <= /24 (for that, it is necessary to calculate the range of IPs in the subnet)
	for L_ip in $L_ips ; do
		# debug
		#echo "L_ip = '$L_ip'"

		ipByte4=$(echo "$L_ip" | cut -f 4 -d.)
		currentIpByte4=$ipByte4
		ipBytes13=$(echo "$L_ip" | cut -f 1-3 -d.)

		# debug
		#echo "ipByte4 = '$ipByte4', ipBytes13 = '$ipBytes13'"

		# ping the whole subnet in parallel and write ip-adresses to a file
		subnet="$ipBytes13."
		( for addr in $(seq 1 1 254); do ( ping -c 1 -t 3 "$subnet$addr" > /dev/null && echo "$subnet$addr" >> "$tmpIPsFile" ) & done ) > /dev/null
	done

	# debug
	#cat $tmpIPsFile

	# check for model description
	while read nextIP ; do
		# debug
		# echo "nextIP = $nextIP"

		ownIP="$subnet$currentIpByte4"
		if [[ "$nextIP" != "$ownIP" ]] ; then
			hbCheck "$nextIP"

			# debug
			#echo "hbDescription (response) = '$hbDescription'"
			#echo "hbModel = '$hbModel'"

			if [ -n "$hbModel" ] ; then
				_BRIDGE_FOUND=true
				bridgeip=$nextIP
				echo "    bridge found at '$nextIP'."
				break
			fi
		fi
	done < $tmpIPsFile
	rm $tmpIPsFile 2> /dev/null
}

hbEnterIP() {
	echo -n "    Please enter the IP of your HUE-bridge: "
	read manualIP
	hbCheck "$manualIP"
	if [ -n "$hbModel" ] ; then
		_BRIDGE_FOUND=true
		bridgeip=$manualIP
		echo "    bridge found."
	fi
}

# this function will be called, if the configuration is valid
startLampe() {
	L_BLIND=0
    L_ONESHOT=0
	if [[ "$1" == "-b" ]] || [[ "$1" == "--blind" ]] ; then
		# blind modus: do not print main screen
		L_BLIND=1
		lampeMain
    elif [[ "$1" == "-s" ]] || [[ "$1" == "--set" ]] ; then
        # set option and quit
        L_ONESHOT=1
        lampeMain "$2" "$3"
	else
		# default: interactive-mode
		lampeMain
	fi
}

hbRegisterUser() {
	echo "    Please press the $(lColor ltblue)Link-button$(lColor off) on your bridge to "
	echo -n "    register a user ... "
	while true ; do
		# try user registration every two seconds
		sleep 2

		hbUser=$(curl -d "{\"devicetype\": \"lampe-$version#$USER\"}" "http://$bridgeip/api" -m 2 2> /dev/null)
		success=$(echo "$hbUser" | grep "success")

		# debug
		#echo "hbUser = $hbUser"

		if [ -n "$success" ] ; then
            registeredUser=$(echo "$success" | sed -r "s/\"username\":/;/" | cut -f 2 -d";" | cut -f 1 -d"}" )
            echo "bridgeuser=${registeredUser}" >> ~/.lamperc
			echo -e "\n    user registered."
			break
		fi
	done
}

showHelp() {
	echo "lampe-$version by André Klausnitzer, CC0"
	echo "    without option (interactive)"
	echo " -b, --blind"
	echo "    blind-mode (interactive)"
    echo " -s, --set [LIGHT] OPTION"
    echo "    set option. for available options press h in interactive-mode or"
    echo "    'lampe -s h'; LIGHT is mandatory for all non-global options."
    echo "    currently cannot be combined with bline-mode (oneshot)"
	echo " -h, --help"
	echo "    show this help"
}

# receive just one light state.
hbReceiveLightState() {
	local light=$1
	hbLightState=$(curl "http://$bridgeip/api/$bridgeuser/lights/$light" -m 2 2> /dev/null)
	lightState=$(echo "$hbLightState" | grep "state")
	if [ -n "$lightState" ] ; then
		# debug
		#echo "lightState[$light] = '$lightState'"

		# no use of JSON.sh, so that users who cannot install JSON.sh are still able to receive the
		# state of the current light
		lightSwi=$( echo "$lightState" | sed -r "s/\"on\":/;/" | cut -f 2 -d";" | cut -f 1 -d"," )
		lightBri=$( echo "$lightState" | sed -r "s/\"bri\":/;/" | cut -f 2 -d";" | cut -f 1 -d"," )

        # get light-bulb model
        _MODEL[$light]=$( echo "$lightState" | sed -r "s/\"modelid\":/;/" | cut -f 2 -d";" | cut -f 1 -d"," )

        # debug
        #echo "_MODEL[$_SELECTED_LAMP] = ${_MODEL[$_SELECTED_LAMP]}"

		if [[ "${_MODEL[$_SELECTED_LAMP]}" == "\"ZLL Light\"" ]] || \
           [[ "${_MODEL[$_SELECTED_LAMP]}" == "\"LWB004\"" ]] || \
           [[ "${_MODEL[$_SELECTED_LAMP]}" == "\"LWB010\"" ]] ; then
			# light models "ZLL", "LWB004", "LWB010" do not support hue and sat
			lightHue=12000
			lightSat=192
        elif [[ "${_MODEL[$_SELECTED_LAMP]}" == "\"LTW001\"" ]] ; then
            lightHue=12000
			lightSat=192
                # TODO  implement "ct" (color temperature)
		else
			lightHue=$(echo "$lightState" | sed -r "s/\"hue\":/;/" | cut -f 2 -d";" | cut -f 1 -d"," )
			lightSat=$(echo "$lightState" | sed -r "s/\"sat\":/;/" | cut -f 2 -d";" | cut -f 1 -d"," )
		fi

		# debug
		#echo "lightSwi[$light] = '$lightSwi'"
		#echo "lightBri[$light] = '$lightBri'"
		#echo "lightHue[$light] = '$lightHue'"
		#echo "lightSat[$light] = '$lightSat'"

		_SWITCH[$light]=$lightSwi
		_BRIGHTNESS[$light]=$lightBri
		_COLOR[$light]=$lightHue
		_SAT[$light]=$lightSat
	fi
}

# receives all light states. received json will be parsed by using JSON.sh
hbReceiveLightStates() {
	local lightStates=""

	lightStates=$(curl "http://$bridgeip/api/$bridgeuser/lights" -m 2 2> /dev/null)
	parsedJsonLightStates=$(echo "$lightStates" | JSON.sh -b 2> /dev/null)
	for (( light=1 ; light<51 ; light=light+1 )) ; do
		# debug
		#echo "light = $light"

		local currentPJLightStates
        currentPJLightStates=$(echo "$parsedJsonLightStates" | grep "\[\"$light\",")
		if [[ -n "$currentPJLightStates" ]] ; then
			lightSwi=$(echo "$currentPJLightStates" | grep "\"$light\",\"state\",\"on\"" | cut -f 2)
			# if [[ -n "$lightSwi" ]] ; then
			_SWITCH[$light]=$lightSwi

			# it is assumed that for every light that has a switch the
			# following params are availabe in the json output
			lightBri=$(echo "$currentPJLightStates" | grep "\"$light\",\"state\",\"bri\"" | cut -f 2)
			_BRIGHTNESS[$light]=$lightBri

            # get light-bulb model
			modelid=$(echo "$currentPJLightStates" | grep "\"$light\",\"modelid\"" | cut -f 2)
			_MODEL[$light]=$modelid

            # debug
	        #echo -e "\n    DEBUG hbSendLightsState().modelid = ${_MODEL[$light]}"
			if [[ "$modelid" == "\"ZLL Light\"" ]] || [[ "$modelid" == "\"LWB004\"" ]] || [[ "$modelid" == "\"LWB010\"" ]] ; then
				_COLOR[$light]=12000
				_SAT[$light]=192
                    # TODO  implement "ct" (color temperature)
			else
				lightHue=$(echo "$currentPJLightStates" | grep "\"$light\",\"state\",\"hue\"" | cut -f 2)
				_COLOR[$light]=$lightHue

				lightSat=$(echo "$currentPJLightStates" | grep "\"$light\",\"state\",\"sat\"" | cut -f 2)
				_SAT[$light]=$lightSat
			fi
			lightReachable=$(echo "$currentPJLightStates" | grep "\"$light\",\"state\",\"reachable\"" | cut -f 2)
			lightName=$(echo "$currentPJLightStates" | grep "\"$light\",\"name\"" | cut -f 2)

			# output for state of all lights
			if [ "$light" -lt 10 ] ; then
				echo -n " "
			fi
			echo -en "$(getColorOfLight $light)$light $(lColor off) "
			if [[ "$lightReachable" == "true" ]] ; then
				echo -en "reachable  $(lColor ltwhite)"
			else
				echo -en "           "
			fi
			if [[ -f "${cache_dir}lampeSequenceR$light" ]] ; then
				echo -n "R  "
			elif [[ -f "${cache_dir}lampeSequenceB$light" ]] ; then
				echo -n "B  "
			elif [[ -f "${cache_dir}lampeSequenceM$light" ]] ; then
				echo -n "M  "
			elif [[ -f "${cache_dir}lampeSequenceT$light" ]] ; then
				echo -n "T  "
			elif [[ -f "${cache_dir}lampeSequenceO$light" ]] ; then
				echo -n "O  "
			else
				echo -n "C  "
			fi
			echo "$(lColor white)$lightName$(lColor off)"
		else
			# defaults
			_SWITCH[$light]="true"
			_BRIGHTNESS[$light]=32
			_COLOR[$light]=12000
			_SAT[$light]=192
		fi

		# debug
		#echo "_COLOR = ${_COLOR[$light]}"
	done
}

checkDependency() {
	which "$1" 2> /dev/null 1> /dev/null
	if [[ "$?" -gt 0 ]] ; then
		echo " $(lColor ltred)?$(lColor off)  warning, '$1' is not installed"
	fi
}

# check for dependencies
checkDependency "JSON.sh"
checkDependency "redshift"

loadLightSetting() {
	if [[ -n "${SAVED_SWITCH[$_SELECTED_LAMP]}" ]] ; then
		# it is assumed that for every light that has a saved switch state
		# there are saved values for all following params
		_SWITCH[$_SELECTED_LAMP]=${SAVED_SWITCH[$_SELECTED_LAMP]}
		_BRIGHTNESS[$_SELECTED_LAMP]=${SAVED_BRIGHTNESS[$_SELECTED_LAMP]}
		_COLOR[$_SELECTED_LAMP]=${SAVED_COLOR[$_SELECTED_LAMP]}
		_SAT[$_SELECTED_LAMP]=${SAVED_SAT[$_SELECTED_LAMP]}
	fi
}

saveLightSetting() {
	SAVED_SWITCH[$_SELECTED_LAMP]=${_SWITCH[$_SELECTED_LAMP]}
	SAVED_BRIGHTNESS[$_SELECTED_LAMP]=${_BRIGHTNESS[$_SELECTED_LAMP]}
	SAVED_COLOR[$_SELECTED_LAMP]=${_COLOR[$_SELECTED_LAMP]}
	SAVED_SAT[$_SELECTED_LAMP]=${_SAT[$_SELECTED_LAMP]}
}

saveAllSettings() {
	mkdir -p ~/.config/lampe 2> /dev/null
	echo "" > ~/.config/lampe/light-settings
	for (( light=1 ; light<51 ; light=light+1 )) ; do
		if [[ -n "${SAVED_SWITCH[$light]}" ]] ; then
			# it is assumed that for every light that has a saved switch state
			# there are saved values for all following params
			{ echo "_SWITCH[$light]=\"${SAVED_SWITCH[$light]}\"" ;
			echo "SAVED_SWITCH[$light]=\"${SAVED_SWITCH[$light]}\"" ;
			echo "_BRIGHTNESS[$light]=\"${SAVED_BRIGHTNESS[$light]}\"" ;
			echo "SAVED_BRIGHTNESS[$light]=\"${SAVED_BRIGHTNESS[$light]}\"" ;
			echo "_COLOR[$light]=\"${SAVED_COLOR[$light]}\"" ;
			echo "SAVED_COLOR[$light]=\"${SAVED_COLOR[$light]}\"" ;
			echo "_SAT[$light]=\"${SAVED_SAT[$light]}\"" ;
			echo "SAVED_SAT[$light]=\"${SAVED_SAT[$light]}\"" ; } >> ~/.config/lampe/light-settings
		fi
	done
}

# load user configuration
loadUserConfig() {
	if [[ -f ~/.config/lampe/light-settings ]] ; then
		source ~/.config/lampe/light-settings
	fi
    if [[ -f ~/.config/lampe/light-defaults ]] ; then
        source ~/.config/lampe/light-defaults
    fi

    # check light defaults
    not_set=0
    if [[ -v "mood_sat" ]] ; then
        echo "    mood_sat=$mood_sat"
    else
        echo " $(lColor ltyellow)?$(lColor off)  mood_sat=[0..254]        default saturation for"
        echo "                             mood sequence not set."
        echo "                             saturation will not be"
        echo "                             changed during sequence"
        not_set=1
    fi

    if [[ -v "redshift_options" ]] ; then
        echo "    redshift_options=\"$redshift_options\""
    else
        echo " $(lColor ltyellow)?$(lColor off)  redshift_options=\"\"      no options will be"
        echo "                             forwarded to redshift"
        not_set=1
    fi

    if [[ -v "noise_range" ]] ; then
        echo "    noise_range=$noise_range"
    else
        echo " $(lColor ltyellow)?$(lColor off)  noise_range=[0..65000]   default 15000, a color"
        echo "                             between two colors"
        not_set=1
        noise_range=15000
    fi
    if [[ -v "noise_interval" ]] ; then
        echo "    noise_interval=$noise_interval"
    else
        echo " $(lColor ltyellow)?$(lColor off)  noise_interval=[1..]     default 3 seconds"
        not_set=1
        noise_interval=3
    fi
    if [[ -v "noise_step" ]] ; then
        echo "    noise_step=$noise_step"
    else
        echo " $(lColor ltyellow)?$(lColor off)  noise_step=[1..65000]    default 5000, could be"
        echo "                             more than just one shade"
        echo "                             of the current color "
        not_set=1
        noise_step=5000
    fi
    if [[ "$not_set" -eq 1 ]] ; then
        echo " *  You can set these vars in"
        echo "    ~/.config/lampe/light-defaults"
        echo ""
    fi

    # bug#46 fixed: gather cache_dir and set default or fallback
    if [[ ! -v "cache_dir" ]] ; then
        cache_dir="$HOME/.cache/"
    fi
    if [[ ! -d "${cache_dir}" ]] ; then
        cache_dir="/tmp/"
    fi
}


if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]] ; then
	# show help and quit
	showHelp
elif [[ -f ~/.lamperc ]] ; then
	# read configuration
	source ~/.lamperc

    loadUserConfig

	# receive light states
	if [[ "$1" != "-s" ]] && [[ "$1" != "--set" ]] ; then
        # FIXME receiving all light states in oneshot-mode is currently not supported;
        # this function takes too much time
        hbReceiveLightStates
    fi

	startLampe "$@"
else
	# first start: ask user what to do
	echo " $(lColor ltyellow)?$(lColor off) [======---$(lColor ltwhite)LAMPE$(lColor off)------------------------------------]"
	echo "    You are running 'lampe' for the first time. Do you "
	echo -n "    wish automatic discovery and configuration? [$(lColor ltwhite)y$(lColor off)/$(lColor ltred)n$(lColor off)] "
	read doIt
	if [[ "$doIt" == "y" ]] || [[ "$doIt" == "Y" ]] || [[ "$doIt" == "" ]] ; then
		hbDetect 2> /dev/null
		if [ "$_BRIDGE_FOUND" == false ] ; then
			echo " $(lColor ltred)?$(lColor off)  warning, unable to find bridge"
			hbEnterIP
		fi
	else
		hbEnterIP
	fi

	if [[ "$_BRIDGE_FOUND" == true ]] ; then
		# register a user at bridge
		hbRegisterUser

		# store configuration if bridge was found
		echo "bridgeip=\"$bridgeip\"" >> ~/.lamperc
        source ~/.lamperc

        loadUserConfig

		# receive light states
		hbReceiveLightStates

		startLampe "$@"
	else
		echo " $(lColor ltred)!$(lColor off)  no bridge found, error 2"
		exit 2
	fi
fi

