package gateway import ( "context" "fmt" "gitea.nik4nao.com/nik/home-services/discord-bot/internal/core/ports/driven" hav1 "gitea.nik4nao.com/nik/home-services/gen/ha/v1" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) type Client struct { conn *grpc.ClientConn lightClient hav1.LightServiceClient switchClient hav1.SwitchServiceClient } func New(ctx context.Context, addr string) (*Client, error) { conn, err := grpc.NewClient( addr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithStatsHandler(otelgrpc.NewClientHandler()), ) if err != nil { return nil, fmt.Errorf("dial ha-gateway: %w", err) } return &Client{ conn: conn, lightClient: hav1.NewLightServiceClient(conn), switchClient: hav1.NewSwitchServiceClient(conn), }, nil } func (c *Client) Close() error { if err := c.conn.Close(); err != nil { return fmt.Errorf("close ha-gateway client: %w", err) } return nil } func (c *Client) ListLights(ctx context.Context) ([]driven.Light, error) { resp, err := c.lightClient.ListLights(ctx, &hav1.ListLightsRequest{}) if err != nil { return nil, fmt.Errorf("list lights: %w", err) } lights := make([]driven.Light, 0, len(resp.GetLights())) for _, light := range resp.GetLights() { lights = append(lights, driven.Light{ EntityID: light.GetEntityId(), FriendlyName: light.GetFriendlyName(), State: light.GetState(), SupportedColorModes: append([]string(nil), light.GetSupportedColorModes()...), MinColorTempKelvin: light.GetMinColorTempKelvin(), MaxColorTempKelvin: light.GetMaxColorTempKelvin(), IsHueGroup: light.GetIsHueGroup(), EffectList: append([]string(nil), light.GetEffectList()...), }) } return lights, nil } func (c *Client) ListSwitches(ctx context.Context) ([]driven.Switch, error) { resp, err := c.switchClient.ListSwitches(ctx, &hav1.ListSwitchesRequest{}) if err != nil { return nil, fmt.Errorf("list switches: %w", err) } switches := make([]driven.Switch, 0, len(resp.GetSwitches())) for _, sw := range resp.GetSwitches() { switches = append(switches, driven.Switch{ EntityID: sw.GetEntityId(), FriendlyName: sw.GetFriendlyName(), State: sw.GetState(), DeviceClass: sw.GetDeviceClass(), }) } return switches, nil } func (c *Client) TurnOnLight(ctx context.Context, entityID string, brightnessPct *uint32, colorTempKelvin *uint32) error { req := &hav1.TurnOnRequest{EntityId: entityID} if brightnessPct != nil { req.BrightnessPct = brightnessPct } if colorTempKelvin != nil { req.ColorTempKelvin = colorTempKelvin } if _, err := c.lightClient.TurnOn(ctx, req); err != nil { return fmt.Errorf("turn on light %s: %w", entityID, err) } return nil } func (c *Client) TurnOffLight(ctx context.Context, entityID string, transition *uint32) error { req := &hav1.TurnOffRequest{EntityId: entityID} if transition != nil { req.Transition = transition } if _, err := c.lightClient.TurnOff(ctx, req); err != nil { return fmt.Errorf("turn off light %s: %w", entityID, err) } return nil } func (c *Client) ToggleLight(ctx context.Context, entityID string) error { if _, err := c.lightClient.Toggle(ctx, &hav1.ToggleRequest{EntityId: entityID}); err != nil { return fmt.Errorf("toggle light %s: %w", entityID, err) } return nil }