As we are near to the turn of the year I was looking for a solution to daily read and forward important dates of 2016 to pimatic. I already have a solution implemented, based on several different shell scripts with references to various internet sites and local text files. In pimatic I have defined variables to be informed about
- public holidays
- school holidays
- birthdays
- important appointments
with a forecast of 7 days.
As my programming skills are very poor and a relict from my unix admin job in the early nineties, I use shell scripting as my preffered language for programming.
But inwardly I hoped that someone provide a modern js plugin to access and read a google calendar. In the meantime I found a Google Calendar Command Line Interface ‘gcalcli’ written in python. After playing around and gaining a bit experience with this application, I wrote a little bash script which execute ‘gcalcli’ to read my google calendars and post the received information to my pimatic server.
Maybe someone has interest also to read and pass google entries to pimatic. Here is the way I accomplished that:
Prerequisites:
A: Google calendar(s)
- You need to have a google calendar - in my case, this calendar is called ‘Familienkalender’
- Optional, if you like to read public holidays and school holidays you need to add additional calendars and to create the respective entries by importing the data in ical format.
- public holidays --> in my case, I added a calendar ‘Feiertage’ and imported the data from http://www.ifeiertage.de to the calendar in google
- school holidays --> same approach as for public holidays, data in ical format from http://www.schulferien.org/iCal/
- Optional, connect your google contacts to your calendar to get access to birthdays the birthday entries of your family and friends --> in my case, I named that addtional calendar ‘Geburtstage’.
So, I ended up with the following google calendars:
- Familienkalender
- Feiertage
- Schulferien
- Geburtstage
B: Python application 'gcalcli’
Access to the google calendar is accomplished by installing the python application ‘gcalcli’.
Installation and usage of ‘gcalcli’ see: https://github.com/insanum/gcalcli
C: Configure application access to a google calendar
To implement access to a google calendar by gcalcli, you need to setup the google access method ‘Authentification OAuth 2.0’. see https://developers.google.com/api-client-library/python/auth/web-app
If you have successful implemented these 3 steps you can start playing with ‘gcalcli’ on the shell commandline. If you able to read your calendars on shell level you can start the last part, installing my shell script to pass the calendar entries to pimatic.
Install the following script into pi’s home directory:
#!/bin/bash
#
# readGoogleCalendar.sh [mm/dd/yyyy]
#
# Use 'gcalcli' to read entries from google calendar
# and write received entries reformated to pimatic
#
# Installation and usage of 'gcalcli' :
# https://github.com/insanum/gcalcli
#
# Setup of google Authentification OAuth 2.0:
# https://developers.google.com/api-client-library/python/auth/web-app
#
#-------------------------------------------------------------------------
#
# run script with effective uid of user pi
#
if [ "$(id -u -n)" != "pi" ]; then
exec su -l pi "$0" "$@"
fi
#
# source shell functions to access pimatic using curl
#
. /home/pi/pimaticAPI.sh
#---------------------------------------------------------------
# function to read google calendar entries by running 'gcalcli'
#
function get_google_calendar_entries() {
gc_index=$1 # index to calendar
gc_calendar=$2 # calendar to read
gc_queryStart=$3 # start of time slot
gc_queryEnd=$4 # end of time slot
gc_entry_for_today="false"
pimatic_infotext=""
if [ "$DEBUG" = "true" ];then echo "DEBUG: get_google_calendar_entries $gc_calendar $gc_queryStart $gc_queryEnd";fi
today=$(date -d $gc_queryStart +"%Y-%m-%d") # "yyyy-mm-dd
prev_gc_date="" # date value of previous read line
lc=0 # line count
while read -r line
do
if [ "$DEBUG" = "true" ];then echo "DEBUG: line from gcalcli >$line<";fi
if [ -n "$line" ] && [ "$line" != "No Events Found..." ] # we got an calendar entry
then
gc_bdat[$lc]=$(echo $line | awk '{print $1}') # get date begin field
gc_time[$lc]=$(echo $line | awk '{print " "$2" "}') # get time field
gc_edat[$lc]=$(echo $line | awk '{print $3}') # get date end field
gc_time[$lc]=${gc_time[$lc]## 00:00} # substitute 00:00 by white space
gc_info[$lc]=$(echo $line | awk '{for(i=5;i<=NF;++i) printf("%s ",$i)}') # info fields
gc_info[$lc]=${gc_info[$lc]% } # delete last space char
if [[ $gc_index -eq 0 && "${gc_info[$lc]:0:1}" == "+" ]] || [ $gc_index -ge 1 ]
then # for Termine we process only entries starting with '+'
if [ "${gc_info[$lc]:0:1}" == "+" ]
then
gc_info[$lc]=${gc_info[$lc]#+} # eleminate 1 char in case of + sign
fi
if [ "$DEBUG" = "true" ];then echo "DEBUG: today: >$today< date:>${gc_bdat[$lc]}< time:>${gc_time[$lc]}< info:>${gc_info[$lc]}<";fi
if [ "${gc_bdat[$lc]}" == "$today" ] # any entry with date of today?
then
gc_entry_for_today="true"
if [ "${gc_bdat[$lc]}" != "$prev_gc_date" ] # format info string for pimatic GUI
then
pimatic_infotext="heute${gc_time[$lc]}${gc_info[$lc]}" # first entry for today
else
pimatic_infotext=" und${gc_time[$lc]}${gc_info[$lc]}" # further entry for today
fi
else # there is an entry within the next days
gcday_t=$(date -d "${gc_bdat[$lc]}" +"%A") # day in long format
gcdate_t=$(date -d "${gc_bdat[$lc]}" +"%d.%b. -") # and date in dd.mm. -
if [ -n "$pimatic_infotext" ] # format info string for pimatic
then
if [ "${gc_bdat[$lc]}" == "$prev_gc_date" ]
then
pimatic_infotext="$pimatic_infotext und${gc_time[$lc]}${gc_info[$lc]}"
else
pimatic_infotext="$pimatic_infotext und am $gcdate_t${gc_time[$lc]}${gc_info[$lc]}"
fi
else
pimatic_infotext="am $gcday_t, $gcdate_t${gc_time[$lc]}${gc_info[$lc]}"
fi
fi
lc=$[$lc+1]
prev_gc_date=${gc_bdat[$lc]}
fi
fi
done < <(/usr/local/bin/gcalcli --nocolor --tsv --nocache --calendar="${gc_calendar}" agenda ${gc_queryStart} ${gc_queryEnd})
gc_entry_count=$lc
if [ -z "$pimatic_infotext" ]
then
pimatic_infotext="-"
fi
}
#---------------------------------------------------------------
# process and format information on holidays
#
function check_for_inside_holiday() {
#
iday=$(date -d "$1" +%-j) # requested day in number format (e.q 237)
fcday=$(date -d "$1 +7 days" +%-j)
year=$(date +%Y)
schoolHoliday="false"
schoolHolidayInfo="-"
hd_record=$2
#
# input record format: beginn - end holidayname (yyyy-mm-dd - yyyy-mm-dd text..)
#
# store information on holidays in array variables
#
_i=1
counter=0
for item in $hd_record
do
#echo $item
counter=$(( counter+1 ))
if [ $counter -eq 1 ];
then
hdbeginn[$_i]=$(echo $item | awk 'BEGIN{FS="-";} {print $1$2$3;}')
fi
if [ $counter -eq 3 ]
then
hdend[$_i]=$(echo $item | awk 'BEGIN{FS="-";} {print $1$2$3;}')
fi
if [ $counter -eq 4 ]
then
hdname[$_i]=$item
hdinfo[$_i]="${hdname[$_i]} $(date -d ${hdbeginn[$_i]} +%d.%b) - $(date -d ${hdend[$_i]} +%d.%b)"
ihdbeginn=$(date -d ${hdbeginn[$_i]} +%-j) # numeric start
ihdend=$(date -d ${hdend[$_i]} +%-j)
ryear=$(date -d ${hdbeginn[$_i]} +%Y)
if [ "$DEBUG" == "true" ];then echo "DEBUG: $ihdbeginn $ihdend $ryear $year";fi
if [ $ihdbeginn -gt $ihdend ] && [ $ryear -eq $year ]
then
hdend[$_i]=$year"1231"
elif [ $ihdbeginn -gt $ihdend ] && [ $ryear -lt $year ]
then
hdbeginn[$_i]=$year"0101"
fi
if [ "$DEBUG" == "true" ];then echo "DEBUG: ${hdinfo[$_i]}";fi
_i=$(( _i+1 ))
counter=0
fi
done
#
# check if current day is a holiday
#
schoolHoliday="false"
counter=1
while [ $counter -lt $_i ]
do
ihdbeginn=$(date -d ${hdbeginn[$counter]} +%-j) # numeric start
ihdend=$(date -d ${hdend[$counter]} +%-j) # numeric end
if [ "$DEBUG" == "true" ];then echo "DEBUG: $counter $fcday $iday $ihdbeginn $ihdend";fi
if ([ "$iday" -ge "$ihdbeginn" ] && [ "$iday" -le "$ihdend" ] )
then
schoolHoliday="true"
schoolHolidayInfo=${hdinfo[$counter]}
elif [ "$fcday" -ge "$ihdbeginn" ] && [ "$fcday" -le "$ihdend" ]
then
schoolHolidayInfo=${hdinfo[$counter]}
fi
counter=$(( counter+1 ))
done
if [ "$DEBUG" == "true" ];then echo "DEBUG: $schoolHoliday $schoolHolidayInfo";fi
}
#
#---------------------------------------------------------------
# main
#
# get runstring parameter [yyyy-mm-dd] (only for debug purposes)
#
# set pimatic: info variables:
# today = 'string'
# Termine = 'string'
# Geburtstage= 'string'
# Feiertage = 'string'
#
if [ "$1" ] # choose a specific day 'yyyy-mm-dd' for testing and debuging
then
DEBUG="true"
querydate="$1"
else
querydate="today" # standard mode, as executed by pimatic
fi
gcalendar[0]="Familienkalender"
gcalendar[1]="Feiertage"
gcalendar[2]="Geburtstage"
gcalendar[3]="Schulferien"
#
# set start and end time for timeslot of calendar query by 'gcalcli'
#
queryTimeslot="7" # number of days to look ahead
queryStart=$(date -d "$querydate" +"%Y-%m-%d") # format yyyy-mm-dd
queryEnd=$(date -d "$querydate +$queryTimeslot days" +"%Y-%m-%d") # format yyyy-mm-dd
#
# read all google calendar in sequence
i=0
while [ $i -lt 4 ]
do
get_google_calendar_entries $i "${gcalendar[$i]}" "$queryStart" "$queryEnd"
case "$i" in
0) # Termine of Familienkalender
if [ $gc_entry_count -gt 0 ]
then
if [ "$gc_entry_for_today" == "true" ]
then
pimatic_LED="711" # we have a entry for today, so switch on reminder LED
else
pimatic_LED="700" # something within the next days
fi
else
pimatic_LED="700" # default value to switch off status LED on info panel
fi
pimatic_var="LEDstatus"
pimatic_var_value="\"$pimatic_LED\""
if [ "$DEBUG" == "true" ];then echo "DEBUG: pimatic variable>$pimatic_var value>$pimatic_var_value";fi
write_to_pimatic $pimatic_var "$pimatic_var_value" # send status to LED info panel
pimatic_var="Termine"
pimatic_var_value="\"$pimatic_infotext\""
if [ "$DEBUG" == "true" ];then echo "DEBUG: pimatic variable>$pimatic_var value>$pimatic_var_value";fi
write_to_pimatic $pimatic_var "$pimatic_var_value" # write info string to pimatic variable
;;
1) # Feiertage
if [ "$gc_entry_for_today" == "true" ]
then
pimatic_today="$(date -d "$querydate" +"%A"), $(date -d "$querydate" +"%d.%m.%Y") - ${gc_info[0]}"
else
pimatic_today="$(date -d "$querydate" +"%A"), $(date -d "$querydate" +"%d.%m.%Y")"
fi
pimatic_var="publicHoliday"
pimatic_var_value="\"$gc_entry_for_today\""
if [ "$DEBUG" == "true" ];then echo "DEBUG: pimatic variable>$pimatic_var value>$pimatic_var_value";fi
write_to_pimatic $pimatic_var "$pimatic_var_value"
pimatic_var="today"
pimatic_var_value="\"$pimatic_today\""
if [ "$DEBUG" == "true" ];then echo "DEBUG: pimatic variable>$pimatic_var value>$pimatic_var_value";fi
write_to_pimatic $pimatic_var "$pimatic_var_value"
pimatic_var="Feiertage"
pimatic_var_value="\"$pimatic_infotext\""
if [ "$DEBUG" == "true" ];then echo "DEBUG: pimatic variable>$pimatic_var value>$pimatic_var_value";fi
write_to_pimatic $pimatic_var "$pimatic_var_value"
;;
2) # Geburtstage
if [ $gc_entry_count -gt 0 ]
then
pimatic_infotext="${pimatic_infotext// hat Geburtstag/}" # we only need name and firstname
if [ "$gc_entry_for_today" == "true" ]
then
pimatic_LED="211" # we have a birthday today
else
pimatic_LED="212" # birthday within the next days
fi
else
pimatic_LED="200" # default value to switch off status LED on info panel
fi
pimatic_var="LEDstatus"
pimatic_var_value="\"$pimatic_LED\""
if [ "$DEBUG" == "true" ];then echo "DEBUG: pimatic variable>$pimatic_var value>$pimatic_var_value";fi
write_to_pimatic $pimatic_var "$pimatic_var_value" # send status to LED info panel
pimatic_var="Geburtstage"
pimatic_var_value="\"$pimatic_infotext\""
if [ "$DEBUG" == "true" ];then echo "DEBUG: pimatic variable>$pimatic_var value>$pimatic_var_value";fi
write_to_pimatic $pimatic_var "$pimatic_var_value" # send status to LED info panel
;;
3) # Schulferien
if [ $gc_entry_count -gt 0 ]
then
if [ "$DEBUG" == "true" ];then echo "DEBUG: check_for_inside_holiday $queryStart ${gc_bdat[0]} - ${gc_edat[0]} ${gc_info[0]}";fi
check_for_inside_holiday "$queryStart" "${gc_bdat[0]} - ${gc_edat[0]} ${gc_info[0]}"
else
schoolHoliday="false"
schoolHolidayInfo="-"
fi
pimatic_var="schoolHoliday"
pimatic_var_value="\"$schoolHoliday\""
if [ "$DEBUG" == "true" ];then echo "DEBUG: pimatic variable>$pimatic_var value>$pimatic_var_value";fi
write_to_pimatic $pimatic_var "$pimatic_var_value"
pimatic_var="schoolHolidayInfo"
pimatic_var_value="\"$schoolHolidayInfo\""
if [ "$DEBUG" == "true" ];then echo "DEBUG: pimatic variable>$pimatic_var value>$pimatic_var_value";fi
write_to_pimatic $pimatic_var "$pimatic_var_value"
;;
esac
i=$[$i+1]
done
exit 0
You need also to place the script 'pimaticAPI.sh to ‘/home/pi’. This script provides the interface to pimatic using ‘curl’ to write information to pimatic variables.
#!/bin/bash
#
# pimaticAPI.sh
#
export LANG=de_DE.UTF-8
export PIMATIC_HOME="/home/pi"
export PIMATIC_CONFIG_FILE="${PIMATIC_HOME}/pimatic-app/config.json"
export PIMATIC_HOST
export PIMATIC_PORT
export PIMATIC_USER=admin
export PIMATIC_PWD
export pimatic_server
export pimatic_var_value
function get_pimatic_server_data() {
#------------------------------------------------------------------------------
# get host and login data to access pimatic server
#
# for local pimatic access, config.json is read to get account data
# for remote access, create a file 'server.host' in PIMATIC_HOME, where host is the 'hostname' of the pimatic server
# and include the host access parameter to login to pimatic server --> explore 'server.example'
#
PIMATIC_SERVER_FILE="${PIMATIC_HOME}/server.${pimatic_server}"
#
# check if a server file exist, otherwise use local hostname and config.json
#
if [ -e "$PIMATIC_SERVER_FILE" ]
then
. $PIMATIC_SERVER_FILE
else
PIMATIC_HOST=$(hostname)
PIMATIC_PWD=$(grep -B 2 -A 2 -w "\"role\": \"admin\"" $PIMATIC_CONFIG_FILE | grep -w "password" | awk 'BEGIN{FS="\"";} {print $4;}')
PIMATIC_PORT=$(grep -B 2 -A 2 -w "\"httpServer\"" $PIMATIC_CONFIG_FILE | grep "\"port\"" | awk '{print $2}')
fi
#-----DEBUG---------------------------
if [ "$DEBUG" = "true" ]
then
echo "PIMATIC_HOST=$PIMATIC_HOST"
echo "PIMATIC_PORT=$PIMATIC_PORT"
echo "PIMATIC_USER=$PIMATIC_USER"
echo "PIMATIC_PWD=$PIMATIC_PWD"
fi
#-------------------------------------
}
function write_to_pimatic() {
#--------------------------------------
# write value trough pimatic variabale
#
pimatic_var=$1
pimatic_var_value=$2
get_pimatic_server_data
#-----DEBUG---------------------------
if [ "$DEBUG" = "true" ]
then
echo ">>>> curl start >>>>"
echo "curl --insecure -X PATCH --header \"Content-Type:application/json\" --data '{\"type\": \"value\", \"valueOrExpression\": '\"${pimatic_var_value}\"'}' --user \"${PIMATIC_USER}:${PIMATIC_PWD}\" http://$PIMATIC_HOST:$PIMATIC_PORT/api/variables/$pimatic_var"
echo "<<<< curl end <<<<"
curl --insecure -X PATCH --header "Content-Type:application/json" --data '{"type": "value", "valueOrExpression": '"${pimatic_var_value}"'}' --user "${PIMATIC_USER}:${PIMATIC_PWD}" http://$PIMATIC_HOST:$PIMATIC_PORT/api/variables/$pimatic_var
#---------------------------------
else
get=$(curl --insecure -X PATCH --header "Content-Type:application/json" --data '{"type": "value", "valueOrExpression": '"${pimatic_var_value}"'}' --user "${PIMATIC_USER}:${PIMATIC_PWD}" http://$PIMATIC_HOST:$PIMATIC_PORT/api/variables/$pimatic_var 2>/dev/null)
success=$(echo $get | grep "\"success\"" | awk '{print $16;}')
if [ "$success" = "true" ]
then
return 0
else
return 1
fi
fi
}
function read_from_pimatic() {
#----------------------------------
# read value from pimatic variable
#
pimatic_var=$1
get_pimatic_server_data
#-----DEBUG---------------------------
if [ "$DEBUG" = "true" ]
then
echo ">>>> curl start >>>>"
echo "curl --insecure -X GET --user \"${PIMATIC_USER}:${PIMATIC_PWD}\" http://$PIMATIC_HOST:$PIMATIC_PORT/api/variables/$pimatic_var"
echo "<<<< curl end <<<<"
get=$(curl --insecure -X GET --user "${PIMATIC_USER}:${PIMATIC_PWD}" http://$PIMATIC_HOST:$PIMATIC_PORT/api/variables/$pimatic_var)
echo $get
success=$(echo $get | grep "\"success\"" | awk '{print $16;}')
echo "success = $success"
pimatic_var_value=$(echo $get | grep "\"value\"" | awk 'BEGIN{FS=",";} {print $4;}' | awk 'BEGIN{FS=":";} {print $2;}')
echo "pimatic_var_value = $pimatic_var_value"
#---------------------------------
else
get=$(curl --insecure -X GET --user "${PIMATIC_USER}:${PIMATIC_PWD}" http://$PIMATIC_HOST:$PIMATIC_PORT/api/variables/$pimatic_var 2>/dev/null)
success=$(echo $get | grep "\"success\"" | awk '{print $16;}')
if [ "$success" = "true" ]
then
pimatic_var_value=$(echo $get | grep "\"value\"" | awk 'BEGIN{FS=",";} {print $4;}' | awk 'BEGIN{FS=":";} {print $2;}')
return 0
else
pimatic_var_value="ERROR"
return 1
fi
fi
}
function post_action_to_pimatic() {
#-------------------------------------
# post action request trough pimatic
#
pimatic_action="\"${1}\""
get_pimatic_server_data
##-----DEBUG---------------------------
if [ "$DEBUG" = "true" ]
then
echo ${pimatic_action}
echo ">>>> curl start >>>>"
curl --insecure -X POST --header "Content-Type:application/json" --data '{"actionString":'"${pimatic_action}"} --user "${PIMATIC_USER}:${PIMATIC_PWD}" http://$PIMATIC_HOST:$PIMATIC_PORT/api/execute-action
echo "<<<< curl end <<<<"
#---------------------------------
else
curl --insecure -X POST --header "Content-Type:application/json" --data '{"actionString":'"${pimatic_action}"} --user "${PIMATIC_USER}:${PIMATIC_PWD}" http://$PIMATIC_HOST:$PIMATIC_PORT/api/execute-action >/dev/null 2>&1
return $?
fi
}
For a better and easy impression in which way the google entries are reformated, I provide a screen shot of my pimatic dash board.
As not every calendar entry is really important for me, the script only take entries starting with a plus sign ‘+’ from my ‘Familenkalendar’. All other entries are ignored.
It would be possible to create an additional calendar (e.g. pimatic) for explicit event triggering in pimatic. The script could be easily adjusted for this case by including the respective calendar and enlarging the loop.