-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnetatmo_cli.py
More file actions
executable file
·267 lines (217 loc) · 10.1 KB
/
netatmo_cli.py
File metadata and controls
executable file
·267 lines (217 loc) · 10.1 KB
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
#!/usr/bin/env python3
"""Application CLI pour piloter le thermostat Netatmo."""
import argparse
import json
import sys
from typing import Any, Dict
from config import Config
from netatmo_client import NetatmoClient
def format_output(data: Any, json_output: bool = False) -> str:
"""Formate la sortie en texte ou JSON."""
if json_output:
return json.dumps(data, indent=2, ensure_ascii=False)
return format_text_output(data)
def format_text_output(data: Any) -> str:
"""Formate la sortie en texte lisible."""
if isinstance(data, dict):
lines = []
for key, value in data.items():
if isinstance(value, (int, float)):
lines.append(f"{key}: {value}")
elif isinstance(value, str):
lines.append(f"{key}: {value}")
elif isinstance(value, dict):
lines.append(f"{key}:")
for sub_key, sub_value in value.items():
lines.append(f" {sub_key}: {sub_value}")
else:
lines.append(f"{key}: {value}")
return "\n".join(lines)
return str(data)
def cmd_status(client: NetatmoClient, args: argparse.Namespace) -> Dict[str, Any]:
"""Affiche le statut du thermostat."""
try:
status = client.get_thermostat_status(debug=args.debug)
current_temp = status.get('current_temp')
target_temp = status.get('target_temp')
output = {
'module_name': status.get('module_name', 'N/A'),
'current_temperature': f"{current_temp:.1f}°C" if current_temp is not None else 'N/A',
'target_temperature': f"{target_temp:.1f}°C" if target_temp is not None else 'N/A',
'setpoint_mode': status.get('setpoint_mode', 'N/A'),
'boiler_status': 'ON' if status.get('boiler_status') else 'OFF',
'heating_power_request': status.get('heating_power_request', 0)
}
print(format_output(output, args.json))
return output
except Exception as e:
print(f"Erreur: {e}", file=sys.stderr)
sys.exit(1)
def cmd_set(client: NetatmoClient, args: argparse.Namespace) -> Dict[str, Any]:
"""Définit la température cible."""
try:
result = client.set_temperature(args.temperature)
output = {
'status': 'success',
'temperature_set': args.temperature,
'message': f"Température réglée à {args.temperature}°C"
}
print(format_output(output, args.json))
return output
except Exception as e:
print(f"Erreur: {e}", file=sys.stderr)
sys.exit(1)
def cmd_frost_guard(client: NetatmoClient, args: argparse.Namespace) -> Dict[str, Any]:
"""Active ou désactive le mode hors gel."""
try:
enabled = args.state.lower() == 'on'
result = client.set_frost_guard(enabled)
output = {
'status': 'success',
'frost_guard': 'enabled' if enabled else 'disabled',
'message': f"Mode hors gel {'activé' if enabled else 'désactivé'}"
}
print(format_output(output, args.json))
return output
except Exception as e:
print(f"Erreur: {e}", file=sys.stderr)
sys.exit(1)
def cmd_history(client: NetatmoClient, args: argparse.Namespace) -> Dict[str, Any]:
"""Affiche l'historique des températures."""
try:
history = client.get_thermostat_history(args.days, debug=args.debug)
if args.json:
print(format_output(history, True))
return history
# Format texte pour l'historique
print(f"Historique des températures (derniers {args.days} jours):")
print("-" * 50)
from datetime import datetime
# Parser les données selon différentes structures possibles
data_found = False
if 'body' in history:
body = history['body']
# Structure 1: body est une liste
if isinstance(body, list):
for entry in body:
if 'value' in entry and entry['value']:
data_found = True
beg_time = entry.get('beg_time', 0)
step_time = entry.get('step_time', 3600) # Par défaut 1 heure
# Les valeurs sont dans value = [[temp1], [temp2], ...]
for index, value_set in enumerate(entry['value']):
if isinstance(value_set, list) and len(value_set) > 0:
temp = value_set[0] # La température est le premier élément
if temp is not None:
# Calculer le timestamp pour cette valeur
timestamp = beg_time + (index * step_time)
dt = datetime.fromtimestamp(timestamp)
print(f"{dt.strftime('%Y-%m-%d %H:%M')}: {temp:.1f}°C")
# Structure 2: body est un dict (structure alternative)
elif isinstance(body, dict):
for key, entry in body.items():
if isinstance(entry, dict) and 'value' in entry:
data_found = True
beg_time = entry.get('beg_time', 0)
step_time = entry.get('step_time', 3600)
values = entry['value']
if isinstance(values, list):
for index, value_set in enumerate(values):
if isinstance(value_set, list) and len(value_set) > 0:
temp = value_set[0]
if temp is not None:
timestamp = beg_time + (index * step_time)
dt = datetime.fromtimestamp(timestamp)
print(f"{dt.strftime('%Y-%m-%d %H:%M')}: {temp:.1f}°C")
if not data_found:
print("Aucune donnée disponible")
if args.debug:
import json
print("\nDEBUG - Structure de la réponse:", file=sys.stderr)
print(json.dumps(history, indent=2)[:2000], file=sys.stderr)
return history
except Exception as e:
print(f"Erreur: {e}", file=sys.stderr)
if args.debug:
import traceback
traceback.print_exc()
sys.exit(1)
def cmd_stats(client: NetatmoClient, args: argparse.Namespace) -> Dict[str, Any]:
"""Affiche les statistiques."""
try:
stats = client.get_statistics(args.days, debug=args.debug)
output = {
'period_days': args.days,
'average_temperature': f"{stats.get('average', 0):.1f}°C" if stats.get('average') else 'N/A',
'min_temperature': f"{stats.get('min', 0):.1f}°C" if stats.get('min') else 'N/A',
'max_temperature': f"{stats.get('max', 0):.1f}°C" if stats.get('max') else 'N/A',
'data_points': stats.get('count', 0)
}
print(format_output(output, args.json))
return output
except Exception as e:
print(f"Erreur: {e}", file=sys.stderr)
sys.exit(1)
def main():
"""Point d'entrée principal de l'application CLI."""
parser = argparse.ArgumentParser(
description='Piloter votre thermostat Netatmo connecté à votre chaudière',
formatter_class=argparse.RawDescriptionHelpFormatter
)
# Arguments globaux partagés
common_args = argparse.ArgumentParser(add_help=False)
common_args.add_argument(
'--json',
action='store_true',
help='Afficher la sortie au format JSON'
)
common_args.add_argument(
'--debug',
action='store_true',
help='Mode debug (affiche plus de détails sur les erreurs)'
)
subparsers = parser.add_subparsers(dest='command', help='Commandes disponibles')
# Commande status
parser_status = subparsers.add_parser('status', help='Afficher le statut du thermostat', parents=[common_args])
parser_status.set_defaults(func=cmd_status)
# Commande set
parser_set = subparsers.add_parser('set', help='Définir la température cible', parents=[common_args])
parser_set.add_argument('temperature', type=float, help='Température cible en °C')
parser_set.set_defaults(func=cmd_set)
# Commande frost-guard
parser_frost = subparsers.add_parser('frost-guard', help='Activer/désactiver le mode hors gel', parents=[common_args])
parser_frost.add_argument('state', choices=['on', 'off'], help='État: on ou off')
parser_frost.set_defaults(func=cmd_frost_guard)
# Commande history
parser_history = subparsers.add_parser('history', help='Afficher l\'historique des températures', parents=[common_args])
parser_history.add_argument('--days', type=int, default=7, help='Nombre de jours (défaut: 7)')
parser_history.set_defaults(func=cmd_history)
# Commande stats
parser_stats = subparsers.add_parser('stats', help='Afficher les statistiques', parents=[common_args])
parser_stats.add_argument('--days', type=int, default=7, help='Nombre de jours (défaut: 7)')
parser_stats.set_defaults(func=cmd_stats)
args = parser.parse_args()
if not args.command:
parser.print_help()
sys.exit(1)
try:
config = Config()
client = NetatmoClient(config)
if args.debug:
import logging
logging.basicConfig(level=logging.DEBUG)
args.func(client, args)
except ValueError as e:
print(f"Erreur de configuration: {e}", file=sys.stderr)
if args.debug:
import traceback
traceback.print_exc()
sys.exit(1)
except Exception as e:
print(f"Erreur: {e}", file=sys.stderr)
if args.debug:
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == '__main__':
main()