diff --git a/meter/homematic/connection.go b/meter/homematic/connection.go index 691789dc08..8ff02d2018 100644 --- a/meter/homematic/connection.go +++ b/meter/homematic/connection.go @@ -4,6 +4,7 @@ import ( "encoding/xml" "fmt" "net/http" + "regexp" "strings" "time" @@ -82,55 +83,37 @@ func (c *Connection) Enable(enable bool) error { // Enabled reads the homematic HMIP-PSM switchchannel state true=on/false=off func (c *Connection) Enabled() (bool, error) { res, err := c.switchCache.Get() - if err != nil { - return false, err - } - return getBoolValue(res, "STATE"), nil + return res.BoolValue("STATE"), err } // CurrentPower reads the homematic HMIP-PSM meterchannel power in W func (c *Connection) CurrentPower() (float64, error) { res, err := c.meterCache.Get() - if err != nil { - return 0, err - } - return getFloatValue(res, "POWER"), nil + return res.FloatValue("POWER"), err } // TotalEnergy reads the homematic HMIP-PSM meterchannel energy in Wh func (c *Connection) TotalEnergy() (float64, error) { res, err := c.meterCache.Get() - if err != nil { - return 0, err - } - return getFloatValue(res, "ENERGY_COUNTER") / 1e3, nil + return res.FloatValue("ENERGY_COUNTER") / 1e3, err } // Currents reads the homematic HMIP-PSM meterchannel L1 current in A func (c *Connection) Currents() (float64, float64, float64, error) { res, err := c.meterCache.Get() - if err != nil { - return 0, 0, 0, err - } - return getFloatValue(res, "CURRENT") / 1e3, 0, 0, nil + return res.FloatValue("CURRENT") / 1e3, 0, 0, err } // GridCurrentPower reads the homematic HM-ES-TX-WM grid meterchannel power in W func (c *Connection) GridCurrentPower() (float64, error) { res, err := c.meterCache.Get() - if err != nil { - return 0, err - } - return getFloatValue(res, "IEC_POWER"), nil + return res.FloatValue("IEC_POWER"), err } // GridTotalEnergy reads the homematic HM-ES-TX-WM grid meterchannel energy in kWh func (c *Connection) GridTotalEnergy() (float64, error) { res, err := c.meterCache.Get() - if err != nil { - return 0, err - } - return getFloatValue(res, "IEC_ENERGY_COUNTER"), nil + return res.FloatValue("IEC_ENERGY_COUNTER"), err } func (c *Connection) XmlCmd(method, channel string, values ...Param) (MethodResponse, error) { @@ -159,64 +142,13 @@ func (c *Connection) XmlCmd(method, channel string, values ...Param) (MethodResp return hmr, err } - // correct Homematic IP Legacy API (CCU port 2010) method response encoding value - res = []byte(strings.Replace(string(res), "ISO-8859-1", "UTF-8", 1)) - - // correct XML-RPC-Schnittstelle (CCU port 2001) method response encoding value - res = []byte(strings.Replace(string(res), "iso-8859-1", "UTF-8", 1)) + // correct Homematic IP Legacy API (CCU port 2010) and XML-RPC-Schnittstelle (CCU port 2001) response encoding + re := regexp.MustCompile("(?i)iso-8859-1") + res = re.ReplaceAll(res, []byte("UTF-8")) if err := xml.Unmarshal(res, &hmr); err != nil { return hmr, err } - return hmr, parseError(hmr) -} - -// getCCUFloat selects a float value of a CCU API response member -func getFloatValue(res MethodResponse, valueName string) float64 { - for _, m := range res.Member { - if m.Name == valueName { - return m.Value.CCUFloat - } - } - - return 0 -} - -// getCCUBool selects a float value of a CCU API response member -func getBoolValue(res MethodResponse, valueName string) bool { - for _, m := range res.Member { - if m.Name == valueName { - return m.Value.CCUBool - } - } - - return false -} - -// parseError checks on Homematic CCU error codes -// Refer to page 30 of https://homematic-ip.com/sites/default/files/downloads/HM_XmlRpc_API.pdf -func parseError(res MethodResponse) error { - var faultCode int64 - var faultString string - - faultCode = 0 - for _, f := range res.Fault { - if f.Name == "faultCode" { - faultCode = f.Value.CCUInt - } - if f.Name == "faultString" { - faultString = f.Value.CCUString - } - } - - if faultString == "" { - faultString = "Unknown Homematic API Error" - } - - if faultCode != 0 { - return fmt.Errorf("%s (%v)", faultString, faultCode) - } - - return nil + return hmr, hmr.Error() } diff --git a/meter/homematic/types.go b/meter/homematic/types.go index 13464c307d..dc5a3b07c5 100644 --- a/meter/homematic/types.go +++ b/meter/homematic/types.go @@ -2,6 +2,7 @@ package homematic import ( "encoding/xml" + "fmt" ) // Homematic CCU XML-RPC types @@ -34,6 +35,55 @@ type MethodResponse struct { Member []Member `xml:"params>param>value>struct>member,omitempty"` Fault []Member `xml:"fault>value>struct>member,omitempty"` } + +// FloatValue selects a float value of a CCU API response member +func (res *MethodResponse) FloatValue(val string) float64 { + for _, m := range res.Member { + if m.Name == val { + return m.Value.CCUFloat + } + } + + return 0 +} + +// BoolValue selects a float value of a CCU API response member +func (res *MethodResponse) BoolValue(val string) bool { + for _, m := range res.Member { + if m.Name == val { + return m.Value.CCUBool + } + } + + return false +} + +// Error checks on Homematic CCU error codes +// Refer to page 30 of https://homematic-ip.com/sites/default/files/downloads/HM_XmlRpc_API.pdf +func (res *MethodResponse) Error() error { + var faultCode int64 + var faultString string + + for _, f := range res.Fault { + if f.Name == "faultCode" { + faultCode = f.Value.CCUInt + } + if f.Name == "faultString" { + faultString = f.Value.CCUString + } + } + + if faultCode != 0 { + if faultString == "" { + faultString = "unknown api error" + } + + return fmt.Errorf("%s (%v)", faultString, faultCode) + } + + return nil +} + type Value struct { XMLName xml.Name `xml:"value"` CCUString string `xml:",chardata"`