v2.2.0 MQTT changes

This commit is contained in:
MickMake 2022-04-21 10:47:05 +10:00
parent a639d2948f
commit d57a1537db
20 changed files with 2067 additions and 745 deletions

114
.idea/workspace.xml generated
View File

@ -5,15 +5,21 @@
</component>
<component name="ChangeListManager">
<list default="true" id="76adadc9-ae71-42a6-82a1-66dbc8ecb14c" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/GoSungrow.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/GoSungrow.iml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/HA/CHANGELOG.md" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/HA/Dockerfile" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/HA/GoSungrow" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/HA/build.yaml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/HA/config.yaml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/HA/src/GoSungrow" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/HA/src/run.sh" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/cmd/cmd_mqtt.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/cmd_mqtt.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/cmd/struct.go" beforeDir="false" afterPath="$PROJECT_DIR$/cmd/struct.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/iSolarCloud/AppService/getPsDetailWithPsType/data.go" beforeDir="false" afterPath="$PROJECT_DIR$/iSolarCloud/AppService/getPsDetailWithPsType/data.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/iSolarCloud/AppService/getPsList/data.go" beforeDir="false" afterPath="$PROJECT_DIR$/iSolarCloud/AppService/getPsList/data.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/iSolarCloud/AppService/queryDeviceList/data.go" beforeDir="false" afterPath="$PROJECT_DIR$/iSolarCloud/AppService/queryDeviceList/data.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/iSolarCloud/api/struct_points.go" beforeDir="false" afterPath="$PROJECT_DIR$/iSolarCloud/api/struct_points.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/iSolarCloud/highlevel.go" beforeDir="false" afterPath="$PROJECT_DIR$/iSolarCloud/highlevel.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/mmMqtt/config.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/mmMqtt/cron.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/mmMqtt/funcs.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/mmMqtt/lights.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/mmMqtt/sensors.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/mmMqtt/struct.go" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/mmMqtt/switch.go" beforeDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -79,7 +85,7 @@
<configuration name="GoSungrow" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="GoSungrow" />
<working_directory value="$PROJECT_DIR$" />
<parameters value="data get template 8092 20220301" />
<parameters value="mqtt run" />
<kind value="PACKAGE" />
<package value="$PROJECT_DIR$" />
<directory value="$PROJECT_DIR$" />
@ -87,6 +93,23 @@
<output_directory value="$PROJECT_DIR$/bin" />
<method v="2" />
</configuration>
<configuration default="true" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="GoSungrow" />
<working_directory value="$PROJECT_DIR$" />
<kind value="FILE" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
<configuration default="true" type="GoTestRunConfiguration" factoryName="Go Test">
<module name="GoSungrow" />
<working_directory value="$PROJECT_DIR$" />
<kind value="DIRECTORY" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<framework value="gotest" />
<method v="2" />
</configuration>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TypeScriptGeneratedFilesManager">
@ -165,12 +188,12 @@
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/cmd/cmd_mqtt.go</url>
<line>207</line>
<line>232</line>
<option name="timeStamp" value="437" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/cmd/cmd_mqtt.go</url>
<line>206</line>
<line>231</line>
<option name="timeStamp" value="446" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
@ -190,27 +213,12 @@
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/iSolarCloud/api/struct_points.go</url>
<line>288</line>
<line>289</line>
<option name="timeStamp" value="523" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/iSolarCloud/api/struct_points.go</url>
<line>309</line>
<option name="timeStamp" value="529" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/cmd/cmd_mqtt.go</url>
<line>138</line>
<option name="timeStamp" value="562" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/cmd/cmd_mqtt.go</url>
<line>234</line>
<option name="timeStamp" value="564" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/iSolarCloud/highlevel.go</url>
<line>492</line>
<line>518</line>
<option name="timeStamp" value="570" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
@ -223,16 +231,6 @@
<line>136</line>
<option name="timeStamp" value="581" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/iSolarCloud/AppService/queryDeviceList/data.go</url>
<line>485</line>
<option name="timeStamp" value="586" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/iSolarCloud/api/web.go</url>
<line>58</line>
<option name="timeStamp" value="589" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/iSolarCloud/highlevel.go</url>
<line>79</line>
@ -243,6 +241,46 @@
<line>75</line>
<option name="timeStamp" value="593" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/iSolarCloud/highlevel.go</url>
<line>658</line>
<option name="timeStamp" value="619" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/mmHa/struct.go</url>
<line>395</line>
<option name="timeStamp" value="622" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/cmd/cmd_mqtt.go</url>
<line>263</line>
<option name="timeStamp" value="623" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/cmd/cmd_mqtt.go</url>
<line>268</line>
<option name="timeStamp" value="624" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/cmd/cmd_mqtt.go</url>
<line>269</line>
<option name="timeStamp" value="625" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/cmd/cmd_mqtt.go</url>
<line>264</line>
<option name="timeStamp" value="660" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/cmd/cmd_mqtt.go</url>
<line>273</line>
<option name="timeStamp" value="668" />
</line-breakpoint>
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
<url>file://$PROJECT_DIR$/cmd/cmd_mqtt.go</url>
<line>295</line>
<option name="timeStamp" value="670" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
<watches-manager>

View File

@ -2,11 +2,12 @@ package cmd
import (
"GoSungrow/Only"
"GoSungrow/mmMqtt"
"GoSungrow/mmHa"
"errors"
"fmt"
"github.com/go-co-op/gocron"
"github.com/spf13/cobra"
"strconv"
"strings"
"time"
)
@ -70,8 +71,33 @@ func (ca *CommandArgs) MqttArgs(cmd *cobra.Command, args []string) error {
break
}
LogPrintDate("Connecting to SunGrow...\n")
Cmd.Error = Cmd.SunGrowArgs(cmd, args)
if Cmd.Error != nil {
break
}
var id int64
id, Cmd.Error = Cmd.SunGrow.GetPsId()
if Cmd.Error != nil {
break
}
var model string
model, Cmd.Error = Cmd.SunGrow.GetPsModel()
if Cmd.Error != nil {
break
}
var serial string
serial, Cmd.Error = Cmd.SunGrow.GetPsSerial()
if Cmd.Error != nil {
break
}
LogPrintDate("Found SunGrow device %s id:%d serial:%s\n", model, id, serial)
LogPrintDate("Connecting to MQTT HASSIO Service...\n")
Cmd.Mqtt = mmMqtt.New(mmMqtt.Mqtt {
Cmd.Mqtt = mmHa.New(mmHa.Mqtt {
ClientId: "GoSunGrow",
Username: Cmd.MqttUsername,
Password: Cmd.MqttPassword,
@ -83,24 +109,23 @@ func (ca *CommandArgs) MqttArgs(cmd *cobra.Command, args []string) error {
break
}
Cmd.Error = Cmd.Mqtt.SetDeviceConfig("GoSunGrow", strconv.FormatInt(id, 10), "GoSungrow", model, "Sungrow", "Roof")
if Cmd.Error != nil {
break
}
Cmd.Error = Cmd.Mqtt.Connect()
if Cmd.Error != nil {
break
}
LogPrintDate("Connecting to SunGrow...\n")
Cmd.Error = Cmd.SunGrowArgs(cmd, args)
if Cmd.Error != nil {
break
}
if Cmd.Mqtt.PsId == 0 {
Cmd.Mqtt.PsId, Cmd.Error = Cmd.SunGrow.GetPsId()
if Cmd.Error != nil {
break
}
LogPrintDate("Found SunGrow device %d\n", Cmd.Mqtt.PsId)
}
// if Cmd.Mqtt.PsId == 0 {
// Cmd.Mqtt.PsId, Cmd.Error = Cmd.SunGrow.GetPsId()
// if Cmd.Error != nil {
// break
// }
// LogPrintDate("Found SunGrow device %d\n", Cmd.Mqtt.PsId)
// }
}
return Cmd.Error
@ -231,21 +256,64 @@ func MqttCron() error {
time.Sleep(time.Second * 40) // Takes up to 40 seconds for data to come in.
}
newDay := false
if Cmd.Mqtt.IsNewDay() {
newDay = true
}
Cmd.Error = Update1(newDay)
if Cmd.Error != nil {
break
}
Cmd.Error = Update2(newDay)
if Cmd.Error != nil {
break
}
Cmd.Mqtt.LastRefresh = time.Now()
}
if Cmd.Error != nil {
LogPrintDate("Error: %s\n", Cmd.Error)
}
return Cmd.Error
}
func Update1(newDay bool) error {
for range Only.Once {
// Also getPowerStatistics, getHouseholdStoragePsReport, getPsList, getUpTimePoint,
ep := Cmd.SunGrow.QueryDevice(Cmd.Mqtt.PsId)
if ep.IsError() {
Cmd.Error = ep.GetError()
break
}
data := ep.GetData()
if Cmd.Mqtt.IsNewDay() {
if newDay {
LogPrintDate("New day: Configuring %d entries in HASSIO.\n", len(data.Entries))
for _, r := range data.Entries {
fmt.Printf(".")
// Cmd.Error = Cmd.Mqtt.SensorPublishConfig(r.PointId, r.PointName, r.Unit, i)
Cmd.Error = Cmd.Mqtt.SensorPublishConfig(r)
fmt.Printf("C")
re := mmHa.EntityConfig {
Type: r.ValueType.Type,
Name: r.ValueType.Id, // PointName,
SubName: "",
ParentId: r.ValueType.PsKey,
ParentName: "",
UniqueId: r.PointId,
FullName: r.ValueType.Description,
Units: r.Unit,
ValueName: r.PointId,
Class: "",
Value: r.Value,
}
Cmd.Error = Cmd.Mqtt.BinarySensorPublishConfig(re)
if Cmd.Error != nil {
break
}
Cmd.Error = Cmd.Mqtt.SensorPublishConfig(re)
if Cmd.Error != nil {
break
}
@ -255,20 +323,111 @@ func MqttCron() error {
LogPrintDate("Updating %d entries to HASSIO.\n", len(data.Entries))
for _, r := range data.Entries {
fmt.Printf(".")
// Cmd.Error = Cmd.Mqtt.SensorPublishState(r.PointId, r.Value)
Cmd.Error = Cmd.Mqtt.SensorPublishValue(r)
fmt.Printf("U")
re := mmHa.EntityConfig {
Type: r.ValueType.Type,
Name: r.ValueType.Id, // PointName,
SubName: "",
ParentId: r.ValueType.PsKey,
ParentName: "",
UniqueId: r.PointId,
FullName: r.ValueType.Description,
Units: r.Unit,
ValueName: r.PointId,
Class: "",
Value: r.Value,
}
Cmd.Error = Cmd.Mqtt.BinarySensorPublishValue(re)
if Cmd.Error != nil {
break
}
Cmd.Error = Cmd.Mqtt.SensorPublishValue(re)
if Cmd.Error != nil {
break
}
}
fmt.Println()
}
Cmd.Mqtt.LastRefresh = time.Now()
if Cmd.Error != nil {
LogPrintDate("Error: %s\n", Cmd.Error)
}
return Cmd.Error
}
if Cmd.Error != nil {
func Update2(newDay bool) error {
for range Only.Once {
// Also getPowerStatistics, getHouseholdStoragePsReport, getPsList, getUpTimePoint,
ep := Cmd.SunGrow.QueryPs(Cmd.Mqtt.PsId)
if ep.IsError() {
Cmd.Error = ep.GetError()
break
}
data := ep.GetData()
if newDay {
LogPrintDate("New day: Configuring %d entries in HASSIO.\n", len(data.Entries))
for _, r := range data.Entries {
fmt.Printf("C")
re := mmHa.EntityConfig {
Type: r.ValueType.Type,
Name: r.ValueType.Id, // PointName,
SubName: "",
ParentId: r.ValueType.PsKey,
ParentName: "",
UniqueId: r.PointId,
FullName: r.ValueType.Description,
Units: r.Unit,
ValueName: r.PointId,
Class: "",
Value: r.Value,
}
Cmd.Error = Cmd.Mqtt.BinarySensorPublishConfig(re)
if Cmd.Error != nil {
break
}
Cmd.Error = Cmd.Mqtt.SensorPublishConfig(re)
if Cmd.Error != nil {
break
}
}
fmt.Println()
}
LogPrintDate("Updating %d entries to HASSIO.\n", len(data.Entries))
for _, r := range data.Entries {
fmt.Printf("U")
re := mmHa.EntityConfig {
Type: r.ValueType.Type,
Name: r.ValueType.Id, // PointName,
SubName: "",
ParentId: r.ValueType.PsKey,
ParentName: "",
UniqueId: r.PointId,
FullName: r.ValueType.Description,
Units: r.Unit,
ValueName: r.PointId,
Class: "",
Value: r.Value,
}
Cmd.Error = Cmd.Mqtt.BinarySensorPublishValue(re)
if Cmd.Error != nil {
break
}
Cmd.Error = Cmd.Mqtt.SensorPublishValue(re)
if Cmd.Error != nil {
break
}
}
fmt.Println()
}
if Cmd.Error != nil {

View File

@ -6,7 +6,7 @@ import (
"GoSungrow/iSolarCloud/AppService/login"
"GoSungrow/lsgo"
"GoSungrow/mmGit"
"GoSungrow/mmMqtt"
"GoSungrow/mmHa"
"errors"
"fmt"
"github.com/spf13/cobra"
@ -21,7 +21,7 @@ var DefaultAreas = []string{"all"}
type CommandArgs struct {
SunGrow *iSolarCloud.SunGrow
Git *mmGit.Git
Mqtt *mmMqtt.Mqtt
Mqtt *mmHa.Mqtt
ConfigDir string
CacheDir string

View File

@ -192,7 +192,7 @@ func (e *EndPoint) GetDataTable() output.Table {
keys := api.GetStructKeys(e.Response.ResultData)
for _, n := range keys.Sort() {
p := api.GetPoint(e.Response.ResultData.PsPsKey, n)
if p != nil {
if p.Valid {
_ = table.AddRow(
now,
api.NameDevicePoint(e.Response.ResultData.PsPsKey, n),
@ -222,7 +222,7 @@ func (e *EndPoint) GetDataTable() output.Table {
keys = api.GetStructKeys(sid)
for _, n := range keys.Sort() {
p := api.GetPoint(sid.PsKey, n)
if p != nil {
if p.Valid {
_ = table.AddRow(
now,
api.NameDevicePoint(sid.PsKey, n),
@ -304,6 +304,45 @@ func (e *EndPoint) GetPsKeys() []string {
return ret
}
func (e *EndPoint) GetPsName() string {
return e.Response.ResultData.PsName
}
func (e *EndPoint) GetPsState() string {
return e.Response.ResultData.PsState
}
func (e *EndPoint) GetPsKey() string {
return e.Response.ResultData.PsPsKey
}
func (e *EndPoint) GetDeviceModelCode() string {
ret := e.Response.ResultData.PsPsKey
for _, l := range e.Response.ResultData.StorageInverterData {
ret = l.DeviceModelCode
break
}
return ret
}
func (e *EndPoint) GetDeviceName() string {
ret := e.Response.ResultData.PsPsKey
for _, l := range e.Response.ResultData.StorageInverterData {
ret = l.DeviceName
break
}
return ret
}
func (e *EndPoint) GetDeviceSerial() string {
ret := e.Response.ResultData.PsPsKey
for _, l := range e.Response.ResultData.StorageInverterData {
ret = l.InverterSn
break
}
return ret
}
// ChargingDischargingPowerMap
// Co2ReduceTotal
// CoalReduceTotal

View File

@ -6,6 +6,7 @@ import (
"GoSungrow/iSolarCloud/api/apiReflect"
"GoSungrow/iSolarCloud/api/output"
"fmt"
"strconv"
"time"
)
@ -200,44 +201,239 @@ func (e *ResultData) GetPsId() int64 {
return ret
}
func (e *EndPoint) GetPsId() int64 {
return e.Response.ResultData.GetPsId()
}
func (e *ResultData) GetData() [][]string {
var ret [][]string
func (e *ResultData) GetPsName() string {
var ret string
for range Only.Once {
i := len(e.PageList)
if i == 0 {
break
}
now := time.Now().Round(5 * time.Minute).Format(api.DtLayoutZeroSeconds)
for _, p := range e.PageList {
ret = append(ret, []string{now, "Co2 Reduce", p.Co2Reduce.Value, p.Co2Reduce.Unit})
ret = append(ret, []string{now, "Co2 Reduce Total", p.Co2ReduceTotal.Value, p.Co2ReduceTotal.Unit})
ret = append(ret, []string{now, "Curr Power", p.CurrPower.Value, p.CurrPower.Unit})
ret = append(ret, []string{now, "Daily Irradiation", p.DailyIrradiation.Value, p.DailyIrradiation.Unit})
ret = append(ret, []string{now, "Equivalent Hour", p.EquivalentHour.Value, p.EquivalentHour.Unit})
ret = append(ret, []string{now, "Es Discharge Energy", p.EsDisenergy.Value, p.EsDisenergy.Unit})
ret = append(ret, []string{now, "Es Energy", p.EsEnergy.Value, p.EsEnergy.Unit})
ret = append(ret, []string{now, "Es Power", p.EsPower.Value, p.EsPower.Unit})
ret = append(ret, []string{now, "Es Total Discharge Energy", p.EsTotalDisenergy.Value, p.EsTotalDisenergy.Unit})
ret = append(ret, []string{now, "Es Total Energy", p.EsTotalEnergy.Value, p.EsTotalEnergy.Unit})
ret = append(ret, []string{now, "Installed Power Map", p.InstalledPowerMap.Value, p.InstalledPowerMap.Unit})
ret = append(ret, []string{now, "Pv Energy", p.PvEnergy.Value, p.PvEnergy.Unit})
ret = append(ret, []string{now, "Pv Power", p.PvPower.Value, p.PvPower.Unit})
ret = append(ret, []string{now, "Radiation", p.Radiation.Value, p.Radiation.Unit})
ret = append(ret, []string{now, "Today Energy", p.TodayEnergy.Value, p.TodayEnergy.Unit})
ret = append(ret, []string{now, "Today Income", p.TodayIncome.Value, p.TodayIncome.Unit})
ret = append(ret, []string{now, "Total Capacity", p.TotalCapcity.Value, p.TotalCapcity.Unit})
ret = append(ret, []string{now, "Total Energy", p.TotalEnergy.Value, p.TotalEnergy.Unit})
ret = append(ret, []string{now, "Total Income", p.TotalIncome.Value, p.TotalIncome.Unit})
ret = append(ret, []string{now, "Use Energy", p.UseEnergy.Value, p.UseEnergy.Unit})
if p.PsID != 0 {
ret = p.PsName
break
}
}
}
return ret
}
func (e *ResultData) GetPsSerial() string {
var ret string
for range Only.Once {
i := len(e.PageList)
if i == 0 {
break
}
for _, p := range e.PageList {
if p.PsID != 0 {
ret = p.PsShortName
break
}
}
}
return ret
}
func (e *EndPoint) GetPsId() int64 {
return e.Response.ResultData.GetPsId()
}
func (e *EndPoint) GetData() api.Data {
return e.Response.ResultData.GetData()
}
func (e *ResultData) GetData() api.Data {
var ret api.Data
for range Only.Once {
i := len(e.PageList)
if i == 0 {
break
}
now := api.NewDateTime(time.Now().Round(5 * time.Minute).Format(api.DtLayoutZeroSeconds))
for _, p := range e.PageList {
psId := strconv.FormatInt(p.PsID, 10)
ret.Entries = append(ret.Entries, add(now, psId, "p83077", "Pv Energy", p.PvEnergy, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "p83089", "Es Discharge Energy", p.EsDisenergy, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "p83095", "Es Total Discharge Energy", p.EsTotalDisenergy, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "p83118", "Use Energy", p.UseEnergy, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "p83120", "Es Energy", p.EsEnergy, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "p83127", "Es Total Energy", p.EsTotalEnergy, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "p83076", "Pv Power", p.PvPower, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "p83081", "Es Power", p.EsPower, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "co2_reduce", "Co2 Reduce", p.Co2Reduce, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "co2_reduce_total", "Co2 Reduce Total", p.Co2ReduceTotal, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "curr_power", "Curr Power", p.CurrPower, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "daily_irradiation", "Daily Irradiation", p.DailyIrradiation, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "equivalent_hour", "Equivalent Hour", p.EquivalentHour, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "installed_power_map", "Installed Power Map", p.InstalledPowerMap, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "radiation", "Radiation", p.Radiation, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "today_energy", "Today Energy", p.TodayEnergy, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "today_income", "Today Income", p.TodayIncome, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "total_capacity", "Total Capacity", p.TotalCapcity, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "total_energy", "Total Energy", p.TotalEnergy, len(ret.Entries)))
ret.Entries = append(ret.Entries, add(now, psId, "total_income", "Total Income", p.TotalIncome, len(ret.Entries)))
ret.Entries = append(ret.Entries, addValue(now, psId, "build_date", "Build Date", p.BuildDate, len(ret.Entries)))
ret.Entries = append(ret.Entries, addIntValue(now, psId, "build_status", "Build Status", p.BuildStatus, len(ret.Entries)))
ret.Entries = append(ret.Entries, addFloatValue(now, psId, "latitude", "Latitude", p.Latitude, len(ret.Entries)))
ret.Entries = append(ret.Entries, addFloatValue(now, psId, "longitude", "Longitude", p.Longitude, len(ret.Entries)))
ret.Entries = append(ret.Entries, addValue(now, psId, "location", "Location", p.Location, len(ret.Entries)))
ret.Entries = append(ret.Entries, addIntValue(now, psId, "installer_ps_fault_status", "Installer PS Fault Status", p.InstallerPsFaultStatus, len(ret.Entries)))
ret.Entries = append(ret.Entries, addIntValue(now, psId, "owner_ps_fault_status", "Owner PS Fault Status", p.OwnerPsFaultStatus, len(ret.Entries)))
ret.Entries = append(ret.Entries, addIntValue(now, psId, "ps_fault_status", "PS Fault Status", p.PsFaultStatus, len(ret.Entries)))
ret.Entries = append(ret.Entries, addValue(now, psId, "ps_health_status", "PS Health Status", p.PsHealthStatus, len(ret.Entries)))
ret.Entries = append(ret.Entries, addValue(now, psId, "ps_holder", "PS Holder", p.PsHolder, len(ret.Entries)))
ret.Entries = append(ret.Entries, addIntValue(now, psId, "ps_id", "PS Id", p.PsID, len(ret.Entries)))
ret.Entries = append(ret.Entries, addValue(now, psId, "ps_name", "PS Name", p.PsName, len(ret.Entries)))
ret.Entries = append(ret.Entries, addValue(now, psId, "ps_short_name", "PS Short Name", p.PsShortName, len(ret.Entries)))
ret.Entries = append(ret.Entries, addIntValue(now, psId, "ps_status", "PS Status", p.PsStatus, len(ret.Entries)))
ret.Entries = append(ret.Entries, addIntValue(now, psId, "ps_type", "PS Type", p.PsType, len(ret.Entries)))
// ret = append(ret, []string{now, "Pv Energy", p.PvEnergy.Value, p.PvEnergy.Unit}) // p83077
// ret = append(ret, []string{now, "Es Discharge Energy", p.EsDisenergy.Value, p.EsDisenergy.Unit}) // p83089
// ret = append(ret, []string{now, "Es Total Discharge Energy", p.EsTotalDisenergy.Value, p.EsTotalDisenergy.Unit}) // p83095
// ret = append(ret, []string{now, "Use Energy", p.UseEnergy.Value, p.UseEnergy.Unit}) // p83118
// ret = append(ret, []string{now, "Es Energy", p.EsEnergy.Value, p.EsEnergy.Unit}) // p83120
// ret = append(ret, []string{now, "Es Total Energy", p.EsTotalEnergy.Value, p.EsTotalEnergy.Unit}) // p83127
// ret = append(ret, []string{now, "Pv Power", p.PvPower.Value, p.PvPower.Unit}) // p83076
// ret = append(ret, []string{now, "Es Power", p.EsPower.Value, p.EsPower.Unit}) // p83081
//
// ret = append(ret, []string{now, "Co2 Reduce", p.Co2Reduce.Value, p.Co2Reduce.Unit})
// ret = append(ret, []string{now, "Co2 Reduce Total", p.Co2ReduceTotal.Value, p.Co2ReduceTotal.Unit})
// ret = append(ret, []string{now, "Curr Power", p.CurrPower.Value, p.CurrPower.Unit})
// ret = append(ret, []string{now, "Daily Irradiation", p.DailyIrradiation.Value, p.DailyIrradiation.Unit})
// ret = append(ret, []string{now, "Equivalent Hour", p.EquivalentHour.Value, p.EquivalentHour.Unit})
// ret = append(ret, []string{now, "Installed Power Map", p.InstalledPowerMap.Value, p.InstalledPowerMap.Unit})
// ret = append(ret, []string{now, "Radiation", p.Radiation.Value, p.Radiation.Unit})
// ret = append(ret, []string{now, "Today Energy", p.TodayEnergy.Value, p.TodayEnergy.Unit})
// ret = append(ret, []string{now, "Today Income", p.TodayIncome.Value, p.TodayIncome.Unit})
// ret = append(ret, []string{now, "Total Capacity", p.TotalCapcity.Value, p.TotalCapcity.Unit})
// ret = append(ret, []string{now, "Total Energy", p.TotalEnergy.Value, p.TotalEnergy.Unit})
// ret = append(ret, []string{now, "Total Income", p.TotalIncome.Value, p.TotalIncome.Unit})
}
}
return ret
}
func addState(now api.DateTime, point string, name string, state bool, index int) api.DataEntry {
return add(now, "virtual", point, name, api.UnitValue{ Value: fmt.Sprintf("%v", state) }, index)
// return api.DataEntry {
// Date: now,
// PointId: api.NameDevicePoint("virtual", point),
// PointGroupName: "Virtual",
// PointName: name,
// Value: fmt.Sprintf("%v", state),
// Unit: "binary",
// ValueType: &api.Point{
// PsKey: "virtual",
// Id: point,
// Description: name,
// Unit: "binary",
// Type: "PointTypeInstant",
// },
// Index: index,
// }
}
func addValue(now api.DateTime, psId string, point string, name string, value string, index int) api.DataEntry {
return add(now, psId, point, name, api.UnitValue{ Value: value }, index)
// vt := api.GetPoint(psId, point)
// if !vt.Valid {
// vt = &api.Point{
// PsKey: psId,
// Id: point,
// Description: name,
// Unit: "",
// Type: "PointTypeInstant",
// }
// }
// return api.DataEntry {
// Date: now,
// PointId: api.NameDevicePoint(psId, point),
// PointGroupName: "Summary",
// PointName: name,
// Value: value,
// Unit: "",
// ValueType: vt,
// Index: index,
// }
}
func addIntValue(now api.DateTime, psId string, point string, name string, value int64, index int) api.DataEntry {
return add(now, psId, point, name, api.UnitValue{ Value: strconv.FormatInt(value, 10) }, index)
}
func addFloatValue(now api.DateTime, psId string, point string, name string, value float64, index int) api.DataEntry {
return add(now, psId, point, name, api.UnitValue{ Value: api.Float64ToString(value) }, index)
}
func add(now api.DateTime, psId string, point string, name string, value api.UnitValue, index int) api.DataEntry {
vt := api.GetPoint(psId, point)
if !vt.Valid {
vt = &api.Point{
PsKey: psId,
Id: point,
Description: name,
Unit: value.Unit,
Type: "PointTypeInstant",
}
}
return api.DataEntry {
Date: now,
PointId: api.NameDevicePoint(psId, point),
PointGroupName: "Virtual",
PointName: name,
Value: value.Value,
Unit: value.Unit,
ValueType: vt,
Index: index,
}
}
// func (e *ResultData) GetData() [][]string {
// var ret [][]string
// for range Only.Once {
// i := len(e.PageList)
// if i == 0 {
// break
// }
// now := time.Now().Round(5 * time.Minute).Format(api.DtLayoutZeroSeconds)
// for _, p := range e.PageList {
// ret = append(ret, []string{now, "Co2 Reduce", p.Co2Reduce.Value, p.Co2Reduce.Unit})
// ret = append(ret, []string{now, "Co2 Reduce Total", p.Co2ReduceTotal.Value, p.Co2ReduceTotal.Unit})
// ret = append(ret, []string{now, "Curr Power", p.CurrPower.Value, p.CurrPower.Unit})
// ret = append(ret, []string{now, "Daily Irradiation", p.DailyIrradiation.Value, p.DailyIrradiation.Unit})
// ret = append(ret, []string{now, "Equivalent Hour", p.EquivalentHour.Value, p.EquivalentHour.Unit})
// ret = append(ret, []string{now, "Es Discharge Energy", p.EsDisenergy.Value, p.EsDisenergy.Unit})
// ret = append(ret, []string{now, "Es Energy", p.EsEnergy.Value, p.EsEnergy.Unit})
// ret = append(ret, []string{now, "Es Power", p.EsPower.Value, p.EsPower.Unit})
// ret = append(ret, []string{now, "Es Total Discharge Energy", p.EsTotalDisenergy.Value, p.EsTotalDisenergy.Unit})
// ret = append(ret, []string{now, "Es Total Energy", p.EsTotalEnergy.Value, p.EsTotalEnergy.Unit})
// ret = append(ret, []string{now, "Installed Power Map", p.InstalledPowerMap.Value, p.InstalledPowerMap.Unit})
// ret = append(ret, []string{now, "Pv Energy", p.PvEnergy.Value, p.PvEnergy.Unit})
// ret = append(ret, []string{now, "Pv Power", p.PvPower.Value, p.PvPower.Unit})
// ret = append(ret, []string{now, "Radiation", p.Radiation.Value, p.Radiation.Unit})
// ret = append(ret, []string{now, "Today Energy", p.TodayEnergy.Value, p.TodayEnergy.Unit})
// ret = append(ret, []string{now, "Today Income", p.TodayIncome.Value, p.TodayIncome.Unit})
// ret = append(ret, []string{now, "Total Capacity", p.TotalCapcity.Value, p.TotalCapcity.Unit})
// ret = append(ret, []string{now, "Total Energy", p.TotalEnergy.Value, p.TotalEnergy.Unit})
// ret = append(ret, []string{now, "Total Income", p.TotalIncome.Value, p.TotalIncome.Unit})
// ret = append(ret, []string{now, "Use Energy", p.UseEnergy.Value, p.UseEnergy.Unit})
// }
// }
// return ret
// }
func (e *EndPoint) GetDataTable() output.Table {
var table output.Table
for range Only.Once {

View File

@ -298,7 +298,9 @@ func (e *EndPoint) GetData() api.Data {
var TotalDcPower float64
index := 0
var TotalLoadActivePower float64
// index := 0
for _, d := range e.Response.ResultData.PageList {
for _, p := range d.PointData {
if p.Unit == "W" {
@ -323,6 +325,16 @@ func (e *EndPoint) GetData() api.Data {
}
}
vt := api.GetPointInt(d.PsKey, p.PointID)
if !vt.Valid {
vt = &api.Point{
PsKey: d.PsKey,
Id: "p" + strings.TrimPrefix(strconv.FormatInt(p.PointID, 10), "p"),
Description: p.PointName,
Unit: p.Unit,
Type: "",
}
}
ret.Entries = append(ret.Entries, api.DataEntry {
Date: api.NewDateTime(p.TimeStamp),
PointId: api.NameDevicePointInt(d.PsKey, p.PointID),
@ -330,12 +342,10 @@ func (e *EndPoint) GetData() api.Data {
PointName: p.PointName,
Value: p.Value,
Unit: p.Unit,
ValueType: api.GetPointInt(d.PsKey, p.PointID),
Index: index,
ValueType: vt,
Index: len(ret.Entries),
})
index++
// Handle virtual results.
switch strings.ReplaceAll(p.PointName, " ", "") {
case "BatteryChargingPower":
@ -346,6 +356,11 @@ func (e *EndPoint) GetData() api.Data {
TotalExportActivePower, _ = strconv.ParseFloat(p.Value, 64)
case "PurchasedPower":
PurchasedPower, _ = strconv.ParseFloat(p.Value, 64)
case "TotalDCPower":
TotalDcPower, _ = strconv.ParseFloat(p.Value, 64)
case "TotalLoadActivePower":
TotalLoadActivePower, _ = strconv.ParseFloat(p.Value, 64)
case "DailyBatteryChargingEnergyFromPv":
DailyBatteryChargingEnergyFromPv, _ = strconv.ParseFloat(p.Value, 64)
case "DailyBatteryDischargingEnergy":
@ -354,8 +369,6 @@ func (e *EndPoint) GetData() api.Data {
DailyFeedInEnergyPv, _ = strconv.ParseFloat(p.Value, 64)
case "DailyPurchasedEnergy":
DailyPurchasedEnergy, _ = strconv.ParseFloat(p.Value, 64)
case "TotalDCPower":
TotalDcPower, _ = strconv.ParseFloat(p.Value, 64)
}
}
}
@ -366,122 +379,313 @@ func (e *EndPoint) GetData() api.Data {
// Add virtual entries.
ts := ret.Entries[0].Date
var value string
var value float64
if BatteryChargingPower > 0 {
value = api.Float64ToString(0 - BatteryChargingPower)
} else {
value = api.Float64ToString(BatteryDischargingPower)
/*
PVPower - TotalDcPower
PVPowerToBattery - BatteryChargingPower
PVPowerToLoad - TotalDcPower - BatteryChargingPower - TotalExportActivePower
PVPowerToGrid - TotalExportActivePower
LoadPower - TotalLoadActivePower
BatteryToLoad - BatteryDischargingPower
BatteryToGrid - ?
GridPower - TotalDcPower
GridToLoad - PurchasedPower
GridToBattery - ?
*/
PVPower := TotalDcPower
PVPowerToBattery := BatteryChargingPower
PVPowerToLoad := TotalDcPower - BatteryChargingPower - TotalExportActivePower
PVPowerToGrid := TotalExportActivePower
ret.Entries = append(ret.Entries, addState(ts, "pv_power_active", "PV Power Active", isActive(PVPower), len(ret.Entries)))
ret.Entries = append(ret.Entries, addFloatValue(ts, "pv_power", "PV Power", PVPower, "kW", len(ret.Entries)))
ret.Entries = append(ret.Entries, addState(ts, "pv_power_to_battery_active", "PV Power To Battery Active", isActive(PVPowerToBattery), len(ret.Entries)))
ret.Entries = append(ret.Entries, addFloatValue(ts, "pv_power_to_battery", "PV Power To Battery", PVPowerToBattery, "kW", len(ret.Entries)))
ret.Entries = append(ret.Entries, addState(ts, "pv_power_to_load_active", "PV Power To Load Active", isActive(PVPowerToLoad), len(ret.Entries)))
ret.Entries = append(ret.Entries, addFloatValue(ts, "pv_power_to_load", "PV Power To Load", PVPowerToLoad, "kW", len(ret.Entries)))
ret.Entries = append(ret.Entries, addState(ts, "pv_power_to_grid_active", "PV Power To Grid Active", isActive(PVPowerToGrid), len(ret.Entries)))
ret.Entries = append(ret.Entries, addFloatValue(ts, "pv_power_to_grid", "PV Power To Grid", PVPowerToGrid, "kW", len(ret.Entries)))
BatteryPower := lowerUpper(BatteryChargingPower, BatteryDischargingPower)
BatteryToLoad := BatteryDischargingPower
BatteryToGrid := 0.0
ret.Entries = append(ret.Entries, addState(ts, "battery_power_active", "Battery Power Active", isActive(BatteryPower), len(ret.Entries)))
ret.Entries = append(ret.Entries, addFloatValue(ts, "battery_power", "Battery Power", BatteryPower, "kW", len(ret.Entries)))
ret.Entries = append(ret.Entries, addState(ts, "battery_power_to_load_active", "Battery Power To Load Active", isActive(BatteryToLoad), len(ret.Entries)))
ret.Entries = append(ret.Entries, addFloatValue(ts, "battery_power_to_load", "Battery Power To Load", BatteryToLoad, "kW", len(ret.Entries)))
ret.Entries = append(ret.Entries, addState(ts, "battery_power_to_grid_active", "Battery Power To Grid Active", isActive(BatteryToGrid), len(ret.Entries)))
ret.Entries = append(ret.Entries, addFloatValue(ts, "battery_power_to_grid", "Battery Power To Grid", BatteryToGrid, "kW", len(ret.Entries)))
GridPower := lowerUpper(TotalExportActivePower, PurchasedPower)
GridToLoad := PurchasedPower
GridToBattery := 0.0
ret.Entries = append(ret.Entries, addState(ts, "grid_power_active", "Grid Power Active", isActive(GridPower), len(ret.Entries)))
ret.Entries = append(ret.Entries, addFloatValue(ts, "grid_power", "Grid Power", GridPower, "kW", len(ret.Entries)))
ret.Entries = append(ret.Entries, addState(ts, "grid_power_to_load_active", "Grid Power To Load Active", isActive(GridToLoad), len(ret.Entries)))
ret.Entries = append(ret.Entries, addFloatValue(ts, "grid_power_to_load", "Grid Power To Load", GridToLoad, "kW", len(ret.Entries)))
ret.Entries = append(ret.Entries, addState(ts, "grid_power_to_battery_active", "Grid Power To Battery Active", isActive(GridToBattery), len(ret.Entries)))
ret.Entries = append(ret.Entries, addFloatValue(ts, "grid_power_to_battery", "Grid Power To Battery", GridToBattery, "kW", len(ret.Entries)))
LoadPower := TotalLoadActivePower
ret.Entries = append(ret.Entries, addState(ts, "load_power_active", "Load Power Active", isActive(LoadPower), len(ret.Entries)))
ret.Entries = append(ret.Entries, addFloatValue(ts, "load_power", "Load Power", LoadPower, "kW", len(ret.Entries)))
// {
// if isActive(BatteryChargingPower) {
// value = 0 - BatteryChargingPower
//
// ret.Entries = append(ret.Entries, addState(ts, "battery_power_active", "Battery Power Active", true, len(ret.Entries)))
// ret.Entries = append(ret.Entries, addState(ts, "pv_flow_to_battery", "PV Flow To Battery", true, len(ret.Entries)))
// ret.Entries = append(ret.Entries, addState(ts, "battery_flow_to_load", "Battery Flow To Load", false, len(ret.Entries)))
//
// } else if isActive(BatteryDischargingPower) {
// value = BatteryDischargingPower
//
// ret.Entries = append(ret.Entries, addState(ts, "battery_power_active", "Battery Power Active", true, len(ret.Entries)))
// ret.Entries = append(ret.Entries, addState(ts, "pv_flow_to_battery", "PV Flow To Battery", false, len(ret.Entries)))
// ret.Entries = append(ret.Entries, addState(ts, "battery_flow_to_load", "Battery Flow To Load", true, len(ret.Entries)))
//
// } else {
// value = 0
//
// ret.Entries = append(ret.Entries, addState(ts, "battery_power_active", "Battery Power Active", false, len(ret.Entries)))
// ret.Entries = append(ret.Entries, addState(ts, "pv_flow_to_battery", "PV Flow To Battery", false, len(ret.Entries)))
// ret.Entries = append(ret.Entries, addState(ts, "battery_flow_to_load", "Battery Flow To Load", false, len(ret.Entries)))
// }
//
// ret.Entries = append(ret.Entries, addFloatValue(ts, "battery_power", "Battery Power", value, len(ret.Entries)))
// }
//
// {
// if isActive(TotalExportActivePower) {
// value = 0 - TotalExportActivePower
//
// ret.Entries = append(ret.Entries, addState(ts, "grid_power_active", "Grid Power Active", true, len(ret.Entries)))
// ret.Entries = append(ret.Entries, addState(ts, "pv_flow_to_grid", "PV Flow To Grid", true, len(ret.Entries)))
// ret.Entries = append(ret.Entries, addState(ts, "grid_flow_to_load", "Grid Flow To Load", false, len(ret.Entries)))
//
// } else if isActive(PurchasedPower) {
// value = PurchasedPower
//
// ret.Entries = append(ret.Entries, addState(ts, "grid_power_active", "Grid Power Active", true, len(ret.Entries)))
// ret.Entries = append(ret.Entries, addState(ts, "pv_flow_to_grid", "PV Flow To Grid", false, len(ret.Entries)))
// ret.Entries = append(ret.Entries, addState(ts, "grid_flow_to_load", "Grid Flow To Load", true, len(ret.Entries)))
//
// } else {
// value = 0
//
// ret.Entries = append(ret.Entries, addState(ts, "grid_power_active", "Grid Power Active", false, len(ret.Entries)))
// ret.Entries = append(ret.Entries, addState(ts, "pv_flow_to_grid", "PV Flow To Grid", false, len(ret.Entries)))
// ret.Entries = append(ret.Entries, addState(ts, "grid_flow_to_load", "Grid Flow To Load", false, len(ret.Entries)))
// }
//
// ret.Entries = append(ret.Entries, addFloatValue(ts, "grid_power", "Grid Power", value, len(ret.Entries)))
// }
//
// {
// ret.Entries = append(ret.Entries, addState(ts, "pv_power_active", "PV Power Active", isActive(TotalDcPower), len(ret.Entries)))
// ret.Entries = append(ret.Entries, addFloatValue(ts, "pv_power", "PV Power", TotalDcPower, len(ret.Entries)))
//
// value = TotalDcPower - BatteryChargingPower - TotalExportActivePower
// ret.Entries = append(ret.Entries, addFloatValue(ts, "pv_power_to_load", "PV Power To Load", value, len(ret.Entries)))
// }
//
// {
// ret.Entries = append(ret.Entries, addState(ts, "load_power_active", "Load Power Active", isActive(TotalLoadActivePower), len(ret.Entries)))
//
// ret.Entries = append(ret.Entries, api.DataEntry{
// Date: ts,
// PointId: "virtual.pv_power_to_grid",
// PointGroupName: "Virtual",
// PointName: "PV Power To Grid",
// Value: api.Float64ToString(TotalExportActivePower),
// Unit: "kW",
// ValueType: &api.Point{
// PsKey: "virtual",
// Id: "pv_power_to_grid",
// Description: "PV Power To Grid",
// Unit: "kW",
// Type: "PointTypeInstant",
// },
// Index: len(ret.Entries),
// })
// }
{
if DailyBatteryChargingEnergyFromPv > 0 {
value = 0 - DailyBatteryChargingEnergyFromPv
} else {
value = DailyBatteryDischargingEnergy
}
ret.Entries = append(ret.Entries, api.DataEntry{
Date: ts,
PointId: "virtual.battery_energy",
PointGroupName: "Virtual",
PointName: "Battery Energy",
Value: api.Float64ToString(value),
Unit: "kWh",
ValueType: &api.Point{
PsKey: "virtual",
Id: "battery_energy",
Description: "Battery Energy",
Unit: "kWh",
Type: "PointTypeInstant",
},
Index: len(ret.Entries),
})
}
ret.Entries = append(ret.Entries, api.DataEntry {
Date: ts,
PointId: "virtual.battery_power",
PointGroupName: "Virtual",
PointName: "Battery Power",
Value: value,
Unit: "kW",
ValueType: &api.Point {
PsKey: "virtual",
Id: "battery_power",
Description: "Battery Power",
Unit: "kW",
Type: "PointTypeInstant",
},
Index: index,
})
index++
if TotalExportActivePower > 0 {
value = api.Float64ToString(0 - TotalExportActivePower)
} else {
value = api.Float64ToString(PurchasedPower)
{
if DailyFeedInEnergyPv > 0 {
value = 0 - DailyFeedInEnergyPv
} else {
value = DailyPurchasedEnergy
}
ret.Entries = append(ret.Entries, api.DataEntry{
Date: ts,
PointId: "virtual.grid_energy",
PointGroupName: "Virtual",
PointName: "Grid Energy",
Value: api.Float64ToString(value),
Unit: "kWh",
ValueType: &api.Point{
PsKey: "virtual",
Id: "grid_energy",
Description: "Grid Energy",
Unit: "kWh",
Type: "PointTypeInstant",
},
Index: len(ret.Entries),
})
}
ret.Entries = append(ret.Entries, api.DataEntry {
Date: ts,
PointId: "virtual.grid_power",
PointGroupName: "Virtual",
PointName: "Grid Power",
Value: value,
Unit: "kW",
ValueType: &api.Point {
PsKey: "virtual",
Id: "grid_power",
Description: "Grid Power",
Unit: "kW",
Type: "PointTypeInstant",
},
Index: index,
})
index++
if DailyBatteryChargingEnergyFromPv > 0 {
value = api.Float64ToString(0 - DailyBatteryChargingEnergyFromPv)
} else {
value = api.Float64ToString(DailyBatteryDischargingEnergy)
}
ret.Entries = append(ret.Entries, api.DataEntry {
Date: ts,
PointId: "virtual.battery_energy",
PointGroupName: "Virtual",
PointName: "Battery Energy",
Value: value,
Unit: "kWh",
ValueType: &api.Point {
PsKey: "virtual",
Id: "battery_energy",
Description: "Battery Energy",
Unit: "kWh",
Type: "PointTypeInstant",
},
Index: index,
})
index++
if DailyFeedInEnergyPv > 0 {
value = api.Float64ToString(0 - DailyFeedInEnergyPv)
} else {
value = api.Float64ToString(DailyPurchasedEnergy)
}
ret.Entries = append(ret.Entries, api.DataEntry {
Date: ts,
PointId: "virtual.grid_energy",
PointGroupName: "Virtual",
PointName: "Grid Energy",
Value: value,
Unit: "kWh",
ValueType: &api.Point {
PsKey: "virtual",
Id: "grid_energy",
Description: "Grid Energy",
Unit: "kWh",
Type: "PointTypeInstant",
},
Index: index,
})
index++
value = api.Float64ToString(TotalDcPower - BatteryChargingPower - TotalExportActivePower)
ret.Entries = append(ret.Entries, api.DataEntry {
Date: ts,
PointId: "virtual.pv_to_load",
PointGroupName: "Virtual",
PointName: "PV To Load Power",
Value: value,
Unit: "kW",
ValueType: &api.Point {
PsKey: "virtual",
Id: "pv_to_load",
Description: "PV To Load Power",
Unit: "kW",
Type: "PointTypeInstant",
},
Index: index,
})
index++
}
return ret
}
func lowerUpper(lower float64, upper float64) float64 {
if lower > 0 {
return 0 - lower
}
return upper
}
func addState(now api.DateTime, point string, name string, state bool, index int) api.DataEntry {
return add(now, "virtual", point, name, api.UnitValue{ Value: fmt.Sprintf("%v", state), Unit: "binary"}, index)
// return api.DataEntry {
// Date: now,
// PointId: api.NameDevicePoint("virtual", point),
// PointGroupName: "Virtual",
// PointName: name,
// Value: fmt.Sprintf("%v", state),
// Unit: "binary",
// ValueType: &api.Point{
// PsKey: "virtual",
// Id: point,
// Description: name,
// Unit: "binary",
// Type: "PointTypeInstant",
// },
// Index: index,
// }
}
func addValue(now api.DateTime, point string, name string, value string, unit string, index int) api.DataEntry {
return add(now, "virtual", point, name, api.UnitValue{ Value: value, Unit: unit}, index)
// vt := api.GetPoint(psId, point)
// if !vt.Valid {
// vt = &api.Point{
// PsKey: psId,
// Id: point,
// Description: name,
// Unit: "",
// Type: "PointTypeInstant",
// }
// }
// return api.DataEntry {
// Date: now,
// PointId: api.NameDevicePoint(psId, point),
// PointGroupName: "Summary",
// PointName: name,
// Value: value,
// Unit: "",
// ValueType: vt,
// Index: index,
// }
}
func addIntValue(now api.DateTime, point string, name string, value int64, unit string, index int) api.DataEntry {
return add(now, "virtual", point, name, api.UnitValue{ Value: strconv.FormatInt(value, 10), Unit: unit }, index)
}
func addFloatValue(now api.DateTime, point string, name string, value float64, unit string, index int) api.DataEntry {
return add(now, "virtual", point, name, api.UnitValue{ Value: api.Float64ToString(value), Unit: unit }, index)
}
func add(now api.DateTime, psId string, point string, name string, value api.UnitValue, index int) api.DataEntry {
vt := api.GetPoint(psId, point)
if !vt.Valid {
vt = &api.Point{
PsKey: psId,
Id: point,
Description: name,
Unit: value.Unit,
Type: "PointTypeInstant",
}
}
return api.DataEntry {
Date: now,
PointId: api.NameDevicePoint(psId, point),
PointGroupName: "Virtual",
PointName: name,
Value: value.Value,
Unit: value.Unit,
ValueType: vt,
Index: index,
}
}
// func addState(now api.DateTime, point string, name string, state bool, index int) api.DataEntry {
// return api.DataEntry {
// Date: now,
// PointId: api.NameDevicePoint("virtual", point),
// PointGroupName: "Virtual",
// PointName: name,
// Value: fmt.Sprintf("%v", state),
// Unit: "binary",
// ValueType: &api.Point{
// PsKey: "virtual",
// Id: point,
// Description: name,
// Unit: "binary",
// Type: "PointTypeInstant",
// },
// Index: index,
// }
// }
func isActive(value float64) bool {
if (value > 0.01) || (value < -0.01) {
return true
}
return false
}

View File

@ -24,6 +24,7 @@ type Point struct {
Description string
Unit string
Type string
Valid bool
}
type PointsMap map[string]Point
@ -292,15 +293,24 @@ func (p Point) String() string {
func (pm PointsMap) Get(device string, point string) *Point {
dp := device + ".p" + strings.TrimPrefix(point, "p")
if p, ok := pm[dp]; ok {
p.Valid = true
return &p
}
dp = "p" + strings.TrimPrefix(point, "p")
if p, ok := pm[dp]; ok {
p.Valid = true
return &p
}
return nil
return &Point {
PsKey: device,
Id: dp,
Description: "",
Unit: "",
Type: "",
Valid: false,
}
}
func (pm PointsMap) GetDevicePoint(devicePoint string) *Point {
@ -372,3 +382,18 @@ type DataEntry struct {
ValueType *Point `json:"value_type"`
Index int `json:"index"`
}
// Type string
// Name string
// SubName string
//
// ParentId string
// ParentName string
//
// UniqueId string
// FullName string
// Units string
// ValueName string
// Class string
//
// Value string

View File

@ -311,6 +311,32 @@ func (sg *SunGrow) QueryDevice(psId int64) queryDeviceList.EndPoint {
return ret
}
func (sg *SunGrow) QueryPs(psId int64) getPsList.EndPoint {
var ret getPsList.EndPoint
for range Only.Once {
if psId == 0 {
psId, sg.Error = sg.GetPsId()
if sg.Error != nil {
break
}
}
// ep = sg.GetByJson("AppService.queryDeviceList", fmt.Sprintf(`{"ps_id":"%d"}`, psId))
ep := sg.GetByStruct(
"AppService.getPsList",
getPsList.RequestData{},
time.Second * 60,
)
// if sg.Error != nil {
// break
// }
ret = getPsList.Assert(ep)
}
return ret
}
func (sg *SunGrow) GetPointNames() error {
for range Only.Once {
for _, dt := range getPowerDevicePointNames.DeviceTypes {
@ -540,6 +566,76 @@ func (sg *SunGrow) GetPsId() (int64, error) {
return ret, sg.Error
}
func (sg *SunGrow) GetPsName() (string, error) {
var ret string
for range Only.Once {
ep := sg.GetByStruct("AppService.getPsList", nil, DefaultCacheTimeout)
if ep.IsError() {
sg.Error = ep.GetError()
break
}
_getPsList := getPsList.AssertResultData(ep)
ret = _getPsList.GetPsName()
}
return ret, sg.Error
}
func (sg *SunGrow) GetPsModel() (string, error) {
var ret string
for range Only.Once {
var psId int64
psId, sg.Error = sg.GetPsId()
if sg.Error != nil {
break
}
ep := sg.GetByStruct(
"AppService.getPsDetailWithPsType",
getPsDetailWithPsType.RequestData{PsId: strconv.FormatInt(psId, 10)},
DefaultCacheTimeout)
if ep.IsError() {
sg.Error = ep.GetError()
break
}
ep2 := getPsDetailWithPsType.Assert(ep)
ret = ep2.GetDeviceName()
}
return ret, sg.Error
}
func (sg *SunGrow) GetPsSerial() (string, error) {
var ret string
for range Only.Once {
var psId int64
psId, sg.Error = sg.GetPsId()
if sg.Error != nil {
break
}
ep := sg.GetByStruct(
"AppService.getPsDetailWithPsType",
getPsDetailWithPsType.RequestData{PsId: strconv.FormatInt(psId, 10)},
DefaultCacheTimeout)
if ep.IsError() {
sg.Error = ep.GetError()
break
}
ep2 := getPsDetailWithPsType.Assert(ep)
ret = ep2.GetDeviceSerial()
}
return ret, sg.Error
}
func (sg *SunGrow) GetPsKeys() ([]string, error) {
var ret []string

122
mmHa/binary_sensor.go Normal file
View File

@ -0,0 +1,122 @@
package mmHa
import (
"GoSungrow/Only"
"encoding/json"
"fmt"
)
func (m *Mqtt) BinarySensorPublishConfig(config EntityConfig) error {
for range Only.Once {
if config.Units != "binary" {
break
}
ValueTemplate := fmt.Sprintf("{{ value_json.value }}")
LastReset := m.GetLastReset(config.UniqueId)
LastResetValueTemplate := ""
if LastReset != "" {
LastResetValueTemplate = "{{ value_json.last_reset | as_datetime() }}"
}
device := m.Device
device.Name = JoinStrings(m.Device.Name, config.ParentId)
device.Connections = [][]string{
{ m.Device.Name, JoinStringsForId(m.Device.Name, config.ParentId) },
{ JoinStringsForId(m.Device.Name, config.ParentId), JoinStringsForId(m.Device.Name, config.ParentId, config.Name) },
}
device.Identifiers = []string{ JoinStringsForId(m.Device.Name, config.ParentId) }
st := JoinStringsForId(m.Device.Name, config.ParentId, config.Name) // , config.ParentName, config.Name)
// UniqueId: JoinStringsForId(m.Device.Name, config.ParentName, config.Name, config.UniqueId),
payload := BinarySensor {
Device: device,
Name: JoinStrings(m.Device.Name, config.ParentName, config.FullName),
StateTopic: JoinStringsForTopic(m.binarySensorPrefix, st, "state"),
StateClass: "measurement",
UniqueId: st,
UnitOfMeasurement: config.Units,
DeviceClass: config.Class,
Qos: 0,
ForceUpdate: true,
ExpireAfter: 0,
Encoding: "utf-8",
EnabledByDefault: true,
LastResetValueTemplate: LastResetValueTemplate,
PayloadOn: "true",
PayloadOff: "false",
ValueTemplate: ValueTemplate,
Icon: "mdi:check-circle-outline", // mdi:check-circle-outline | mdi:arrow-right-bold
}
ct := JoinStringsForTopic(m.binarySensorPrefix, st, "config")
t := m.client.Publish(ct, 0, true, payload.Json())
if !t.WaitTimeout(m.Timeout) {
m.err = t.Error()
}
}
return m.err
}
func (m *Mqtt) BinarySensorPublishValue(config EntityConfig) error {
for range Only.Once {
if config.Units != "binary" {
break
}
st := JoinStringsForId(m.Device.Name, config.ParentId, config.Name)
payload := MqttState {
LastReset: m.GetLastReset(config.UniqueId),
Value: config.Value,
}
st = JoinStringsForTopic(m.binarySensorPrefix, st, "state")
t := m.client.Publish(st, 0, true, payload.Json())
if !t.WaitTimeout(m.Timeout) {
m.err = t.Error()
}
}
return m.err
}
type BinarySensor struct {
Availability *Availability `json:"availability,omitempty" required:"false"`
AvailabilityMode string `json:"availability_mode,omitempty" required:"false"`
AvailabilityTemplate string `json:"availability_template,omitempty" required:"false"`
AvailabilityTopic string `json:"availability_topic,omitempty" required:"false"`
Device Device `json:"device,omitempty" required:"false"`
DeviceClass string `json:"device_class,omitempty" required:"false"`
EnabledByDefault bool `json:"enabled_by_default,omitempty" required:"false"`
Encoding string `json:"encoding,omitempty" required:"false"`
EntityCategory string `json:"entity_category,omitempty" required:"false"`
ExpireAfter int `json:"expire_after,omitempty" required:"false"`
ForceUpdate bool `json:"force_update,omitempty" required:"false"`
Icon string `json:"icon,omitempty" required:"false"`
JsonAttributesTemplate string `json:"json_attributes_template,omitempty" required:"false"`
JsonAttributesTopic string `json:"json_attributes_topic,omitempty" required:"false"`
LastResetValueTemplate string `json:"last_reset_value_template,omitempty" required:"false"`
Name string `json:"name,omitempty" required:"false"`
ObjectId string `json:"object_id,omitempty" required:"false"`
PayloadAvailable string `json:"payload_available,omitempty" required:"false"`
PayloadNotAvailable string `json:"payload_not_available,omitempty" required:"false"`
Qos int `json:"qos,omitempty" required:"false"`
StateClass string `json:"state_class,omitempty" required:"false"`
StateTopic string `json:"state_topic" required:"true"`
UniqueId string `json:"unique_id,omitempty" required:"false"`
UnitOfMeasurement string `json:"unit_of_measurement,omitempty" required:"false"`
ValueTemplate string `json:"value_template,omitempty" required:"false"`
OffDelay int `json:"off_delay,omitempty" required:"false"`
PayloadOff string `json:"pl_off,omitempty" required:"false"`
PayloadOn string `json:"pl_on,omitempty" required:"false"`
}
func (c *BinarySensor) Json() string {
j, _ := json.Marshal(*c)
return string(j)
}

View File

@ -1,24 +1,8 @@
package mmMqtt
package mmHa
import "encoding/json"
// {
// "device": {
// "identifiers": [
// "sungrow"
// ],
// "manufacturer": "MickMake",
// "model": "GoLang",
// "name": "sungrow",
// "sw_version": "sungrow https://github.com/MickMake/GoSungrow"
// },
// "name": "sungrow",
// "stat_t": "~/state",
// "unique_id": "sungrow",
// "~": "homeassistant/binary_sensor/SunGrow"
// }
type Config struct {
Entry string `json:"~,omitempty" required:"false"`
Name string `json:"name,omitempty" required:"false"`
@ -51,3 +35,19 @@ type Device struct {
SwVersion string `json:"sw_version,omitempty" required:"false"`
ViaDevice string `json:"via_device,omitempty" required:"false"`
}
// {
// "device": {
// "identifiers": [
// "sungrow"
// ],
// "manufacturer": "MickMake",
// "model": "GoLang",
// "name": "sungrow",
// "sw_version": "sungrow https://github.com/MickMake/GoSungrow"
// },
// "name": "sungrow",
// "stat_t": "~/state",
// "unique_id": "sungrow",
// "~": "homeassistant/binary_sensor/SunGrow"
// }

1
mmHa/cron.go Normal file
View File

@ -0,0 +1 @@
package mmHa

View File

@ -1,4 +1,51 @@
package mmMqtt
package mmHa
import (
"regexp"
"strings"
)
func JoinStrings(args ...string) string {
return strings.TrimSpace(strings.Join(args, " "))
}
func JoinStringsForId(args ...string) string {
var newargs []string
var re = regexp.MustCompile(`(/| |:|\.)+`)
for _, a := range args {
if a == "" {
continue
}
a = strings.TrimSpace(a)
a = re.ReplaceAllString(a, `_`)
newargs = append(newargs, a)
}
// return strings.ReplaceAll(strings.TrimSpace(strings.Join(args, ".")), ".", "_")
return strings.Join(newargs, "-")
}
// func (c *Config) JoinStringsForId() string {
// return JoinStringsForId(m.Device.FullName, c.ParentName, c.FullName)
// }
func JoinStringsForTopic(args ...string) string {
var newargs []string
var re = regexp.MustCompile(`( |:)+`)
for _, a := range args {
if a == "" {
continue
}
a = strings.TrimSpace(a)
a = re.ReplaceAllString(a, `_`)
newargs = append(newargs, a)
}
// return strings.ReplaceAll(strings.TrimSpace(strings.Join(args, ".")), ".", "_")
return strings.Join(newargs, "/")
// ret := strings.ReplaceAll(strings.Join(args, "/"), "//", "/")
// return ret
}
// const DiscoveryPrefix = "homeassistant"

View File

@ -1,4 +1,61 @@
package mmMqtt
package mmHa
import (
"GoSungrow/Only"
"encoding/json"
)
func (m *Mqtt) PublishLightConfig(id string, name string, subName string, units string, valueName string, class string) error {
for range Only.Once {
id = JoinStringsForId(m.EntityPrefix, m.Device.Name, id)
payload := Light {
Device: m.Device,
Name: JoinStrings(m.Device.ViaDevice, name),
StateTopic: JoinStringsForTopic(m.switchPrefix, id, "state"),
// StateClass: "measurement",
// UniqueId: id,
// UnitOfMeasurement: units,
// DeviceClass: class,
// Qos: 0,
// ForceUpdate: true,
// ExpireAfter: 0,
// Encoding: "utf-8",
// EnabledByDefault: true,
// LastResetValueTemplate: LastResetValueTemplate,
// LastReset: LastReset,
// ValueTemplate: "{{ value_json.value | float }}",
// LastReset: time.Now().Format("2006-01-02T00:00:00+00:00"),
// LastResetValueTemplate: "{{entity_id}}",
// LastResetValueTemplate: "{{ (as_datetime((value_json.last_reset | int | timestamp_utc)|string+'Z')).isoformat() }}",
}
m.client.Publish(JoinStringsForTopic(m.lightPrefix, id, "config"), 0, true, payload.Json())
}
return m.err
}
func (m *Mqtt) PublishLight(subtopic string, payload interface{}) error {
for range Only.Once {
t := m.client.Publish(JoinStringsForTopic(m.lightPrefix, subtopic), 0, true, payload)
if !t.WaitTimeout(m.Timeout) {
m.err = t.Error()
}
}
return m.err
}
func (m *Mqtt) PublishLightState(topic string, payload interface{}) error {
for range Only.Once {
topic = JoinStringsForId(m.EntityPrefix, m.Device.Name, topic)
t := m.client.Publish(JoinStringsForTopic(m.lightPrefix, topic, "state"), 0, true, payload)
if !t.WaitTimeout(m.Timeout) {
m.err = t.Error()
}
}
return m.err
}
type Light struct {
@ -74,6 +131,11 @@ type Light struct {
// messageHandler mqtt.MessageHandler
}
func (c *Light) Json() string {
j, _ := json.Marshal(*c)
return string(j)
}
// {
// "brightness": true,

293
mmHa/sensors.go Normal file
View File

@ -0,0 +1,293 @@
package mmHa
import (
"GoSungrow/Only"
"encoding/json"
"fmt"
)
// func (m *Mqtt) PublishSensorConfig(config EntityConfig) error {
// // func (m *Mqtt) PublishSensorConfig(id string, name string, subName string, units string, valueName string, class string) error {
// for range Only.Once {
// // name = strings.ReplaceAll(name, "/", ".")
// // config.SubName = strings.ReplaceAll(config.SubName, "/", ".")
// // id = JoinStringsForId(m.Device.FullName, config.SubName, id)
// // st := JoinStringsForTopic(m.sensorPrefix, JoinStringsForId(m.EntityPrefix, m.Device.FullName, strings.ReplaceAll(config.SubName, "/", ".")), "state")
// // st := m.GetSensorStateTopic(name, config.SubName)
//
// device := m.Device
// device.Name = JoinStrings(m.Device.Name, config.ParentId)
// device.Connections = [][]string{{m.Device.Name, device.Name}}
// device.Identifiers = []string{device.Name}
// st := JoinStringsForId(m.Device.Name, config.ParentName, config.Name)
//
// payload := Sensor {
// Device: device,
// DeviceClass: config.Class,
// Name: JoinStrings(m.Device.Name, config.ParentName, config.Name, config.SubName), // config.FullName, // JoinStrings(m.Device.ViaDevice, config.SubName),
// StateTopic: JoinStringsForTopic(m.sensorPrefix, st, "state"), // m.GetSensorStateTopic(name, config.SubName),m.EntityPrefix, m.Device.FullName, config.SubName
// StateClass: "measurement",
// UniqueId: JoinStringsForId(m.Device.Name, config.ParentName, config.Name, config.UniqueId),
// UnitOfMeasurement: config.Units,
// Qos: 0,
// ForceUpdate: true,
// ExpireAfter: 0,
// Encoding: "utf-8",
// EnabledByDefault: true,
// // LastResetValueTemplate: LastResetValueTemplate,
// // LastReset: LastReset,
// // "{%if is_state(value_json.ifadminstatus,\"up\")-%}ON{%-else-%}OFF{%-endif%}"
// ValueTemplate: fmt.Sprintf("{{ value_json.%s | is_defined }}", config.ValueName),
// // LastReset: time.Now().Format("2006-01-02T00:00:00+00:00"),
// // LastResetValueTemplate: "{{entity_id}}",
// // LastResetValueTemplate: "{{ (as_datetime((value_json.last_reset | int | timestamp_utc)|string+'Z')).isoformat() }}",
// }
//
// ct := JoinStringsForId(m.Device.Name, config.ParentName, config.Name, config.SubName)
// m.client.Publish(JoinStringsForTopic(m.sensorPrefix, ct, "config"), 0, true, payload.Json())
// // m.client.Publish(JoinStringsForTopic(m.sensorPrefix, config.UniqueId, "config"), 0, true, payload.Json())
// }
//
// return m.err
// }
type Fields map[string]string
func (m *Mqtt) PublishSensorValues(configs []EntityConfig) error {
for range Only.Once {
cs := make(map[string]Fields)
topic := ""
for _, oid := range configs {
if topic == "" {
topic = JoinStringsForId(m.Device.Name, oid.ParentName, oid.Name)
}
if _, ok := cs[oid.Type]; !ok {
cs[oid.Type] = make(Fields)
}
cs[oid.Type][oid.ValueName] = oid.Value
}
for n, c := range cs {
j, _ := json.Marshal(c)
fmt.Printf("%s (%s) -> %s\n", topic, n, string(j))
m.err = m.PublishValue(n, topic, string(j))
}
}
return m.err
}
func (m *Mqtt) GetSensorStateTopic(config EntityConfig) string {
st := JoinStringsForId(m.Device.Name, config.ParentName, config.Name)
st = JoinStringsForTopic(m.sensorPrefix, st, "state") // m.GetSensorStateTopic(name, config.SubName),m.EntityPrefix, m.Device.FullName, config.SubName
return st
}
func (m *Mqtt) SensorPublishConfig(config EntityConfig) error {
for range Only.Once {
if config.Units == "binary" {
break
}
ValueTemplate := "{{ value_json.value | float }}"
// ValueTemplate := fmt.Sprintf("{{ value_json.%s | float }}", config.ValueName) // <- Used with merged values.
LastReset := m.GetLastReset(config.UniqueId)
LastResetValueTemplate := ""
if LastReset != "" {
LastResetValueTemplate = "{{ value_json.last_reset | as_datetime() }}"
// LastResetValueTemplate = "{{ value_json.last_reset | int | timestamp_local | as_datetime }}"
}
switch config.Units {
case "MW":
fallthrough
case "kW":
fallthrough
case "W":
config.Class = "power"
case "MWh":
fallthrough
case "kWh":
fallthrough
case "Wh":
config.Class = "energy"
case "kvar":
config.Class = "reactive_power"
case "Hz":
config.Class = "frequency"
case "V":
config.Class = "voltage"
case "A":
config.Class = "current"
case "℃":
config.Class = "temperature"
// point.Unit = "C"
case "C":
config.Class = "temperature"
config.Units = "℃"
case "%":
config.Class = "battery"
default:
ValueTemplate = "{{ value_json.value }}"
}
device := m.Device
device.Name = JoinStrings(m.Device.Name, config.ParentId)
device.Connections = [][]string {
{ m.Device.Name, JoinStringsForId(m.Device.Name, config.ParentId) },
{ JoinStringsForId(m.Device.Name, config.ParentId), JoinStringsForId(m.Device.Name, config.ParentId, config.Name) },
}
device.Identifiers = []string{ JoinStringsForId(m.Device.Name, config.ParentId) }
st := JoinStringsForId(m.Device.Name, config.ParentId, config.Name) // , config.ParentName, config.Name)
// UniqueId: JoinStringsForId(m.Device.Name, config.ParentName, config.Name, config.UniqueId),
payload := Sensor {
Device: device,
Name: JoinStrings(m.Device.Name, config.ParentName, config.FullName),
StateTopic: JoinStringsForTopic(m.sensorPrefix, st, "state"),
// m.GetSensorStateTopic(name, config.SubName),m.EntityPrefix, m.Device.FullName, config.SubName
StateClass: "measurement",
UniqueId: st,
UnitOfMeasurement: config.Units,
DeviceClass: config.Class,
Qos: 0,
ForceUpdate: true,
ExpireAfter: 0,
Encoding: "utf-8",
EnabledByDefault: true,
LastResetValueTemplate: LastResetValueTemplate,
LastReset: LastReset,
ValueTemplate: ValueTemplate,
}
ct := JoinStringsForTopic(m.sensorPrefix, st, "config")
t := m.client.Publish(ct, 0, true, payload.Json())
if !t.WaitTimeout(m.Timeout) {
m.err = t.Error()
}
}
return m.err
}
// func (m *Mqtt) PublishSensor(subtopic string, payload interface{}) error {
// for range Only.Once {
// t := m.client.Publish(subtopic, 0, true, payload)
// if !t.WaitTimeout(m.Timeout) {
// m.err = t.Error()
// }
// }
// return m.err
// }
// func (m *Mqtt) SensorPublish(subtopic string, payload interface{}) error {
// for range Only.Once {
// m.client.Publish(SensorBaseTopic + "/" + subtopic, 0, true, payload)
// }
// return m.err
// }
//
// func (m *Mqtt) PublishSensorState(topic string, payload interface{}) error {
// for range Only.Once {
// // topic = JoinStringsForId(m.EntityPrefix, m.Device.Name, topic)
// // topic = JoinStringsForTopic(m.sensorPrefix, topic, "state")
// // st := JoinStringsForTopic(m.sensorPrefix, JoinStringsForId(m.EntityPrefix, m.Device.FullName, strings.ReplaceAll(subName, "/", ".")), "state")
// t := m.client.Publish(topic, 0, true, payload)
// if !t.WaitTimeout(m.Timeout) {
// m.err = t.Error()
// }
// }
// return m.err
// }
// func (m *Mqtt) SensorPublishState(id string, payload interface{}) error {
// for range Only.Once {
// id = strings.ReplaceAll("sungrow_" + id, ".", "-")
// m.client.Publish(SensorBaseTopic + "/" + id + "/state", 0, true, payload)
// }
// return m.err
// }
//
// func (m *Mqtt) PublishSensorValue(topic string, value string) error {
// for range Only.Once {
// topic = JoinStringsForId(m.EntityPrefix, m.Device.Name, topic)
// // st := JoinStringsForTopic(m.sensorPrefix, JoinStringsForId(m.EntityPrefix, m.Device.FullName, strings.ReplaceAll(subName, "/", ".")), "state")
// payload := MqttState {
// LastReset: "", // m.GetLastReset(point.PointId),
// Value: value,
// }
// m.client.Publish(JoinStringsForTopic(m.sensorPrefix, topic, "state"), 0, true, payload.Json())
// }
// return m.err
// }
func (m *Mqtt) SensorPublishValue(config EntityConfig) error {
// func (m *Mqtt) SensorPublishValue(point api.DataEntry) error {
for range Only.Once {
if config.Units == "binary" {
break
}
st := JoinStringsForId(m.Device.Name, config.ParentId, config.Name)
payload := MqttState {
LastReset: m.GetLastReset(config.UniqueId),
Value: config.Value,
}
st = JoinStringsForTopic(m.sensorPrefix, st, "state")
t := m.client.Publish(st, 0, true, payload.Json())
if !t.WaitTimeout(m.Timeout) {
m.err = t.Error()
}
}
return m.err
}
type Sensor struct {
Availability *Availability `json:"availability,omitempty" required:"false"`
AvailabilityMode string `json:"availability_mode,omitempty" required:"false"`
AvailabilityTemplate string `json:"availability_template,omitempty" required:"false"`
AvailabilityTopic string `json:"availability_topic,omitempty" required:"false"`
Device Device `json:"device,omitempty" required:"false"`
DeviceClass string `json:"device_class,omitempty" required:"false"`
EnabledByDefault bool `json:"enabled_by_default,omitempty" required:"false"`
Encoding string `json:"encoding,omitempty" required:"false"`
EntityCategory string `json:"entity_category,omitempty" required:"false"`
ExpireAfter int `json:"expire_after,omitempty" required:"false"`
ForceUpdate bool `json:"force_update,omitempty" required:"false"`
Icon string `json:"icon,omitempty" required:"false"`
JsonAttributesTemplate string `json:"json_attributes_template,omitempty" required:"false"`
JsonAttributesTopic string `json:"json_attributes_topic,omitempty" required:"false"`
LastResetValueTemplate string `json:"last_reset_value_template,omitempty" required:"false"`
Name string `json:"name,omitempty" required:"false"`
ObjectId string `json:"object_id,omitempty" required:"false"`
PayloadAvailable string `json:"payload_available,omitempty" required:"false"`
PayloadNotAvailable string `json:"payload_not_available,omitempty" required:"false"`
Qos int `json:"qos,omitempty" required:"false"`
StateClass string `json:"state_class,omitempty" required:"false"`
StateTopic string `json:"state_topic" required:"true"`
UniqueId string `json:"unique_id,omitempty" required:"false"`
UnitOfMeasurement string `json:"unit_of_measurement,omitempty" required:"false"`
ValueTemplate string `json:"value_template,omitempty" required:"false"`
LastReset string `json:"last_reset,omitempty" required:"false"`
// StateFunc func() string `json:"-"`
//
// UpdateInterval float64 `json:"-"`
// ForceUpdateMQTT bool `json:"-"`
}
func (c *Sensor) Json() string {
j, _ := json.Marshal(*c)
return string(j)
}

460
mmHa/struct.go Normal file
View File

@ -0,0 +1,460 @@
package mmHa
import (
"GoSungrow/Only"
"GoSungrow/iSolarCloud/api"
"encoding/json"
"errors"
"fmt"
mqtt "github.com/eclipse/paho.mqtt.golang"
"net/url"
"time"
)
type Mqtt struct {
ClientId string `json:"client_id"`
Username string `json:"username"`
Password string `json:"password"`
Host string `json:"host"`
Port string `json:"port"`
Timeout time.Duration `json:"timeout"`
EntityPrefix string `json:"entity_prefix"`
url *url.URL
client mqtt.Client
pubClient mqtt.Client
clientOptions *mqtt.ClientOptions
LastRefresh time.Time `json:"-"`
PsId int64 `json:"-"`
Device Device
servicePrefix string
sensorPrefix string
lightPrefix string
switchPrefix string
binarySensorPrefix string
token mqtt.Token
firstRun bool
err error
}
func New(req Mqtt) *Mqtt {
var ret Mqtt
for range Only.Once {
ret.err = ret.setUrl(req)
if ret.err != nil {
break
}
ret.firstRun = true
ret.EntityPrefix = req.EntityPrefix
ret.servicePrefix = "homeassistant/sensor/" + req.ClientId
ret.sensorPrefix = "homeassistant/sensor/" + req.ClientId
ret.lightPrefix = "homeassistant/light/" + req.ClientId
ret.switchPrefix = "homeassistant/switch/" + req.ClientId
ret.binarySensorPrefix = "homeassistant/binary_sensor/" + req.ClientId
}
return &ret
}
func (m *Mqtt) IsFirstRun() bool {
return m.firstRun
}
func (m *Mqtt) IsNotFirstRun() bool {
return !m.firstRun
}
func (m *Mqtt) UnsetFirstRun() {
m.firstRun = false
}
func (m *Mqtt) GetError() error {
return m.err
}
func (m *Mqtt) IsError() bool {
if m.err != nil {
return true
}
return false
}
func (m *Mqtt) IsNewDay() bool {
var yes bool
for range Only.Once {
last := m.LastRefresh.Format("20060102")
now := time.Now().Format("20060102")
if last != now {
yes = true
break
}
}
return yes
}
func (m *Mqtt) setUrl(req Mqtt) error {
for range Only.Once {
// if req.Username == "" {
// m.err = errors.New("username empty")
// break
// }
m.Username = req.Username
// if req.Password == "" {
// m.err = errors.New("password empty")
// break
// }
m.Password = req.Password
if req.Host == "" {
m.err = errors.New("HASSIO mqtt host not defined")
break
}
m.Host = req.Host
if req.Port == "" {
req.Port = "1883"
}
m.Port = req.Port
u := fmt.Sprintf("tcp://%s:%s@%s:%s",
m.Username,
m.Password,
m.Host,
m.Port,
)
m.url, m.err = url.Parse(u)
}
return m.err
}
func (m *Mqtt) SetAuth(username string, password string) error {
for range Only.Once {
if username == "" {
m.err = errors.New("username empty")
break
}
m.Username = username
if password == "" {
m.err = errors.New("password empty")
break
}
m.Password = password
}
return m.err
}
func (m *Mqtt) Connect() error {
for range Only.Once {
m.err = m.createClientOptions()
if m.err != nil {
break
}
m.client = mqtt.NewClient(m.clientOptions)
token := m.client.Connect()
for !token.WaitTimeout(3 * time.Second) {
}
if m.err = token.Error(); m.err != nil {
break
}
if m.ClientId == "" {
m.ClientId = "GoSunGrow"
}
device := Config {
Entry: m.servicePrefix,
Name: m.ClientId,
UniqueId: m.ClientId, // + "_Service",
StateTopic: "~/state",
DeviceConfig: DeviceConfig {
Identifiers: []string{"GoSunGrow"},
SwVersion: "GoSunGrow https://github.com/MickMake/GoSungrow",
Name: m.ClientId + " Service",
Manufacturer: "MickMake",
Model: "SunGrow",
},
}
m.err = m.Publish(JoinStringsForTopic(m.servicePrefix, "config"), 0, true, device.Json())
if m.err != nil {
break
}
m.err = m.Publish(JoinStringsForTopic(m.servicePrefix, "state"), 0, true, "ON")
if m.err != nil {
break
}
}
return m.err
}
func (m *Mqtt) Disconnect() error {
for range Only.Once {
m.client.Disconnect(250)
time.Sleep(1 * time.Second)
}
return m.err
}
// const ServiceBaseName = "GoSunGrow"
// const ServiceBaseUniqueId = ServiceBaseName + "_Service"
// const ServiceBaseTopic = "homeassistant/sensor/" + ServiceBaseName
// const SensorBaseTopic = "homeassistant/sensor/" + ServiceBaseName
func (m *Mqtt) createClientOptions() error {
for range Only.Once {
m.clientOptions = mqtt.NewClientOptions()
m.clientOptions.AddBroker(fmt.Sprintf("tcp://%s", m.url.Host))
m.clientOptions.SetUsername(m.url.User.Username())
password, _ := m.url.User.Password()
m.clientOptions.SetPassword(password)
m.clientOptions.SetClientID(m.ClientId)
m.clientOptions.WillTopic = JoinStringsForTopic(m.servicePrefix, "state")
m.clientOptions.WillPayload = []byte("OFF")
m.clientOptions.WillQos = 0
m.clientOptions.WillRetained = true
m.clientOptions.WillEnabled = true
}
return m.err
}
func (m *Mqtt) Subscribe(topic string) error {
for range Only.Once {
t := m.client.Subscribe(topic, 0, func(client mqtt.Client, msg mqtt.Message) {
fmt.Printf("* [%s] %s\n", msg.Topic(), string(msg.Payload()))
})
if !t.WaitTimeout(m.Timeout) {
m.err = t.Error()
// m.err = errors.New("mqtt subscribe timeout")
}
}
return m.err
}
func (m *Mqtt) Publish(topic string, qos byte, retained bool, payload interface{}) error {
for range Only.Once {
t := m.client.Publish(topic, qos, retained, payload)
if !t.WaitTimeout(m.Timeout) {
m.err = t.Error()
// m.err = errors.New("mqtt publish timeout")
}
}
return m.err
}
// func (m *Mqtt) PublishConfig(config EntityConfig) error {
// // func (m *Mqtt) PublishConfig(t string, id string, name string, subName string, units string, valueName string, class string) error {
// switch config.Type {
// // case "sensor":
// // m.err = m.PublishSensorConfig(id, name, subName, units, valueName, class)
// case "binary_sensor":
// m.err = m.PublishBinarySensorConfig(config) // (id, name, subName, units, valueName, class)
// // case "lights":
// // m.err = m.PublishLightConfig(id, name, subName, units, valueName, class)
// // case "switch":
// // m.err = m.PublishSwitchConfig(id, name, subName, units, valueName, class)
// // default:
// // m.err = m.PublishSensorConfig(config)
// }
//
// return m.err
// }
func (m *Mqtt) PublishState(Type string, subtopic string, payload interface{}) error {
for range Only.Once {
// topic = JoinStringsForId(m.EntityPrefix, m.Device.Name, topic)
// topic = JoinStringsForTopic(m.sensorPrefix, topic, "state")
// st := JoinStringsForTopic(m.sensorPrefix, JoinStringsForId(m.EntityPrefix, m.Device.FullName, strings.ReplaceAll(subName, "/", ".")), "state")
topic := ""
switch Type {
case "sensor":
topic = JoinStringsForTopic(m.sensorPrefix, subtopic, "state")
case "binary_sensor":
topic = JoinStringsForTopic(m.binarySensorPrefix, subtopic, "state")
case "lights":
topic = JoinStringsForTopic(m.lightPrefix, subtopic, "state")
case "switch":
topic = JoinStringsForTopic(m.switchPrefix, subtopic, "state")
default:
topic = JoinStringsForTopic(m.sensorPrefix, subtopic, "state")
}
t := m.client.Publish(topic, 0, true, payload)
if !t.WaitTimeout(m.Timeout) {
m.err = t.Error()
}
}
return m.err
}
func (m *Mqtt) PublishValue(Type string, subtopic string, value string) error {
for range Only.Once {
topic := ""
switch Type {
case "sensor":
topic = JoinStringsForTopic(m.sensorPrefix, subtopic, "state")
// state := MqttState {
// LastReset: "", // m.GetLastReset(point.PointId),
// Value: value,
// }
// value = state.Json()
case "binary_sensor":
topic = JoinStringsForTopic(m.binarySensorPrefix, subtopic, "state")
// state := MqttState {
// LastReset: "", // m.GetLastReset(point.PointId),
// Value: value,
// }
// value = state.Json()
case "lights":
topic = JoinStringsForTopic(m.lightPrefix, subtopic, "state")
// state := MqttState {
// LastReset: "", // m.GetLastReset(point.PointId),
// Value: value,
// }
// value = state.Json()
case "switch":
topic = JoinStringsForTopic(m.switchPrefix, subtopic, "state")
// state := MqttState {
// LastReset: "", // m.GetLastReset(point.PointId),
// Value: value,
// }
// value = state.Json()
default:
topic = JoinStringsForTopic(m.sensorPrefix, subtopic, "state")
}
// t = JoinStringsForId(m.EntityPrefix, m.Device.Name, t)
// st := JoinStringsForTopic(m.sensorPrefix, JoinStringsForId(m.EntityPrefix, m.Device.FullName, strings.ReplaceAll(subName, "/", ".")), "state")
// payload := MqttState {
// LastReset: "", // m.GetLastReset(point.PointId),
// Value: value,
// }
// m.client.Publish(JoinStringsForTopic(m.sensorPrefix, t, "state"), 0, true, payload.Json())
t := m.client.Publish(topic, 0, true, value)
if !t.WaitTimeout(m.Timeout) {
m.err = t.Error()
}
}
return m.err
}
// func (m *Mqtt) PublishValue(t string, topic string, value string) error {
// switch t {
// case "sensor":
// m.err = m.PublishSensorValue(topic, value)
// case "binary_sensor":
// m.err = m.PublishBinarySensorState(topic, value)
// case "lights":
// m.err = m.PublishLightState(topic, value)
// case "switch":
// m.err = m.PublishSwitchState(topic, value)
// default:
// m.err = m.PublishSensorState(topic, value)
// }
//
// return m.err
// }
func (m *Mqtt) SetDeviceConfig(swname string, id string, name string, model string, vendor string, area string) error {
for range Only.Once {
id = JoinStringsForId(m.EntityPrefix, id)
m.Device = Device {
Connections: [][]string{
{swname, id},
},
Identifiers: []string{id},
Manufacturer: vendor,
Model: model,
Name: name,
SwVersion: swname + " https://github.com/MickMake/" + swname,
ViaDevice: swname,
SuggestedArea: area,
}
}
return m.err
}
type MqttState struct {
LastReset string `json:"last_reset,omitempty"`
Value string `json:"value"`
}
func (mq *MqttState) Json() string {
var ret string
for range Only.Once {
j, err := json.Marshal(*mq)
if err != nil {
ret = fmt.Sprintf("{ \"error\": \"%s\"", err)
break
}
ret = string(j)
}
return ret
}
type Availability struct {
PayloadAvailable string `json:"payload_available,omitempty" required:"false"`
PayloadNotAvailable string `json:"payload_not_available,omitempty" required:"false"`
Topic string `json:"topic,omitempty" required:"true"`
ValueTemplate string `json:"value_template,omitempty" required:"false"`
}
type SensorState string
func (m *Mqtt) GetLastReset(pointType string) string {
var ret string
for range Only.Once {
pt := api.GetDevicePoint(pointType)
if !pt.Valid {
break
}
ret = pt.WhenReset()
}
return ret
}
type EntityConfig struct {
Type string
Name string
SubName string
ParentId string
ParentName string
UniqueId string
FullName string
Units string
ValueName string
Class string
Icon string
Value string
}

94
mmHa/switch.go Normal file
View File

@ -0,0 +1,94 @@
package mmHa
import (
"GoSungrow/Only"
"encoding/json"
)
func (m *Mqtt) PublishSwitchConfig(id string, name string, subName string, units string, valueName string, class string) error {
for range Only.Once {
id = JoinStringsForId(m.EntityPrefix, m.Device.Name, id)
payload := Switch {
Device: m.Device,
Name: JoinStrings(m.Device.ViaDevice, name),
StateTopic: JoinStringsForTopic(m.switchPrefix, id, "state"),
// StateClass: "measurement",
// UniqueId: id,
// UnitOfMeasurement: units,
// DeviceClass: class,
// Qos: 0,
// ForceUpdate: true,
// ExpireAfter: 0,
// Encoding: "utf-8",
// EnabledByDefault: true,
// LastResetValueTemplate: LastResetValueTemplate,
// LastReset: LastReset,
// ValueTemplate: "{{ value_json.value | float }}",
// LastReset: time.Now().Format("2006-01-02T00:00:00+00:00"),
// LastResetValueTemplate: "{{entity_id}}",
// LastResetValueTemplate: "{{ (as_datetime((value_json.last_reset | int | timestamp_utc)|string+'Z')).isoformat() }}",
}
m.client.Publish(JoinStringsForTopic(m.switchPrefix, id, "config"), 0, true, payload.Json())
}
return m.err
}
func (m *Mqtt) PublishSwitch(subtopic string, payload interface{}) error {
for range Only.Once {
t := m.client.Publish(JoinStringsForTopic(m.switchPrefix, subtopic), 0, true, payload)
if !t.WaitTimeout(m.Timeout) {
m.err = t.Error()
}
}
return m.err
}
func (m *Mqtt) PublishSwitchState(topic string, payload interface{}) error {
for range Only.Once {
topic = JoinStringsForId(m.EntityPrefix, m.Device.Name, topic)
t := m.client.Publish(JoinStringsForTopic(m.switchPrefix, topic, "state"), 0, true, payload)
if !t.WaitTimeout(m.Timeout) {
m.err = t.Error()
}
}
return m.err
}
type Switch struct {
AvailabilityTopic string `json:"avty_t,omitempty"`
CommandTopic string `json:"cmd_t"`
Device Device `json:"dev,omitempty"`
Icon string `json:"ic,omitempty"`
JSONAttributesTemplate string `json:"json_attr_tpl,omitempty"`
JSONAttributesTopic string `json:"json_attr_t,omitempty"`
Name string `json:"name,omitempty"`
Optimistic bool `json:"opt,omitempty"`
PayloadAvailable string `json:"pl_avail,omitempty"`
PayloadNotAvailable string `json:"pl_not_avail,omitempty"`
PayloadOff string `json:"pl_off,omitempty"`
PayloadOn string `json:"pl_on,omitempty"`
QOS int `json:"qos,omitempty"`
Retain bool `json:"ret,omitempty"`
StateOff string `json:"stat_off,omitempty"`
StateOn string `json:"stat_on,omitempty"`
StateTopic string `json:"stat_t,omitempty"`
UniqueID string `json:"uniq_id,omitempty"`
ValueTemplate string `json:"val_tpl,omitempty"`
// CommandFunc func(mqtt.Message, mqtt.Client) `json:"-"`
// StateFunc func() string `json:"-"`
//
// UpdateInterval float64 `json:"-"`
// ForceUpdateMQTT bool `json:"-"`
//
// messageHandler mqtt.MessageHandler
}
func (c *Switch) Json() string {
j, _ := json.Marshal(*c)
return string(j)
}

View File

@ -1 +0,0 @@
package mmMqtt

View File

@ -1,110 +0,0 @@
package mmMqtt
import "encoding/json"
// {
// "device": {
// "connections": [
// [
// "cbus_group_address",
// "0"
// ]
// ],
// "identifiers": [
// "cbus_bin_sensor_0"
// ],
// "manufacturer": "Clipsal",
// "model": "C-Bus Lighting Application",
// "name": "C-Bus Light 000",
// "sw_version": "cmqttd https://github.com/micolous/cbus",
// "via_device": "cmqttd"
// },
// "name": "C-Bus Light 000 (as binary sensor)",
// "stat_t": "homeassistant/binary_sensor/cbus_0/state",
// "unique_id": "cbus_bin_sensor_0"
// }
type BinarySensor struct {
Availability *Availability `json:"availability,omitempty" required:"false"`
AvailabilityMode string `json:"availability_mode,omitempty" required:"false"`
AvailabilityTemplate string `json:"availability_template,omitempty" required:"false"`
AvailabilityTopic string `json:"availability_topic,omitempty" required:"false"`
Device Device `json:"device,omitempty" required:"false"`
DeviceClass string `json:"device_class,omitempty" required:"false"`
EnabledByDefault bool `json:"enabled_by_default,omitempty" required:"false"`
Encoding string `json:"encoding,omitempty" required:"false"`
EntityCategory string `json:"entity_category,omitempty" required:"false"`
ExpireAfter int `json:"expire_after,omitempty" required:"false"`
ForceUpdate bool `json:"force_update,omitempty" required:"false"`
Icon string `json:"icon,omitempty" required:"false"`
JsonAttributesTemplate string `json:"json_attributes_template,omitempty" required:"false"`
JsonAttributesTopic string `json:"json_attributes_topic,omitempty" required:"false"`
LastResetValueTemplate string `json:"last_reset_value_template,omitempty" required:"false"`
Name string `json:"name,omitempty" required:"false"`
ObjectId string `json:"object_id,omitempty" required:"false"`
PayloadAvailable string `json:"payload_available,omitempty" required:"false"`
PayloadNotAvailable string `json:"payload_not_available,omitempty" required:"false"`
Qos int `json:"qos,omitempty" required:"false"`
StateClass string `json:"state_class,omitempty" required:"false"`
StateTopic string `json:"state_topic" required:"true"`
UniqueId string `json:"unique_id,omitempty" required:"false"`
UnitOfMeasurement string `json:"unit_of_measurement,omitempty" required:"false"`
ValueTemplate string `json:"value_template,omitempty" required:"false"`
OffDelay int `json:"off_delay,omitempty" required:"false"`
PayloadOff string `json:"pl_off,omitempty" required:"false"`
PayloadOn string `json:"pl_on,omitempty" required:"false"`
}
type Availability struct {
PayloadAvailable string `json:"payload_available,omitempty" required:"false"`
PayloadNotAvailable string `json:"payload_not_available,omitempty" required:"false"`
Topic string `json:"topic,omitempty" required:"true"`
ValueTemplate string `json:"value_template,omitempty" required:"false"`
}
type SensorState string
func (c *BinarySensor) Json() string {
j, _ := json.Marshal(*c)
return string(j)
}
type Sensor struct {
Availability *Availability `json:"availability,omitempty" required:"false"`
AvailabilityMode string `json:"availability_mode,omitempty" required:"false"`
AvailabilityTemplate string `json:"availability_template,omitempty" required:"false"`
AvailabilityTopic string `json:"availability_topic,omitempty" required:"false"`
Device Device `json:"device,omitempty" required:"false"`
DeviceClass string `json:"device_class,omitempty" required:"false"`
EnabledByDefault bool `json:"enabled_by_default,omitempty" required:"false"`
Encoding string `json:"encoding,omitempty" required:"false"`
EntityCategory string `json:"entity_category,omitempty" required:"false"`
ExpireAfter int `json:"expire_after,omitempty" required:"false"`
ForceUpdate bool `json:"force_update,omitempty" required:"false"`
Icon string `json:"icon,omitempty" required:"false"`
JsonAttributesTemplate string `json:"json_attributes_template,omitempty" required:"false"`
JsonAttributesTopic string `json:"json_attributes_topic,omitempty" required:"false"`
LastResetValueTemplate string `json:"last_reset_value_template,omitempty" required:"false"`
Name string `json:"name,omitempty" required:"false"`
ObjectId string `json:"object_id,omitempty" required:"false"`
PayloadAvailable string `json:"payload_available,omitempty" required:"false"`
PayloadNotAvailable string `json:"payload_not_available,omitempty" required:"false"`
Qos int `json:"qos,omitempty" required:"false"`
StateClass string `json:"state_class,omitempty" required:"false"`
StateTopic string `json:"state_topic" required:"true"`
UniqueId string `json:"unique_id,omitempty" required:"false"`
UnitOfMeasurement string `json:"unit_of_measurement,omitempty" required:"false"`
ValueTemplate string `json:"value_template,omitempty" required:"false"`
LastReset string `json:"last_reset,omitempty" required:"false"`
// StateFunc func() string `json:"-"`
//
// UpdateInterval float64 `json:"-"`
// ForceUpdateMQTT bool `json:"-"`
}
func (c *Sensor) Json() string {
j, _ := json.Marshal(*c)
return string(j)
}

View File

@ -1,371 +0,0 @@
package mmMqtt
import (
"GoSungrow/Only"
"GoSungrow/iSolarCloud/api"
"encoding/json"
"errors"
"fmt"
mqtt "github.com/eclipse/paho.mqtt.golang"
"net/url"
"strconv"
"strings"
"time"
)
type Mqtt struct {
ClientId string `json:"client_id"`
Username string `json:"username"`
Password string `json:"password"`
Host string `json:"host"`
Port string `json:"port"`
url *url.URL
client mqtt.Client
pubClient mqtt.Client
clientOptions *mqtt.ClientOptions
LastRefresh time.Time `json:"-"`
PsId int64 `json:"-"`
firstRun bool
err error
}
func New(req Mqtt) *Mqtt {
var ret Mqtt
for range Only.Once {
ret.err = ret.setUrl(req)
if ret.err != nil {
break
}
ret.firstRun = true
}
return &ret
}
func (m *Mqtt) IsFirstRun() bool {
return m.firstRun
}
func (m *Mqtt) IsNotFirstRun() bool {
return !m.firstRun
}
func (m *Mqtt) UnsetFirstRun() {
m.firstRun = false
}
func (m *Mqtt) GetError() error {
return m.err
}
func (m *Mqtt) IsError() bool {
if m.err != nil {
return true
}
return false
}
func (m *Mqtt) IsNewDay() bool {
var yes bool
for range Only.Once {
last := m.LastRefresh.Format("20060102")
now := time.Now().Format("20060102")
if last != now {
yes = true
break
}
}
return yes
}
func (m *Mqtt) setUrl(req Mqtt) error {
for range Only.Once {
// if req.Username == "" {
// m.err = errors.New("username empty")
// break
// }
m.Username = req.Username
// if req.Password == "" {
// m.err = errors.New("password empty")
// break
// }
m.Password = req.Password
if req.Host == "" {
m.err = errors.New("HASSIO mqtt host not defined")
break
}
m.Host = req.Host
if req.Port == "" {
req.Port = "1883"
}
m.Port = req.Port
u := fmt.Sprintf("tcp://%s:%s@%s:%s",
m.Username,
m.Password,
m.Host,
m.Port,
)
m.url, m.err = url.Parse(u)
}
return m.err
}
func (m *Mqtt) SetAuth(username string, password string) error {
for range Only.Once {
if username == "" {
m.err = errors.New("username empty")
break
}
m.Username = username
if password == "" {
m.err = errors.New("password empty")
break
}
m.Password = password
}
return m.err
}
func (m *Mqtt) Connect() error {
for range Only.Once {
m.err = m.createClientOptions()
if m.err != nil {
break
}
m.client = mqtt.NewClient(m.clientOptions)
token := m.client.Connect()
for !token.WaitTimeout(3 * time.Second) {
}
if m.err = token.Error(); m.err != nil {
break
}
device := Config {
Entry: ServiceBaseTopic,
Name: ServiceBaseName,
UniqueId: ServiceBaseUniqueId,
StateTopic: "~/state",
DeviceConfig: DeviceConfig {
Identifiers: []string{"GoSunGrow", "SunGrow"},
SwVersion: "GoSunGrow https://github.com/MickMake/GoSungrow",
Name: ServiceBaseName,
Manufacturer: "MickMake",
Model: "SunGrow",
},
}
m.err = m.Publish(ServiceBaseTopic + "/config", 0, true, device.Json())
if m.err != nil {
break
}
m.err = m.Publish(ServiceBaseTopic + "/state", 0, true, "ON")
if m.err != nil {
break
}
}
return m.err
}
const ServiceBaseName = "GoSunGrow"
const ServiceBaseUniqueId = ServiceBaseName + "_Service"
const ServiceBaseTopic = "homeassistant/sensor/" + ServiceBaseName
const SensorBaseTopic = "homeassistant/sensor/" + ServiceBaseName
func (m *Mqtt) createClientOptions() error {
for range Only.Once {
m.clientOptions = mqtt.NewClientOptions()
m.clientOptions.AddBroker(fmt.Sprintf("tcp://%s", m.url.Host))
m.clientOptions.SetUsername(m.url.User.Username())
password, _ := m.url.User.Password()
m.clientOptions.SetPassword(password)
m.clientOptions.SetClientID(m.ClientId)
m.clientOptions.WillTopic = ServiceBaseTopic + "/state"
m.clientOptions.WillPayload = []byte("OFF")
m.clientOptions.WillQos = 0
m.clientOptions.WillRetained = true
m.clientOptions.WillEnabled = true
}
return m.err
}
func (m *Mqtt) Subscribe(topic string) error {
for range Only.Once {
m.client.Subscribe(topic, 0, func(client mqtt.Client, msg mqtt.Message) {
fmt.Printf("* [%s] %s\n", msg.Topic(), string(msg.Payload()))
})
}
return m.err
}
func (m *Mqtt) Publish(topic string, qos byte, retained bool, payload interface{}) error {
for range Only.Once {
m.client.Publish(topic, qos, retained, payload)
}
return m.err
}
func (m *Mqtt) SensorPublish(subtopic string, payload interface{}) error {
for range Only.Once {
m.client.Publish(SensorBaseTopic + "/" + subtopic, 0, true, payload)
}
return m.err
}
// func (m *Mqtt) SensorPublishConfig(id string, name string, units string, address int) error {
func (m *Mqtt) SensorPublishConfig(point api.DataEntry) error {
for range Only.Once {
a := strconv.Itoa(point.Index)
id := strings.ReplaceAll("sungrow_" + point.PointId, ".", "-")
class := ""
switch point.Unit {
case "MW":
fallthrough
case "kW":
fallthrough
case "W":
class = "power"
case "MWh":
fallthrough
case "kWh":
fallthrough
case "Wh":
class = "energy"
case "kvar":
class = "reactive_power"
case "Hz":
class = "frequency"
case "V":
class = "voltage"
case "A":
class = "current"
case "℃":
class = "temperature"
// point.Unit = "C"
case "C":
class = "temperature"
point.Unit = "℃"
case "%":
class = "battery"
}
LastReset := m.GetLastReset(point.PointId)
LastResetValueTemplate := ""
if LastReset != "" {
LastResetValueTemplate = "{{ value_json.last_reset | as_datetime() }}"
// LastResetValueTemplate = "{{ value_json.last_reset | int | timestamp_local | as_datetime }}"
}
payload := Sensor {
Device: Device {
Connections: [][]string{{"sungrow_address", a}},
Identifiers: []string{id, "sungrow_address_" + a},
Manufacturer: "MickMake",
Model: "SunGrow inverter",
Name: point.PointName,
SwVersion: "GoSunGrow https://github.com/MickMake/GoSungrow",
ViaDevice: "gosungrow",
},
Name: "SunGrow " + point.PointName,
StateClass: "measurement",
StateTopic: SensorBaseTopic + "/" + id + "/state",
UniqueId: id,
UnitOfMeasurement: point.Unit,
DeviceClass: class,
Qos: 0,
ForceUpdate: true,
ExpireAfter: 0,
Encoding: "utf-8",
EnabledByDefault: true,
LastResetValueTemplate: LastResetValueTemplate,
LastReset: LastReset,
ValueTemplate: "{{ value_json.value | float }}",
// LastReset: time.Now().Format("2006-01-02T00:00:00+00:00"),
// LastResetValueTemplate: "{{entity_id}}",
// LastResetValueTemplate: "{{ (as_datetime((value_json.last_reset | int | timestamp_utc)|string+'Z')).isoformat() }}",
}
m.client.Publish(SensorBaseTopic + "/" + id + "/config", 0, true, payload.Json())
}
return m.err
}
// func (m *Mqtt) SensorPublishValue(id string, value string) error {
func (m *Mqtt) SensorPublishValue(point api.DataEntry) error {
for range Only.Once {
id := strings.ReplaceAll("sungrow_" + point.PointId, ".", "-")
payload := MqttState {
LastReset: m.GetLastReset(point.PointId),
Value: point.Value,
}
m.client.Publish(SensorBaseTopic + "/" + id + "/state", 0, true, payload.Json())
}
return m.err
}
func (m *Mqtt) GetLastReset(pointType string) string {
var ret string
for range Only.Once {
pt := api.GetDevicePoint(pointType)
if pt == nil {
break
}
ret = pt.WhenReset()
}
return ret
}
func (m *Mqtt) SensorPublishState(id string, payload interface{}) error {
for range Only.Once {
id = strings.ReplaceAll("sungrow_" + id, ".", "-")
m.client.Publish(SensorBaseTopic + "/" + id + "/state", 0, true, payload)
}
return m.err
}
type MqttState struct {
LastReset string `json:"last_reset,omitempty"`
Value string `json:"value"`
}
func (mq *MqttState) Json() string {
var ret string
for range Only.Once {
j, err := json.Marshal(*mq)
if err != nil {
ret = fmt.Sprintf("{ \"error\": \"%s\"", err)
break
}
ret = string(j)
}
return ret
}

View File

@ -1,32 +0,0 @@
package mmMqtt
type Switch struct {
AvailabilityTopic string `json:"avty_t,omitempty"`
CommandTopic string `json:"cmd_t"`
Device Device `json:"dev,omitempty"`
Icon string `json:"ic,omitempty"`
JSONAttributesTemplate string `json:"json_attr_tpl,omitempty"`
JSONAttributesTopic string `json:"json_attr_t,omitempty"`
Name string `json:"name,omitempty"`
Optimistic bool `json:"opt,omitempty"`
PayloadAvailable string `json:"pl_avail,omitempty"`
PayloadNotAvailable string `json:"pl_not_avail,omitempty"`
PayloadOff string `json:"pl_off,omitempty"`
PayloadOn string `json:"pl_on,omitempty"`
QOS int `json:"qos,omitempty"`
Retain bool `json:"ret,omitempty"`
StateOff string `json:"stat_off,omitempty"`
StateOn string `json:"stat_on,omitempty"`
StateTopic string `json:"stat_t,omitempty"`
UniqueID string `json:"uniq_id,omitempty"`
ValueTemplate string `json:"val_tpl,omitempty"`
// CommandFunc func(mqtt.Message, mqtt.Client) `json:"-"`
// StateFunc func() string `json:"-"`
//
// UpdateInterval float64 `json:"-"`
// ForceUpdateMQTT bool `json:"-"`
//
// messageHandler mqtt.MessageHandler
}