src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

temp.go (2299B)


      1 package temp
      2 
      3 import (
      4 	"context"
      5 	"encoding/json"
      6 	"fmt"
      7 	"os/exec"
      8 	"strings"
      9 )
     10 
     11 // Sensor describes a single temperature sensor to be read.
     12 type Sensor struct {
     13 	Label string   `json:"label"`
     14 	Path  []string `json:"path"`
     15 }
     16 
     17 // Config holds the configuration for the temp block.
     18 type Config struct {
     19 	Sensors []Sensor `json:"sensors"`
     20 }
     21 
     22 type Block struct {
     23 	config Config
     24 }
     25 
     26 func New(c Config) *Block {
     27 	return &Block{config: c}
     28 }
     29 
     30 func (b *Block) Name() string {
     31 	return "temp"
     32 }
     33 
     34 func (b *Block) Render(ctx context.Context) (string, error) {
     35 	out, err := exec.CommandContext(ctx, "sensors", "-j").Output()
     36 	if err != nil {
     37 		return "", fmt.Errorf("exec sensors failed: %v", err)
     38 	}
     39 	if len(b.config.Sensors) == 0 {
     40 		return "", nil
     41 	}
     42 
     43 	var parts = make([]string, 0, len(b.config.Sensors))
     44 	for _, sensor := range b.config.Sensors {
     45 		temp, err := parse(out, sensor.Path)
     46 		if err != nil {
     47 			return "", fmt.Errorf(
     48 				"failed to get temp for %v: %w",
     49 				sensor.Path, err,
     50 			)
     51 		}
     52 
     53 		var renderedSensor string
     54 		if sensor.Label != "" {
     55 			renderedSensor = fmt.Sprintf(
     56 				"%s %.0f℃", sensor.Label, temp,
     57 			)
     58 		} else {
     59 			renderedSensor = fmt.Sprintf("%.0f℃", temp)
     60 		}
     61 		parts = append(parts, renderedSensor)
     62 	}
     63 
     64 	return strings.Join(parts, " "), nil
     65 }
     66 
     67 // parse traverses the json data using the given path to find a temperature
     68 // value.
     69 func parse(data []byte, path []string) (float64, error) {
     70 	if len(path) == 0 {
     71 		return 0, fmt.Errorf("sensor path cannot be empty")
     72 	}
     73 
     74 	// We start with the full JSON output.
     75 	var current = json.RawMessage(data)
     76 
     77 	// For each key in the path, we dive one level deeper into the JSON
     78 	// structure.
     79 	for i, key := range path {
     80 		var m map[string]json.RawMessage
     81 		if err := json.Unmarshal(current, &m); err != nil {
     82 			return 0, fmt.Errorf(
     83 				"failed to unmarshal object at path segment %d ('%s'): %w",
     84 				i, key, err,
     85 			)
     86 		}
     87 
     88 		var ok bool
     89 		current, ok = m[key]
     90 		if !ok {
     91 			return 0, fmt.Errorf(
     92 				"key not found at path segment %d ('%s')",
     93 				i, key,
     94 			)
     95 		}
     96 	}
     97 
     98 	// After the loop, 'current' holds the raw JSON for the temperature
     99 	// value.
    100 	var temp float64
    101 	if err := json.Unmarshal(current, &temp); err != nil {
    102 		return 0, fmt.Errorf(
    103 			"final value at path %v is not a number: %w", path, err,
    104 		)
    105 	}
    106 
    107 	return temp, nil
    108 }