213 lines
6.1 KiB
Python
213 lines
6.1 KiB
Python
""" 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()
|