package app import ( "context" "fmt" "strings" "gitea.nik4nao.com/nik/home-services/ha-gateway/internal/core/domain" "gitea.nik4nao.com/nik/home-services/ha-gateway/internal/core/ports/driven" ) type EntityApp struct { ha driven.HAClient } // NewEntityApp constructs the entity application service. func NewEntityApp(ha driven.HAClient) *EntityApp { return &EntityApp{ha: ha} } // GetState loads a single entity state through the HA driven port. func (a *EntityApp) GetState(ctx context.Context, id domain.EntityID) (*domain.EntityState, error) { s, err := a.ha.GetState(ctx, string(id)) if err != nil { return nil, err } return haStateToDomain(s), nil } // ListStates filters the full Home Assistant state list by explicit IDs and/or domain. func (a *EntityApp) ListStates(ctx context.Context, ids []domain.EntityID, domainFilter string) ([]*domain.EntityState, error) { all, err := a.ha.ListStates(ctx) if err != nil { return nil, err } idSet := make(map[string]struct{}, len(ids)) for _, id := range ids { idSet[string(id)] = struct{}{} } var out []*domain.EntityState for _, s := range all { if len(ids) > 0 { if _, ok := idSet[s.EntityID]; !ok { continue } } if domainFilter != "" { if !strings.HasPrefix(s.EntityID, domainFilter+".") { continue } } out = append(out, haStateToDomain(s)) } return out, nil } // haStateToDomain stringifies raw Home Assistant attributes so the core model // stays transport-agnostic and easy to serialize over gRPC. func haStateToDomain(s *driven.HAState) *domain.EntityState { attrs := make(map[string]string, len(s.Attributes)) for k, v := range s.Attributes { attrs[k] = fmt.Sprintf("%v", v) } return &domain.EntityState{ EntityID: domain.EntityID(s.EntityID), State: s.State, Attributes: attrs, LastChanged: s.LastChanged, LastUpdated: s.LastUpdated, } }