Skip to content

Commit

Permalink
refactor(linux): ♻️ improve code readability
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuar committed Jun 8, 2024
1 parent 17927ed commit f344aeb
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 65 deletions.
4 changes: 4 additions & 0 deletions internal/linux/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ func Chassis() string {
// used for getting information on running apps.
func FindPortal() string {
desktop := os.Getenv("XDG_CURRENT_DESKTOP")

switch {
case strings.Contains(desktop, "KDE"):
return "org.freedesktop.impl.portal.desktop.kde"
Expand Down Expand Up @@ -223,9 +224,12 @@ func GetDistroDetails() (name, version string) {
}

// GetKernelVersion will retrieve the kernel version.
//
//nolint:prealloc
func GetKernelVersion() string {
var utsname syscall.Utsname
var versionBytes []byte

err := syscall.Uname(&utsname)
if err != nil {
log.Warn().Err(err).Msg("Could not retrieve kernel version.")
Expand Down
56 changes: 31 additions & 25 deletions internal/linux/device_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

//nolint:paralleltest,dupl
package linux

import (
Expand All @@ -13,54 +14,58 @@ import (
"testing"

mqtthass "github.com/joshuar/go-hass-anything/v9/pkg/hass"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/joshuar/go-hass-agent/internal/preferences"
"github.com/joshuar/go-hass-agent/pkg/linux/whichdistro"
)

func compareDevice(t *testing.T, a, b *Device) bool {
func compareDevice(t *testing.T, got, want *Device) bool {
t.Helper()

switch {
case !reflect.DeepEqual(a.appName, b.appName):
case !reflect.DeepEqual(got.appName, want.appName):
t.Error("appName does not match")
return false
case !reflect.DeepEqual(a.appVersion, b.appVersion):
case !reflect.DeepEqual(got.appVersion, want.appVersion):
t.Error("appVersion does not match")
return false
case !reflect.DeepEqual(a.distro, b.distro):
case !reflect.DeepEqual(got.distro, want.distro):
t.Error("distro does not match")
return false
case !reflect.DeepEqual(a.distroVersion, b.distroVersion):
case !reflect.DeepEqual(got.distroVersion, want.distroVersion):
t.Error("distroVersion does not match")
return false
case !reflect.DeepEqual(a.hostname, b.hostname):
case !reflect.DeepEqual(got.hostname, want.hostname):
t.Error("hostname does not match")
return false
case !reflect.DeepEqual(a.hwModel, b.hwModel):
case !reflect.DeepEqual(got.hwModel, want.hwModel):
t.Error("hwModel does not match")
return false
case !reflect.DeepEqual(a.hwVendor, b.hwVendor):
case !reflect.DeepEqual(got.hwVendor, want.hwVendor):
t.Error("hwVendor does not match")
return false
}
return true
}

func compareMQTTDevice(t *testing.T, a, b *mqtthass.Device) bool {
func compareMQTTDevice(t *testing.T, got, want *mqtthass.Device) bool {
t.Helper()

switch {
case !reflect.DeepEqual(a.Name, b.Name):
case !reflect.DeepEqual(got.Name, want.Name):
t.Error("name does not match")
return false
case !reflect.DeepEqual(a.URL, b.URL):
case !reflect.DeepEqual(got.URL, want.URL):
t.Error("URL does not match")
return false
case !reflect.DeepEqual(a.SWVersion, b.SWVersion):
case !reflect.DeepEqual(got.SWVersion, want.SWVersion):
t.Error("SWVersion does not match")
return false
case !reflect.DeepEqual(a.Manufacturer, b.Manufacturer):
case !reflect.DeepEqual(got.Manufacturer, want.Manufacturer):
t.Error("Manufacturer does not match")
return false
case !reflect.DeepEqual(a.Model, b.Model):
case !reflect.DeepEqual(got.Model, want.Model):
t.Error("Model does not match")
return false
}
Expand Down Expand Up @@ -92,9 +97,9 @@ func TestNewDevice(t *testing.T) {
osReleaseFiles []string
}
tests := []struct {
want *Device
name string
args args
want *Device
}{
{
name: "with OS Release",
Expand Down Expand Up @@ -139,8 +144,8 @@ func TestMQTTDevice(t *testing.T) {
Identifiers: []string{dev.DeviceID()},
}
tests := []struct {
name string
want *mqtthass.Device
name string
}{
{
name: "default",
Expand All @@ -156,6 +161,7 @@ func TestMQTTDevice(t *testing.T) {
}
}

//revive:disable:unhandled-error
func TestFindPortal(t *testing.T) {
type args struct {
setup func()
Expand Down Expand Up @@ -202,36 +208,36 @@ func TestGetDistroID(t *testing.T) {
id := "testdistro"
goodFile := filepath.Join(t.TempDir(), "goodfile")
fh, err := os.Create(goodFile)
assert.Nil(t, err)
require.NoError(t, err)
fmt.Fprintln(fh, `VERSION_ID="`+versionID+`"`)
fmt.Fprintln(fh, `ID="`+id+`"`)
fh.Close()

tests := []struct {
name string
wantId string
wantID string
wantVersionid string
osReleaseFile string
}{
{
name: "File exists",
wantId: id,
wantID: id,
wantVersionid: versionID,
osReleaseFile: goodFile,
},
{
name: "File does not exist.",
wantId: unknownDistro,
wantID: unknownDistro,
wantVersionid: unknownDistroVersion,
osReleaseFile: "/dev/null",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
whichdistro.OSReleaseFile = tt.osReleaseFile
gotId, gotVersionid := GetDistroID()
if gotId != tt.wantId {
t.Errorf("GetDistroID() gotId = %v, want %v", gotId, tt.wantId)
gotID, gotVersionid := GetDistroID()
if gotID != tt.wantID {
t.Errorf("GetDistroID() gotId = %v, want %v", gotID, tt.wantID)
}
if gotVersionid != tt.wantVersionid {
t.Errorf("GetDistroID() gotVersionid = %v, want %v", gotVersionid, tt.wantVersionid)
Expand All @@ -245,7 +251,7 @@ func TestGetDistroDetails(t *testing.T) {
name := "Test Distro"
goodFile := filepath.Join(t.TempDir(), "goodfile")
fh, err := os.Create(goodFile)
assert.Nil(t, err)
require.NoError(t, err)
fmt.Fprintln(fh, `VERSION="`+version+`"`)
fmt.Fprintln(fh, `NAME="`+name+`"`)
fh.Close()
Expand Down
27 changes: 20 additions & 7 deletions internal/linux/location/location.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ package location

import (
"context"
"errors"

"github.com/godbus/dbus/v5"
"github.com/rs/zerolog/log"
Expand Down Expand Up @@ -37,26 +36,30 @@ type locationSensor struct {
linux.Sensor
}

//revive:disable:unused-receiver
//nolint:unused-receiver
func (s *locationSensor) Name() string { return "Location" }

//revive:disable:unused-receiver
//nolint:unused-receiver
func (s *locationSensor) ID() string { return "location" }

type worker struct {
clientPath dbus.ObjectPath
}

//nolint:exhaustruct
func (w *worker) Setup(ctx context.Context) *dbusx.Watch {
var err error
w.clientPath, err = dbusx.GetData[dbus.ObjectPath](ctx, dbusx.SystemBus, managerPath, geoclueInterface, getClientCall)

if !w.clientPath.IsValid() || err != nil {
log.Error().Err(err).Msg("Could not set up a geoclue client.")

return nil
}

if err = dbusx.SetProp(ctx, dbusx.SystemBus, string(w.clientPath), geoclueInterface, desktopIDProp, preferences.AppID); err != nil {
log.Error().Err(err).Msg("Could not set a geoclue client id.")

return nil
}

Expand All @@ -73,6 +76,7 @@ func (w *worker) Setup(ctx context.Context) *dbusx.Watch {
// Request to start tracking location updates.
if err = dbusx.Call(ctx, dbusx.SystemBus, string(w.clientPath), geoclueInterface, startCall); err != nil {
log.Warn().Err(err).Msg("Could not start geoclue client.")

return nil
}

Expand All @@ -91,14 +95,17 @@ func (w *worker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) chan s

go func() {
defer close(sensorCh)

for {
select {
case <-ctx.Done():
err := dbusx.Call(ctx, dbusx.SystemBus, string(w.clientPath), geoclueInterface, stopCall)
if err != nil {
log.Debug().Caller().Err(err).Msg("Failed to stop location updater.")

return
}

return
case event := <-triggerCh:
if locationPath, ok := event.Content[1].(dbus.ObjectPath); ok {
Expand All @@ -109,14 +116,16 @@ func (w *worker) Watch(ctx context.Context, triggerCh chan dbusx.Trigger) chan s
}
}
}()

return sensorCh
}

//revive:disable:unused-receiver
func (w *worker) Sensors(_ context.Context) ([]sensor.Details, error) {
return nil, errors.New("unimplemented")
return nil, linux.ErrUnimplemented
}

//nolint:exhaustruct
func NewLocationWorker() (*linux.SensorWorker, error) {
return &linux.SensorWorker{
WorkerName: "Location Sensor",
Expand All @@ -126,22 +135,26 @@ func NewLocationWorker() (*linux.SensorWorker, error) {
nil
}

//nolint:exhaustruct
func newLocation(ctx context.Context, locationPath dbus.ObjectPath) *locationSensor {
getProp := func(prop string) float64 {
value, err := dbusx.GetProp[float64](ctx, dbusx.SystemBus, string(locationPath), geoclueInterface, locationInterface+"."+prop)
if err != nil {
log.Debug().Caller().Err(err).
Msgf("Could not retrieve %s.", prop)

return 0
}

return value
}
s := &locationSensor{}
s.Value = &sensor.LocationRequest{
location := &locationSensor{}
location.Value = &sensor.LocationRequest{
Gps: []float64{getProp("Latitude"), getProp("Longitude")},
GpsAccuracy: int(getProp("Accuracy")),
Speed: int(getProp("Speed")),
Altitude: int(getProp("Altitude")),
}
return s

return location
}
22 changes: 13 additions & 9 deletions internal/linux/sensor.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package linux

import (
"errors"
"fmt"
"strings"

Expand All @@ -20,6 +21,8 @@ const (
DataSrcSysfs = "SysFS"
)

var ErrUnimplemented = errors.New("unimplemented functionality")

// Sensor represents a generic sensor on the Linux platform. Most sensors
// will be able to use this struct, which satisfies the sensor.Sensor
// interface, alllowing them to be sent as a sensor to Home Assistant.
Expand Down Expand Up @@ -84,7 +87,7 @@ func (l *Sensor) Units() string {
func (l *Sensor) Attributes() any {
if l.SensorSrc != "" {
return struct {
DataSource string `json:"Data Source"`
DataSource string `json:"data_source"`
}{
DataSource: l.SensorSrc,
}
Expand All @@ -93,20 +96,21 @@ func (l *Sensor) Attributes() any {
}

func (l *Sensor) String() string {
var s strings.Builder
fmt.Fprintf(&s, "Name: %s (ID: %s)", l.Name(), l.ID())
var sensorStr strings.Builder

fmt.Fprintf(&sensorStr, "Name: %s (ID: %s)", l.Name(), l.ID())
if l.DeviceClass() > 0 {
fmt.Fprintf(&s, " Device Class: %s", l.DeviceClass().String())
fmt.Fprintf(&sensorStr, " Device Class: %s", l.DeviceClass().String())
}
if l.StateClass() > 0 {
fmt.Fprintf(&s, " State Class: %s", l.DeviceClass().String())
fmt.Fprintf(&sensorStr, " State Class: %s", l.DeviceClass().String())
}
fmt.Fprintf(&s, " Value: %v", l.Value)
fmt.Fprintf(&sensorStr, " Value: %v", l.Value)
if l.UnitsString != "" {
fmt.Fprintf(&s, " %s", l.UnitsString)
fmt.Fprintf(&sensorStr, " %s", l.UnitsString)
}
if l.Attributes() != nil {
fmt.Fprintf(&s, " Attributes: %v", l.Attributes())
fmt.Fprintf(&sensorStr, " Attributes: %v", l.Attributes())
}
return s.String()
return sensorStr.String()
}
Loading

0 comments on commit f344aeb

Please sign in to comment.