Added new app, ha-gateway
This commit is contained in:
parent
3d9690e939
commit
2e99c464ff
10
buf.gen.yaml
Normal file
10
buf.gen.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
version: v2
|
||||
plugins:
|
||||
- remote: buf.build/protocolbuffers/go
|
||||
out: gen
|
||||
opt:
|
||||
- paths=source_relative
|
||||
- remote: buf.build/grpc/go
|
||||
out: gen
|
||||
opt:
|
||||
- paths=source_relative
|
||||
15
gen/go.mod
Normal file
15
gen/go.mod
Normal file
@ -0,0 +1,15 @@
|
||||
module gitea.nik4nao.com/nik/home-services/gen
|
||||
|
||||
go 1.26
|
||||
|
||||
require (
|
||||
google.golang.org/grpc v1.79.3
|
||||
google.golang.org/protobuf v1.36.11
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
)
|
||||
38
gen/go.sum
Normal file
38
gen/go.sum
Normal file
@ -0,0 +1,38 @@
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
|
||||
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
|
||||
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
|
||||
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
|
||||
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
|
||||
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
230
gen/ha/v1/common.pb.go
Normal file
230
gen/ha/v1/common.pb.go
Normal file
@ -0,0 +1,230 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc (unknown)
|
||||
// source: ha/v1/common.proto
|
||||
|
||||
package hav1
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type EntityState struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
EntityId string `protobuf:"bytes,1,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"`
|
||||
State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"`
|
||||
Attributes map[string]string `protobuf:"bytes,3,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
LastChanged string `protobuf:"bytes,4,opt,name=last_changed,json=lastChanged,proto3" json:"last_changed,omitempty"` // RFC3339
|
||||
LastUpdated string `protobuf:"bytes,5,opt,name=last_updated,json=lastUpdated,proto3" json:"last_updated,omitempty"` // RFC3339
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *EntityState) Reset() {
|
||||
*x = EntityState{}
|
||||
mi := &file_ha_v1_common_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *EntityState) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*EntityState) ProtoMessage() {}
|
||||
|
||||
func (x *EntityState) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ha_v1_common_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use EntityState.ProtoReflect.Descriptor instead.
|
||||
func (*EntityState) Descriptor() ([]byte, []int) {
|
||||
return file_ha_v1_common_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *EntityState) GetEntityId() string {
|
||||
if x != nil {
|
||||
return x.EntityId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *EntityState) GetState() string {
|
||||
if x != nil {
|
||||
return x.State
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *EntityState) GetAttributes() map[string]string {
|
||||
if x != nil {
|
||||
return x.Attributes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *EntityState) GetLastChanged() string {
|
||||
if x != nil {
|
||||
return x.LastChanged
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *EntityState) GetLastUpdated() string {
|
||||
if x != nil {
|
||||
return x.LastUpdated
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type RGBColor struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
R uint32 `protobuf:"varint,1,opt,name=r,proto3" json:"r,omitempty"`
|
||||
G uint32 `protobuf:"varint,2,opt,name=g,proto3" json:"g,omitempty"`
|
||||
B uint32 `protobuf:"varint,3,opt,name=b,proto3" json:"b,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RGBColor) Reset() {
|
||||
*x = RGBColor{}
|
||||
mi := &file_ha_v1_common_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *RGBColor) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RGBColor) ProtoMessage() {}
|
||||
|
||||
func (x *RGBColor) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ha_v1_common_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RGBColor.ProtoReflect.Descriptor instead.
|
||||
func (*RGBColor) Descriptor() ([]byte, []int) {
|
||||
return file_ha_v1_common_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *RGBColor) GetR() uint32 {
|
||||
if x != nil {
|
||||
return x.R
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *RGBColor) GetG() uint32 {
|
||||
if x != nil {
|
||||
return x.G
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *RGBColor) GetB() uint32 {
|
||||
if x != nil {
|
||||
return x.B
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
var File_ha_v1_common_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_ha_v1_common_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x12ha/v1/common.proto\x12\x05ha.v1\"\x89\x02\n" +
|
||||
"\vEntityState\x12\x1b\n" +
|
||||
"\tentity_id\x18\x01 \x01(\tR\bentityId\x12\x14\n" +
|
||||
"\x05state\x18\x02 \x01(\tR\x05state\x12B\n" +
|
||||
"\n" +
|
||||
"attributes\x18\x03 \x03(\v2\".ha.v1.EntityState.AttributesEntryR\n" +
|
||||
"attributes\x12!\n" +
|
||||
"\flast_changed\x18\x04 \x01(\tR\vlastChanged\x12!\n" +
|
||||
"\flast_updated\x18\x05 \x01(\tR\vlastUpdated\x1a=\n" +
|
||||
"\x0fAttributesEntry\x12\x10\n" +
|
||||
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
|
||||
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"4\n" +
|
||||
"\bRGBColor\x12\f\n" +
|
||||
"\x01r\x18\x01 \x01(\rR\x01r\x12\f\n" +
|
||||
"\x01g\x18\x02 \x01(\rR\x01g\x12\f\n" +
|
||||
"\x01b\x18\x03 \x01(\rR\x01bB4Z2gitea.nik4nao.com/nik/home-services/gen/ha/v1;hav1b\x06proto3"
|
||||
|
||||
var (
|
||||
file_ha_v1_common_proto_rawDescOnce sync.Once
|
||||
file_ha_v1_common_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_ha_v1_common_proto_rawDescGZIP() []byte {
|
||||
file_ha_v1_common_proto_rawDescOnce.Do(func() {
|
||||
file_ha_v1_common_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ha_v1_common_proto_rawDesc), len(file_ha_v1_common_proto_rawDesc)))
|
||||
})
|
||||
return file_ha_v1_common_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_ha_v1_common_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
|
||||
var file_ha_v1_common_proto_goTypes = []any{
|
||||
(*EntityState)(nil), // 0: ha.v1.EntityState
|
||||
(*RGBColor)(nil), // 1: ha.v1.RGBColor
|
||||
nil, // 2: ha.v1.EntityState.AttributesEntry
|
||||
}
|
||||
var file_ha_v1_common_proto_depIdxs = []int32{
|
||||
2, // 0: ha.v1.EntityState.attributes:type_name -> ha.v1.EntityState.AttributesEntry
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_ha_v1_common_proto_init() }
|
||||
func file_ha_v1_common_proto_init() {
|
||||
if File_ha_v1_common_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_ha_v1_common_proto_rawDesc), len(file_ha_v1_common_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 3,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_ha_v1_common_proto_goTypes,
|
||||
DependencyIndexes: file_ha_v1_common_proto_depIdxs,
|
||||
MessageInfos: file_ha_v1_common_proto_msgTypes,
|
||||
}.Build()
|
||||
File_ha_v1_common_proto = out.File
|
||||
file_ha_v1_common_proto_goTypes = nil
|
||||
file_ha_v1_common_proto_depIdxs = nil
|
||||
}
|
||||
285
gen/ha/v1/entity.pb.go
Normal file
285
gen/ha/v1/entity.pb.go
Normal file
@ -0,0 +1,285 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc (unknown)
|
||||
// source: ha/v1/entity.proto
|
||||
|
||||
package hav1
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type GetStateRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
EntityId string `protobuf:"bytes,1,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GetStateRequest) Reset() {
|
||||
*x = GetStateRequest{}
|
||||
mi := &file_ha_v1_entity_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GetStateRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetStateRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetStateRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ha_v1_entity_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetStateRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetStateRequest) Descriptor() ([]byte, []int) {
|
||||
return file_ha_v1_entity_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *GetStateRequest) GetEntityId() string {
|
||||
if x != nil {
|
||||
return x.EntityId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type GetStateResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
State *EntityState `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GetStateResponse) Reset() {
|
||||
*x = GetStateResponse{}
|
||||
mi := &file_ha_v1_entity_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GetStateResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetStateResponse) ProtoMessage() {}
|
||||
|
||||
func (x *GetStateResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ha_v1_entity_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GetStateResponse.ProtoReflect.Descriptor instead.
|
||||
func (*GetStateResponse) Descriptor() ([]byte, []int) {
|
||||
return file_ha_v1_entity_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *GetStateResponse) GetState() *EntityState {
|
||||
if x != nil {
|
||||
return x.State
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListStatesRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
EntityIds []string `protobuf:"bytes,1,rep,name=entity_ids,json=entityIds,proto3" json:"entity_ids,omitempty"`
|
||||
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ListStatesRequest) Reset() {
|
||||
*x = ListStatesRequest{}
|
||||
mi := &file_ha_v1_entity_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ListStatesRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListStatesRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ListStatesRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ha_v1_entity_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListStatesRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ListStatesRequest) Descriptor() ([]byte, []int) {
|
||||
return file_ha_v1_entity_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *ListStatesRequest) GetEntityIds() []string {
|
||||
if x != nil {
|
||||
return x.EntityIds
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ListStatesRequest) GetDomain() string {
|
||||
if x != nil {
|
||||
return x.Domain
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ListStatesResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
States []*EntityState `protobuf:"bytes,1,rep,name=states,proto3" json:"states,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ListStatesResponse) Reset() {
|
||||
*x = ListStatesResponse{}
|
||||
mi := &file_ha_v1_entity_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ListStatesResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListStatesResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ListStatesResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ha_v1_entity_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListStatesResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ListStatesResponse) Descriptor() ([]byte, []int) {
|
||||
return file_ha_v1_entity_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *ListStatesResponse) GetStates() []*EntityState {
|
||||
if x != nil {
|
||||
return x.States
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_ha_v1_entity_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_ha_v1_entity_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x12ha/v1/entity.proto\x12\x05ha.v1\x1a\x12ha/v1/common.proto\".\n" +
|
||||
"\x0fGetStateRequest\x12\x1b\n" +
|
||||
"\tentity_id\x18\x01 \x01(\tR\bentityId\"<\n" +
|
||||
"\x10GetStateResponse\x12(\n" +
|
||||
"\x05state\x18\x01 \x01(\v2\x12.ha.v1.EntityStateR\x05state\"J\n" +
|
||||
"\x11ListStatesRequest\x12\x1d\n" +
|
||||
"\n" +
|
||||
"entity_ids\x18\x01 \x03(\tR\tentityIds\x12\x16\n" +
|
||||
"\x06domain\x18\x02 \x01(\tR\x06domain\"@\n" +
|
||||
"\x12ListStatesResponse\x12*\n" +
|
||||
"\x06states\x18\x01 \x03(\v2\x12.ha.v1.EntityStateR\x06states2\x8f\x01\n" +
|
||||
"\rEntityService\x12;\n" +
|
||||
"\bGetState\x12\x16.ha.v1.GetStateRequest\x1a\x17.ha.v1.GetStateResponse\x12A\n" +
|
||||
"\n" +
|
||||
"ListStates\x12\x18.ha.v1.ListStatesRequest\x1a\x19.ha.v1.ListStatesResponseB4Z2gitea.nik4nao.com/nik/home-services/gen/ha/v1;hav1b\x06proto3"
|
||||
|
||||
var (
|
||||
file_ha_v1_entity_proto_rawDescOnce sync.Once
|
||||
file_ha_v1_entity_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_ha_v1_entity_proto_rawDescGZIP() []byte {
|
||||
file_ha_v1_entity_proto_rawDescOnce.Do(func() {
|
||||
file_ha_v1_entity_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ha_v1_entity_proto_rawDesc), len(file_ha_v1_entity_proto_rawDesc)))
|
||||
})
|
||||
return file_ha_v1_entity_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_ha_v1_entity_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_ha_v1_entity_proto_goTypes = []any{
|
||||
(*GetStateRequest)(nil), // 0: ha.v1.GetStateRequest
|
||||
(*GetStateResponse)(nil), // 1: ha.v1.GetStateResponse
|
||||
(*ListStatesRequest)(nil), // 2: ha.v1.ListStatesRequest
|
||||
(*ListStatesResponse)(nil), // 3: ha.v1.ListStatesResponse
|
||||
(*EntityState)(nil), // 4: ha.v1.EntityState
|
||||
}
|
||||
var file_ha_v1_entity_proto_depIdxs = []int32{
|
||||
4, // 0: ha.v1.GetStateResponse.state:type_name -> ha.v1.EntityState
|
||||
4, // 1: ha.v1.ListStatesResponse.states:type_name -> ha.v1.EntityState
|
||||
0, // 2: ha.v1.EntityService.GetState:input_type -> ha.v1.GetStateRequest
|
||||
2, // 3: ha.v1.EntityService.ListStates:input_type -> ha.v1.ListStatesRequest
|
||||
1, // 4: ha.v1.EntityService.GetState:output_type -> ha.v1.GetStateResponse
|
||||
3, // 5: ha.v1.EntityService.ListStates:output_type -> ha.v1.ListStatesResponse
|
||||
4, // [4:6] is the sub-list for method output_type
|
||||
2, // [2:4] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_ha_v1_entity_proto_init() }
|
||||
func file_ha_v1_entity_proto_init() {
|
||||
if File_ha_v1_entity_proto != nil {
|
||||
return
|
||||
}
|
||||
file_ha_v1_common_proto_init()
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_ha_v1_entity_proto_rawDesc), len(file_ha_v1_entity_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_ha_v1_entity_proto_goTypes,
|
||||
DependencyIndexes: file_ha_v1_entity_proto_depIdxs,
|
||||
MessageInfos: file_ha_v1_entity_proto_msgTypes,
|
||||
}.Build()
|
||||
File_ha_v1_entity_proto = out.File
|
||||
file_ha_v1_entity_proto_goTypes = nil
|
||||
file_ha_v1_entity_proto_depIdxs = nil
|
||||
}
|
||||
159
gen/ha/v1/entity_grpc.pb.go
Normal file
159
gen/ha/v1/entity_grpc.pb.go
Normal file
@ -0,0 +1,159 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc (unknown)
|
||||
// source: ha/v1/entity.proto
|
||||
|
||||
package hav1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
EntityService_GetState_FullMethodName = "/ha.v1.EntityService/GetState"
|
||||
EntityService_ListStates_FullMethodName = "/ha.v1.EntityService/ListStates"
|
||||
)
|
||||
|
||||
// EntityServiceClient is the client API for EntityService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type EntityServiceClient interface {
|
||||
GetState(ctx context.Context, in *GetStateRequest, opts ...grpc.CallOption) (*GetStateResponse, error)
|
||||
ListStates(ctx context.Context, in *ListStatesRequest, opts ...grpc.CallOption) (*ListStatesResponse, error)
|
||||
}
|
||||
|
||||
type entityServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewEntityServiceClient(cc grpc.ClientConnInterface) EntityServiceClient {
|
||||
return &entityServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *entityServiceClient) GetState(ctx context.Context, in *GetStateRequest, opts ...grpc.CallOption) (*GetStateResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetStateResponse)
|
||||
err := c.cc.Invoke(ctx, EntityService_GetState_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *entityServiceClient) ListStates(ctx context.Context, in *ListStatesRequest, opts ...grpc.CallOption) (*ListStatesResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListStatesResponse)
|
||||
err := c.cc.Invoke(ctx, EntityService_ListStates_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// EntityServiceServer is the server API for EntityService service.
|
||||
// All implementations must embed UnimplementedEntityServiceServer
|
||||
// for forward compatibility.
|
||||
type EntityServiceServer interface {
|
||||
GetState(context.Context, *GetStateRequest) (*GetStateResponse, error)
|
||||
ListStates(context.Context, *ListStatesRequest) (*ListStatesResponse, error)
|
||||
mustEmbedUnimplementedEntityServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedEntityServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedEntityServiceServer struct{}
|
||||
|
||||
func (UnimplementedEntityServiceServer) GetState(context.Context, *GetStateRequest) (*GetStateResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetState not implemented")
|
||||
}
|
||||
func (UnimplementedEntityServiceServer) ListStates(context.Context, *ListStatesRequest) (*ListStatesResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListStates not implemented")
|
||||
}
|
||||
func (UnimplementedEntityServiceServer) mustEmbedUnimplementedEntityServiceServer() {}
|
||||
func (UnimplementedEntityServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeEntityServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to EntityServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeEntityServiceServer interface {
|
||||
mustEmbedUnimplementedEntityServiceServer()
|
||||
}
|
||||
|
||||
func RegisterEntityServiceServer(s grpc.ServiceRegistrar, srv EntityServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedEntityServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&EntityService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _EntityService_GetState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetStateRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(EntityServiceServer).GetState(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: EntityService_GetState_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(EntityServiceServer).GetState(ctx, req.(*GetStateRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _EntityService_ListStates_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListStatesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(EntityServiceServer).ListStates(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: EntityService_ListStates_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(EntityServiceServer).ListStates(ctx, req.(*ListStatesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// EntityService_ServiceDesc is the grpc.ServiceDesc for EntityService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var EntityService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "ha.v1.EntityService",
|
||||
HandlerType: (*EntityServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetState",
|
||||
Handler: _EntityService_GetState_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListStates",
|
||||
Handler: _EntityService_ListStates_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "ha/v1/entity.proto",
|
||||
}
|
||||
218
gen/ha/v1/event.pb.go
Normal file
218
gen/ha/v1/event.pb.go
Normal file
@ -0,0 +1,218 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc (unknown)
|
||||
// source: ha/v1/event.proto
|
||||
|
||||
package hav1
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type SubscribeRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
EntityIds []string `protobuf:"bytes,1,rep,name=entity_ids,json=entityIds,proto3" json:"entity_ids,omitempty"`
|
||||
Domains []string `protobuf:"bytes,2,rep,name=domains,proto3" json:"domains,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SubscribeRequest) Reset() {
|
||||
*x = SubscribeRequest{}
|
||||
mi := &file_ha_v1_event_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *SubscribeRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SubscribeRequest) ProtoMessage() {}
|
||||
|
||||
func (x *SubscribeRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ha_v1_event_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SubscribeRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SubscribeRequest) Descriptor() ([]byte, []int) {
|
||||
return file_ha_v1_event_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *SubscribeRequest) GetEntityIds() []string {
|
||||
if x != nil {
|
||||
return x.EntityIds
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SubscribeRequest) GetDomains() []string {
|
||||
if x != nil {
|
||||
return x.Domains
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type StateChangeEvent struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
EntityId string `protobuf:"bytes,1,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"`
|
||||
OldState *EntityState `protobuf:"bytes,2,opt,name=old_state,json=oldState,proto3,oneof" json:"old_state,omitempty"` // absent on first appearance
|
||||
NewState *EntityState `protobuf:"bytes,3,opt,name=new_state,json=newState,proto3" json:"new_state,omitempty"`
|
||||
EventTime string `protobuf:"bytes,4,opt,name=event_time,json=eventTime,proto3" json:"event_time,omitempty"` // RFC3339
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *StateChangeEvent) Reset() {
|
||||
*x = StateChangeEvent{}
|
||||
mi := &file_ha_v1_event_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *StateChangeEvent) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*StateChangeEvent) ProtoMessage() {}
|
||||
|
||||
func (x *StateChangeEvent) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ha_v1_event_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use StateChangeEvent.ProtoReflect.Descriptor instead.
|
||||
func (*StateChangeEvent) Descriptor() ([]byte, []int) {
|
||||
return file_ha_v1_event_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *StateChangeEvent) GetEntityId() string {
|
||||
if x != nil {
|
||||
return x.EntityId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *StateChangeEvent) GetOldState() *EntityState {
|
||||
if x != nil {
|
||||
return x.OldState
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *StateChangeEvent) GetNewState() *EntityState {
|
||||
if x != nil {
|
||||
return x.NewState
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *StateChangeEvent) GetEventTime() string {
|
||||
if x != nil {
|
||||
return x.EventTime
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_ha_v1_event_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_ha_v1_event_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x11ha/v1/event.proto\x12\x05ha.v1\x1a\x12ha/v1/common.proto\"K\n" +
|
||||
"\x10SubscribeRequest\x12\x1d\n" +
|
||||
"\n" +
|
||||
"entity_ids\x18\x01 \x03(\tR\tentityIds\x12\x18\n" +
|
||||
"\adomains\x18\x02 \x03(\tR\adomains\"\xc3\x01\n" +
|
||||
"\x10StateChangeEvent\x12\x1b\n" +
|
||||
"\tentity_id\x18\x01 \x01(\tR\bentityId\x124\n" +
|
||||
"\told_state\x18\x02 \x01(\v2\x12.ha.v1.EntityStateH\x00R\boldState\x88\x01\x01\x12/\n" +
|
||||
"\tnew_state\x18\x03 \x01(\v2\x12.ha.v1.EntityStateR\bnewState\x12\x1d\n" +
|
||||
"\n" +
|
||||
"event_time\x18\x04 \x01(\tR\teventTimeB\f\n" +
|
||||
"\n" +
|
||||
"_old_state2O\n" +
|
||||
"\fEventService\x12?\n" +
|
||||
"\tSubscribe\x12\x17.ha.v1.SubscribeRequest\x1a\x17.ha.v1.StateChangeEvent0\x01B4Z2gitea.nik4nao.com/nik/home-services/gen/ha/v1;hav1b\x06proto3"
|
||||
|
||||
var (
|
||||
file_ha_v1_event_proto_rawDescOnce sync.Once
|
||||
file_ha_v1_event_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_ha_v1_event_proto_rawDescGZIP() []byte {
|
||||
file_ha_v1_event_proto_rawDescOnce.Do(func() {
|
||||
file_ha_v1_event_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ha_v1_event_proto_rawDesc), len(file_ha_v1_event_proto_rawDesc)))
|
||||
})
|
||||
return file_ha_v1_event_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_ha_v1_event_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_ha_v1_event_proto_goTypes = []any{
|
||||
(*SubscribeRequest)(nil), // 0: ha.v1.SubscribeRequest
|
||||
(*StateChangeEvent)(nil), // 1: ha.v1.StateChangeEvent
|
||||
(*EntityState)(nil), // 2: ha.v1.EntityState
|
||||
}
|
||||
var file_ha_v1_event_proto_depIdxs = []int32{
|
||||
2, // 0: ha.v1.StateChangeEvent.old_state:type_name -> ha.v1.EntityState
|
||||
2, // 1: ha.v1.StateChangeEvent.new_state:type_name -> ha.v1.EntityState
|
||||
0, // 2: ha.v1.EventService.Subscribe:input_type -> ha.v1.SubscribeRequest
|
||||
1, // 3: ha.v1.EventService.Subscribe:output_type -> ha.v1.StateChangeEvent
|
||||
3, // [3:4] is the sub-list for method output_type
|
||||
2, // [2:3] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_ha_v1_event_proto_init() }
|
||||
func file_ha_v1_event_proto_init() {
|
||||
if File_ha_v1_event_proto != nil {
|
||||
return
|
||||
}
|
||||
file_ha_v1_common_proto_init()
|
||||
file_ha_v1_event_proto_msgTypes[1].OneofWrappers = []any{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_ha_v1_event_proto_rawDesc), len(file_ha_v1_event_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_ha_v1_event_proto_goTypes,
|
||||
DependencyIndexes: file_ha_v1_event_proto_depIdxs,
|
||||
MessageInfos: file_ha_v1_event_proto_msgTypes,
|
||||
}.Build()
|
||||
File_ha_v1_event_proto = out.File
|
||||
file_ha_v1_event_proto_goTypes = nil
|
||||
file_ha_v1_event_proto_depIdxs = nil
|
||||
}
|
||||
144
gen/ha/v1/event_grpc.pb.go
Normal file
144
gen/ha/v1/event_grpc.pb.go
Normal file
@ -0,0 +1,144 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc (unknown)
|
||||
// source: ha/v1/event.proto
|
||||
|
||||
package hav1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
EventService_Subscribe_FullMethodName = "/ha.v1.EventService/Subscribe"
|
||||
)
|
||||
|
||||
// EventServiceClient is the client API for EventService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
//
|
||||
// TODO: implement EventService fan-out.
|
||||
// Architecture:
|
||||
// 1. adapters/secondary/ha/websocket.go connects to HA WebSocket,
|
||||
// authenticates, and subscribes to state_changed events.
|
||||
// 2. An internal broker (internal/fanout/broker.go) holds a sync.Map of
|
||||
// subscriber channels, one per active Subscribe stream.
|
||||
// 3. The WebSocket adapter publishes to the broker; the gRPC Subscribe
|
||||
// handler reads from its channel and streams to the client.
|
||||
// 4. On client disconnect (ctx.Done()), the handler deregisters its channel.
|
||||
type EventServiceClient interface {
|
||||
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[StateChangeEvent], error)
|
||||
}
|
||||
|
||||
type eventServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewEventServiceClient(cc grpc.ClientConnInterface) EventServiceClient {
|
||||
return &eventServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *eventServiceClient) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[StateChangeEvent], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &EventService_ServiceDesc.Streams[0], EventService_Subscribe_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[SubscribeRequest, StateChangeEvent]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type EventService_SubscribeClient = grpc.ServerStreamingClient[StateChangeEvent]
|
||||
|
||||
// EventServiceServer is the server API for EventService service.
|
||||
// All implementations must embed UnimplementedEventServiceServer
|
||||
// for forward compatibility.
|
||||
//
|
||||
// TODO: implement EventService fan-out.
|
||||
// Architecture:
|
||||
// 1. adapters/secondary/ha/websocket.go connects to HA WebSocket,
|
||||
// authenticates, and subscribes to state_changed events.
|
||||
// 2. An internal broker (internal/fanout/broker.go) holds a sync.Map of
|
||||
// subscriber channels, one per active Subscribe stream.
|
||||
// 3. The WebSocket adapter publishes to the broker; the gRPC Subscribe
|
||||
// handler reads from its channel and streams to the client.
|
||||
// 4. On client disconnect (ctx.Done()), the handler deregisters its channel.
|
||||
type EventServiceServer interface {
|
||||
Subscribe(*SubscribeRequest, grpc.ServerStreamingServer[StateChangeEvent]) error
|
||||
mustEmbedUnimplementedEventServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedEventServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedEventServiceServer struct{}
|
||||
|
||||
func (UnimplementedEventServiceServer) Subscribe(*SubscribeRequest, grpc.ServerStreamingServer[StateChangeEvent]) error {
|
||||
return status.Error(codes.Unimplemented, "method Subscribe not implemented")
|
||||
}
|
||||
func (UnimplementedEventServiceServer) mustEmbedUnimplementedEventServiceServer() {}
|
||||
func (UnimplementedEventServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeEventServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to EventServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeEventServiceServer interface {
|
||||
mustEmbedUnimplementedEventServiceServer()
|
||||
}
|
||||
|
||||
func RegisterEventServiceServer(s grpc.ServiceRegistrar, srv EventServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedEventServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&EventService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _EventService_Subscribe_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(SubscribeRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(EventServiceServer).Subscribe(m, &grpc.GenericServerStream[SubscribeRequest, StateChangeEvent]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type EventService_SubscribeServer = grpc.ServerStreamingServer[StateChangeEvent]
|
||||
|
||||
// EventService_ServiceDesc is the grpc.ServiceDesc for EventService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var EventService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "ha.v1.EventService",
|
||||
HandlerType: (*EventServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "Subscribe",
|
||||
Handler: _EventService_Subscribe_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "ha/v1/event.proto",
|
||||
}
|
||||
338
gen/ha/v1/light.pb.go
Normal file
338
gen/ha/v1/light.pb.go
Normal file
@ -0,0 +1,338 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc (unknown)
|
||||
// source: ha/v1/light.proto
|
||||
|
||||
package hav1
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// optional fields require protobuf 3.15+ / buf >= 1.0. They generate
|
||||
// pointer fields in Go with Has*() accessor methods. This is intentional —
|
||||
// it lets the gateway distinguish "brightness not set" from "brightness = 0".
|
||||
type TurnOnRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
EntityId string `protobuf:"bytes,1,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"`
|
||||
BrightnessPct *uint32 `protobuf:"varint,2,opt,name=brightness_pct,json=brightnessPct,proto3,oneof" json:"brightness_pct,omitempty"` // 0–100
|
||||
ColorTempKelvin *uint32 `protobuf:"varint,3,opt,name=color_temp_kelvin,json=colorTempKelvin,proto3,oneof" json:"color_temp_kelvin,omitempty"` // e.g. 2700–6500
|
||||
RgbColor *RGBColor `protobuf:"bytes,4,opt,name=rgb_color,json=rgbColor,proto3,oneof" json:"rgb_color,omitempty"` // ignored if color_temp_kelvin set
|
||||
Transition *uint32 `protobuf:"varint,5,opt,name=transition,proto3,oneof" json:"transition,omitempty"` // seconds
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TurnOnRequest) Reset() {
|
||||
*x = TurnOnRequest{}
|
||||
mi := &file_ha_v1_light_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TurnOnRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TurnOnRequest) ProtoMessage() {}
|
||||
|
||||
func (x *TurnOnRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ha_v1_light_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TurnOnRequest.ProtoReflect.Descriptor instead.
|
||||
func (*TurnOnRequest) Descriptor() ([]byte, []int) {
|
||||
return file_ha_v1_light_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *TurnOnRequest) GetEntityId() string {
|
||||
if x != nil {
|
||||
return x.EntityId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TurnOnRequest) GetBrightnessPct() uint32 {
|
||||
if x != nil && x.BrightnessPct != nil {
|
||||
return *x.BrightnessPct
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TurnOnRequest) GetColorTempKelvin() uint32 {
|
||||
if x != nil && x.ColorTempKelvin != nil {
|
||||
return *x.ColorTempKelvin
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *TurnOnRequest) GetRgbColor() *RGBColor {
|
||||
if x != nil {
|
||||
return x.RgbColor
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TurnOnRequest) GetTransition() uint32 {
|
||||
if x != nil && x.Transition != nil {
|
||||
return *x.Transition
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type TurnOffRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
EntityId string `protobuf:"bytes,1,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"`
|
||||
Transition *uint32 `protobuf:"varint,2,opt,name=transition,proto3,oneof" json:"transition,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TurnOffRequest) Reset() {
|
||||
*x = TurnOffRequest{}
|
||||
mi := &file_ha_v1_light_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TurnOffRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TurnOffRequest) ProtoMessage() {}
|
||||
|
||||
func (x *TurnOffRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ha_v1_light_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TurnOffRequest.ProtoReflect.Descriptor instead.
|
||||
func (*TurnOffRequest) Descriptor() ([]byte, []int) {
|
||||
return file_ha_v1_light_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *TurnOffRequest) GetEntityId() string {
|
||||
if x != nil {
|
||||
return x.EntityId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TurnOffRequest) GetTransition() uint32 {
|
||||
if x != nil && x.Transition != nil {
|
||||
return *x.Transition
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type ToggleRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
EntityId string `protobuf:"bytes,1,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *ToggleRequest) Reset() {
|
||||
*x = ToggleRequest{}
|
||||
mi := &file_ha_v1_light_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *ToggleRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ToggleRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ToggleRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ha_v1_light_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ToggleRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ToggleRequest) Descriptor() ([]byte, []int) {
|
||||
return file_ha_v1_light_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *ToggleRequest) GetEntityId() string {
|
||||
if x != nil {
|
||||
return x.EntityId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type LightResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
State *EntityState `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *LightResponse) Reset() {
|
||||
*x = LightResponse{}
|
||||
mi := &file_ha_v1_light_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *LightResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*LightResponse) ProtoMessage() {}
|
||||
|
||||
func (x *LightResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ha_v1_light_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use LightResponse.ProtoReflect.Descriptor instead.
|
||||
func (*LightResponse) Descriptor() ([]byte, []int) {
|
||||
return file_ha_v1_light_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *LightResponse) GetState() *EntityState {
|
||||
if x != nil {
|
||||
return x.State
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_ha_v1_light_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_ha_v1_light_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x11ha/v1/light.proto\x12\x05ha.v1\x1a\x12ha/v1/common.proto\"\xa7\x02\n" +
|
||||
"\rTurnOnRequest\x12\x1b\n" +
|
||||
"\tentity_id\x18\x01 \x01(\tR\bentityId\x12*\n" +
|
||||
"\x0ebrightness_pct\x18\x02 \x01(\rH\x00R\rbrightnessPct\x88\x01\x01\x12/\n" +
|
||||
"\x11color_temp_kelvin\x18\x03 \x01(\rH\x01R\x0fcolorTempKelvin\x88\x01\x01\x121\n" +
|
||||
"\trgb_color\x18\x04 \x01(\v2\x0f.ha.v1.RGBColorH\x02R\brgbColor\x88\x01\x01\x12#\n" +
|
||||
"\n" +
|
||||
"transition\x18\x05 \x01(\rH\x03R\n" +
|
||||
"transition\x88\x01\x01B\x11\n" +
|
||||
"\x0f_brightness_pctB\x14\n" +
|
||||
"\x12_color_temp_kelvinB\f\n" +
|
||||
"\n" +
|
||||
"_rgb_colorB\r\n" +
|
||||
"\v_transition\"a\n" +
|
||||
"\x0eTurnOffRequest\x12\x1b\n" +
|
||||
"\tentity_id\x18\x01 \x01(\tR\bentityId\x12#\n" +
|
||||
"\n" +
|
||||
"transition\x18\x02 \x01(\rH\x00R\n" +
|
||||
"transition\x88\x01\x01B\r\n" +
|
||||
"\v_transition\",\n" +
|
||||
"\rToggleRequest\x12\x1b\n" +
|
||||
"\tentity_id\x18\x01 \x01(\tR\bentityId\"9\n" +
|
||||
"\rLightResponse\x12(\n" +
|
||||
"\x05state\x18\x01 \x01(\v2\x12.ha.v1.EntityStateR\x05state2\xb2\x01\n" +
|
||||
"\fLightService\x124\n" +
|
||||
"\x06TurnOn\x12\x14.ha.v1.TurnOnRequest\x1a\x14.ha.v1.LightResponse\x126\n" +
|
||||
"\aTurnOff\x12\x15.ha.v1.TurnOffRequest\x1a\x14.ha.v1.LightResponse\x124\n" +
|
||||
"\x06Toggle\x12\x14.ha.v1.ToggleRequest\x1a\x14.ha.v1.LightResponseB4Z2gitea.nik4nao.com/nik/home-services/gen/ha/v1;hav1b\x06proto3"
|
||||
|
||||
var (
|
||||
file_ha_v1_light_proto_rawDescOnce sync.Once
|
||||
file_ha_v1_light_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_ha_v1_light_proto_rawDescGZIP() []byte {
|
||||
file_ha_v1_light_proto_rawDescOnce.Do(func() {
|
||||
file_ha_v1_light_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ha_v1_light_proto_rawDesc), len(file_ha_v1_light_proto_rawDesc)))
|
||||
})
|
||||
return file_ha_v1_light_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_ha_v1_light_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_ha_v1_light_proto_goTypes = []any{
|
||||
(*TurnOnRequest)(nil), // 0: ha.v1.TurnOnRequest
|
||||
(*TurnOffRequest)(nil), // 1: ha.v1.TurnOffRequest
|
||||
(*ToggleRequest)(nil), // 2: ha.v1.ToggleRequest
|
||||
(*LightResponse)(nil), // 3: ha.v1.LightResponse
|
||||
(*RGBColor)(nil), // 4: ha.v1.RGBColor
|
||||
(*EntityState)(nil), // 5: ha.v1.EntityState
|
||||
}
|
||||
var file_ha_v1_light_proto_depIdxs = []int32{
|
||||
4, // 0: ha.v1.TurnOnRequest.rgb_color:type_name -> ha.v1.RGBColor
|
||||
5, // 1: ha.v1.LightResponse.state:type_name -> ha.v1.EntityState
|
||||
0, // 2: ha.v1.LightService.TurnOn:input_type -> ha.v1.TurnOnRequest
|
||||
1, // 3: ha.v1.LightService.TurnOff:input_type -> ha.v1.TurnOffRequest
|
||||
2, // 4: ha.v1.LightService.Toggle:input_type -> ha.v1.ToggleRequest
|
||||
3, // 5: ha.v1.LightService.TurnOn:output_type -> ha.v1.LightResponse
|
||||
3, // 6: ha.v1.LightService.TurnOff:output_type -> ha.v1.LightResponse
|
||||
3, // 7: ha.v1.LightService.Toggle:output_type -> ha.v1.LightResponse
|
||||
5, // [5:8] is the sub-list for method output_type
|
||||
2, // [2:5] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_ha_v1_light_proto_init() }
|
||||
func file_ha_v1_light_proto_init() {
|
||||
if File_ha_v1_light_proto != nil {
|
||||
return
|
||||
}
|
||||
file_ha_v1_common_proto_init()
|
||||
file_ha_v1_light_proto_msgTypes[0].OneofWrappers = []any{}
|
||||
file_ha_v1_light_proto_msgTypes[1].OneofWrappers = []any{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_ha_v1_light_proto_rawDesc), len(file_ha_v1_light_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_ha_v1_light_proto_goTypes,
|
||||
DependencyIndexes: file_ha_v1_light_proto_depIdxs,
|
||||
MessageInfos: file_ha_v1_light_proto_msgTypes,
|
||||
}.Build()
|
||||
File_ha_v1_light_proto = out.File
|
||||
file_ha_v1_light_proto_goTypes = nil
|
||||
file_ha_v1_light_proto_depIdxs = nil
|
||||
}
|
||||
197
gen/ha/v1/light_grpc.pb.go
Normal file
197
gen/ha/v1/light_grpc.pb.go
Normal file
@ -0,0 +1,197 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc (unknown)
|
||||
// source: ha/v1/light.proto
|
||||
|
||||
package hav1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
LightService_TurnOn_FullMethodName = "/ha.v1.LightService/TurnOn"
|
||||
LightService_TurnOff_FullMethodName = "/ha.v1.LightService/TurnOff"
|
||||
LightService_Toggle_FullMethodName = "/ha.v1.LightService/Toggle"
|
||||
)
|
||||
|
||||
// LightServiceClient is the client API for LightService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type LightServiceClient interface {
|
||||
TurnOn(ctx context.Context, in *TurnOnRequest, opts ...grpc.CallOption) (*LightResponse, error)
|
||||
TurnOff(ctx context.Context, in *TurnOffRequest, opts ...grpc.CallOption) (*LightResponse, error)
|
||||
Toggle(ctx context.Context, in *ToggleRequest, opts ...grpc.CallOption) (*LightResponse, error)
|
||||
}
|
||||
|
||||
type lightServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewLightServiceClient(cc grpc.ClientConnInterface) LightServiceClient {
|
||||
return &lightServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *lightServiceClient) TurnOn(ctx context.Context, in *TurnOnRequest, opts ...grpc.CallOption) (*LightResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(LightResponse)
|
||||
err := c.cc.Invoke(ctx, LightService_TurnOn_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *lightServiceClient) TurnOff(ctx context.Context, in *TurnOffRequest, opts ...grpc.CallOption) (*LightResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(LightResponse)
|
||||
err := c.cc.Invoke(ctx, LightService_TurnOff_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *lightServiceClient) Toggle(ctx context.Context, in *ToggleRequest, opts ...grpc.CallOption) (*LightResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(LightResponse)
|
||||
err := c.cc.Invoke(ctx, LightService_Toggle_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// LightServiceServer is the server API for LightService service.
|
||||
// All implementations must embed UnimplementedLightServiceServer
|
||||
// for forward compatibility.
|
||||
type LightServiceServer interface {
|
||||
TurnOn(context.Context, *TurnOnRequest) (*LightResponse, error)
|
||||
TurnOff(context.Context, *TurnOffRequest) (*LightResponse, error)
|
||||
Toggle(context.Context, *ToggleRequest) (*LightResponse, error)
|
||||
mustEmbedUnimplementedLightServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedLightServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedLightServiceServer struct{}
|
||||
|
||||
func (UnimplementedLightServiceServer) TurnOn(context.Context, *TurnOnRequest) (*LightResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method TurnOn not implemented")
|
||||
}
|
||||
func (UnimplementedLightServiceServer) TurnOff(context.Context, *TurnOffRequest) (*LightResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method TurnOff not implemented")
|
||||
}
|
||||
func (UnimplementedLightServiceServer) Toggle(context.Context, *ToggleRequest) (*LightResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Toggle not implemented")
|
||||
}
|
||||
func (UnimplementedLightServiceServer) mustEmbedUnimplementedLightServiceServer() {}
|
||||
func (UnimplementedLightServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeLightServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to LightServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeLightServiceServer interface {
|
||||
mustEmbedUnimplementedLightServiceServer()
|
||||
}
|
||||
|
||||
func RegisterLightServiceServer(s grpc.ServiceRegistrar, srv LightServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedLightServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&LightService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _LightService_TurnOn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(TurnOnRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(LightServiceServer).TurnOn(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: LightService_TurnOn_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(LightServiceServer).TurnOn(ctx, req.(*TurnOnRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _LightService_TurnOff_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(TurnOffRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(LightServiceServer).TurnOff(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: LightService_TurnOff_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(LightServiceServer).TurnOff(ctx, req.(*TurnOffRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _LightService_Toggle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ToggleRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(LightServiceServer).Toggle(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: LightService_Toggle_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(LightServiceServer).Toggle(ctx, req.(*ToggleRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// LightService_ServiceDesc is the grpc.ServiceDesc for LightService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var LightService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "ha.v1.LightService",
|
||||
HandlerType: (*LightServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "TurnOn",
|
||||
Handler: _LightService_TurnOn_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "TurnOff",
|
||||
Handler: _LightService_TurnOff_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Toggle",
|
||||
Handler: _LightService_Toggle_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "ha/v1/light.proto",
|
||||
}
|
||||
182
gen/ha/v1/switch.pb.go
Normal file
182
gen/ha/v1/switch.pb.go
Normal file
@ -0,0 +1,182 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.11
|
||||
// protoc (unknown)
|
||||
// source: ha/v1/switch.proto
|
||||
|
||||
package hav1
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type SwitchRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
EntityId string `protobuf:"bytes,1,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SwitchRequest) Reset() {
|
||||
*x = SwitchRequest{}
|
||||
mi := &file_ha_v1_switch_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *SwitchRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SwitchRequest) ProtoMessage() {}
|
||||
|
||||
func (x *SwitchRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ha_v1_switch_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SwitchRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SwitchRequest) Descriptor() ([]byte, []int) {
|
||||
return file_ha_v1_switch_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *SwitchRequest) GetEntityId() string {
|
||||
if x != nil {
|
||||
return x.EntityId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type SwitchResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
State *EntityState `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SwitchResponse) Reset() {
|
||||
*x = SwitchResponse{}
|
||||
mi := &file_ha_v1_switch_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *SwitchResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SwitchResponse) ProtoMessage() {}
|
||||
|
||||
func (x *SwitchResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ha_v1_switch_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SwitchResponse.ProtoReflect.Descriptor instead.
|
||||
func (*SwitchResponse) Descriptor() ([]byte, []int) {
|
||||
return file_ha_v1_switch_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *SwitchResponse) GetState() *EntityState {
|
||||
if x != nil {
|
||||
return x.State
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_ha_v1_switch_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_ha_v1_switch_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x12ha/v1/switch.proto\x12\x05ha.v1\x1a\x12ha/v1/common.proto\",\n" +
|
||||
"\rSwitchRequest\x12\x1b\n" +
|
||||
"\tentity_id\x18\x01 \x01(\tR\bentityId\":\n" +
|
||||
"\x0eSwitchResponse\x12(\n" +
|
||||
"\x05state\x18\x01 \x01(\v2\x12.ha.v1.EntityStateR\x05state2\xb5\x01\n" +
|
||||
"\rSwitchService\x125\n" +
|
||||
"\x06TurnOn\x12\x14.ha.v1.SwitchRequest\x1a\x15.ha.v1.SwitchResponse\x126\n" +
|
||||
"\aTurnOff\x12\x14.ha.v1.SwitchRequest\x1a\x15.ha.v1.SwitchResponse\x125\n" +
|
||||
"\x06Toggle\x12\x14.ha.v1.SwitchRequest\x1a\x15.ha.v1.SwitchResponseB4Z2gitea.nik4nao.com/nik/home-services/gen/ha/v1;hav1b\x06proto3"
|
||||
|
||||
var (
|
||||
file_ha_v1_switch_proto_rawDescOnce sync.Once
|
||||
file_ha_v1_switch_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_ha_v1_switch_proto_rawDescGZIP() []byte {
|
||||
file_ha_v1_switch_proto_rawDescOnce.Do(func() {
|
||||
file_ha_v1_switch_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_ha_v1_switch_proto_rawDesc), len(file_ha_v1_switch_proto_rawDesc)))
|
||||
})
|
||||
return file_ha_v1_switch_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_ha_v1_switch_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_ha_v1_switch_proto_goTypes = []any{
|
||||
(*SwitchRequest)(nil), // 0: ha.v1.SwitchRequest
|
||||
(*SwitchResponse)(nil), // 1: ha.v1.SwitchResponse
|
||||
(*EntityState)(nil), // 2: ha.v1.EntityState
|
||||
}
|
||||
var file_ha_v1_switch_proto_depIdxs = []int32{
|
||||
2, // 0: ha.v1.SwitchResponse.state:type_name -> ha.v1.EntityState
|
||||
0, // 1: ha.v1.SwitchService.TurnOn:input_type -> ha.v1.SwitchRequest
|
||||
0, // 2: ha.v1.SwitchService.TurnOff:input_type -> ha.v1.SwitchRequest
|
||||
0, // 3: ha.v1.SwitchService.Toggle:input_type -> ha.v1.SwitchRequest
|
||||
1, // 4: ha.v1.SwitchService.TurnOn:output_type -> ha.v1.SwitchResponse
|
||||
1, // 5: ha.v1.SwitchService.TurnOff:output_type -> ha.v1.SwitchResponse
|
||||
1, // 6: ha.v1.SwitchService.Toggle:output_type -> ha.v1.SwitchResponse
|
||||
4, // [4:7] is the sub-list for method output_type
|
||||
1, // [1:4] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_ha_v1_switch_proto_init() }
|
||||
func file_ha_v1_switch_proto_init() {
|
||||
if File_ha_v1_switch_proto != nil {
|
||||
return
|
||||
}
|
||||
file_ha_v1_common_proto_init()
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_ha_v1_switch_proto_rawDesc), len(file_ha_v1_switch_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_ha_v1_switch_proto_goTypes,
|
||||
DependencyIndexes: file_ha_v1_switch_proto_depIdxs,
|
||||
MessageInfos: file_ha_v1_switch_proto_msgTypes,
|
||||
}.Build()
|
||||
File_ha_v1_switch_proto = out.File
|
||||
file_ha_v1_switch_proto_goTypes = nil
|
||||
file_ha_v1_switch_proto_depIdxs = nil
|
||||
}
|
||||
209
gen/ha/v1/switch_grpc.pb.go
Normal file
209
gen/ha/v1/switch_grpc.pb.go
Normal file
@ -0,0 +1,209 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc (unknown)
|
||||
// source: ha/v1/switch.proto
|
||||
|
||||
package hav1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
SwitchService_TurnOn_FullMethodName = "/ha.v1.SwitchService/TurnOn"
|
||||
SwitchService_TurnOff_FullMethodName = "/ha.v1.SwitchService/TurnOff"
|
||||
SwitchService_Toggle_FullMethodName = "/ha.v1.SwitchService/Toggle"
|
||||
)
|
||||
|
||||
// SwitchServiceClient is the client API for SwitchService service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
//
|
||||
// TODO: implement SwitchService. Follow the same pattern as LightService:
|
||||
// - domain type in core/domain/switch.go
|
||||
// - port interface in core/ports/driving/switch.go
|
||||
// - application logic in app/switch.go
|
||||
// - gRPC adapter in adapters/primary/grpc/switch.go
|
||||
type SwitchServiceClient interface {
|
||||
TurnOn(ctx context.Context, in *SwitchRequest, opts ...grpc.CallOption) (*SwitchResponse, error)
|
||||
TurnOff(ctx context.Context, in *SwitchRequest, opts ...grpc.CallOption) (*SwitchResponse, error)
|
||||
Toggle(ctx context.Context, in *SwitchRequest, opts ...grpc.CallOption) (*SwitchResponse, error)
|
||||
}
|
||||
|
||||
type switchServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewSwitchServiceClient(cc grpc.ClientConnInterface) SwitchServiceClient {
|
||||
return &switchServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *switchServiceClient) TurnOn(ctx context.Context, in *SwitchRequest, opts ...grpc.CallOption) (*SwitchResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SwitchResponse)
|
||||
err := c.cc.Invoke(ctx, SwitchService_TurnOn_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *switchServiceClient) TurnOff(ctx context.Context, in *SwitchRequest, opts ...grpc.CallOption) (*SwitchResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SwitchResponse)
|
||||
err := c.cc.Invoke(ctx, SwitchService_TurnOff_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *switchServiceClient) Toggle(ctx context.Context, in *SwitchRequest, opts ...grpc.CallOption) (*SwitchResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SwitchResponse)
|
||||
err := c.cc.Invoke(ctx, SwitchService_Toggle_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// SwitchServiceServer is the server API for SwitchService service.
|
||||
// All implementations must embed UnimplementedSwitchServiceServer
|
||||
// for forward compatibility.
|
||||
//
|
||||
// TODO: implement SwitchService. Follow the same pattern as LightService:
|
||||
// - domain type in core/domain/switch.go
|
||||
// - port interface in core/ports/driving/switch.go
|
||||
// - application logic in app/switch.go
|
||||
// - gRPC adapter in adapters/primary/grpc/switch.go
|
||||
type SwitchServiceServer interface {
|
||||
TurnOn(context.Context, *SwitchRequest) (*SwitchResponse, error)
|
||||
TurnOff(context.Context, *SwitchRequest) (*SwitchResponse, error)
|
||||
Toggle(context.Context, *SwitchRequest) (*SwitchResponse, error)
|
||||
mustEmbedUnimplementedSwitchServiceServer()
|
||||
}
|
||||
|
||||
// UnimplementedSwitchServiceServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedSwitchServiceServer struct{}
|
||||
|
||||
func (UnimplementedSwitchServiceServer) TurnOn(context.Context, *SwitchRequest) (*SwitchResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method TurnOn not implemented")
|
||||
}
|
||||
func (UnimplementedSwitchServiceServer) TurnOff(context.Context, *SwitchRequest) (*SwitchResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method TurnOff not implemented")
|
||||
}
|
||||
func (UnimplementedSwitchServiceServer) Toggle(context.Context, *SwitchRequest) (*SwitchResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Toggle not implemented")
|
||||
}
|
||||
func (UnimplementedSwitchServiceServer) mustEmbedUnimplementedSwitchServiceServer() {}
|
||||
func (UnimplementedSwitchServiceServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeSwitchServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to SwitchServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeSwitchServiceServer interface {
|
||||
mustEmbedUnimplementedSwitchServiceServer()
|
||||
}
|
||||
|
||||
func RegisterSwitchServiceServer(s grpc.ServiceRegistrar, srv SwitchServiceServer) {
|
||||
// If the following call panics, it indicates UnimplementedSwitchServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&SwitchService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _SwitchService_TurnOn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SwitchRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SwitchServiceServer).TurnOn(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: SwitchService_TurnOn_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SwitchServiceServer).TurnOn(ctx, req.(*SwitchRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SwitchService_TurnOff_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SwitchRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SwitchServiceServer).TurnOff(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: SwitchService_TurnOff_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SwitchServiceServer).TurnOff(ctx, req.(*SwitchRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _SwitchService_Toggle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SwitchRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SwitchServiceServer).Toggle(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: SwitchService_Toggle_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SwitchServiceServer).Toggle(ctx, req.(*SwitchRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// SwitchService_ServiceDesc is the grpc.ServiceDesc for SwitchService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var SwitchService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "ha.v1.SwitchService",
|
||||
HandlerType: (*SwitchServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "TurnOn",
|
||||
Handler: _SwitchService_TurnOn_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "TurnOff",
|
||||
Handler: _SwitchService_TurnOff_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Toggle",
|
||||
Handler: _SwitchService_Toggle_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "ha/v1/switch.proto",
|
||||
}
|
||||
92
go.work.sum
Normal file
92
go.work.sum
Normal file
@ -0,0 +1,92 @@
|
||||
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=
|
||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
|
||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
4
ha-gateway/.env.example
Normal file
4
ha-gateway/.env.example
Normal file
@ -0,0 +1,4 @@
|
||||
GRPC_PORT=50051
|
||||
HA_BASE_URL=http://ha.home.arpa:8123
|
||||
HA_TOKEN=your-long-lived-token-here
|
||||
OTEL_ENDPOINT=
|
||||
19
ha-gateway/Dockerfile
Normal file
19
ha-gateway/Dockerfile
Normal file
@ -0,0 +1,19 @@
|
||||
FROM golang:1.26-alpine AS builder
|
||||
WORKDIR /workspace
|
||||
|
||||
COPY go.work go.work.sum ./
|
||||
COPY gen/ ./gen/
|
||||
COPY ha-gateway/ ./ha-gateway/
|
||||
|
||||
WORKDIR /workspace/ha-gateway
|
||||
RUN go mod download
|
||||
|
||||
ARG VERSION=dev
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build \
|
||||
-ldflags="-s -w -X main.version=${VERSION}" \
|
||||
-o /ha-gateway ./cmd/gateway
|
||||
|
||||
FROM gcr.io/distroless/static:nonroot
|
||||
COPY --from=builder /ha-gateway /ha-gateway
|
||||
EXPOSE 50051
|
||||
ENTRYPOINT ["/ha-gateway"]
|
||||
143
ha-gateway/cmd/gateway/main.go
Normal file
143
ha-gateway/cmd/gateway/main.go
Normal file
@ -0,0 +1,143 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/peer"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
|
||||
hav1 "gitea.nik4nao.com/nik/home-services/gen/ha/v1"
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/adapters/secondary/ha"
|
||||
grpcadapter "gitea.nik4nao.com/nik/home-services/ha-gateway/internal/adapters/primary/grpc"
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/app"
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/config"
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/telemetry"
|
||||
)
|
||||
|
||||
// MEMO: auth is not implemented.
|
||||
// Add one of the following before exposing this service to any untrusted network:
|
||||
// Option A — shared API key per client: unary + stream interceptors read
|
||||
// "authorization" from gRPC metadata and compare to a secret
|
||||
// from config. Good for small number of known clients.
|
||||
// Option B — mTLS (recommended): tls.Config with ClientAuth: RequireAndVerifyClientCert,
|
||||
// cert pool from the internal CA. Each client gets a cert from
|
||||
// cert-manager. No runtime auth dependency, identity in the cert CN/SAN.
|
||||
|
||||
// version is set at build time via -ldflags "-X main.version=<tag>".
|
||||
var version = "dev"
|
||||
|
||||
func main() {
|
||||
// 1. Load .env if present (ignored in K8s where env vars come from Secrets/ConfigMaps).
|
||||
_ = godotenv.Load()
|
||||
|
||||
// 2. Load and validate config.
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
slog.Error("config error", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)
|
||||
defer stop()
|
||||
|
||||
// 3. Set up telemetry.
|
||||
shutdown, err := telemetry.Setup(ctx, cfg, version)
|
||||
if err != nil {
|
||||
slog.Error("telemetry setup failed", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 4. Build the secondary adapter.
|
||||
haClient := ha.NewClient(cfg)
|
||||
|
||||
// 5. Build application services.
|
||||
entityApp := app.NewEntityApp(haClient)
|
||||
lightApp := app.NewLightApp(haClient)
|
||||
|
||||
// 6. Build the gRPC server with OTel stats handler and logging interceptors.
|
||||
srv := grpc.NewServer(
|
||||
grpc.StatsHandler(otelgrpc.NewServerHandler()),
|
||||
grpc.ChainUnaryInterceptor(loggingUnaryInterceptor),
|
||||
grpc.ChainStreamInterceptor(loggingStreamInterceptor),
|
||||
)
|
||||
|
||||
// 7. Register services.
|
||||
hav1.RegisterEntityServiceServer(srv, grpcadapter.NewEntityGRPC(entityApp))
|
||||
hav1.RegisterLightServiceServer(srv, grpcadapter.NewLightGRPC(lightApp))
|
||||
hav1.RegisterSwitchServiceServer(srv, &grpcadapter.SwitchGRPC{})
|
||||
hav1.RegisterEventServiceServer(srv, &grpcadapter.EventGRPC{})
|
||||
|
||||
// 8. Start listener.
|
||||
lis, err := net.Listen("tcp", ":"+cfg.GRPCPort)
|
||||
if err != nil {
|
||||
slog.Error("listen failed", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 9. Serve in background.
|
||||
go func() {
|
||||
slog.Info("listening", "port", cfg.GRPCPort, "version", version)
|
||||
if err := srv.Serve(lis); err != nil {
|
||||
slog.Error("serve failed", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// 10. Block until signal.
|
||||
<-ctx.Done()
|
||||
slog.Info("shutting down")
|
||||
|
||||
// 11. Graceful stop, then flush telemetry.
|
||||
srv.GracefulStop()
|
||||
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
if err := shutdown(shutdownCtx); err != nil {
|
||||
slog.Error("telemetry shutdown error", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
func loggingUnaryInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
||||
start := time.Now()
|
||||
resp, err := handler(ctx, req)
|
||||
attrs := []any{
|
||||
"method", info.FullMethod,
|
||||
"duration", time.Since(start).String(),
|
||||
}
|
||||
if p, ok := peer.FromContext(ctx); ok {
|
||||
attrs = append(attrs, "peer", p.Addr.String())
|
||||
}
|
||||
if err != nil {
|
||||
attrs = append(attrs, "err", err)
|
||||
slog.ErrorContext(ctx, "rpc", attrs...)
|
||||
} else {
|
||||
slog.InfoContext(ctx, "rpc", attrs...)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func loggingStreamInterceptor(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
start := time.Now()
|
||||
err := handler(srv, ss)
|
||||
attrs := []any{
|
||||
"method", info.FullMethod,
|
||||
"duration", time.Since(start).String(),
|
||||
}
|
||||
if p, ok := peer.FromContext(ss.Context()); ok {
|
||||
attrs = append(attrs, "peer", p.Addr.String())
|
||||
}
|
||||
if err != nil {
|
||||
attrs = append(attrs, "err", err)
|
||||
slog.ErrorContext(ss.Context(), "rpc stream", attrs...)
|
||||
} else {
|
||||
slog.InfoContext(ss.Context(), "rpc stream", attrs...)
|
||||
}
|
||||
return err
|
||||
}
|
||||
37
ha-gateway/go.mod
Normal file
37
ha-gateway/go.mod
Normal file
@ -0,0 +1,37 @@
|
||||
module gitea.nik4nao.com/nik/home-services/ha-gateway
|
||||
|
||||
go 1.26
|
||||
|
||||
require (
|
||||
gitea.nik4nao.com/nik/home-services/gen v0.0.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0
|
||||
go.opentelemetry.io/otel v1.39.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0
|
||||
go.opentelemetry.io/otel/sdk v1.39.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0
|
||||
google.golang.org/grpc v1.79.3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.39.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.39.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
)
|
||||
|
||||
replace gitea.nik4nao.com/nik/home-services/gen => ../gen
|
||||
67
ha-gateway/go.sum
Normal file
67
ha-gateway/go.sum
Normal file
@ -0,0 +1,67 @@
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
|
||||
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0 h1:QcFwRrZLc82r8wODjvyCbP7Ifp3UANaBSmhDSFjnqSc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.35.0/go.mod h1:CXIWhUomyWBG/oY2/r/kLp6K/cmx9e/7DLpBuuGdLCA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
|
||||
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
|
||||
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
|
||||
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
|
||||
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
59
ha-gateway/internal/adapters/primary/grpc/entity.go
Normal file
59
ha-gateway/internal/adapters/primary/grpc/entity.go
Normal file
@ -0,0 +1,59 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
hav1 "gitea.nik4nao.com/nik/home-services/gen/ha/v1"
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/core/domain"
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/core/ports/driving"
|
||||
)
|
||||
|
||||
type EntityGRPC struct {
|
||||
hav1.UnimplementedEntityServiceServer
|
||||
svc driving.EntityService
|
||||
}
|
||||
|
||||
func NewEntityGRPC(svc driving.EntityService) *EntityGRPC {
|
||||
return &EntityGRPC{svc: svc}
|
||||
}
|
||||
|
||||
func (h *EntityGRPC) GetState(ctx context.Context, req *hav1.GetStateRequest) (*hav1.GetStateResponse, error) {
|
||||
s, err := h.svc.GetState(ctx, domain.EntityID(req.EntityId))
|
||||
if err != nil {
|
||||
return nil, grpcError(err)
|
||||
}
|
||||
return &hav1.GetStateResponse{State: domainStateToProto(s)}, nil
|
||||
}
|
||||
|
||||
func (h *EntityGRPC) ListStates(ctx context.Context, req *hav1.ListStatesRequest) (*hav1.ListStatesResponse, error) {
|
||||
ids := make([]domain.EntityID, len(req.EntityIds))
|
||||
for i, id := range req.EntityIds {
|
||||
ids[i] = domain.EntityID(id)
|
||||
}
|
||||
|
||||
states, err := h.svc.ListStates(ctx, ids, req.Domain)
|
||||
if err != nil {
|
||||
return nil, grpcError(err)
|
||||
}
|
||||
|
||||
proto := make([]*hav1.EntityState, len(states))
|
||||
for i, s := range states {
|
||||
proto[i] = domainStateToProto(s)
|
||||
}
|
||||
return &hav1.ListStatesResponse{States: proto}, nil
|
||||
}
|
||||
|
||||
// grpcError maps domain errors to appropriate gRPC status codes.
|
||||
func grpcError(err error) error {
|
||||
if errors.Is(err, ErrNotFound) {
|
||||
return status.Errorf(codes.NotFound, "%v", err)
|
||||
}
|
||||
return status.Errorf(codes.Internal, "%v", err)
|
||||
}
|
||||
|
||||
// ErrNotFound is returned by the app layer when an entity does not exist.
|
||||
var ErrNotFound = errors.New("not found")
|
||||
12
ha-gateway/internal/adapters/primary/grpc/event.go
Normal file
12
ha-gateway/internal/adapters/primary/grpc/event.go
Normal file
@ -0,0 +1,12 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
hav1 "gitea.nik4nao.com/nik/home-services/gen/ha/v1"
|
||||
)
|
||||
|
||||
type EventGRPC struct {
|
||||
hav1.UnimplementedEventServiceServer
|
||||
}
|
||||
|
||||
// Subscribe returns codes.Unimplemented via the embedded UnimplementedEventServiceServer.
|
||||
// TODO: inject fanout broker here once websocket.go is implemented.
|
||||
42
ha-gateway/internal/adapters/primary/grpc/light.go
Normal file
42
ha-gateway/internal/adapters/primary/grpc/light.go
Normal file
@ -0,0 +1,42 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
hav1 "gitea.nik4nao.com/nik/home-services/gen/ha/v1"
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/core/domain"
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/core/ports/driving"
|
||||
)
|
||||
|
||||
type LightGRPC struct {
|
||||
hav1.UnimplementedLightServiceServer
|
||||
svc driving.LightService
|
||||
}
|
||||
|
||||
func NewLightGRPC(svc driving.LightService) *LightGRPC {
|
||||
return &LightGRPC{svc: svc}
|
||||
}
|
||||
|
||||
func (h *LightGRPC) TurnOn(ctx context.Context, req *hav1.TurnOnRequest) (*hav1.LightResponse, error) {
|
||||
s, err := h.svc.TurnOn(ctx, protoTurnOnToParams(req))
|
||||
if err != nil {
|
||||
return nil, grpcError(err)
|
||||
}
|
||||
return &hav1.LightResponse{State: domainStateToProto(s)}, nil
|
||||
}
|
||||
|
||||
func (h *LightGRPC) TurnOff(ctx context.Context, req *hav1.TurnOffRequest) (*hav1.LightResponse, error) {
|
||||
s, err := h.svc.TurnOff(ctx, protoTurnOffToParams(req))
|
||||
if err != nil {
|
||||
return nil, grpcError(err)
|
||||
}
|
||||
return &hav1.LightResponse{State: domainStateToProto(s)}, nil
|
||||
}
|
||||
|
||||
func (h *LightGRPC) Toggle(ctx context.Context, req *hav1.ToggleRequest) (*hav1.LightResponse, error) {
|
||||
s, err := h.svc.Toggle(ctx, domain.EntityID(req.EntityId))
|
||||
if err != nil {
|
||||
return nil, grpcError(err)
|
||||
}
|
||||
return &hav1.LightResponse{State: domainStateToProto(s)}, nil
|
||||
}
|
||||
53
ha-gateway/internal/adapters/primary/grpc/mapping.go
Normal file
53
ha-gateway/internal/adapters/primary/grpc/mapping.go
Normal file
@ -0,0 +1,53 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/core/domain"
|
||||
hav1 "gitea.nik4nao.com/nik/home-services/gen/ha/v1"
|
||||
)
|
||||
|
||||
func domainStateToProto(s *domain.EntityState) *hav1.EntityState {
|
||||
return &hav1.EntityState{
|
||||
EntityId: string(s.EntityID),
|
||||
State: s.State,
|
||||
Attributes: s.Attributes,
|
||||
LastChanged: s.LastChanged.Format("2006-01-02T15:04:05Z07:00"),
|
||||
LastUpdated: s.LastUpdated.Format("2006-01-02T15:04:05Z07:00"),
|
||||
}
|
||||
}
|
||||
|
||||
func protoTurnOnToParams(r *hav1.TurnOnRequest) domain.TurnOnParams {
|
||||
p := domain.TurnOnParams{
|
||||
EntityID: domain.EntityID(r.EntityId),
|
||||
}
|
||||
if r.BrightnessPct != nil {
|
||||
v := r.GetBrightnessPct()
|
||||
p.BrightnessPct = &v
|
||||
}
|
||||
if r.ColorTempKelvin != nil {
|
||||
v := r.GetColorTempKelvin()
|
||||
p.ColorTempKelvin = &v
|
||||
}
|
||||
if r.RgbColor != nil {
|
||||
p.RGBColor = &domain.RGBColor{
|
||||
R: uint8(r.RgbColor.R),
|
||||
G: uint8(r.RgbColor.G),
|
||||
B: uint8(r.RgbColor.B),
|
||||
}
|
||||
}
|
||||
if r.Transition != nil {
|
||||
v := r.GetTransition()
|
||||
p.Transition = &v
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func protoTurnOffToParams(r *hav1.TurnOffRequest) domain.TurnOffParams {
|
||||
p := domain.TurnOffParams{
|
||||
EntityID: domain.EntityID(r.EntityId),
|
||||
}
|
||||
if r.Transition != nil {
|
||||
v := r.GetTransition()
|
||||
p.Transition = &v
|
||||
}
|
||||
return p
|
||||
}
|
||||
12
ha-gateway/internal/adapters/primary/grpc/switch.go
Normal file
12
ha-gateway/internal/adapters/primary/grpc/switch.go
Normal file
@ -0,0 +1,12 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
hav1 "gitea.nik4nao.com/nik/home-services/gen/ha/v1"
|
||||
)
|
||||
|
||||
type SwitchGRPC struct {
|
||||
hav1.UnimplementedSwitchServiceServer
|
||||
}
|
||||
|
||||
// All methods return codes.Unimplemented via the embedded UnimplementedSwitchServiceServer.
|
||||
// TODO: follow the same pattern as LightGRPC once app/switch.go is implemented.
|
||||
183
ha-gateway/internal/adapters/secondary/ha/client.go
Normal file
183
ha-gateway/internal/adapters/secondary/ha/client.go
Normal file
@ -0,0 +1,183 @@
|
||||
package ha
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/config"
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/core/ports/driven"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
)
|
||||
|
||||
var tracer = otel.Tracer("ha-gateway/ha-client")
|
||||
|
||||
type Client struct {
|
||||
baseURL string
|
||||
token string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
func NewClient(cfg *config.Config) *Client {
|
||||
return &Client{
|
||||
baseURL: strings.TrimRight(cfg.HABaseURL, "/"),
|
||||
token: cfg.HAToken,
|
||||
httpClient: &http.Client{Timeout: 10 * time.Second},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetState(ctx context.Context, entityID string) (*driven.HAState, error) {
|
||||
ctx, span := tracer.Start(ctx, "ha.GetState")
|
||||
defer span.End()
|
||||
span.SetAttributes(attribute.String("entity_id", entityID))
|
||||
|
||||
var raw haStateRaw
|
||||
if err := c.get(ctx, "/api/states/"+entityID, &raw); err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
return raw.toDriven()
|
||||
}
|
||||
|
||||
func (c *Client) ListStates(ctx context.Context) ([]*driven.HAState, error) {
|
||||
ctx, span := tracer.Start(ctx, "ha.ListStates")
|
||||
defer span.End()
|
||||
|
||||
var raw []haStateRaw
|
||||
if err := c.get(ctx, "/api/states", &raw); err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make([]*driven.HAState, 0, len(raw))
|
||||
for i := range raw {
|
||||
s, err := raw[i].toDriven()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, s)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *Client) CallService(ctx context.Context, domain, service string, payload map[string]any) ([]*driven.HAState, error) {
|
||||
ctx, span := tracer.Start(ctx, "ha.CallService")
|
||||
defer span.End()
|
||||
span.SetAttributes(
|
||||
attribute.String("ha.domain", domain),
|
||||
attribute.String("ha.service", service),
|
||||
)
|
||||
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal payload: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
|
||||
c.baseURL+"/api/services/"+domain+"/"+service,
|
||||
strings.NewReader(string(body)))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("build request: %w", err)
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+c.token)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, fmt.Errorf("call service %s/%s: %w", domain, service, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
preview := string(respBody)
|
||||
if len(preview) > 200 {
|
||||
preview = preview[:200]
|
||||
}
|
||||
err := fmt.Errorf("HA returned %d: %s", resp.StatusCode, preview)
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var raw []haStateRaw
|
||||
if err := json.Unmarshal(respBody, &raw); err != nil {
|
||||
// HA may return an empty body or non-array on some calls; treat as empty.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
out := make([]*driven.HAState, 0, len(raw))
|
||||
for i := range raw {
|
||||
s, err := raw[i].toDriven()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, s)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *Client) get(ctx context.Context, path string, dst any) error {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.baseURL+path, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("build request: %w", err)
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+c.token)
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GET %s: %w", path, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
preview := string(body)
|
||||
if len(preview) > 200 {
|
||||
preview = preview[:200]
|
||||
}
|
||||
return fmt.Errorf("HA returned %d for GET %s: %s", resp.StatusCode, path, preview)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(body, dst); err != nil {
|
||||
return fmt.Errorf("decode response for GET %s: %w", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// haStateRaw is the raw JSON shape returned by the HA REST API.
|
||||
type haStateRaw struct {
|
||||
EntityID string `json:"entity_id"`
|
||||
State string `json:"state"`
|
||||
Attributes map[string]any `json:"attributes"`
|
||||
LastChanged string `json:"last_changed"`
|
||||
LastUpdated string `json:"last_updated"`
|
||||
}
|
||||
|
||||
func (r *haStateRaw) toDriven() (*driven.HAState, error) {
|
||||
lc, err := time.Parse(time.RFC3339, r.LastChanged)
|
||||
if err != nil {
|
||||
lc = time.Time{}
|
||||
}
|
||||
lu, err := time.Parse(time.RFC3339, r.LastUpdated)
|
||||
if err != nil {
|
||||
lu = time.Time{}
|
||||
}
|
||||
return &driven.HAState{
|
||||
EntityID: r.EntityID,
|
||||
State: r.State,
|
||||
Attributes: r.Attributes,
|
||||
LastChanged: lc,
|
||||
LastUpdated: lu,
|
||||
}, nil
|
||||
}
|
||||
7
ha-gateway/internal/adapters/secondary/ha/websocket.go
Normal file
7
ha-gateway/internal/adapters/secondary/ha/websocket.go
Normal file
@ -0,0 +1,7 @@
|
||||
package ha
|
||||
|
||||
// TODO: implement HA WebSocket client.
|
||||
// Auth flow: receive auth_required → send {"type":"auth","access_token":"..."} → receive auth_ok.
|
||||
// Subscribe: send {"id":1,"type":"subscribe_events","event_type":"state_changed"}
|
||||
// Events: stream {"type":"event","event":{"event_type":"state_changed","data":{...}}}
|
||||
// This adapter will publish to the internal fanout broker once EventService is implemented.
|
||||
68
ha-gateway/internal/app/entity.go
Normal file
68
ha-gateway/internal/app/entity.go
Normal file
@ -0,0 +1,68 @@
|
||||
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
|
||||
}
|
||||
|
||||
func NewEntityApp(ha driven.HAClient) *EntityApp {
|
||||
return &EntityApp{ha: ha}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
65
ha-gateway/internal/app/light.go
Normal file
65
ha-gateway/internal/app/light.go
Normal file
@ -0,0 +1,65 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/core/domain"
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/core/ports/driven"
|
||||
)
|
||||
|
||||
type LightApp struct {
|
||||
ha driven.HAClient
|
||||
}
|
||||
|
||||
func NewLightApp(ha driven.HAClient) *LightApp {
|
||||
return &LightApp{ha: ha}
|
||||
}
|
||||
|
||||
func (a *LightApp) TurnOn(ctx context.Context, p domain.TurnOnParams) (*domain.EntityState, error) {
|
||||
payload := map[string]any{"entity_id": string(p.EntityID)}
|
||||
if p.BrightnessPct != nil {
|
||||
payload["brightness_pct"] = *p.BrightnessPct
|
||||
}
|
||||
if p.ColorTempKelvin != nil {
|
||||
payload["color_temp_kelvin"] = *p.ColorTempKelvin
|
||||
}
|
||||
if p.RGBColor != nil {
|
||||
payload["rgb_color"] = []uint8{p.RGBColor.R, p.RGBColor.G, p.RGBColor.B}
|
||||
}
|
||||
if p.Transition != nil {
|
||||
payload["transition"] = *p.Transition
|
||||
}
|
||||
return a.callService(ctx, "light", "turn_on", payload)
|
||||
}
|
||||
|
||||
func (a *LightApp) TurnOff(ctx context.Context, p domain.TurnOffParams) (*domain.EntityState, error) {
|
||||
payload := map[string]any{"entity_id": string(p.EntityID)}
|
||||
if p.Transition != nil {
|
||||
payload["transition"] = *p.Transition
|
||||
}
|
||||
return a.callService(ctx, "light", "turn_off", payload)
|
||||
}
|
||||
|
||||
func (a *LightApp) Toggle(ctx context.Context, id domain.EntityID) (*domain.EntityState, error) {
|
||||
payload := map[string]any{"entity_id": string(id)}
|
||||
return a.callService(ctx, "light", "toggle", payload)
|
||||
}
|
||||
|
||||
func (a *LightApp) callService(ctx context.Context, svcDomain, service string, payload map[string]any) (*domain.EntityState, error) {
|
||||
states, err := a.ha.CallService(ctx, svcDomain, service, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entityID, _ := payload["entity_id"].(string)
|
||||
for _, s := range states {
|
||||
if s.EntityID == entityID {
|
||||
return haStateToDomain(s), nil
|
||||
}
|
||||
}
|
||||
// HA may return an empty list on success; fall back to GetState.
|
||||
s, err := a.ha.GetState(ctx, entityID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return haStateToDomain(s), nil
|
||||
}
|
||||
33
ha-gateway/internal/config/config.go
Normal file
33
ha-gateway/internal/config/config.go
Normal file
@ -0,0 +1,33 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
GRPCPort string // GRPC_PORT, default "50051"
|
||||
HABaseURL string // HA_BASE_URL, e.g. "http://ha.home.arpa:8123"
|
||||
HAToken string // HA_TOKEN — long-lived access token (required)
|
||||
OTELEndpoint string // OTEL_ENDPOINT, e.g. "otel-collector.monitoring.svc:4317"
|
||||
// empty = telemetry disabled (local dev default)
|
||||
}
|
||||
|
||||
func Load() (*Config, error) {
|
||||
token := os.Getenv("HA_TOKEN")
|
||||
if token == "" {
|
||||
return nil, errors.New("HA_TOKEN is required but not set")
|
||||
}
|
||||
|
||||
port := os.Getenv("GRPC_PORT")
|
||||
if port == "" {
|
||||
port = "50051"
|
||||
}
|
||||
|
||||
return &Config{
|
||||
GRPCPort: port,
|
||||
HABaseURL: os.Getenv("HA_BASE_URL"),
|
||||
HAToken: token,
|
||||
OTELEndpoint: os.Getenv("OTEL_ENDPOINT"),
|
||||
}, nil
|
||||
}
|
||||
13
ha-gateway/internal/core/domain/entity.go
Normal file
13
ha-gateway/internal/core/domain/entity.go
Normal file
@ -0,0 +1,13 @@
|
||||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
type EntityID string
|
||||
|
||||
type EntityState struct {
|
||||
EntityID EntityID
|
||||
State string
|
||||
Attributes map[string]string
|
||||
LastChanged time.Time
|
||||
LastUpdated time.Time
|
||||
}
|
||||
18
ha-gateway/internal/core/domain/light.go
Normal file
18
ha-gateway/internal/core/domain/light.go
Normal file
@ -0,0 +1,18 @@
|
||||
package domain
|
||||
|
||||
type TurnOnParams struct {
|
||||
EntityID EntityID
|
||||
BrightnessPct *uint32 // nil = not set
|
||||
ColorTempKelvin *uint32
|
||||
RGBColor *RGBColor
|
||||
Transition *uint32
|
||||
}
|
||||
|
||||
type RGBColor struct {
|
||||
R, G, B uint8
|
||||
}
|
||||
|
||||
type TurnOffParams struct {
|
||||
EntityID EntityID
|
||||
Transition *uint32
|
||||
}
|
||||
22
ha-gateway/internal/core/ports/driven/ha.go
Normal file
22
ha-gateway/internal/core/ports/driven/ha.go
Normal file
@ -0,0 +1,22 @@
|
||||
package driven
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HAClient interface {
|
||||
GetState(ctx context.Context, entityID string) (*HAState, error)
|
||||
ListStates(ctx context.Context) ([]*HAState, error)
|
||||
CallService(ctx context.Context, domain, service string, payload map[string]any) ([]*HAState, error)
|
||||
}
|
||||
|
||||
// HAState mirrors the HA REST response. Internal type — not exposed outside
|
||||
// the driven port layer.
|
||||
type HAState struct {
|
||||
EntityID string
|
||||
State string
|
||||
Attributes map[string]any
|
||||
LastChanged time.Time
|
||||
LastUpdated time.Time
|
||||
}
|
||||
12
ha-gateway/internal/core/ports/driving/entity.go
Normal file
12
ha-gateway/internal/core/ports/driving/entity.go
Normal file
@ -0,0 +1,12 @@
|
||||
package driving
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/core/domain"
|
||||
)
|
||||
|
||||
type EntityService interface {
|
||||
GetState(ctx context.Context, id domain.EntityID) (*domain.EntityState, error)
|
||||
ListStates(ctx context.Context, ids []domain.EntityID, domainFilter string) ([]*domain.EntityState, error)
|
||||
}
|
||||
13
ha-gateway/internal/core/ports/driving/light.go
Normal file
13
ha-gateway/internal/core/ports/driving/light.go
Normal file
@ -0,0 +1,13 @@
|
||||
package driving
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/core/domain"
|
||||
)
|
||||
|
||||
type LightService interface {
|
||||
TurnOn(ctx context.Context, p domain.TurnOnParams) (*domain.EntityState, error)
|
||||
TurnOff(ctx context.Context, p domain.TurnOffParams) (*domain.EntityState, error)
|
||||
Toggle(ctx context.Context, id domain.EntityID) (*domain.EntityState, error)
|
||||
}
|
||||
78
ha-gateway/internal/telemetry/telemetry.go
Normal file
78
ha-gateway/internal/telemetry/telemetry.go
Normal file
@ -0,0 +1,78 @@
|
||||
package telemetry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
|
||||
|
||||
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/config"
|
||||
)
|
||||
|
||||
// Setup initialises OTel trace and metric providers. If cfg.OTELEndpoint is
|
||||
// empty, no-op providers are installed and Setup returns immediately. The
|
||||
// returned shutdown func flushes and closes both exporters.
|
||||
func Setup(ctx context.Context, cfg *config.Config, version string) (shutdown func(context.Context) error, err error) {
|
||||
if cfg.OTELEndpoint == "" {
|
||||
// Local dev — no telemetry.
|
||||
return func(context.Context) error { return nil }, nil
|
||||
}
|
||||
|
||||
res, err := resource.New(ctx,
|
||||
resource.WithAttributes(
|
||||
semconv.ServiceName("ha-gateway"),
|
||||
semconv.ServiceVersion(version),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Trace exporter.
|
||||
traceExp, err := otlptracegrpc.New(ctx,
|
||||
otlptracegrpc.WithEndpoint(cfg.OTELEndpoint),
|
||||
otlptracegrpc.WithInsecure(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tp := sdktrace.NewTracerProvider(
|
||||
sdktrace.WithBatcher(traceExp),
|
||||
sdktrace.WithResource(res),
|
||||
)
|
||||
otel.SetTracerProvider(tp)
|
||||
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
|
||||
propagation.TraceContext{},
|
||||
propagation.Baggage{},
|
||||
))
|
||||
|
||||
// Metric exporter.
|
||||
metricExp, err := otlpmetricgrpc.New(ctx,
|
||||
otlpmetricgrpc.WithEndpoint(cfg.OTELEndpoint),
|
||||
otlpmetricgrpc.WithInsecure(),
|
||||
)
|
||||
if err != nil {
|
||||
_ = tp.Shutdown(ctx)
|
||||
return nil, err
|
||||
}
|
||||
mp := sdkmetric.NewMeterProvider(
|
||||
sdkmetric.WithReader(sdkmetric.NewPeriodicReader(metricExp,
|
||||
sdkmetric.WithInterval(30*time.Second))),
|
||||
sdkmetric.WithResource(res),
|
||||
)
|
||||
otel.SetMeterProvider(mp)
|
||||
|
||||
return func(ctx context.Context) error {
|
||||
if err := tp.Shutdown(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
return mp.Shutdown(ctx)
|
||||
}, nil
|
||||
}
|
||||
17
proto/ha/v1/common.proto
Normal file
17
proto/ha/v1/common.proto
Normal file
@ -0,0 +1,17 @@
|
||||
syntax = "proto3";
|
||||
package ha.v1;
|
||||
option go_package = "gitea.nik4nao.com/nik/home-services/gen/ha/v1;hav1";
|
||||
|
||||
message EntityState {
|
||||
string entity_id = 1;
|
||||
string state = 2;
|
||||
map<string, string> attributes = 3;
|
||||
string last_changed = 4; // RFC3339
|
||||
string last_updated = 5; // RFC3339
|
||||
}
|
||||
|
||||
message RGBColor {
|
||||
uint32 r = 1;
|
||||
uint32 g = 2;
|
||||
uint32 b = 3;
|
||||
}
|
||||
18
proto/ha/v1/entity.proto
Normal file
18
proto/ha/v1/entity.proto
Normal file
@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
package ha.v1;
|
||||
option go_package = "gitea.nik4nao.com/nik/home-services/gen/ha/v1;hav1";
|
||||
import "ha/v1/common.proto";
|
||||
|
||||
service EntityService {
|
||||
rpc GetState(GetStateRequest) returns (GetStateResponse);
|
||||
rpc ListStates(ListStatesRequest) returns (ListStatesResponse);
|
||||
}
|
||||
|
||||
message GetStateRequest { string entity_id = 1; }
|
||||
message GetStateResponse { EntityState state = 1; }
|
||||
|
||||
message ListStatesRequest {
|
||||
repeated string entity_ids = 1;
|
||||
string domain = 2;
|
||||
}
|
||||
message ListStatesResponse { repeated EntityState states = 1; }
|
||||
29
proto/ha/v1/event.proto
Normal file
29
proto/ha/v1/event.proto
Normal file
@ -0,0 +1,29 @@
|
||||
syntax = "proto3";
|
||||
package ha.v1;
|
||||
option go_package = "gitea.nik4nao.com/nik/home-services/gen/ha/v1;hav1";
|
||||
import "ha/v1/common.proto";
|
||||
|
||||
// TODO: implement EventService fan-out.
|
||||
// Architecture:
|
||||
// 1. adapters/secondary/ha/websocket.go connects to HA WebSocket,
|
||||
// authenticates, and subscribes to state_changed events.
|
||||
// 2. An internal broker (internal/fanout/broker.go) holds a sync.Map of
|
||||
// subscriber channels, one per active Subscribe stream.
|
||||
// 3. The WebSocket adapter publishes to the broker; the gRPC Subscribe
|
||||
// handler reads from its channel and streams to the client.
|
||||
// 4. On client disconnect (ctx.Done()), the handler deregisters its channel.
|
||||
service EventService {
|
||||
rpc Subscribe(SubscribeRequest) returns (stream StateChangeEvent);
|
||||
}
|
||||
|
||||
message SubscribeRequest {
|
||||
repeated string entity_ids = 1;
|
||||
repeated string domains = 2;
|
||||
}
|
||||
|
||||
message StateChangeEvent {
|
||||
string entity_id = 1;
|
||||
optional EntityState old_state = 2; // absent on first appearance
|
||||
EntityState new_state = 3;
|
||||
string event_time = 4; // RFC3339
|
||||
}
|
||||
27
proto/ha/v1/light.proto
Normal file
27
proto/ha/v1/light.proto
Normal file
@ -0,0 +1,27 @@
|
||||
syntax = "proto3";
|
||||
package ha.v1;
|
||||
option go_package = "gitea.nik4nao.com/nik/home-services/gen/ha/v1;hav1";
|
||||
import "ha/v1/common.proto";
|
||||
|
||||
service LightService {
|
||||
rpc TurnOn(TurnOnRequest) returns (LightResponse);
|
||||
rpc TurnOff(TurnOffRequest) returns (LightResponse);
|
||||
rpc Toggle(ToggleRequest) returns (LightResponse);
|
||||
}
|
||||
|
||||
// optional fields require protobuf 3.15+ / buf >= 1.0. They generate
|
||||
// pointer fields in Go with Has*() accessor methods. This is intentional —
|
||||
// it lets the gateway distinguish "brightness not set" from "brightness = 0".
|
||||
message TurnOnRequest {
|
||||
string entity_id = 1;
|
||||
optional uint32 brightness_pct = 2; // 0–100
|
||||
optional uint32 color_temp_kelvin = 3; // e.g. 2700–6500
|
||||
optional RGBColor rgb_color = 4; // ignored if color_temp_kelvin set
|
||||
optional uint32 transition = 5; // seconds
|
||||
}
|
||||
message TurnOffRequest {
|
||||
string entity_id = 1;
|
||||
optional uint32 transition = 2;
|
||||
}
|
||||
message ToggleRequest { string entity_id = 1; }
|
||||
message LightResponse { EntityState state = 1; }
|
||||
18
proto/ha/v1/switch.proto
Normal file
18
proto/ha/v1/switch.proto
Normal file
@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
package ha.v1;
|
||||
option go_package = "gitea.nik4nao.com/nik/home-services/gen/ha/v1;hav1";
|
||||
import "ha/v1/common.proto";
|
||||
|
||||
// TODO: implement SwitchService. Follow the same pattern as LightService:
|
||||
// - domain type in core/domain/switch.go
|
||||
// - port interface in core/ports/driving/switch.go
|
||||
// - application logic in app/switch.go
|
||||
// - gRPC adapter in adapters/primary/grpc/switch.go
|
||||
service SwitchService {
|
||||
rpc TurnOn(SwitchRequest) returns (SwitchResponse);
|
||||
rpc TurnOff(SwitchRequest) returns (SwitchResponse);
|
||||
rpc Toggle(SwitchRequest) returns (SwitchResponse);
|
||||
}
|
||||
|
||||
message SwitchRequest { string entity_id = 1; }
|
||||
message SwitchResponse { EntityState state = 1; }
|
||||
Loading…
x
Reference in New Issue
Block a user