469 lines
21 KiB
Go
469 lines
21 KiB
Go
// SPDX-License-Identifier: MIT
|
|
// SPDX-FileCopyrightText: 2023 mysubarumq
|
|
// SPDX-FileContributor: alex-savin
|
|
|
|
package workers
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"git.savin.nyc/alex/mysubaru"
|
|
"git.savin.nyc/alex/mysubaru-mq/bus"
|
|
"git.savin.nyc/alex/mysubaru-mq/config"
|
|
msc "git.savin.nyc/alex/mysubaru/config"
|
|
)
|
|
|
|
// MySubaruClient is a client that connects to the MySubaru server by establishing tcp connection
|
|
type MySubaruClient struct {
|
|
sync.RWMutex
|
|
id string // the internal id of the listener
|
|
bus *bus.Bus // the internal bus for the interapp communication
|
|
config *config.Config // configuration values for the listener
|
|
mysubaru *mysubaru.Client //
|
|
cancel context.CancelFunc //
|
|
end uint32 // ensure the close methods are only called once
|
|
log *slog.Logger // server logger
|
|
}
|
|
|
|
// NewMySubaruClient initialises and returns a MySubaru client
|
|
func NewMySubaruClient(id string, bus *bus.Bus, config *config.Config) *MySubaruClient {
|
|
s := &MySubaruClient{
|
|
id: id,
|
|
bus: bus,
|
|
config: config,
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
// ID returns the id of the listener.
|
|
func (s *MySubaruClient) ID() string {
|
|
return s.id
|
|
}
|
|
|
|
// ID returns the id of the listener.
|
|
func (s *MySubaruClient) Type() string {
|
|
return "mysubaru-client"
|
|
}
|
|
|
|
// Init .
|
|
func (s *MySubaruClient) Init(log *slog.Logger) error {
|
|
s.log = log
|
|
|
|
mys, err := mysubaru.New(&msc.Config{MySubaru: s.config.MySubaru, TimeZone: s.config.Timezone, Logger: s.log})
|
|
if err != nil {
|
|
s.log.Error("couldn't connect to MySubaru server", "error", err.Error())
|
|
}
|
|
|
|
s.mysubaru = mys
|
|
|
|
return nil
|
|
}
|
|
|
|
// OneTime .
|
|
func (s *MySubaruClient) OneTime() error {
|
|
if s.config.Hassio.AutoDiscovery {
|
|
time.Sleep(3 * time.Second)
|
|
vehicles := s.mysubaru.GetVehicles()
|
|
|
|
for _, vehicle := range vehicles {
|
|
err := s.bus.Publish("mqtt:publish", s.mySubaruConfigToMQTTHassioConfig(vehicle))
|
|
if err != nil {
|
|
s.log.Error("got an error from bus", "error", err.Error())
|
|
|
|
return err
|
|
}
|
|
err = s.bus.Publish("mqtt:publish", s.mySubaruStatusToMQTTMessage(vehicle))
|
|
if err != nil {
|
|
s.log.Error("got an error from bus", "error", err.Error())
|
|
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Serve starts waiting for new TCP connections, and calls the establish
|
|
// connection callback for any received.
|
|
func (s *MySubaruClient) Serve() {
|
|
if atomic.LoadUint32(&s.end) == 1 {
|
|
return
|
|
}
|
|
|
|
var subs []*bus.Message
|
|
// Subscribing for the topic to resend auto discovery topics after Home Assistant restarted and got a status "online"
|
|
if s.config.Hassio.AutoDiscovery {
|
|
subs = append(subs, &bus.Message{
|
|
Topic: s.config.Hassio.Topics.Status,
|
|
QOS: 0,
|
|
})
|
|
}
|
|
// TODO: Go over MySubaru devices with switch and lock options
|
|
subs = append(subs, &bus.Message{
|
|
Topic: "mysubarumq/4S4BTGPD0P3199198/lock/set",
|
|
QOS: 0,
|
|
})
|
|
subs = append(subs, &bus.Message{
|
|
Topic: "mysubarumq/4S4BTGPD0P3199198/ignition/set",
|
|
QOS: 0,
|
|
})
|
|
s.bus.Publish("mqtt:subscribe", subs)
|
|
|
|
tickerS := time.NewTicker(time.Duration(60) * time.Second)
|
|
tickerM := time.NewTicker(time.Duration(60) * time.Minute)
|
|
|
|
chMQTTLockStatus, err := s.bus.Subscribe("mysubarumq:4S4BTGPD0P3199198:lock", "mysubarumq:4S4BTGPD0P3199198:lock", s.config.SubscriptionSize["device:95452:status"])
|
|
if err != nil {
|
|
s.log.Error("couldn't subscribe to a channel", "channel", "mysubarumq:4S4BTGPD0P3199198:lock", "error", err.Error())
|
|
}
|
|
|
|
chMQTTIgnitionStatus, err := s.bus.Subscribe("mysubarumq:4S4BTGPD0P3199198:ignition", "mysubarumq:4S4BTGPD0P3199198:ignition", s.config.SubscriptionSize["device:95452:status"])
|
|
if err != nil {
|
|
s.log.Error("couldn't subscribe to a channel", "channel", "mysubarumq:4S4BTGPD0P3199198:ignition", "error", err.Error())
|
|
}
|
|
|
|
chMQTTHassioStatus, err := s.bus.Subscribe("hassio:status", s.ID(), s.config.SubscriptionSize["hassio:status"])
|
|
if err != nil {
|
|
s.log.Error("couldn't subscribe to a channel", "channel", "hassio:status", "error", err.Error())
|
|
}
|
|
|
|
// ctx is used only by tests.
|
|
// ctx, ctxCancel := context.WithCancel(context.Background())
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
s.cancel = cancel
|
|
|
|
go s.eventLoop(ctx, chMQTTLockStatus, chMQTTIgnitionStatus, chMQTTHassioStatus)
|
|
|
|
if atomic.LoadUint32(&s.end) == 0 {
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-tickerS.C:
|
|
vehicles := s.mysubaru.GetVehicles()
|
|
for _, vehicle := range vehicles {
|
|
err := s.bus.Publish("mqtt:publish", s.mySubaruStatusToMQTTMessage(vehicle))
|
|
if err != nil {
|
|
s.log.Error("got an error from bus", "error", err.Error())
|
|
}
|
|
}
|
|
case <-tickerM.C:
|
|
vehicles := s.mysubaru.GetVehicles()
|
|
for _, vehicle := range vehicles {
|
|
vehicle.GetLocation(true)
|
|
err := s.bus.Publish("mqtt:publish", s.mySubaruStatusToMQTTMessage(vehicle))
|
|
if err != nil {
|
|
s.log.Error("got an error from bus", "error", err.Error())
|
|
}
|
|
}
|
|
case <-ctx.Done():
|
|
s.log.Info("stopping communication eventloop", "type", s.Type())
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
}
|
|
|
|
// Close closes the listener and any client connections.
|
|
func (s *MySubaruClient) Close() {
|
|
s.Lock()
|
|
defer s.Unlock()
|
|
|
|
if atomic.CompareAndSwapUint32(&s.end, 0, 1) {
|
|
s.cancel()
|
|
s.log.Info("disconnected from mysubaru server", "type", s.Type())
|
|
}
|
|
}
|
|
|
|
// eventLoop loops forever
|
|
func (s *MySubaruClient) eventLoop(ctx context.Context, chMQTTLockStatus, chMQTTIgnitionStatus, chMQTTHassioStatus chan bus.Event) {
|
|
s.log.Debug("mysubaru communication event loop started")
|
|
defer s.log.Debug("mysubaru communication event loop halted")
|
|
|
|
for {
|
|
select {
|
|
case event := <-chMQTTLockStatus:
|
|
for _, message := range event.Data.([]*bus.Message) {
|
|
s.log.Debug("received a message with mysubary lock status", "topic", message.Topic, "payload", message.Payload)
|
|
if message.Payload == "LOCK" {
|
|
v := s.mysubaru.GetVehicleByVIN("4S4BTGPD0P3199198")
|
|
v.Lock()
|
|
var msgs []*bus.Message
|
|
msgs = s.messages("mysubarumq/4S4BTGPD0P3199198/lock", 0, true, "LOCK", msgs)
|
|
s.bus.Publish("mqtt:publish", msgs)
|
|
}
|
|
if message.Payload == "UNLOCK" {
|
|
v := s.mysubaru.GetVehicleByVIN("4S4BTGPD0P3199198")
|
|
v.Unlock()
|
|
var msgs []*bus.Message
|
|
msgs = s.messages("mysubarumq/4S4BTGPD0P3199198/lock", 0, true, "UNLOCK", msgs)
|
|
s.bus.Publish("mqtt:publish", msgs)
|
|
}
|
|
}
|
|
case event := <-chMQTTIgnitionStatus:
|
|
for _, message := range event.Data.([]*bus.Message) {
|
|
s.log.Debug("received a message with mysubary ignition status", "topic", message.Topic, "payload", message.Payload)
|
|
if message.Payload == "ON" {
|
|
v := s.mysubaru.GetVehicleByVIN("4S4BTGPD0P3199198")
|
|
v.EngineStart()
|
|
var msgs []*bus.Message
|
|
msgs = s.messages("mysubarumq/4S4BTGPD0P3199198/ignition", 0, true, "ON", msgs)
|
|
s.bus.Publish("mqtt:publish", msgs)
|
|
}
|
|
if message.Payload == "OFF" {
|
|
v := s.mysubaru.GetVehicleByVIN("4S4BTGPD0P3199198")
|
|
v.EngineStop()
|
|
var msgs []*bus.Message
|
|
msgs = s.messages("mysubarumq/4S4BTGPD0P3199198/ignition", 0, true, "OFF", msgs)
|
|
s.bus.Publish("mqtt:publish", msgs)
|
|
}
|
|
}
|
|
case event := <-chMQTTHassioStatus:
|
|
for _, message := range event.Data.([]*bus.Message) {
|
|
s.log.Info("received a message with hassio instance status", "topic", message.Topic, "payload", message.Payload)
|
|
if message.Payload == "online" {
|
|
s.OneTime()
|
|
}
|
|
}
|
|
case <-ctx.Done():
|
|
s.log.Info("stopping mqtt communication event loop")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// mySubaruConfigToMQTTHassioConfig .
|
|
func (s *MySubaruClient) mySubaruConfigToMQTTHassioConfig(v *mysubaru.Vehicle) []*bus.Message {
|
|
// {
|
|
// "~": "homeassistant/sensor/VIN_SENSOR_NAME",
|
|
// "name": null,
|
|
// "uniq_id": "4S4BTGPD0P3199198_SENSOR_NAME",
|
|
// "obj_id": "",
|
|
// "ic": "",
|
|
// "stat_t": "~/state",
|
|
// "json_attr_t": "~/state",
|
|
// "val_tpl": "{{value_json.value}}",
|
|
// "dev_cla": "",
|
|
// "stat_cla": "",
|
|
// "unit_of_meas": "",
|
|
// "en": true,
|
|
// "ent_cat": "",
|
|
// "ent_pic": "",
|
|
// "dev": {
|
|
// "ids": [
|
|
// "4S4BTGPD0P3199198"
|
|
// ],
|
|
// "name": "Subaru Outback Touring TX (2023)",
|
|
// "mf": "Subaru",
|
|
// "mdl": "Outback Touring TX",
|
|
// "sw": "1.0",
|
|
// "hw": "PDL"
|
|
// },
|
|
// "o": {
|
|
// "name": "MySubaruMQ",
|
|
// "sw": "1.0.0",
|
|
// "url": "https://www.github.com/alex-savin/"
|
|
// },
|
|
// "avty": [
|
|
// {
|
|
// "topic": ""
|
|
// }
|
|
// ]
|
|
// }
|
|
var hassioConfig = map[string]string{}
|
|
|
|
// availability := `{"avty":["{"topic":"mysubaru/` + v.Vin + `"}"]}`
|
|
origin := `"o":{"name":"MySubaruMQ","sw":"1.0.1","url":"https://git.savin.nyc/alex/mysubaru-mq"},`
|
|
// availability := `"avty":[{"t":"musubarymq/status"}],"avty_t":"{{value_json.value}}",`
|
|
device := `"dev":{"ids":["` + v.Vin + `"],"name":"` + v.CarNickname + `","mf":"Subaru Corp.","mdl":"` + v.CarNickname + `","hw":"` + v.ModelCode + `"},` // TODO chnage model to the proper one
|
|
obj_id_prefix := strings.Replace(strings.ToLower(v.CarNickname), " ", "_", -1)
|
|
// topic := `"~":"mysubaru/` + v.Vin + `",`
|
|
topic := ""
|
|
// homeassistant/sensor/mysubaru/VIN-NUMBER-HERE/odometer_km/config
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/sensor/`+v.Vin+`/odometer_km/config`] = `{` + device + origin + topic + `"name":"Odometer (km)","uniq_id":"` + v.Vin + `_odometer_km","obj_id":"` + obj_id_prefix + `_odometer_km","ic":"mdi:counter","stat_t":"mysubarumq/` + v.Vin + `/state","val_tpl":"{{value_json.odometer_km}}","unit_of_meas":"km"}`
|
|
// homeassistant/sensor/mysubaru/VIN-NUMBER-HERE/odometer_mi/config
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/sensor/`+v.Vin+`/odometer_mi/config`] = `{` + device + origin + topic + `"name":"Odometer (mi)","uniq_id":"` + v.Vin + `_odometer_mi","obj_id":"` + obj_id_prefix + `_odometer_mi","ic":"mdi:counter","stat_t":"mysubarumq/` + v.Vin + `/state","val_tpl":"{{value_json.odometer_mi}}","unit_of_meas":"mi"}`
|
|
// homeassistant/sensor/mysubaru/VIN-NUMBER-HERE/dist_to_empty_km/config
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/sensor/`+v.Vin+`/dist_to_empty_km/config`] = `{` + device + origin + topic + `"name":"Distance to Empty (km)","uniq_id":"` + v.Vin + `_dist_to_empty_km","obj_id":"` + obj_id_prefix + `_dist_to_empty_km","ic":"mdi:gas-station","stat_t":"mysubarumq/` + v.Vin + `/state","val_tpl":"{{value_json.dist_to_empty_km}}","unit_of_meas":"km"}`
|
|
// homeassistant/sensor/mysubaru/VIN-NUMBER-HERE/dist_to_empty_mi/config
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/sensor/`+v.Vin+`/dist_to_empty_mi/config`] = `{` + device + origin + topic + `"name":"Distance to Empty (mi)","uniq_id":"` + v.Vin + `_dist_to_empty_mi","obj_id":"` + obj_id_prefix + `_dist_to_empty_mi","ic":"mdi:gas-station","stat_t":"mysubarumq/` + v.Vin + `/state","val_tpl":"{{value_json.dist_to_empty_mi}}","unit_of_meas":"mi"}`
|
|
// homeassistant/sensor/mysubaru/VIN-NUMBER-HERE/dist_to_empty_pc/config
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/sensor/`+v.Vin+`/dist_to_empty_pc/config`] = `{` + device + origin + topic + `"name":"Gas Tank (%)","uniq_id":"` + v.Vin + `_dist_to_empty_pc","obj_id":"` + obj_id_prefix + `_dist_to_empty_pc","ic":"mdi:gauge","stat_t":"mysubarumq/` + v.Vin + `/state","val_tpl":"{{value_json.dist_to_empty_pc}}","unit_of_meas":"%"}`
|
|
// homeassistant/sensor/mysubaru/VIN-NUMBER-HERE/consumption_us/config
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/sensor/`+v.Vin+`/consumption_us/config`] = `{` + device + origin + topic + `"name":"Consumption (MPG)","uniq_id":"` + v.Vin + `_consumption_us","obj_id":"` + obj_id_prefix + `_consumption_us","ic":"mdi:map-marker-distance","stat_t":"mysubarumq/` + v.Vin + `/state","val_tpl":"{{value_json.consumption_us}}","unit_of_meas":"MPG"}`
|
|
// homeassistant/sensor/mysubaru/VIN-NUMBER-HERE/consumption_eu/config
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/sensor/`+v.Vin+`/consumption_eu/config`] = `{` + device + origin + topic + `"name":"Consumption (L/100km)","uniq_id":"` + v.Vin + `_consumption_eu","obj_id":"` + obj_id_prefix + `_consumption_eu","ic":"mdi:map-marker-distance","stat_t":"mysubarumq/` + v.Vin + `/state","val_tpl":"{{value_json.consumption_eu}}","unit_of_meas":"L100km"}`
|
|
// homeassistant/sensor/mysubaru/VIN-NUMBER-HERE/engine_state/consumption_eu/config
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/sensor/`+v.Vin+`/engine_state/config`] = `{` + device + origin + topic + `"name":"Engine State","uniq_id":"` + v.Vin + `_engine_state","obj_id":"` + obj_id_prefix + `_engine_state","ic":"mdi:engine","stat_t":"mysubarumq/` + v.Vin + `/state","val_tpl":"{{value_json.engine_state}}"}`
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/device_tracker/`+v.Vin+`/config`] = `{` + device + origin + `"name":"` + v.CarNickname + `","uniq_id":"` + v.Vin + `_device_tracker","obj_id":"` + obj_id_prefix + `","ic":"mdi:car-connected","json_attr_t":"mysubarumq/` + v.Vin + `/attr"}`
|
|
|
|
// homeassistant/sensor/mysubaru/VIN-NUMBER-HERE/troubles/config
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/sensor/`+v.Vin+`/troubles/config`] = `{` + device + origin + topic + `"name":"Troubles","uniq_id":"` + v.Vin + `_troubles","obj_id":"` + obj_id_prefix + `_troubles","ic":"mdi:car-wrench","stat_t":"mysubarumq/` + v.Vin + `/state","val_tpl":"{{value_json.troubles}}","json_attr_t":"mysubarumq/` + v.Vin + `/troubles/attr","unit_of_meas":""}`
|
|
for n, d := range v.Doors {
|
|
// homeassistant/sensor/mysubaru/VIN-NUMBER-HERE/door_frontleft_status/config
|
|
position := d.Position + ` ` + d.SubPosition
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/sensor/`+v.Vin+`/`+n+`_status/config`] = `{` + device + origin + topic + `"name":"Door ` + position + ` Status","uniq_id":"` + v.Vin + `_` + n + `_status","obj_id":"` + obj_id_prefix + n + `_status","ic":"mdi:car-door","stat_t":"mysubarumq/` + v.Vin + `/doors/state","val_tpl":"{{value_json.` + n + `}}","json_attr_t":"mysubarumq/` + v.Vin + `/doors/` + n + `/attr"}`
|
|
}
|
|
for n, w := range v.Windows {
|
|
// homeassistant/sensor/mysubaru/VIN-NUMBER-HERE/window_frontleft/config
|
|
position := w.Position + ` ` + w.SubPosition
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/sensor/`+v.Vin+`/`+n+`/config`] = `{` + device + origin + topic + `"name":"Window ` + position + `","uniq_id":"` + v.Vin + `_` + n + `_status","obj_id":"` + obj_id_prefix + n + `_status","ic":"mdi:car-door","stat_t":"mysubarumq/` + v.Vin + `/windows/state","val_tpl":"{{value_json.` + n + `}}"}`
|
|
}
|
|
for n, t := range v.Tires {
|
|
// homeassistant/sensor/mysubaru/VIN-NUMBER-HERE/tire_frontleft_psi/config
|
|
position := t.Position + ` ` + t.SubPosition
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/sensor/`+v.Vin+`/`+n+`_psi/config`] = `{` + device + origin + topic + `"name":"Tire ` + position + ` (Psi)","uniq_id":"` + v.Vin + `_` + n + `_psi","obj_id":"` + obj_id_prefix + n + `_psi","ic":"mdi:tire","stat_t":"mysubarumq/` + v.Vin + `/tires/state","val_tpl":"{{value_json.` + n + `}}","unit_of_meas":"psi"}`
|
|
}
|
|
|
|
topicState := `mysubarumq/` + v.Vin + `/ignition`
|
|
topicSet := `mysubarumq/` + v.Vin + `/ignition/set`
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/switch/`+v.Vin+`/ignition/config`] = `{` + origin + device + `"name":"Ignition","cmd_t":"` + topicSet + `","stat_t":"` + topicState + `","name":null,"obj_id":"` + obj_id_prefix + `_ignition","ic":"mdi:engine","pl_off":"OFF","pl_on":"ON","uniq_id":"` + v.Vin + `_ignition"}`
|
|
|
|
topicState = `mysubarumq/` + v.Vin + `/lock`
|
|
topicSet = `mysubarumq/` + v.Vin + `/lock/set`
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/lock/`+v.Vin+`/config`] = `{` + origin + device + `"name":"Lock","cmd_t":"` + topicSet + `","stat_t":"` + topicState + `","name":null,"obj_id":"` + obj_id_prefix + `_lock","ic":"mdi:car-key","pl_unlk":"UNLOCK","pl_lock":"LOCK","stat_locked":"LOCK","stat_unlocked":"UNLOCK","uniq_id":"` + v.Vin + `_lock"}`
|
|
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/event/`+v.Vin+`/engine/config`] = `{` + device + origin + `"name":"Ignition Events","dev_cla":"button","evt_typ":["start","stop"],"uniq_id":"` + v.Vin + `_event_ignition","obj_id":"` + obj_id_prefix + `_event_ignition","ic":"mdi:engine","stat_t":"mysubarumq/` + v.Vin + `/event/ignition"}`
|
|
hassioConfig[s.config.Hassio.Topics.Discovery+`/event/`+v.Vin+`/lock/config`] = `{` + device + origin + `"name":"Lock Events","dev_cla":"button","evt_typ":["lock","unlock"],"uniq_id":"` + v.Vin + `_event_lock","obj_id":"` + obj_id_prefix + `_event_lock","ic":"mdi:car-key","stat_t":"mysubarumq/` + v.Vin + `/event/lock"}`
|
|
|
|
// {"availability":[{"topic":"zigbee02/bridge/state"}],"availability_mode":"all","command_topic":"zigbee02/bridge/request/restart","device":{"hw_version":"zStack3x0 20230507","identifiers":["zigbee2mqtt_bridge_0x00124b00237e0682"],"manufacturer":"Zigbee2MQTT","model":"Bridge","name":"Zigbee2MQTT Bridge","sw_version":"1.35.1"},"device_class":"restart","name":"Restart","object_id":"zigbee2mqtt_bridge_restart","origin":{"name":"Zigbee2MQTT","sw":"1.35.1","url":"https://www.zigbee2mqtt.io"},"payload_press":"","unique_id":"bridge_0x00124b00237e0682_restart_zigbee02"}
|
|
// LOCK
|
|
// state_topic: "home-assistant/frontdoor/state"
|
|
// code_format: "^\\d{4}$"
|
|
// command_topic: "home-assistant/frontdoor/set"
|
|
// command_template: '{ "action": "{{ value }}", "code":"{{ code }}" }'
|
|
// payload_lock: "LOCK"
|
|
// payload_unlock: "UNLOCK"
|
|
// state_locked: "LOCK"
|
|
// state_unlocked: "UNLOCK"
|
|
// state_locking: "LOCKING"
|
|
// state_unlocking: "UNLOCKING"
|
|
// state_jammed: "MOTOR_JAMMED"
|
|
// state_ok: "MOTOR_OK"
|
|
// optimistic: false
|
|
// qos: 1
|
|
// retain: true
|
|
// value_template: "{{ value.x }}"
|
|
|
|
// mdi:tire
|
|
// mdi:car-door
|
|
// mdi:car-door-lock | mdi:car-door-lock-open | mdi:car-key
|
|
|
|
var msgs []*bus.Message
|
|
for topic, payload := range hassioConfig {
|
|
msgs = s.messages(topic, 1, true, payload, msgs)
|
|
s.log.Debug("hassio mqtt configuration", "topic", string(topic), "payload", string(payload))
|
|
}
|
|
return msgs
|
|
}
|
|
|
|
// mySubaruStatusToMQTTMessage .
|
|
func (s *MySubaruClient) mySubaruStatusToMQTTMessage(v *mysubaru.Vehicle) []*bus.Message {
|
|
var state = map[string]string{}
|
|
|
|
tank := ""
|
|
if v.DistanceToEmpty.Percentage > 0 && 101 > v.DistanceToEmpty.Percentage {
|
|
tank = `"dist_to_empty_pc":` + strconv.Itoa(v.DistanceToEmpty.Percentage) + `,`
|
|
}
|
|
state[`mysubarumq/`+v.Vin+`/state`] = `{` + tank + `"odometer_km":` + strconv.Itoa(v.Odometer.Kilometers) + `,"odometer_mi":` + strconv.Itoa(v.Odometer.Miles) + `,"dist_to_empty_km":` + strconv.Itoa(v.DistanceToEmpty.Kilometers) + `,"dist_to_empty_mi":` + strconv.Itoa(v.DistanceToEmpty.Miles) + `,"consumption_us":` + fmt.Sprintf("%.2f", v.FuelConsumptionAvg.MPG) + `,"consumption_eu":` + fmt.Sprintf("%.2f", v.FuelConsumptionAvg.LP100Km) + `,"engine_state":"` + v.EngineState + `","troubles":"` + strconv.Itoa(len(v.Troubles)) + `"}`
|
|
state[`mysubarumq/`+v.Vin+`/attr`] = `{"source_type":"gps","latitude":` + fmt.Sprintf("%.6f", v.GeoLocation.Latitude) + `,"longitude":` + fmt.Sprintf("%.6f", v.GeoLocation.Longitude) + `,"course":` + strconv.Itoa(v.GeoLocation.Heading) + `,"speed":` + fmt.Sprintf("%.2f", v.GeoLocation.Speed) + `,"friendly_name":"` + v.CarNickname + `"}`
|
|
|
|
// Doors
|
|
doors := `{`
|
|
// locks := `{`
|
|
dq := len(v.Doors)
|
|
dc := 1
|
|
for n, d := range v.Doors {
|
|
doors = doors + `"` + n + `":"` + d.Status + `"`
|
|
// locks = locks + `"` + n + `":"` + d.Lock + `"`
|
|
if dc != dq {
|
|
doors = doors + `,`
|
|
// locks = locks + `,`
|
|
}
|
|
dc++
|
|
}
|
|
doors = doors + `}`
|
|
// locks = locks + `}`
|
|
state[`mysubarumq/`+v.Vin+`/doors/state`] = doors
|
|
// state[`mysubarumq/`+v.Vin+`/doors/attr`] = locks
|
|
|
|
// Windows
|
|
windows := `{`
|
|
wq := len(v.Windows)
|
|
wc := 1
|
|
for n, w := range v.Windows {
|
|
windows = windows + `"` + n + `":"` + w.Status + `"`
|
|
if wc != wq {
|
|
windows = windows + `,`
|
|
}
|
|
wc++
|
|
}
|
|
windows = windows + `}`
|
|
state[`mysubarumq/`+v.Vin+`/windows/state`] = windows
|
|
|
|
// Tires
|
|
tires := `{`
|
|
tq := len(v.Tires)
|
|
tc := 1
|
|
for n, t := range v.Tires {
|
|
tires = tires + `"` + n + `":` + strconv.Itoa(t.PressurePsi)
|
|
if tc != tq {
|
|
tires = tires + `,`
|
|
}
|
|
tc++
|
|
}
|
|
tires = tires + `}`
|
|
state[`mysubarumq/`+v.Vin+`/tires/state`] = tires
|
|
|
|
// Vehicle Health Items
|
|
troubles := `{`
|
|
trq := len(v.Troubles)
|
|
trc := 1
|
|
for n, t := range v.Troubles {
|
|
troubles = troubles + `"` + n + `":"` + t.Description + `"`
|
|
if trc != trq {
|
|
troubles = troubles + `,`
|
|
}
|
|
trc++
|
|
}
|
|
troubles = troubles + `}`
|
|
state[`mysubarumq/`+v.Vin+`/troubles/attr`] = troubles
|
|
|
|
var msgs []*bus.Message
|
|
for topic, payload := range state {
|
|
msgs = s.messages(topic, 0, false, payload, msgs)
|
|
s.log.Debug("hassio mqtt configuration", "topic", string(topic), "payload", string(payload))
|
|
}
|
|
|
|
return msgs
|
|
}
|
|
|
|
// messages .
|
|
func (s *MySubaruClient) messages(t string, q byte, r bool, p string, l []*bus.Message) []*bus.Message {
|
|
s.log.Debug("hassio mqtt configuration", "topic", string(t), "qos", q, "retained", r, "payload", string(p))
|
|
m := bus.Message{
|
|
Topic: t,
|
|
QOS: q,
|
|
Retained: r,
|
|
Payload: p,
|
|
}
|
|
|
|
if l != nil {
|
|
l = append(l, &m)
|
|
return l
|
|
} else {
|
|
var l []*bus.Message
|
|
l = append(l, &m)
|
|
return l
|
|
}
|
|
}
|