-
-
Notifications
You must be signed in to change notification settings - Fork 36
/
eq3cli.py
212 lines (171 loc) · 6.07 KB
/
eq3cli.py
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
""" Cli tool for testing connectivity with EQ3 smart thermostats. """
import logging
import re
import click
from eq3bt import Thermostat
pass_dev = click.make_pass_decorator(Thermostat)
def validate_mac(ctx, param, mac):
if re.match("^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$", mac) is None:
raise click.BadParameter(mac + " is no valid mac address")
return mac
@click.group(invoke_without_command=True)
@click.option("--mac", envvar="EQ3_MAC", required=True, callback=validate_mac)
@click.option("--interface", default=None)
@click.option("--debug/--normal", default=False)
@click.option(
"--backend", type=click.Choice(["bleak", "bluepy", "gattlib"]), default="bleak"
)
@click.pass_context
def cli(ctx, mac, interface, debug, backend):
"""Tool to query and modify the state of EQ3 BT smart thermostat."""
if debug:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)
if backend == "bluepy":
from .connection import BTLEConnection
connection_cls = BTLEConnection
elif backend == "gattlib":
from .gattlibconnection import BTLEConnection
connection_cls = BTLEConnection
else:
from .bleakconnection import BleakConnection
connection_cls = BleakConnection
thermostat = Thermostat(mac, interface, connection_cls)
thermostat.update()
ctx.obj = thermostat
if ctx.invoked_subcommand is None:
ctx.invoke(state)
@cli.command()
@click.option("--target", type=float, required=False)
@pass_dev
def temp(dev, target):
"""Gets or sets the target temperature."""
click.echo("Current target temp: %s" % dev.target_temperature)
if target:
click.echo("Setting target temp: %s" % target)
dev.target_temperature = target
@cli.command()
@click.option("--target", type=int, required=False)
@pass_dev
def mode(dev, target):
"""Gets or sets the active mode."""
click.echo("Current mode: %s" % dev.mode_readable)
if target:
click.echo("Setting mode: %s" % target)
dev.mode = target
@cli.command()
@click.option("--target", type=bool, required=False)
@pass_dev
def boost(dev, target):
"""Gets or sets the boost mode."""
click.echo("Boost: %s" % dev.boost)
if target is not None:
click.echo("Setting boost: %s" % target)
dev.boost = target
@cli.command()
@pass_dev
def valve_state(dev):
"""Gets the state of the valve."""
click.echo("Valve: %s" % dev.valve_state)
@cli.command()
@click.option("--target", type=bool, required=False)
@pass_dev
def locked(dev, target):
"""Gets or sets the lock."""
click.echo("Locked: %s" % dev.locked)
if target is not None:
click.echo("Setting lock: %s" % target)
dev.locked = target
@cli.command()
@pass_dev
def low_battery(dev):
"""Gets the low battery status."""
click.echo("Batter low: %s" % dev.low_battery)
@cli.command()
@click.option("--temp", type=float, required=False)
@click.option("--duration", type=float, required=False)
@pass_dev
def window_open(dev, temp, duration):
"""Gets and sets the window open settings."""
click.echo("Window open: %s" % dev.window_open)
if dev.window_open_temperature is not None:
click.echo("Window open temp: %s" % dev.window_open_temperature)
if dev.window_open_time is not None:
click.echo("Window open time: %s" % dev.window_open_time)
if temp and duration:
click.echo(f"Setting window open conf, temp: {temp} duration: {duration}")
dev.window_open_config(temp, duration)
@cli.command()
@click.option("--comfort", type=float, required=False)
@click.option("--eco", type=float, required=False)
@pass_dev
def presets(dev, comfort, eco):
"""Sets the preset temperatures for auto mode."""
if dev.comfort_temperature is not None:
click.echo("Current comfort temp: %s" % dev.comfort_temperature)
if dev.eco_temperature is not None:
click.echo("Current eco temp: %s" % dev.eco_temperature)
if comfort and eco:
click.echo(f"Setting presets: comfort {comfort}, eco {eco}")
dev.temperature_presets(comfort, eco)
@cli.command()
@pass_dev
def schedule(dev):
"""Gets the schedule from the thermostat."""
# TODO: expose setting the schedule somehow?
for d in range(7):
dev.query_schedule(d)
for day in dev.schedule.values():
click.echo(f"Day {day.day}, base temp: {day.base_temp}")
current_hour = day.next_change_at
for hour in day.hours:
if current_hour == 0:
continue
click.echo(f"\t[{current_hour}-{hour.next_change_at}] {hour.target_temp}")
current_hour = hour.next_change_at
@cli.command()
@click.argument("offset", type=float, required=False)
@pass_dev
def offset(dev, offset):
"""Sets the temperature offset [-3,5 3,5]"""
if dev.temperature_offset is not None:
click.echo("Current temp offset: %s" % dev.temperature_offset)
if offset is not None:
click.echo("Setting the offset to %s" % offset)
dev.temperature_offset = offset
@cli.command()
@click.argument("away_end", type=click.DateTime(), default=None, required=False)
@click.argument("temperature", type=float, default=None, required=False)
@pass_dev
def away(dev, away_end, temperature):
"""Enables or disables the away mode."""
if away_end:
click.echo(f"Setting away until {away_end}, temperature: {temperature}")
else:
click.echo("Disabling away mode")
dev.set_away(away_end, temperature)
@cli.command()
@pass_dev
def device(dev):
"""Displays basic device information."""
dev.query_id()
click.echo("Firmware version: %s" % dev.firmware_version)
click.echo("Device serial: %s" % dev.device_serial)
@cli.command()
@click.pass_context
def state(ctx):
"""Prints out all available information."""
dev = ctx.obj
click.echo(dev)
ctx.forward(locked)
ctx.forward(low_battery)
ctx.forward(window_open)
ctx.forward(boost)
ctx.forward(temp)
ctx.forward(presets)
ctx.forward(offset)
ctx.forward(mode)
ctx.forward(valve_state)
if __name__ == "__main__":
cli()