188 lines
4.6 KiB
Go
188 lines
4.6 KiB
Go
// Copyright (c) 2024 Joshua Rich <joshua.rich@gmail.com>
|
|
//
|
|
// This software is released under the MIT License.
|
|
// https://opensource.org/licenses/MIT
|
|
|
|
package net
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"strconv"
|
|
|
|
"github.com/godbus/dbus/v5"
|
|
"github.com/iancoleman/strcase"
|
|
|
|
"github.com/joshuar/go-hass-agent/internal/linux"
|
|
"github.com/joshuar/go-hass-agent/pkg/linux/dbusx"
|
|
)
|
|
|
|
const (
|
|
ipv4 = 4
|
|
ipv6 = 6
|
|
)
|
|
|
|
type connectionAddrSensor struct {
|
|
bus *dbusx.Bus
|
|
configProp *dbusx.Property[dbus.ObjectPath]
|
|
addr string
|
|
gateway string
|
|
nameservers []string
|
|
linux.Sensor
|
|
prefix int
|
|
ver int
|
|
}
|
|
|
|
func (c *connectionAddrSensor) Attributes() map[string]any {
|
|
attributes := c.Sensor.Attributes()
|
|
attributes["prefix"] = c.prefix
|
|
attributes["gateway"] = c.gateway
|
|
|
|
if c.ver == ipv4 {
|
|
attributes["nameservers"] = c.nameservers
|
|
}
|
|
|
|
return attributes
|
|
}
|
|
|
|
func (c *connectionAddrSensor) State() any {
|
|
return c.addr
|
|
}
|
|
|
|
func (c *connectionAddrSensor) setState(configPath any) error {
|
|
var path string
|
|
// Acceptable values for configPath are converted to a string.
|
|
switch value := configPath.(type) {
|
|
case string:
|
|
path = value
|
|
case dbus.ObjectPath:
|
|
path = string(value)
|
|
case dbus.Variant:
|
|
variant, err := dbusx.VariantToValue[string](value)
|
|
if err != nil {
|
|
return fmt.Errorf("could not transform variant into objectPath: %w", err)
|
|
}
|
|
|
|
path = variant
|
|
default:
|
|
return ErrUnsupportedValue
|
|
}
|
|
|
|
if err := c.updateAddr(path); err != nil {
|
|
return fmt.Errorf("could not update address: %w", err)
|
|
}
|
|
|
|
c.updateGateway(path)
|
|
c.updateNameservers(path)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *connectionAddrSensor) updateState() error {
|
|
configPath, err := c.configProp.Get()
|
|
if err != nil {
|
|
return fmt.Errorf("cannot update address: %w", err)
|
|
}
|
|
|
|
if err := c.setState(configPath); err != nil {
|
|
return fmt.Errorf("cannot update address: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *connectionAddrSensor) updateAddr(path string) error {
|
|
addrIntr := dBusNMObj + ".IP" + strconv.Itoa(c.ver) + "Config.AddressData"
|
|
|
|
// Get the address details property using the given config path.
|
|
addrDetails, err := dbusx.NewProperty[[]map[string]dbus.Variant](c.bus, path, dBusNMObj, addrIntr).Get()
|
|
if err != nil {
|
|
return fmt.Errorf("could not retrieve address data from D-Bus: %w", err)
|
|
}
|
|
|
|
var (
|
|
address string
|
|
prefix int
|
|
)
|
|
|
|
if len(addrDetails) > 0 {
|
|
address, err = dbusx.VariantToValue[string](addrDetails[0]["address"])
|
|
if err != nil {
|
|
return fmt.Errorf("could not parse address: %w", err)
|
|
}
|
|
|
|
prefix, err = dbusx.VariantToValue[int](addrDetails[0]["prefix"])
|
|
if err != nil {
|
|
return fmt.Errorf("could not parse prefix: %w", err)
|
|
}
|
|
}
|
|
|
|
c.addr = address
|
|
c.prefix = prefix
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *connectionAddrSensor) updateGateway(configPath string) {
|
|
gatewayIntr := dBusNMObj + ".IP" + strconv.Itoa(c.ver) + "Config.Gateway"
|
|
|
|
// Get the gateway property using the given config path.
|
|
gateway, err := dbusx.NewProperty[string](c.bus, configPath, dBusNMObj, gatewayIntr).Get()
|
|
if err != nil {
|
|
slog.With(slog.String("connection", c.Name())).Debug("Could not retrieve gateway from D-Bus.", slog.Any("error", err))
|
|
} else {
|
|
c.gateway = gateway
|
|
}
|
|
}
|
|
|
|
func (c *connectionAddrSensor) updateNameservers(path string) {
|
|
if c.ver == ipv6 {
|
|
return
|
|
}
|
|
|
|
nameserversIntr := dBusNMObj + ".IP" + strconv.Itoa(c.ver) + "Config.NameserverData"
|
|
|
|
// Get the gateway property using the given config path.
|
|
nameservers, err := dbusx.NewProperty[[]map[string]dbus.Variant](c.bus, path, dBusNMObj, nameserversIntr).Get()
|
|
if err != nil {
|
|
slog.With(slog.String("connection", c.Name())).Debug("Could not retrieve nameservers from D-Bus.", slog.Any("error", err))
|
|
} else {
|
|
for _, details := range nameservers {
|
|
nameserver, err := dbusx.VariantToValue[string](details["address"])
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
c.nameservers = append(c.nameservers, nameserver)
|
|
}
|
|
}
|
|
}
|
|
|
|
func newConnectionAddrSensor(bus *dbusx.Bus, ver int, connectionPath, connectionName string) *connectionAddrSensor {
|
|
var configPropName string
|
|
|
|
name := connectionName + " Connection IPv" + strconv.Itoa(ver) + " Address"
|
|
id := strcase.ToSnake(connectionName) + "_connection_ipv" + strconv.Itoa(ver) + "_address"
|
|
icon := "mdi:numeric-" + strconv.Itoa(ver)
|
|
|
|
switch ver {
|
|
case ipv4:
|
|
configPropName = connectionIPv4ConfigProp
|
|
case ipv6:
|
|
configPropName = connectionIPv6ConfigProp
|
|
}
|
|
|
|
return &connectionAddrSensor{
|
|
bus: bus,
|
|
ver: ver,
|
|
Sensor: linux.Sensor{
|
|
DataSource: linux.DataSrcDbus,
|
|
IconString: icon,
|
|
IsDiagnostic: true,
|
|
DisplayName: name,
|
|
UniqueID: id,
|
|
},
|
|
configProp: dbusx.NewProperty[dbus.ObjectPath](bus, connectionPath, dBusNMObj, configPropName),
|
|
}
|
|
}
|