-
Notifications
You must be signed in to change notification settings - Fork 1
/
tado.sh
executable file
·295 lines (247 loc) · 7.71 KB
/
tado.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
#!/bin/bash
LOCKFILE="/tmp/synclock"
# Your tado account details
TADO_USER="[email protected]"
TADO_PASS="p@ssw0rd"
TADO_TOKENFILE="/tmp/tadotoken"
# Your domoticz account details
DOMOTICZ_USER="username"
DOMOTICZ_PASS="p@ssw0rd"
DOMOTICZ_PIN="1234"
DOMOTICZ_URL="https://your.domoticz.local:8443";
# Id's of the outside temperature device and the inside temp+humidity device
DOMOTICZ_INSIDE_TEMP_HUM_IDX=96
DOMOTICZ_OUTSIDE_TEMP_IDX=95
MYPATH=$(dirname $0);
VERBOSE=0;
SETTEMP="0";
SETMINS=120;
#
# Awesome parameter parsing, see http://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash
#
for i in "$@"
do
case ${i} in
--temp=* )
SETTEMP="${i#*=}"
shift ;
;;
--mins=* )
SETMINS="${i#*=}"
shift ;
;;
-v | --verbose | verbose)
VERBOSE=1
shift ;
;;
*)
(>&2 echo "Error, unknown parameter \"${i}\" given!");
;;
esac
done
#
# Include tado variables, if exists
#
if [ -f "${MYPATH}/tado.conf" ];
then
source "${MYPATH}/tado.conf"
fi
type jq >/dev/null 2>&1 || { echo >&2 "I require JQ but it's not installed. Aborting."; exit 1; }
JQ=$(which jq);
#
# Check if there is a lockfile. This prevents double execution of the script
#
if [[ -f ${LOCKFILE} ]];
then
# Yes, there is a lockfile.
# Check if it is more than 60 minutes old
if test $(find "$LOCKFILE" -mmin +60)
then
# Lockfile was too old, ignore it and remove it
rm ${LOCKFILE}
else
PID=$(cat ${LOCKFILE});
if ps -p ${PID} > /dev/null 2>/dev/null
then
# Lockfile was new enough, and process still exists. Do NOT run this cron!
if [[ ${VERBOSE} -eq 1 ]]
then
echo >&2 "Job was already running"
fi
exit
else
# Lockfile was new enough, however process does not exist anymore. Remove lockfile and continue.
rm ${LOCKFILE}
fi
fi
fi
#
# Display verbose output if wanted
#
function verbose()
{
if [[ ${VERBOSE} -eq 1 ]];
then
echo $1;
fi
}
#
# This function is responsible for fetching results from the TADO API.
# The response is placed in a variable called $JSON.
#
function fetchResponse()
{
URL=$1
PUTDATA="";
ATTEMPT=1
if [[ ! -z "$2" ]];
then
PUTDATA="$2";
fi
if [[ ! -z "$3" ]];
then
ATTEMPT="$3"
fi
if [[ "${PUTDATA}" != "" ]];
then
verbose "Fetch result for url: ${URL}";
verbose "Putting data: ${PUTDATA}";
JSON=$(curl -s -X PUT "${URL}" --connect-timeout 10 -H "Authorization: Bearer ${TOKEN}" -H "Content-Type:application/json;charset=UTF-8" --data-binary "${PUTDATA}");
else
verbose "Fetch result for url: ${URL}";
JSON=$(curl -s "${URL}" --connect-timeout 10 -H "Authorization: Bearer ${TOKEN}");
fi
# Token failure? Then retry
if [[ ${JSON} == *"expired"* ]];
then
verbose "Access token is expired, fetch new one... (Attempt ${ATTEMPT})";
# If we did not retried yet..
if [[ ${ATTEMPT} -le 2 ]];
then
ATTEMPT=$((ATTEMPT+1));
fetchToken;
fetchResponse "${URL}" "${PUTDATA}" "${ATTEMPT}";
fi
fi
}
#
# Retrieve a new TADO token and put it in the $TADO_TOKENFILE file.
#
function fetchToken()
{
verbose "Retrieve tado settings";
SETTINGS=$( \
curl 'https://app.tado.com/env.js' \
--connect-timeout 10 -s \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0' \
-H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8' \
-H 'Accept-Language: nl,en-US;q=0.7,en;q=0.3' \
-H 'Alt-Used: app.tado.com' \
-H 'Connection: keep-alive');
verbose "${SETTINGS}"
CLIENT_ID=$(echo "${SETTINGS}" | grep "clientId" | sed -e "s/[',]//g" | awk -F': ' '{print $2}' );
CLIENT_SECRET=$(echo "${SETTINGS}" | grep "clientSecret" | sed -e "s/[',]//g" | awk -F': ' '{print $2}' );
verbose "Retrieved client id: ${CLIENT_ID}";
verbose "Retrieved client secret: ${CLIENT_SECRET}";
verbose "Retrieving new tado token... ";
TOKEN=$(curl \
--connect-timeout 10 \
-s "https://auth.tado.com/oauth/token" \
-d client_id="${CLIENT_ID}" \
-d client_secret="${CLIENT_SECRET}" \
-d grant_type=password \
-d scope=home.user \
-d username="${TADO_USER}" \
-d password="${TADO_PASS}" | ${JQ} -r '.access_token' );
verbose "Retrieved token: ${TOKEN}";
# Check if token is wrong
if [[ "${TOKEN}" = "" ]] || [[ "${TOKEN}" = "null" ]];
then
(>&2 echo "Error, token is still incorrect!")
exit 1;
fi
# store in file
verbose "Storing token in file: ${TADO_TOKENFILE}";
echo "${TOKEN}"> ${TADO_TOKENFILE};
}
#
# Start execution of the script
#
DATE=$(date +"%d-%m-%Y %k:%M");
verbose "Date: ${DATE}"
if [[ ! -f ${TADO_TOKENFILE} ]];
then
verbose "Tado token file is missing, fetch new one...";
fetchToken;
fi
verbose "Fetching token from file: ${TADO_TOKENFILE}";
TOKEN=$( cat "${TADO_TOKENFILE}" );
if [[ "${TOKEN}" = "" ]] || [[ "${TOKEN}" = "null" ]];
then
verbose "Token is empty, fetching new one...";
fetchToken;
fi
#
# First, fetch the home id.
#
fetchResponse "https://my.tado.com/api/v2/me"
HOMEID=$(echo ${JSON} | ${JQ} -r '.homes[0].id' )
verbose "HOME ID: ${HOMEID}" ;
if [[ "${HOMEID}" = "null" ]] || [[ "${HOMEID}" = "" ]];
then
(>&2 echo "Error, we failed to fetch HOME id")
exit 1;
fi
#
# Set the temperature if wanted
#
if [[ ${SETTEMP} != "0" ]];
then
SETSEC=$((SETMINS*60))
verbose "Set current temperature for ${SETMINS} minutes ( = ${SETSEC} seconds)"
fetchResponse "https://my.tado.com/api/v2/homes/${HOMEID}/zones/1/overlay" "{\"setting\":{\"type\":\"HEATING\",\"power\":\"ON\",\"temperature\":{\"celsius\":${SETTEMP}}},\"termination\":{\"type\":\"TIMER\",\"durationInSeconds\":${SETSEC}}}"
verbose ${JSON};
TADOTEMP=$(echo ${JSON} | ${JQ} '.setting.temperature.celsius');
if [[ ${TADOTEMP} -eq ${SETTEMP} ]];
then
verbose "Temperature SUCCESSFUL set to ${SETTEMP} C";
else
verbose "ERROR, something went wrong while setting the temperature. Raw response: ${JSON}";
fi
# Stop the script
verbose "Done!\n";
exit 0;
fi
#
# Now fetch the inside temperature and humidity. Note: we assume that device 1 is the thermostat.
#
fetchResponse "https://my.tado.com/api/v2/homes/${HOMEID}/zones/1/state"
TEMP=$(echo ${JSON} | ${JQ} '.sensorDataPoints.insideTemperature.celsius');
HUMIDITY=$(echo ${JSON} | ${JQ} '.sensorDataPoints.humidity.percentage');
#
# Now fetch the outside temperature
#
fetchResponse "https://my.tado.com/api/v2/homes/${HOMEID}/weather"
OUTSIDE_TEMP=$(echo ${JSON} | ${JQ} '.outsideTemperature.celsius');
verbose "Temperature: ${TEMP}";
verbose "Humidity: ${HUMIDITY}"
verbose "Outside Temperature: ${OUTSIDE_TEMP}";
verbose "Push values to domoticz...";
URL="${DOMOTICZ_URL}/json.htm?type=command¶m=udevice&idx=${DOMOTICZ_OUTSIDE_TEMP_IDX}&nvalue=0&svalue=${OUTSIDE_TEMP}&passcode=${DOMOTICZ_PIN}";
verbose "Requesting URL: ${URL}";
if [[ -z "${DOMOTICZ_USER}" ]]; then
OUTPUT=$(curl --connect-timeout 10 -k -s "${URL}");
else
OUTPUT=$(curl --connect-timeout 10 -u ${DOMOTICZ_USER}:${DOMOTICZ_PASS} -k -s "${URL}");
fi
verbose "$OUTPUT";
URL="${DOMOTICZ_URL}/json.htm?type=command¶m=udevice&idx=${DOMOTICZ_INSIDE_TEMP_HUM_IDX}&nvalue=0&svalue=${TEMP};${HUMIDITY};0&passcode=${DOMOTICZ_PIN}";
verbose "Requesting URL: ${URL}";
if [[ -z "${DOMOTICZ_USER}" ]]; then
OUTPUT=$(curl --connect-timeout 10 -k -s "${URL}");
else
OUTPUT=$(curl --connect-timeout 10 -u ${DOMOTICZ_USER}:${DOMOTICZ_PASS} -k -s "${URL}");
fi
verbose "$OUTPUT";
verbose "Done!";
exit 0;