Skip to content

Commit c9558d4

Browse files
daviddsapirDavid Sapir
authored and
David Sapir
committedOct 30, 2024
Feat: Add CDT Support as CEM Client Actor
This commit introduces CDT support as a CEM client actor. It provides full support for the CDT use case, specifically Scenario 1. This includes the ability to retrieve setpoints, their constraints, and map them to their corresponding operation modes via HvacSetpointRelations. For the use case to fully function, support for the CDSF (Configuration of DHW System Function) use case is necessary. Specifically, we need to request HvacOperationModeDescriptionListDataType, which is used in conjunction with HvacSystemFunctionSetpointRelationListDataType to establish the mapping between operation modes and their setpoints, and to enable the ability to write setpoints. Note: Writing setpoints was tested and confirmed to work with Vaillant's HeatPump by requesting the HvacOperationModeDescriptionListDataType message and performing the mapping without the CDSF use case. Resources used (specifications): - EEBus UC Technical Specification Configuration of DHW Temperature - EEBus SPINE Technical Specification Resource Specification
1 parent 2a6167e commit c9558d4

15 files changed

+1215
-6
lines changed
 

‎features/client/hvac.go

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package client
2+
3+
import (
4+
"github.com/enbility/eebus-go/features/internal"
5+
spineapi "github.com/enbility/spine-go/api"
6+
"github.com/enbility/spine-go/model"
7+
)
8+
9+
type Hvac struct {
10+
*Feature
11+
12+
*internal.HvacCommon
13+
}
14+
15+
// Get a new HVAC features helper
16+
//
17+
// - The feature on the local entity has to be of role client
18+
// - The feature on the remote entity has to be of role server
19+
func NewHvac(
20+
localEntity spineapi.EntityLocalInterface,
21+
remoteEntity spineapi.EntityRemoteInterface,
22+
) (*Hvac, error) {
23+
feature, err := NewFeature(model.FeatureTypeTypeHvac, localEntity, remoteEntity)
24+
if err != nil {
25+
return nil, err
26+
}
27+
28+
hvac := &Hvac{
29+
Feature: feature,
30+
HvacCommon: internal.NewRemoteHvac(feature.featureRemote),
31+
}
32+
33+
return hvac, nil
34+
}
35+
36+
// request FunctionTypeHvacSystemFunctionSetPointRelationListData from a remote device
37+
func (h *Hvac) RequestHvacSystemFunctionSetPointRelations(
38+
selector *model.HvacSystemFunctionSetpointRelationListDataSelectorsType,
39+
elements *model.HvacSystemFunctionSetpointRelationDataElementsType,
40+
) (*model.MsgCounterType, error) {
41+
return h.requestData(model.FunctionTypeHvacSystemFunctionSetPointRelationListData, selector, elements)
42+
}
43+
44+
// request FunctionTypeHvacOperationModeDescriptionListData from a remote device
45+
func (h *Hvac) RequestHvacOperationModeDescriptions(
46+
selector *model.HvacOperationModeDescriptionListDataSelectorsType,
47+
elements *model.HvacOperationModeDescriptionDataElementsType,
48+
) (*model.MsgCounterType, error) {
49+
return h.requestData(model.FunctionTypeHvacOperationModeDescriptionListData, selector, elements)
50+
}

‎features/client/setpoint.go

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package client
2+
3+
import (
4+
"github.com/enbility/eebus-go/api"
5+
"github.com/enbility/eebus-go/features/internal"
6+
spineapi "github.com/enbility/spine-go/api"
7+
"github.com/enbility/spine-go/model"
8+
)
9+
10+
type Setpoint struct {
11+
*Feature
12+
13+
*internal.SetPointCommon
14+
}
15+
16+
// Get a new SetPoint features helper
17+
//
18+
// - The feature on the local entity has to be of role client
19+
// - The feature on the remote entity has to be of role server
20+
func NewSetpoint(
21+
localEntity spineapi.EntityLocalInterface,
22+
remoteEntity spineapi.EntityRemoteInterface,
23+
) (*Setpoint, error) {
24+
feature, err := NewFeature(model.FeatureTypeTypeSetpoint, localEntity, remoteEntity)
25+
if err != nil {
26+
return nil, err
27+
}
28+
29+
sp := &Setpoint{
30+
Feature: feature,
31+
SetPointCommon: internal.NewRemoteSetPoint(feature.featureRemote),
32+
}
33+
34+
return sp, nil
35+
}
36+
37+
// request FunctionTypeSetpointDescriptionListData from a remote device
38+
func (s *Setpoint) RequestSetPointDescriptions(
39+
selector *model.SetpointDescriptionListDataSelectorsType,
40+
elements *model.SetpointDescriptionDataElementsType,
41+
) (*model.MsgCounterType, error) {
42+
return s.requestData(model.FunctionTypeSetpointDescriptionListData, selector, elements)
43+
}
44+
45+
// request FunctionTypeSetpointConstraintsListData from a remote device
46+
func (s *Setpoint) RequestSetPointConstraints(
47+
selector *model.SetpointConstraintsListDataSelectorsType,
48+
elements *model.SetpointConstraintsDataElementsType,
49+
) (*model.MsgCounterType, error) {
50+
return s.requestData(model.FunctionTypeSetpointConstraintsListData, selector, elements)
51+
}
52+
53+
// request FunctionTypeSetpointListData from a remote device
54+
func (s *Setpoint) RequestSetPoints(
55+
selector *model.SetpointListDataSelectorsType,
56+
elements *model.SetpointDataElementsType,
57+
) (*model.MsgCounterType, error) {
58+
return s.requestData(model.FunctionTypeSetpointListData, selector, elements)
59+
}
60+
61+
// WriteSetPointListData writes the given setpoint data
62+
//
63+
// Parameters:
64+
// - data: the setpoint data to write
65+
//
66+
// Returns:
67+
// - the message counter of the sent message
68+
// - an error if the data could not be written
69+
func (s *Setpoint) WriteSetPointListData(
70+
data []model.SetpointDataType,
71+
) (*model.MsgCounterType, error) {
72+
if len(data) == 0 {
73+
return nil, api.ErrMissingData
74+
}
75+
76+
cmd := model.CmdType{
77+
SetpointListData: &model.SetpointListDataType{
78+
SetpointData: data,
79+
},
80+
}
81+
82+
return s.remoteDevice.Sender().Write(s.featureLocal.Address(), s.featureRemote.Address(), cmd)
83+
}

‎features/internal/hvac.go

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package internal
2+
3+
import (
4+
spineapi "github.com/enbility/spine-go/api"
5+
"github.com/enbility/spine-go/model"
6+
)
7+
8+
type HvacCommon struct {
9+
featureLocal spineapi.FeatureLocalInterface
10+
featureRemote spineapi.FeatureRemoteInterface
11+
}
12+
13+
// NewLocalHvac creates a new HvacCommon helper for local entities
14+
func NewLocalHvac(featureLocal spineapi.FeatureLocalInterface) *HvacCommon {
15+
return &HvacCommon{
16+
featureLocal: featureLocal,
17+
}
18+
}
19+
20+
// NewRemoteHvac creates a new HvacCommon helper for remote entities
21+
func NewRemoteHvac(featureRemote spineapi.FeatureRemoteInterface) *HvacCommon {
22+
return &HvacCommon{
23+
featureRemote: featureRemote,
24+
}
25+
}
26+
27+
// GetHvacOperationModeDescriptions returns the operation mode descriptions
28+
func (h *HvacCommon) GetHvacOperationModeDescriptions() ([]model.HvacOperationModeDescriptionDataType, error) {
29+
function := model.FunctionTypeHvacOperationModeDescriptionListData
30+
operationModeDescriptions := make([]model.HvacOperationModeDescriptionDataType, 0)
31+
32+
data, err := featureDataCopyOfType[model.HvacOperationModeDescriptionListDataType](h.featureLocal, h.featureRemote, function)
33+
if err == nil || data != nil {
34+
operationModeDescriptions = append(operationModeDescriptions, data.HvacOperationModeDescriptionData...)
35+
}
36+
37+
return operationModeDescriptions, nil
38+
}
39+
40+
// GetHvacSystemFunctionOperationModeRelations returns the operation mode relations (used to map operation modes to setpoints)
41+
func (h *HvacCommon) GetHvacSystemFunctionOperationModeRelations() ([]model.HvacSystemFunctionSetpointRelationDataType, error) {
42+
function := model.FunctionTypeHvacSystemFunctionSetPointRelationListData
43+
relations := make([]model.HvacSystemFunctionSetpointRelationDataType, 0)
44+
45+
data, err := featureDataCopyOfType[model.HvacSystemFunctionSetpointRelationListDataType](h.featureLocal, h.featureRemote, function)
46+
if err == nil || data != nil {
47+
relations = append(relations, data.HvacSystemFunctionSetpointRelationData...)
48+
}
49+
50+
return relations, nil
51+
}

‎features/internal/setpoint.go

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package internal
2+
3+
import (
4+
"github.com/enbility/eebus-go/api"
5+
"github.com/enbility/ship-go/util"
6+
spineapi "github.com/enbility/spine-go/api"
7+
"github.com/enbility/spine-go/model"
8+
)
9+
10+
type SetPointCommon struct {
11+
featureLocal spineapi.FeatureLocalInterface
12+
featureRemote spineapi.FeatureRemoteInterface
13+
}
14+
15+
// NewLocalSetPoint creates a new SetPointCommon helper for local entities
16+
func NewLocalSetPoint(featureLocal spineapi.FeatureLocalInterface) *SetPointCommon {
17+
return &SetPointCommon{
18+
featureLocal: featureLocal,
19+
}
20+
}
21+
22+
// NewRemoteSetPoint creates a new SetPointCommon helper for remote entities
23+
func NewRemoteSetPoint(featureRemote spineapi.FeatureRemoteInterface) *SetPointCommon {
24+
return &SetPointCommon{
25+
featureRemote: featureRemote,
26+
}
27+
}
28+
29+
// GetSetpointDescriptions returns the setpoint descriptions
30+
func (s *SetPointCommon) GetSetpointDescriptions() ([]model.SetpointDescriptionDataType, error) {
31+
function := model.FunctionTypeSetpointDescriptionListData
32+
33+
data, err := featureDataCopyOfType[model.SetpointDescriptionListDataType](s.featureLocal, s.featureRemote, function)
34+
if err != nil || data == nil || data.SetpointDescriptionData == nil {
35+
return nil, api.ErrDataNotAvailable
36+
}
37+
38+
return data.SetpointDescriptionData, nil
39+
}
40+
41+
// GetSetpointForId returns the setpoint data for a given setpoint ID
42+
func (s *SetPointCommon) GetSetpointForId(
43+
id model.SetpointIdType,
44+
) (*model.SetpointDataType, error) {
45+
filter := model.SetpointDataType{
46+
SetpointId: &id,
47+
}
48+
49+
result, err := s.GetSetpointDataForFilter(filter)
50+
if err != nil || len(result) == 0 {
51+
return nil, api.ErrDataNotAvailable
52+
}
53+
54+
return util.Ptr(result[0]), nil
55+
}
56+
57+
// GetSetpoints returns the setpoints
58+
func (s *SetPointCommon) GetSetpoints() []model.SetpointDataType {
59+
function := model.FunctionTypeSetpointListData
60+
61+
data, err := featureDataCopyOfType[model.SetpointListDataType](s.featureLocal, s.featureRemote, function)
62+
if err != nil || data == nil || data.SetpointData == nil {
63+
return []model.SetpointDataType{}
64+
}
65+
66+
return data.SetpointData
67+
}
68+
69+
// GetSetpointDataForFilter returns the setpoint data for a given filter
70+
func (s *SetPointCommon) GetSetpointDataForFilter(
71+
filter model.SetpointDataType,
72+
) ([]model.SetpointDataType, error) {
73+
function := model.FunctionTypeSetpointListData
74+
75+
data, err := featureDataCopyOfType[model.SetpointListDataType](s.featureLocal, s.featureRemote, function)
76+
if err != nil || data == nil || data.SetpointData == nil {
77+
return nil, api.ErrDataNotAvailable
78+
}
79+
80+
result := searchFilterInList[model.SetpointDataType](data.SetpointData, filter)
81+
82+
return result, nil
83+
}
84+
85+
// GetSetpointConstraints returns the setpoints constraints.
86+
func (s *SetPointCommon) GetSetpointConstraints() []model.SetpointConstraintsDataType {
87+
function := model.FunctionTypeSetpointConstraintsListData
88+
89+
data, err := featureDataCopyOfType[model.SetpointConstraintsListDataType](s.featureLocal, s.featureRemote, function)
90+
if err != nil || data == nil || data.SetpointConstraintsData == nil {
91+
return []model.SetpointConstraintsDataType{}
92+
}
93+
94+
return data.SetpointConstraintsData
95+
}
96+
97+
// GetSetpointConstraintsForId returns the setpoint constraints for a given setpoint ID
98+
func (s *SetPointCommon) GetSetpointConstraintsForId(
99+
id model.SetpointIdType,
100+
) (*model.SetpointConstraintsDataType, error) {
101+
filter := model.SetpointConstraintsDataType{
102+
SetpointId: &id,
103+
}
104+
105+
result, err := s.GetSetpointConstraintsForFilter(filter)
106+
if err != nil || len(result) == 0 {
107+
return nil, api.ErrDataNotAvailable
108+
}
109+
110+
return util.Ptr(result[0]), nil
111+
}
112+
113+
// GetSetpointConstraintsForFilter returns the setpoint constraints for a given filter
114+
func (s *SetPointCommon) GetSetpointConstraintsForFilter(
115+
filter model.SetpointConstraintsDataType,
116+
) ([]model.SetpointConstraintsDataType, error) {
117+
function := model.FunctionTypeSetpointConstraintsListData
118+
119+
data, err := featureDataCopyOfType[model.SetpointConstraintsListDataType](s.featureLocal, s.featureRemote, function)
120+
if err != nil || data == nil || data.SetpointConstraintsData == nil {
121+
return nil, api.ErrDataNotAvailable
122+
}
123+
124+
result := searchFilterInList[model.SetpointConstraintsDataType](data.SetpointConstraintsData, filter)
125+
126+
return result, nil
127+
}

‎go.mod

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ module github.com/enbility/eebus-go
33
go 1.22.0
44

55
require (
6-
github.com/enbility/ship-go v0.0.0-20241006160314-3a4325a1a6d6
7-
github.com/enbility/spine-go v0.0.0-20241007182100-30ee8bc405a7
6+
github.com/enbility/ship-go v0.6.1-0.20241023165311-5963bf4d9424
7+
github.com/enbility/spine-go v0.7.1-0.20241023170915-0b14938a9a37
88
github.com/stretchr/testify v1.9.0
99
)
1010

‎go.sum

+6-4
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
44
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
55
github.com/enbility/go-avahi v0.0.0-20240909195612-d5de6b280d7a h1:foChWb8lhzqa6lWDRs6COYMdp649YlUirFP8GqoT0JQ=
66
github.com/enbility/go-avahi v0.0.0-20240909195612-d5de6b280d7a/go.mod h1:H64mhYcAQUGUUnVqMdZQf93kPecH4M79xwH95Lddt3U=
7-
github.com/enbility/ship-go v0.0.0-20241006160314-3a4325a1a6d6 h1:bjrcJ4wxEsG5rXHlXnedRzqAV9JYglj82S14Nf1oLvs=
8-
github.com/enbility/ship-go v0.0.0-20241006160314-3a4325a1a6d6/go.mod h1:JJp8EQcJhUhTpZ2LSEU4rpdaM3E2n08tswWFWtmm/wU=
9-
github.com/enbility/spine-go v0.0.0-20241007182100-30ee8bc405a7 h1:n6tv+YUMncSR9qxUs6k7d/YsKD9ujHHp5pUspIvM6sc=
10-
github.com/enbility/spine-go v0.0.0-20241007182100-30ee8bc405a7/go.mod h1:ZoI9TaJO/So/677uknrli8sc6iryD7wC5iWhVIre+MI=
7+
github.com/enbility/ship-go v0.6.1-0.20241023165311-5963bf4d9424 h1:yzf1pWKZn+vhxtWE1ZNyspAX8GiX/r4uBXZU+SdidNY=
8+
github.com/enbility/ship-go v0.6.1-0.20241023165311-5963bf4d9424/go.mod h1:JJp8EQcJhUhTpZ2LSEU4rpdaM3E2n08tswWFWtmm/wU=
9+
github.com/enbility/spine-go v0.7.0 h1:UZeghFgnM3VFU0ghc57Htt6gnxwP9jLppfU2GUMJGgY=
10+
github.com/enbility/spine-go v0.7.0/go.mod h1:IF1sBTr7p3wXqlejeBJcJ8BYFlzzRaZcJsGw8XjgEgc=
11+
github.com/enbility/spine-go v0.7.1-0.20241023170915-0b14938a9a37 h1:oZFPU4fHYBbSMVCwP3c9GHov8dFXqqQ2McvEyalsBY8=
12+
github.com/enbility/spine-go v0.7.1-0.20241023170915-0b14938a9a37/go.mod h1:ZoI9TaJO/So/677uknrli8sc6iryD7wC5iWhVIre+MI=
1113
github.com/enbility/zeroconf/v2 v2.0.0-20240920094356-be1cae74fda6 h1:XOYvxKtT1oxT37w/5oEiRLuPbm9FuJPt3fiYhX0h8Po=
1214
github.com/enbility/zeroconf/v2 v2.0.0-20240920094356-be1cae74fda6/go.mod h1:BszP9qFV14mPXgyIREbgIdQtWxbAj3OKqvK02HihMoM=
1315
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=

‎usecases/api/ca_cdt.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package api
2+
3+
import (
4+
"github.com/enbility/eebus-go/api"
5+
spineapi "github.com/enbility/spine-go/api"
6+
"github.com/enbility/spine-go/model"
7+
)
8+
9+
type CemCDTInterface interface {
10+
api.UseCaseInterface
11+
12+
// Scenario 1
13+
14+
// Return the current setpoints data
15+
//
16+
// parameters:
17+
// - entity: the entity to get the setpoints data from
18+
//
19+
// return values:
20+
// - setpoints: A map of the setpoints for supported modes
21+
//
22+
// possible errors:
23+
// - ErrDataNotAvailable if no such limit is (yet) available
24+
// - and others
25+
Setpoints(entity spineapi.EntityRemoteInterface) ([]Setpoint, error)
26+
27+
// Return the constraints for the setpoints
28+
//
29+
// parameters:
30+
// - entity: the entity to get the setpoints constraints from
31+
//
32+
// return values:
33+
// - setpointConstraints: A map of the constraints for supported modes
34+
//
35+
// possible errors:
36+
// - ErrDataNotAvailable if no such limit is (yet) available
37+
// - and others
38+
SetpointConstraints(entity spineapi.EntityRemoteInterface) ([]SetpointConstraints, error)
39+
40+
// Write a setpoint
41+
//
42+
// parameters:
43+
// - entity: the entity to write the setpoint to
44+
// - mode: the mode to write the setpoint for
45+
// - degC: the temperature setpoint value to write
46+
WriteSetpoint(entity spineapi.EntityRemoteInterface, mode model.HvacOperationModeTypeType, degC float64) error
47+
}

0 commit comments

Comments
 (0)