Skip to content

Commit

Permalink
support setting & reading system output power from Home Assistant
Browse files Browse the repository at this point in the history
  • Loading branch information
mtrossbach committed Aug 5, 2024
1 parent e876b64 commit e3fc27e
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 26 deletions.
2 changes: 1 addition & 1 deletion internal/growatt/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func (h *Client) GetBatteryData(serialNumber string) (*BatteryInfo, error) {
}

func (h *Client) SetDefaultPower(serialNumber string, power float64) error {
p := math.Max(10, math.Min(800, power))
p := math.Max(0, math.Min(800, power))
var data map[string]any
if _, err := h.postForm(growattUrl+"/noahDeviceApi/noah/set", url.Values{
"serialNum": {serialNumber},
Expand Down
14 changes: 8 additions & 6 deletions internal/homeassistant/device_info.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package homeassistant

type DeviceInfo struct {
SerialNumber string
Model string
Version string
Alias string
StateTopic string
Batteries []BatteryInfo
SerialNumber string
Model string
Version string
Alias string
StateTopic string
ParameterStateTopic string
ParameterCommandTopic string
Batteries []BatteryInfo
}

type BatteryInfo struct {
Expand Down
22 changes: 22 additions & 0 deletions internal/homeassistant/discovery_base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package homeassistant

import "fmt"

func generateDevice(info DeviceInfo) Device {
return Device{
Identifiers: []string{fmt.Sprintf("noah_%s", info.SerialNumber)},
Name: info.Alias,
Manufacturer: "Growatt",
SwVersion: info.Version,
Model: info.Model,
SerialNumber: info.SerialNumber,
}
}

func generateOrigin(appVersion string) Origin {
return Origin{
Name: "noah-mqtt",
SwVersion: appVersion,
SupportUrl: "https://github.com/mtrossbach/noah-mqtt",
}
}
31 changes: 31 additions & 0 deletions internal/homeassistant/discovery_numbers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package homeassistant

import "fmt"

func generateNumberDiscoveryPayload(appVersion string, info DeviceInfo) []Number {
device := generateDevice(info)
origin := generateOrigin(appVersion)

numbers := []Number{
{
Name: "System Output Power",
UniqueId: fmt.Sprintf("%s_system_output_power", info.SerialNumber),
CommandTemplate: "{\"output_power_w\": {{ value }}}",
CommandTopic: info.ParameterCommandTopic,
Device: device,
Origin: origin,
Icon: "",
DeviceClass: DeviceClassPower,
StateTopic: info.ParameterStateTopic,
StateClass: StateClassMeasurement,
Mode: ModeSlider,
Step: 1,
Min: 0,
Max: 800,
UnitOfMeasurement: UnitWatt,
ValueTemplate: "{{ value_json.output_power_w }}",
},
}

return numbers
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,8 @@ package homeassistant
import "fmt"

func generateSensorDiscoveryPayload(appVersion string, info DeviceInfo) []Sensor {
device := Device{
Identifiers: []string{fmt.Sprintf("noah_%s", info.SerialNumber)},
Name: info.Alias,
Manufacturer: "Growatt",
SwVersion: info.Version,
Model: info.Model,
SerialNumber: info.SerialNumber,
}
origin := Origin{
Name: "noah-mqtt",
SwVersion: appVersion,
SupportUrl: "https://github.com/mtrossbach/noah-mqtt",
}
device := generateDevice(info)
origin := generateOrigin(appVersion)

sensors := []Sensor{
{
Expand Down
26 changes: 26 additions & 0 deletions internal/homeassistant/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,29 @@ type Origin struct {
SwVersion string `json:"sw_version,omitempty"`
SupportUrl string `json:"support_url,omitempty"`
}

type Number struct {
Name string `json:"name"`
UniqueId string `json:"unique_id,omitempty"`
CommandTemplate string `json:"command_template,omitempty"`
CommandTopic string `json:"command_topic"`
Device Device `json:"device,omitempty"`
Origin Origin `json:"origin,omitempty"`
Icon Icon `json:"icon,omitempty"`
DeviceClass DeviceClass `json:"device_class,omitempty"`
StateTopic string `json:"state_topic"`
StateClass StateClass `json:"state_class,omitempty"`
Mode Mode `json:"mode,omitempty"`
Step float64 `json:"step,omitempty"`
Min float64 `json:"min,omitempty"`
Max float64 `json:"max,omitempty"`
UnitOfMeasurement Unit `json:"unit_of_measurement,omitempty"`
ValueTemplate string `json:"value_template,omitempty"`
}

type Mode string

const (
ModeBox Mode = "box"
ModeSlider Mode = "slider"
)
15 changes: 15 additions & 0 deletions internal/homeassistant/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,24 @@ func (s *Service) sendDiscovery() {
s.options.MqttClient.Publish(topic, 0, false, string(b))
}
}

numbers := generateNumberDiscoveryPayload(s.options.Version, d)
for _, number := range numbers {
if b, err := json.Marshal(number); err != nil {
slog.Error("could not marshal number discovery payload", slog.Any("number", number))
} else {
topic := s.numberTopic(number)
s.options.MqttClient.Publish(topic, 0, false, string(b))
}
}
}
}

func (s *Service) sensorTopic(sensor Sensor) string {
return fmt.Sprintf("%s/sensor/%s/%s/config", s.options.TopicPrefix, fmt.Sprintf("noah_%s", sensor.Device.SerialNumber), strings.ReplaceAll(sensor.Name, " ", ""))
}

func (s *Service) numberTopic(number Number) string {
return fmt.Sprintf("%s/number/%s/%s/config", s.options.TopicPrefix, fmt.Sprintf("noah_%s", number.Device.SerialNumber), strings.ReplaceAll(number.Name, " ", ""))

}
14 changes: 8 additions & 6 deletions internal/polling/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@ func (s *Service) enumerateDevices() {
}

devices = append(devices, homeassistant.DeviceInfo{
SerialNumber: serialNumber,
Alias: data.Obj.Noah.Alias,
StateTopic: s.deviceStateTopic(serialNumber),
Batteries: batteries,
Model: data.Obj.Noah.Model,
Version: data.Obj.Noah.Version,
SerialNumber: serialNumber,
Model: data.Obj.Noah.Model,
Version: data.Obj.Noah.Version,
Alias: data.Obj.Noah.Alias,
StateTopic: s.deviceStateTopic(serialNumber),
ParameterStateTopic: s.parameterStateTopic(serialNumber),
ParameterCommandTopic: s.parameterCommandTopic(serialNumber),
Batteries: batteries,
})
}
}
Expand Down

0 comments on commit e3fc27e

Please sign in to comment.