Repository: bpicode/fritzctl
Pull request #138 (2018-03-03 16:30:47)

Issue #136: Support applying comfort/saving temperature to thermostats
Each device is provisioned with its respective value.
Execution terminates upon the first encountered error.



Patch:
diff --git a/cmd/temperature.go b/cmd/temperature.go
index 0d88e99..ead8602 100644
--- a/cmd/temperature.go
+++ b/cmd/temperature.go
@@ -1,21 +1,26 @@
 package cmd
 
 import (
+	"fmt"
 	"strconv"
 	"strings"
 
+	"github.com/bpicode/fritzctl/fritz"
 	"github.com/bpicode/fritzctl/logger"
 	"github.com/spf13/cobra"
 )
 
 var temperatureCmd = &cobra.Command{
-	Use:   "temperature [value in °C, on, off] [device/group names]",
+	Use:   "temperature [value in °C, on, off, sav, comf] [device/group names]",
 	Short: "Set the temperature of HKR devices/groups or turn them on/off",
 	Long: "Change the temperature of HKR devices/groups by supplying the desired value in °C. " +
-		"When turning HKR devices on/off, replace the value by 'on'/'off' respectively.",
+		"When turning HKR devices on/off, replace the value by 'on'/'off' respectively." +
+		"To reset each devices to its comfort/saving temperature, replace the value by 'comf'/'sav'.",
 	Example: `fritzctl temperature 21.0 HKR_1 HKR_2
 fritzctl temperature off HKR_1
 fritzctl temperature on HKR_2
+fritzctl temperature comf HK1 HKR_2
+fritzctl temperature sav HK1 HKR_2
 `,
 	RunE: changeTemperature,
 }
@@ -26,10 +31,49 @@ func init() {
 
 func changeTemperature(cmd *cobra.Command, args []string) error {
 	assertMinLen(args, 2, "insufficient input: at least two parameters expected.\n\n", cmd.UsageString())
-	temp, err := parseTemperature(args[0])
+	val := args[0]
+	if strings.EqualFold(val, "sav") || strings.EqualFold(val, "saving") {
+		return changeByCallback(func(t fritz.Thermostat) string {
+			return t.FmtSavingTemperature()
+		}, args[1:]...)
+	}
+	if strings.EqualFold(val, "comf") || strings.EqualFold(val, "comfort") {
+		return changeByCallback(func(t fritz.Thermostat) string {
+			return t.FmtComfortTemperature()
+		}, args[1:]...)
+	}
+	return changeByValue(nil, val, args[1:]...)
+}
+
+func changeByCallback(supplier func(t fritz.Thermostat) string, names ...string) error {
+	c := homeAutoClient(fritz.Caching(true))
+	devices, err := c.List()
+	assertNoErr(err, "cannot list available devices")
+	for _, name := range names {
+		device := deviceWithName(name, devices.Thermostats())
+		assertTrue(device != nil, fmt.Sprintf("device with name '%s' not found", name))
+		err = changeByValue(c, supplier(device.Thermostat), name)
+		assertNoErr(err, "error setting temperature of %s", name)
+	}
+	return nil
+}
+
+func deviceWithName(name string, list []fritz.Device) *fritz.Device {
+	for _, d := range list {
+		if d.Name == name {
+			return &d
+		}
+	}
+	return nil
+}
+
+func changeByValue(c fritz.HomeAuto, val string, names ...string) error {
+	temp, err := parseTemperature(val)
 	assertNoErr(err, "cannot parse temperature value")
-	c := homeAutoClient()
-	err = c.Temp(temp, args[1:]...)
+	if c == nil {
+		c = homeAutoClient()
+	}
+	err = c.Temp(temp, names...)
 	assertNoErr(err, "error setting temperature")
 	logger.Info("It may take a few minutes until the changes propagate to the end device(s)")
 	return nil


Test patch:
diff --git a/cmd/commands_test.go b/cmd/commands_test.go
index 93f386c..82ffff1 100644
--- a/cmd/commands_test.go
+++ b/cmd/commands_test.go
@@ -26,6 +26,8 @@ func TestCommands(t *testing.T) {
 		{cmd: versionCmd, srv: mock.New().UnstartedServer()},
 		{cmd: toggleCmd, args: []string{"SWITCH_3"}, srv: mock.New().UnstartedServer()},
 		{cmd: temperatureCmd, args: []string{"19.5", "HKR_1"}, srv: mock.New().UnstartedServer()},
+		{cmd: temperatureCmd, args: []string{"comf", "HKR_1"}, srv: mock.New().UnstartedServer()},
+		{cmd: temperatureCmd, args: []string{"sav", "HKR_1"}, srv: mock.New().UnstartedServer()},
 		{cmd: switchOnCmd, args: []string{"SWITCH_1"}, srv: mock.New().UnstartedServer()},
 		{cmd: switchOffCmd, args: []string{"SWITCH_2"}, srv: mock.New().UnstartedServer()},
 		{cmd: sessionIDCmd, srv: mock.New().UnstartedServer()},
diff --git a/cmd/temperature_test.go b/cmd/temperature_test.go
index a042747..32f2da0 100644
--- a/cmd/temperature_test.go
+++ b/cmd/temperature_test.go
@@ -3,6 +3,7 @@ package cmd
 import (
 	"testing"
 
+	"github.com/bpicode/fritzctl/fritz"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -22,3 +23,10 @@ func TestParseTemperature(t *testing.T) {
 	assertions.NoError(err)
 	assertions.Equal(float64(126.5), off)
 }
+
+// TestDeviceWithName test the device selection by name.
+func TestDeviceWithName(t *testing.T) {
+	assertions := assert.New(t)
+	assertions.Nil(deviceWithName("DEVICE", []fritz.Device{}))
+	assertions.NotNil(deviceWithName("DEVICE", []fritz.Device{{Name: "DEVICE"}}))
+}