fritz/ain_based.go
package fritz
import (
"fmt"
"github.com/bpicode/fritzctl/httpread"
)
// ainBased API definition, guided by
// https://avm.de/fileadmin/user_upload/Global/Service/Schnittstellen/AHA-HTTP-Interface.pdf.
type ainBased interface {
listDevices() (*Devicelist, error)
switchOn(ain string) (string, error)
switchOff(ain string) (string, error)
toggle(ain string) (string, error)
applyTemperature(value float64, ain string) (string, error)
}
// newAinBased creates a Fritz AHA API (working on AINs) from a given client.
func newAinBased(client *Client) ainBased {
return &ainBasedClient{client: client}
}
type ainBasedClient struct {
client *Client
}
// listDevices lists the basic data of the smart home devices.
func (a *ainBasedClient) listDevices() (*Devicelist, error) {
url := a.homeAutoSwitch().
query("switchcmd", "getdevicelistinfos").
build()
var deviceList Devicelist
errRead := httpread.XML(a.client.getf(url), &deviceList)
return &deviceList, errRead
}
// switchOn switches a device on. The device is identified by its AIN.
func (a *ainBasedClient) switchOn(ain string) (string, error) {
return a.switchForAin(ain, "setswitchon")
}
// switchOff switches a device off. The device is identified by its AIN.
func (a *ainBasedClient) switchOff(ain string) (string, error) {
return a.switchForAin(ain, "setswitchoff")
}
// toggle toggles the on/off state of a device. The device is identified by its AIN.
func (a *ainBasedClient) toggle(ain string) (string, error) {
url := a.homeAutoSwitch().
query("ain", ain).
query("switchcmd", "setswitchtoggle").
build()
return httpread.String(a.client.getf(url))
}
// applyTemperature sets the desired temperature on a "HKR" device. The device is identified by its AIN.
func (a *ainBasedClient) applyTemperature(value float64, ain string) (string, error) {
param, err := temperatureParam(value)
if err != nil {
return "", err
}
url := a.homeAutoSwitch().
query("ain", ain).
query("switchcmd", "sethkrtsoll").
query("param", fmt.Sprintf("%d", param)).
build()
return httpread.String(a.client.getf(url))
}
func (a *ainBasedClient) switchForAin(ain, command string) (string, error) {
url := a.homeAutoSwitch().
query("ain", ain).
query("switchcmd", command).
build()
return httpread.String(a.client.getf(url))
}
func (a *ainBasedClient) homeAutoSwitch() fritzURLBuilder {
return a.client.query().path(homeAutomationURI)
}
func temperatureParam(t float64) (int64, error) {
doubled := round(2 * t)
regular := doubled >= 16 && doubled <= 56
special := doubled == 253 || doubled == 254
if !(regular || special) {
return 0, fmt.Errorf("invalid temperature value: %.1f°C is not contained in the set of acceptable values: 8-28°C, 126.5, 127", t)
}
return doubled, nil
}
// round rounds a float64 value to an integer.
func round(v float64) int64 {
return int64(v + 0.5)
}