Skip to content

Commit

Permalink
Add more statistics published to HomeAssistant
Browse files Browse the repository at this point in the history
  • Loading branch information
koesie10 committed Dec 12, 2021
1 parent ececad0 commit bb87443
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 22 deletions.
68 changes: 46 additions & 22 deletions mqtt/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,63 +7,87 @@ import (
"go.uber.org/zap"
)

type homeAssistantDiscovery struct {
p *publisher
Device *homeAssistantDevice
}

type homeAssistantDevice struct {
Identifiers []string `json:"identifiers,omitempty"`
Manufacturer string `json:"manufacturer,omitempty"`
Model string `json:"model,omitempty"`
Name string `json:"name,omitempty"`
}

type homeAssistantConfig struct {
type homeAssistantEntity struct {
InternalID string `json:"-"`

DeviceClass string `json:"device_class,omitempty"`
Name string `json:"name"`
StateTopic string `json:"state_topic"`
StateClass string `json:"state_class,omitempty"`
UnitOfMeasurement string `json:"unit_of_measurement,omitempty"`
ValueTemplate string `json:"value_template"`

UniqueID string `json:"unique_id,omitempty"`
Device homeAssistantDevice `json:"device"`
UniqueID string `json:"unique_id,omitempty"`
Device *homeAssistantDevice `json:"device"`
}

func (p *publisher) publishDiscovery() error {
if !p.options.HomeAssistant.DiscoveryEnabled {
return nil
}

device := homeAssistantDevice{
Identifiers: p.options.HomeAssistant.DeviceIdentifiers,
Manufacturer: p.options.HomeAssistant.DeviceManufacturer,
Model: p.options.HomeAssistant.DeviceModel,
Name: p.options.HomeAssistant.DeviceName,
discovery := homeAssistantDiscovery{
p: p,
Device: &homeAssistantDevice{
Identifiers: p.options.HomeAssistant.DeviceIdentifiers,
Manufacturer: p.options.HomeAssistant.DeviceManufacturer,
Model: p.options.HomeAssistant.DeviceModel,
Name: p.options.HomeAssistant.DeviceName,
},
}

config := homeAssistantConfig{
DeviceClass: "power",
Name: "Energy Consumption (tariff 1)",
StateTopic: p.options.Topic,
StateClass: "total_increasing",
UnitOfMeasurement: "kWh",
ValueTemplate: "{{ value_json.Electricity.Tariffs[0].Consumed }}",

UniqueID: fmt.Sprintf("%s%s", p.options.HomeAssistant.UniqueIDPrefix, "tarrif1_consumed"),
Device: device,
entities := discovery.configureEntities()
for _, entity := range entities {
if err := discovery.publishEntity(entity); err != nil {
p.logger.With(zap.Error(err)).Warnf("Failed to publish entity %s", entity.InternalID)
}
}

topic := fmt.Sprintf("%s/sensor/%s%s/config", p.options.HomeAssistant.DiscoveryPrefix, p.options.HomeAssistant.DevicePrefix, "tarrif1_consumed")
return nil
}

func (d *homeAssistantDiscovery) publishEntity(entity *homeAssistantEntity) error {
topic := fmt.Sprintf(
"%s/sensor/%s%s/config",
d.p.options.HomeAssistant.DiscoveryPrefix,
d.p.options.HomeAssistant.DevicePrefix,
entity.InternalID,
)

data, err := json.Marshal(config)
data, err := json.Marshal(entity)
if err != nil {
return fmt.Errorf("failed to marshal config to JSON: %w", err)
}

token := p.client.Publish(topic, byte(p.options.HomeAssistant.DiscoveryQoS), true, string(data))
token := d.p.client.Publish(topic, byte(d.p.options.HomeAssistant.DiscoveryQoS), true, string(data))
go func(topic string) {
token.Wait()
if err := token.Error(); err != nil {
p.logger.With(zap.Error(err)).Warnf("Failed to publish config %s to MQTT", topic)
d.p.logger.With(zap.Error(err)).Warnf("Failed to publish config %s to MQTT", topic)
}
}(topic)

return nil
}

func (d *homeAssistantDiscovery) configureEntity(id string, config *homeAssistantEntity) *homeAssistantEntity {
config.InternalID = id

config.StateTopic = d.p.options.Topic
config.UniqueID = fmt.Sprintf("%s%s", d.p.options.HomeAssistant.UniqueIDPrefix, id)
config.Device = d.Device

return config
}
94 changes: 94 additions & 0 deletions mqtt/entities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package mqtt

import "fmt"

// https://developers.home-assistant.io/docs/core/entity/sensor/#long-term-statistics
func (d *homeAssistantDiscovery) configureEntities() []*homeAssistantEntity {
var result []*homeAssistantEntity

result = append(result,
d.configureEntity("electricity_equipment_id", &homeAssistantEntity{
Name: "Electricity Equipment ID",
ValueTemplate: "{{ value_json.Electricity.EquipmentID }}",
}),
)

for tarrif := 0; tarrif < 2; tarrif++ {
result = append(
result,
d.configureEntity(fmt.Sprintf("tarrif%d_consumed", tarrif+1), &homeAssistantEntity{
DeviceClass: "energy",
Name: fmt.Sprintf("Energy Consumption (tariff %d)", tarrif+1),
StateClass: "total",
UnitOfMeasurement: "kWh",
ValueTemplate: fmt.Sprintf("{{ value_json.Electricity.Tariffs[%d].Consumed }}", tarrif),
}),
d.configureEntity(fmt.Sprintf("tarrif%d_produced", tarrif+1), &homeAssistantEntity{
DeviceClass: "energy",
Name: fmt.Sprintf("Energy Production (tariff %d)", tarrif+1),
StateClass: "total",
UnitOfMeasurement: "kWh",
ValueTemplate: fmt.Sprintf("{{ value_json.Electricity.Tariffs[%d].Produced }}", tarrif),
}),
)
}

result = append(result,
d.configureEntity("tarrif", &homeAssistantEntity{
Name: "Energy Tarrif",
ValueTemplate: "{{ value_json.Electricity.Tarrif }}",
}),

d.configureEntity("current_consumption", &homeAssistantEntity{
DeviceClass: "power",
Name: "Energy Consumption",
StateClass: "measurement",
UnitOfMeasurement: "kW",
ValueTemplate: "{{ value_json.Electricity.CurrentConsumed }}",
}),
d.configureEntity("current_production", &homeAssistantEntity{
DeviceClass: "power",
Name: "Energy Production",
StateClass: "measurement",
UnitOfMeasurement: "kW",
ValueTemplate: "{{ value_json.Electricity.CurrentProduced }}",
}),
)

for phase := 0; phase < 3; phase++ {
result = append(
result,
d.configureEntity(fmt.Sprintf("phase%d_instantaneous_voltage", phase+1), &homeAssistantEntity{
DeviceClass: "voltage",
Name: fmt.Sprintf("Instantaneous voltage (phase %d)", phase+1),
StateClass: "measurement",
UnitOfMeasurement: "V",
ValueTemplate: fmt.Sprintf("{{ value_json.Electricity.Phases[%d].InstantaneousVoltage }}", phase),
}),
d.configureEntity(fmt.Sprintf("phase%d_instantaneous_current", phase+1), &homeAssistantEntity{
DeviceClass: "current",
Name: fmt.Sprintf("Instantaneous current (phase %d)", phase+1),
StateClass: "measurement",
UnitOfMeasurement: "A",
ValueTemplate: fmt.Sprintf("{{ value_json.Electricity.Phases[%d].InstantaneousCurrent }}", phase),
}),
)
}

result = append(result,
d.configureEntity("gas_equipment_id", &homeAssistantEntity{
Name: "Gas Equipment ID",
ValueTemplate: "{{ value_json.Gas.EquipmentID }}",
}),

d.configureEntity("gas_consumed", &homeAssistantEntity{
DeviceClass: "gas",
Name: "Gas Consumed",
StateClass: "total",
UnitOfMeasurement: "m³",
ValueTemplate: "{{ value_json.Gas.Consumed }}",
}),
)

return result
}

0 comments on commit bb87443

Please sign in to comment.