Rename SoC to Soc and LoadPoint to Loadpoint (#5549)
This commit is contained in:
parent
54f204c0f8
commit
03d6d1418a
165 changed files with 1054 additions and 1032 deletions
18
api/api.go
18
api/api.go
|
|
@ -55,8 +55,8 @@ type ActionConfig struct {
|
|||
Mode *ChargeMode `mapstructure:"mode,omitempty"` // Charge Mode
|
||||
MinCurrent *float64 `mapstructure:"minCurrent,omitempty"` // Minimum Current
|
||||
MaxCurrent *float64 `mapstructure:"maxCurrent,omitempty"` // Maximum Current
|
||||
MinSoC *int `mapstructure:"minSoC,omitempty"` // Minimum SoC
|
||||
TargetSoC *int `mapstructure:"targetSoC,omitempty"` // Target SoC
|
||||
MinSoc *int `mapstructure:"minSoc,omitempty"` // Minimum Soc
|
||||
TargetSoc *int `mapstructure:"targetSoc,omitempty"` // Target Soc
|
||||
}
|
||||
|
||||
// Merge merges all non-nil properties of the additional config into the base config.
|
||||
|
|
@ -71,11 +71,11 @@ func (a ActionConfig) Merge(m ActionConfig) ActionConfig {
|
|||
if m.MaxCurrent != nil {
|
||||
a.MaxCurrent = m.MaxCurrent
|
||||
}
|
||||
if m.MinSoC != nil {
|
||||
a.MinSoC = m.MinSoC
|
||||
if m.MinSoc != nil {
|
||||
a.MinSoc = m.MinSoc
|
||||
}
|
||||
if m.TargetSoC != nil {
|
||||
a.TargetSoC = m.TargetSoC
|
||||
if m.TargetSoc != nil {
|
||||
a.TargetSoc = m.TargetSoc
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
|
@ -107,9 +107,9 @@ type MeterCurrent interface {
|
|||
Currents() (float64, float64, float64, error)
|
||||
}
|
||||
|
||||
// Battery is able to provide battery SoC in %
|
||||
// Battery is able to provide battery Soc in %
|
||||
type Battery interface {
|
||||
SoC() (float64, error)
|
||||
Soc() (float64, error)
|
||||
}
|
||||
|
||||
// ChargeState provides current charging status
|
||||
|
|
@ -204,7 +204,7 @@ type VehiclePosition interface {
|
|||
|
||||
// SocLimiter returns the vehicles charge limit
|
||||
type SocLimiter interface {
|
||||
TargetSoC() (float64, error)
|
||||
TargetSoc() (float64, error)
|
||||
}
|
||||
|
||||
// VehicleChargeController allows to start/stop the charging session on the vehicle side
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ func (x *NewReply) GetVehicleId() int64 {
|
|||
return 0
|
||||
}
|
||||
|
||||
type SoCRequest struct {
|
||||
type SocRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
|
@ -139,8 +139,8 @@ type SoCRequest struct {
|
|||
VehicleId int64 `protobuf:"varint,2,opt,name=vehicle_id,json=vehicleId,proto3" json:"vehicle_id,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SoCRequest) Reset() {
|
||||
*x = SoCRequest{}
|
||||
func (x *SocRequest) Reset() {
|
||||
*x = SocRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_vehicle_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
|
|
@ -148,13 +148,13 @@ func (x *SoCRequest) Reset() {
|
|||
}
|
||||
}
|
||||
|
||||
func (x *SoCRequest) String() string {
|
||||
func (x *SocRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SoCRequest) ProtoMessage() {}
|
||||
func (*SocRequest) ProtoMessage() {}
|
||||
|
||||
func (x *SoCRequest) ProtoReflect() protoreflect.Message {
|
||||
func (x *SocRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_vehicle_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
|
|
@ -166,26 +166,26 @@ func (x *SoCRequest) ProtoReflect() protoreflect.Message {
|
|||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SoCRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SoCRequest) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use SocRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SocRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_vehicle_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *SoCRequest) GetToken() string {
|
||||
func (x *SocRequest) GetToken() string {
|
||||
if x != nil {
|
||||
return x.Token
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SoCRequest) GetVehicleId() int64 {
|
||||
func (x *SocRequest) GetVehicleId() int64 {
|
||||
if x != nil {
|
||||
return x.VehicleId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type SoCReply struct {
|
||||
type SocReply struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
|
@ -193,8 +193,8 @@ type SoCReply struct {
|
|||
Soc float64 `protobuf:"fixed64,1,opt,name=soc,proto3" json:"soc,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SoCReply) Reset() {
|
||||
*x = SoCReply{}
|
||||
func (x *SocReply) Reset() {
|
||||
*x = SocReply{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_vehicle_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
|
|
@ -202,13 +202,13 @@ func (x *SoCReply) Reset() {
|
|||
}
|
||||
}
|
||||
|
||||
func (x *SoCReply) String() string {
|
||||
func (x *SocReply) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SoCReply) ProtoMessage() {}
|
||||
func (*SocReply) ProtoMessage() {}
|
||||
|
||||
func (x *SoCReply) ProtoReflect() protoreflect.Message {
|
||||
func (x *SocReply) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_vehicle_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
|
|
@ -220,12 +220,12 @@ func (x *SoCReply) ProtoReflect() protoreflect.Message {
|
|||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SoCReply.ProtoReflect.Descriptor instead.
|
||||
func (*SoCReply) Descriptor() ([]byte, []int) {
|
||||
// Deprecated: Use SocReply.ProtoReflect.Descriptor instead.
|
||||
func (*SocReply) Descriptor() ([]byte, []int) {
|
||||
return file_proto_vehicle_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *SoCReply) GetSoc() float64 {
|
||||
func (x *SocReply) GetSoc() float64 {
|
||||
if x != nil {
|
||||
return x.Soc
|
||||
}
|
||||
|
|
@ -280,16 +280,16 @@ var file_proto_vehicle_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
|||
var file_proto_vehicle_proto_goTypes = []interface{}{
|
||||
(*NewRequest)(nil), // 0: NewRequest
|
||||
(*NewReply)(nil), // 1: NewReply
|
||||
(*SoCRequest)(nil), // 2: SoCRequest
|
||||
(*SoCReply)(nil), // 3: SoCReply
|
||||
(*SocRequest)(nil), // 2: SocRequest
|
||||
(*SocReply)(nil), // 3: SocReply
|
||||
nil, // 4: NewRequest.ConfigEntry
|
||||
}
|
||||
var file_proto_vehicle_proto_depIdxs = []int32{
|
||||
4, // 0: NewRequest.config:type_name -> NewRequest.ConfigEntry
|
||||
0, // 1: Vehicle.New:input_type -> NewRequest
|
||||
2, // 2: Vehicle.SoC:input_type -> SoCRequest
|
||||
2, // 2: Vehicle.Soc:input_type -> SocRequest
|
||||
1, // 3: Vehicle.New:output_type -> NewReply
|
||||
3, // 4: Vehicle.SoC:output_type -> SoCReply
|
||||
3, // 4: Vehicle.Soc:output_type -> SocReply
|
||||
3, // [3:5] is the sub-list for method output_type
|
||||
1, // [1:3] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
|
|
@ -328,7 +328,7 @@ func file_proto_vehicle_proto_init() {
|
|||
}
|
||||
}
|
||||
file_proto_vehicle_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SoCRequest); i {
|
||||
switch v := v.(*SocRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
|
|
@ -340,7 +340,7 @@ func file_proto_vehicle_proto_init() {
|
|||
}
|
||||
}
|
||||
file_proto_vehicle_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SoCReply); i {
|
||||
switch v := v.(*SocReply); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const _ = grpc.SupportPackageIsVersion7
|
|||
// 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 VehicleClient interface {
|
||||
New(ctx context.Context, in *NewRequest, opts ...grpc.CallOption) (*NewReply, error)
|
||||
SoC(ctx context.Context, in *SoCRequest, opts ...grpc.CallOption) (*SoCReply, error)
|
||||
Soc(ctx context.Context, in *SocRequest, opts ...grpc.CallOption) (*SocReply, error)
|
||||
}
|
||||
|
||||
type vehicleClient struct {
|
||||
|
|
@ -39,9 +39,9 @@ func (c *vehicleClient) New(ctx context.Context, in *NewRequest, opts ...grpc.Ca
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (c *vehicleClient) SoC(ctx context.Context, in *SoCRequest, opts ...grpc.CallOption) (*SoCReply, error) {
|
||||
out := new(SoCReply)
|
||||
err := c.cc.Invoke(ctx, "/Vehicle/SoC", in, out, opts...)
|
||||
func (c *vehicleClient) Soc(ctx context.Context, in *SocRequest, opts ...grpc.CallOption) (*SocReply, error) {
|
||||
out := new(SocReply)
|
||||
err := c.cc.Invoke(ctx, "/Vehicle/Soc", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -53,7 +53,7 @@ func (c *vehicleClient) SoC(ctx context.Context, in *SoCRequest, opts ...grpc.Ca
|
|||
// for forward compatibility
|
||||
type VehicleServer interface {
|
||||
New(context.Context, *NewRequest) (*NewReply, error)
|
||||
SoC(context.Context, *SoCRequest) (*SoCReply, error)
|
||||
Soc(context.Context, *SocRequest) (*SocReply, error)
|
||||
mustEmbedUnimplementedVehicleServer()
|
||||
}
|
||||
|
||||
|
|
@ -64,8 +64,8 @@ type UnimplementedVehicleServer struct {
|
|||
func (UnimplementedVehicleServer) New(context.Context, *NewRequest) (*NewReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method New not implemented")
|
||||
}
|
||||
func (UnimplementedVehicleServer) SoC(context.Context, *SoCRequest) (*SoCReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SoC not implemented")
|
||||
func (UnimplementedVehicleServer) Soc(context.Context, *SocRequest) (*SocReply, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Soc not implemented")
|
||||
}
|
||||
func (UnimplementedVehicleServer) mustEmbedUnimplementedVehicleServer() {}
|
||||
|
||||
|
|
@ -98,20 +98,20 @@ func _Vehicle_New_Handler(srv interface{}, ctx context.Context, dec func(interfa
|
|||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Vehicle_SoC_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SoCRequest)
|
||||
func _Vehicle_Soc_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SocRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(VehicleServer).SoC(ctx, in)
|
||||
return srv.(VehicleServer).Soc(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/Vehicle/SoC",
|
||||
FullMethod: "/Vehicle/Soc",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(VehicleServer).SoC(ctx, req.(*SoCRequest))
|
||||
return srv.(VehicleServer).Soc(ctx, req.(*SocRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
|
@ -128,8 +128,8 @@ var Vehicle_ServiceDesc = grpc.ServiceDesc{
|
|||
Handler: _Vehicle_New_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SoC",
|
||||
Handler: _Vehicle_SoC_Handler,
|
||||
MethodName: "Soc",
|
||||
Handler: _Vehicle_Soc_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ option go_package = "proto/pb";
|
|||
|
||||
service Vehicle {
|
||||
rpc New (NewRequest) returns (NewReply) {}
|
||||
rpc SoC (SoCRequest) returns (SoCReply) {}
|
||||
rpc Soc (SocRequest) returns (SocReply) {}
|
||||
}
|
||||
|
||||
message NewRequest {
|
||||
|
|
@ -19,11 +19,11 @@ message NewReply {
|
|||
int64 vehicle_id = 1;
|
||||
}
|
||||
|
||||
message SoCRequest {
|
||||
message SocRequest {
|
||||
string token = 1;
|
||||
int64 vehicle_id = 2;
|
||||
}
|
||||
|
||||
message SoCReply {
|
||||
message SocReply {
|
||||
double soc = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import Energyflow from "./Energyflow.vue";
|
|||
:gridPower="1200"
|
||||
:homePower="2000"
|
||||
:batteryPower="800"
|
||||
:batterySoC="77"
|
||||
:batterySoc="77"
|
||||
siteTitle="Home"
|
||||
/>
|
||||
</Variant>
|
||||
|
|
@ -40,7 +40,7 @@ import Energyflow from "./Energyflow.vue";
|
|||
:loadpointsPower="1400"
|
||||
:activeLoadpointsCount="1"
|
||||
:batteryPower="-1500"
|
||||
:batterySoC="75"
|
||||
:batterySoc="75"
|
||||
siteTitle="Home"
|
||||
/>
|
||||
</Variant>
|
||||
|
|
@ -53,7 +53,7 @@ import Energyflow from "./Energyflow.vue";
|
|||
:gridPower="700"
|
||||
:homePower="3300"
|
||||
:batteryPower="1500"
|
||||
:batterySoC="30"
|
||||
:batterySoc="30"
|
||||
siteTitle="Home"
|
||||
/>
|
||||
</Variant>
|
||||
|
|
@ -67,7 +67,7 @@ import Energyflow from "./Energyflow.vue";
|
|||
:loadpointsPower="7500"
|
||||
:activeLoadpointsCount="2"
|
||||
:batteryPower="-700"
|
||||
:batterySoC="95"
|
||||
:batterySoc="95"
|
||||
siteTitle="Home"
|
||||
/>
|
||||
</Variant>
|
||||
|
|
@ -82,7 +82,7 @@ import Energyflow from "./Energyflow.vue";
|
|||
:loadpointsPower="5600"
|
||||
:activeLoadpointsCount="2"
|
||||
:batteryPower="800"
|
||||
:batterySoC="76"
|
||||
:batterySoc="76"
|
||||
siteTitle="Home"
|
||||
/>
|
||||
</Variant>
|
||||
|
|
@ -97,7 +97,7 @@ import Energyflow from "./Energyflow.vue";
|
|||
:loadpointsPower="5500"
|
||||
:activeLoadpointsCount="1"
|
||||
:batteryPower="0"
|
||||
:batterySoC="0"
|
||||
:batterySoc="0"
|
||||
siteTitle="Home"
|
||||
/>
|
||||
</Variant>
|
||||
|
|
@ -110,7 +110,7 @@ import Energyflow from "./Energyflow.vue";
|
|||
:gridPower="-300"
|
||||
:homePower="300"
|
||||
:batteryPower="-100"
|
||||
:batterySoC="55"
|
||||
:batterySoc="55"
|
||||
siteTitle="Home"
|
||||
/>
|
||||
</Variant>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
:batteryDischarge="batteryDischarge"
|
||||
:pvProduction="pvProduction"
|
||||
:homePower="homePower"
|
||||
:batterySoC="batterySoC"
|
||||
:batterySoc="batterySoc"
|
||||
:valuesInKw="valuesInKw"
|
||||
:vehicleIcons="vehicleIcons"
|
||||
/>
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
v-if="batteryConfigured"
|
||||
:name="$t('main.energyflow.batteryDischarge')"
|
||||
icon="battery"
|
||||
:soc="batterySoC"
|
||||
:soc="batterySoc"
|
||||
:power="batteryDischarge"
|
||||
:valuesInKw="valuesInKw"
|
||||
/>
|
||||
|
|
@ -105,7 +105,7 @@
|
|||
v-if="batteryConfigured"
|
||||
:name="$t('main.energyflow.batteryCharge')"
|
||||
icon="battery"
|
||||
:soc="batterySoC"
|
||||
:soc="batterySoc"
|
||||
:power="batteryCharge"
|
||||
:valuesInKw="valuesInKw"
|
||||
/>
|
||||
|
|
@ -143,7 +143,7 @@ export default {
|
|||
activeLoadpointsCount: { type: Number, default: 0 },
|
||||
batteryConfigured: Boolean,
|
||||
batteryPower: { type: Number, default: 0 },
|
||||
batterySoC: { type: Number, default: 0 },
|
||||
batterySoc: { type: Number, default: 0 },
|
||||
vehicleIcons: { type: Array },
|
||||
},
|
||||
data: () => {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
</span>
|
||||
<span class="text-nowrap flex-grow-1 ms-3">{{ name }}</span>
|
||||
<span class="text-end text-nowrap ps-1 fw-bold"
|
||||
><span v-if="hasSoC">{{ soc }}% / </span>
|
||||
><span v-if="hasSoc">{{ soc }}% / </span>
|
||||
<AnimatedNumber :to="power" :format="kw" />
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -44,7 +44,7 @@ export default {
|
|||
isVehicle: function () {
|
||||
return this.icon === "vehicle";
|
||||
},
|
||||
hasSoC: function () {
|
||||
hasSoc: function () {
|
||||
return this.isBattery && !isNaN(this.soc);
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<shopicon-regular-sun></shopicon-regular-sun>
|
||||
</LabelBar>
|
||||
<LabelBar v-bind="labelBarProps('top', 'batteryDischarge')">
|
||||
<BatteryIcon :soc="batterySoC" />
|
||||
<BatteryIcon :soc="batterySoc" />
|
||||
</LabelBar>
|
||||
<LabelBar v-bind="labelBarProps('top', 'gridImport')">
|
||||
<shopicon-regular-powersupply></shopicon-regular-powersupply>
|
||||
|
|
@ -64,7 +64,7 @@
|
|||
<VehicleIcon :names="vehicleIcons" />
|
||||
</LabelBar>
|
||||
<LabelBar v-bind="labelBarProps('bottom', 'batteryCharge')">
|
||||
<BatteryIcon :soc="batterySoC" />
|
||||
<BatteryIcon :soc="batterySoc" />
|
||||
</LabelBar>
|
||||
<LabelBar v-bind="labelBarProps('bottom', 'gridExport')">
|
||||
<shopicon-regular-powersupply></shopicon-regular-powersupply>
|
||||
|
|
@ -97,7 +97,7 @@ export default {
|
|||
batteryDischarge: { type: Number, default: 0 },
|
||||
pvProduction: { type: Number, default: 0 },
|
||||
homePower: { type: Number, default: 0 },
|
||||
batterySoC: { type: Number, default: 0 },
|
||||
batterySoc: { type: Number, default: 0 },
|
||||
valuesInKw: { type: Boolean, default: false },
|
||||
vehicleIcons: { type: Array },
|
||||
},
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ const state = reactive({
|
|||
connected: true,
|
||||
mode: "pv",
|
||||
charging: true,
|
||||
vehicleSoC: 66,
|
||||
targetSoC: 90,
|
||||
vehicleSoc: 66,
|
||||
targetSoc: 90,
|
||||
chargeCurrent: 7,
|
||||
minCurrent: 6,
|
||||
maxCurrent: 16,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
@maxcurrent-updated="setMaxCurrent"
|
||||
@mincurrent-updated="setMinCurrent"
|
||||
@phasesconfigured-updated="setPhasesConfigured"
|
||||
@minsoc-updated="setMinSoC"
|
||||
@minsoc-updated="setMinSoc"
|
||||
/>
|
||||
|
||||
<div
|
||||
|
|
@ -92,7 +92,7 @@
|
|||
<hr class="divider" />
|
||||
<Vehicle
|
||||
v-bind="vehicle"
|
||||
@target-soc-updated="setTargetSoC"
|
||||
@target-soc-updated="setTargetSoc"
|
||||
@target-energy-updated="setTargetEnergy"
|
||||
@target-time-updated="setTargetTime"
|
||||
@target-time-removed="removeTargetTime"
|
||||
|
|
@ -133,7 +133,7 @@ export default {
|
|||
// main
|
||||
title: String,
|
||||
mode: String,
|
||||
targetSoC: Number,
|
||||
targetSoc: Number,
|
||||
targetEnergy: Number,
|
||||
remoteDisabled: Boolean,
|
||||
remoteDisabledSource: String,
|
||||
|
|
@ -147,14 +147,14 @@ export default {
|
|||
vehicleDetectionActive: Boolean,
|
||||
vehiclePresent: Boolean,
|
||||
vehicleRange: Number,
|
||||
vehicleSoC: Number,
|
||||
vehicleSoc: Number,
|
||||
vehicleTitle: String,
|
||||
vehicleIcon: String,
|
||||
vehicleTargetSoC: Number,
|
||||
vehicleTargetSoc: Number,
|
||||
vehicleCapacity: Number,
|
||||
vehicleFeatureOffline: Boolean,
|
||||
vehicles: Array,
|
||||
minSoC: Number,
|
||||
minSoc: Number,
|
||||
targetTime: String,
|
||||
targetTimeActive: Boolean,
|
||||
targetTimeProjectedStart: String,
|
||||
|
|
@ -255,7 +255,7 @@ export default {
|
|||
setTargetMode: function (mode) {
|
||||
api.post(this.apiPath("mode") + "/" + mode);
|
||||
},
|
||||
setTargetSoC: function (soc) {
|
||||
setTargetSoc: function (soc) {
|
||||
api.post(this.apiPath("targetsoc") + "/" + soc);
|
||||
},
|
||||
setTargetEnergy: function (kWh) {
|
||||
|
|
@ -270,11 +270,11 @@ export default {
|
|||
setPhasesConfigured: function (phases) {
|
||||
api.post(this.apiPath("phases") + "/" + phases);
|
||||
},
|
||||
setMinSoC: function (soc) {
|
||||
setMinSoc: function (soc) {
|
||||
api.post(this.apiPath("minsoc") + "/" + soc);
|
||||
},
|
||||
setTargetTime: function (date) {
|
||||
api.post(`${this.apiPath("targetcharge")}/${this.targetSoC}/${date.toISOString()}`);
|
||||
api.post(`${this.apiPath("targetcharge")}/${this.targetSoc}/${date.toISOString()}`);
|
||||
},
|
||||
removeTargetTime: function () {
|
||||
api.delete(this.apiPath("targetcharge"));
|
||||
|
|
|
|||
|
|
@ -24,25 +24,25 @@
|
|||
<div class="modal-body">
|
||||
<div class="container">
|
||||
<h4
|
||||
v-if="showMinSoCSettings"
|
||||
v-if="showMinSocSettings"
|
||||
class="d-flex align-items-center mb-3 mt-0 text-evcc"
|
||||
>
|
||||
{{ $t("main.loadpointSettings.vehicle") }}
|
||||
<shopicon-bold-car3 class="ms-2"></shopicon-bold-car3>
|
||||
</h4>
|
||||
<div v-if="showMinSoCSettings" class="mb-3 row">
|
||||
<div v-if="showMinSocSettings" class="mb-3 row">
|
||||
<label
|
||||
:for="formId('minsoc')"
|
||||
class="col-sm-4 col-form-label pt-0 pt-sm-1"
|
||||
>
|
||||
{{ $t("main.loadpointSettings.minSoC.label") }}
|
||||
{{ $t("main.loadpointSettings.minSoc.label") }}
|
||||
</label>
|
||||
<div class="col-sm-8 pe-0">
|
||||
<select
|
||||
:id="formId('minsoc')"
|
||||
v-model.number="selectedMinSoC"
|
||||
v-model.number="selectedMinSoc"
|
||||
class="form-select form-select-sm mb-2 w-50"
|
||||
@change="changeMinSoC"
|
||||
@change="changeMinSoc"
|
||||
>
|
||||
<option
|
||||
v-for="soc in [
|
||||
|
|
@ -56,8 +56,8 @@
|
|||
</select>
|
||||
<small>
|
||||
{{
|
||||
$t("main.loadpointSettings.minSoC.description", [
|
||||
selectedMinSoC || "x",
|
||||
$t("main.loadpointSettings.minSoc.description", [
|
||||
selectedMinSoc || "x",
|
||||
])
|
||||
}}
|
||||
</small>
|
||||
|
|
@ -230,7 +230,7 @@ export default {
|
|||
props: {
|
||||
id: [String, Number],
|
||||
phasesConfigured: Number,
|
||||
minSoC: Number,
|
||||
minSoc: Number,
|
||||
maxCurrent: Number,
|
||||
minCurrent: Number,
|
||||
title: String,
|
||||
|
|
@ -246,7 +246,7 @@ export default {
|
|||
selectedMaxCurrent: this.maxCurrent,
|
||||
selectedMinCurrent: this.minCurrent,
|
||||
selectedPhases: this.phasesConfigured,
|
||||
selectedMinSoC: this.minSoC,
|
||||
selectedMinSoc: this.minSoc,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -274,7 +274,7 @@ export default {
|
|||
showCurrentSettings: function () {
|
||||
return this.$hiddenFeatures;
|
||||
},
|
||||
showMinSoCSettings: function () {
|
||||
showMinSocSettings: function () {
|
||||
return this.$hiddenFeatures;
|
||||
},
|
||||
},
|
||||
|
|
@ -288,8 +288,8 @@ export default {
|
|||
phasesConfigured: function (value) {
|
||||
this.selectedPhases = value;
|
||||
},
|
||||
minSoC: function (value) {
|
||||
this.selectedMinSoC = value;
|
||||
minSoc: function (value) {
|
||||
this.selectedMinSoc = value;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -305,8 +305,8 @@ export default {
|
|||
changePhasesConfigured: function () {
|
||||
this.$emit("phasesconfigured-updated", this.selectedPhases);
|
||||
},
|
||||
changeMinSoC: function () {
|
||||
this.$emit("minsoc-updated", this.selectedMinSoC);
|
||||
changeMinSoc: function () {
|
||||
this.$emit("minsoc-updated", this.selectedMinSoc);
|
||||
},
|
||||
currentOptions: function (max, defaultCurrent = 16) {
|
||||
const result = [];
|
||||
|
|
|
|||
|
|
@ -60,9 +60,9 @@ export default {
|
|||
pvPower: Number,
|
||||
batteryConfigured: Boolean,
|
||||
batteryPower: Number,
|
||||
batterySoC: Number,
|
||||
batterySoc: Number,
|
||||
gridCurrents: Array,
|
||||
prioritySoC: Number,
|
||||
prioritySoc: Number,
|
||||
siteTitle: String,
|
||||
vehicles: Array,
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
<div class="form-group">
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<label for="targetTimeLabel" class="mb-3">
|
||||
{{ $t("main.targetCharge.description", { targetSoC }) }}
|
||||
{{ $t("main.targetCharge.description", { targetSoc }) }}
|
||||
</label>
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
<div
|
||||
|
|
@ -136,7 +136,7 @@ export default {
|
|||
id: [String, Number],
|
||||
targetTime: String,
|
||||
targetTimeActive: Boolean,
|
||||
targetSoC: Number,
|
||||
targetSoc: Number,
|
||||
disabled: Boolean,
|
||||
},
|
||||
emits: ["target-time-updated", "target-time-removed"],
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@
|
|||
</span>
|
||||
</label>
|
||||
|
||||
<div v-if="estimatedTargetSoC" class="extraValue ms-0 ms-sm-1 text-nowrap">
|
||||
<AnimatedNumber :to="estimatedTargetSoC" :format="fmtSoC" />
|
||||
<div v-if="estimatedTargetSoc" class="extraValue ms-0 ms-sm-1 text-nowrap">
|
||||
<AnimatedNumber :to="estimatedTargetSoc" :format="fmtSoc" />
|
||||
</div>
|
||||
</h3>
|
||||
</LabelAndValue>
|
||||
|
|
@ -61,23 +61,23 @@ export default {
|
|||
for (let energy = 0; energy <= this.maxEnergy; energy += this.steps) {
|
||||
let text = this.fmtEnergy(energy);
|
||||
const disabled = energy < this.chargedEnergy / 1e3 && energy !== 0;
|
||||
const soc = this.estimatedSoC(energy);
|
||||
const soc = this.estimatedSoc(energy);
|
||||
if (soc) {
|
||||
text += ` (${this.fmtSoC(soc)})`;
|
||||
text += ` (${this.fmtSoc(soc)})`;
|
||||
}
|
||||
result.push({ energy, text, disabled });
|
||||
}
|
||||
return result;
|
||||
},
|
||||
estimatedTargetSoC: function () {
|
||||
return this.estimatedSoC(this.targetEnergy);
|
||||
estimatedTargetSoc: function () {
|
||||
return this.estimatedSoc(this.targetEnergy);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
change: function (e) {
|
||||
return this.$emit("target-energy-updated", parseFloat(e.target.value));
|
||||
},
|
||||
estimatedSoC: function (kWh) {
|
||||
estimatedSoc: function (kWh) {
|
||||
if (this.socPerKwh) {
|
||||
return Math.round(kWh * this.socPerKwh);
|
||||
}
|
||||
|
|
@ -92,7 +92,7 @@ export default {
|
|||
const digits = inKWh && this.steps < 1 ? 1 : 0;
|
||||
return this.fmtKWh(value * 1e3, inKWh, true, digits);
|
||||
},
|
||||
fmtSoC: function (value) {
|
||||
fmtSoc: function (value) {
|
||||
return `+${Math.round(value)}%`;
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<LabelAndValue class="flex-grow-1" :label="$t('main.vehicle.targetSoC')" align="end">
|
||||
<LabelAndValue class="flex-grow-1" :label="$t('main.vehicle.targetSoc')" align="end">
|
||||
<h3 class="value m-0 d-block d-sm-flex align-items-baseline justify-content-end">
|
||||
<label class="position-relative">
|
||||
<select :value="targetSoc" class="custom-select" @change="change">
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
</option>
|
||||
</select>
|
||||
<span class="text-decoration-underline">
|
||||
<AnimatedNumber :to="targetSoc" :format="formatSoC" />
|
||||
<AnimatedNumber :to="targetSoc" :format="formatSoc" />
|
||||
</span>
|
||||
</label>
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ import AnimatedNumber from "./AnimatedNumber.vue";
|
|||
import { distanceUnit } from "../units";
|
||||
|
||||
export default {
|
||||
name: "TargetSoCSelect",
|
||||
name: "TargetSocSelect",
|
||||
components: { LabelAndValue, AnimatedNumber },
|
||||
props: {
|
||||
targetSoc: Number,
|
||||
|
|
@ -37,7 +37,7 @@ export default {
|
|||
options: function () {
|
||||
const result = [];
|
||||
for (let soc = 20; soc <= 100; soc += 5) {
|
||||
let text = this.formatSoC(soc);
|
||||
let text = this.formatSoc(soc);
|
||||
const range = this.estimatedRange(soc);
|
||||
if (range) {
|
||||
text += ` (${this.formatKm(range)})`;
|
||||
|
|
@ -60,7 +60,7 @@ export default {
|
|||
}
|
||||
return null;
|
||||
},
|
||||
formatSoC: function (value) {
|
||||
formatSoc: function (value) {
|
||||
return `${Math.round(value)}%`;
|
||||
},
|
||||
formatKm: function (value) {
|
||||
|
|
@ -7,9 +7,9 @@ const state = reactive({
|
|||
enabled: false,
|
||||
connected: true,
|
||||
vehiclePresent: true,
|
||||
vehicleSoC: 42,
|
||||
vehicleSoc: 42,
|
||||
vehicleRange: 231,
|
||||
targetSoC: 90,
|
||||
targetSoc: 90,
|
||||
vehicleCapacity: 72,
|
||||
chargedEnergy: 14123,
|
||||
socBasedCharging: true,
|
||||
|
|
@ -77,10 +77,10 @@ const hoursFromNow = function (hours) {
|
|||
/>
|
||||
</Variant>
|
||||
<Variant title="vehicle limit">
|
||||
<Vehicle v-bind="state" enabled charging :vehicleTargetSoC="80" />
|
||||
<Vehicle v-bind="state" enabled charging :vehicleTargetSoc="80" />
|
||||
</Variant>
|
||||
<Variant title="vehicle limit reached">
|
||||
<Vehicle v-bind="state" enabled :vehicleTargetSoC="80" :vehicleSoC="80" />
|
||||
<Vehicle v-bind="state" enabled :vehicleTargetSoc="80" :vehicleSoc="80" />
|
||||
</Variant>
|
||||
<Variant title="target charge planned">
|
||||
<Vehicle v-bind="state" :targetTime="hoursFromNow(14)" mode="pv" />
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
<LabelAndValue
|
||||
v-if="socBasedCharging"
|
||||
class="flex-grow-1"
|
||||
:label="$t('main.vehicle.vehicleSoC')"
|
||||
:value="vehicleSoC ? `${vehicleSoC}%` : '--'"
|
||||
:label="$t('main.vehicle.vehicleSoc')"
|
||||
:value="vehicleSoc ? `${vehicleSoc}%` : '--'"
|
||||
:extraValue="range ? `${Math.round(range)} ${rangeUnit}` : null"
|
||||
align="start"
|
||||
/>
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
class="flex-grow-1"
|
||||
:label="$t('main.loadpoint.charged')"
|
||||
:value="fmtEnergy(chargedEnergy)"
|
||||
:extraValue="chargedSoC"
|
||||
:extraValue="chargedSoc"
|
||||
align="start"
|
||||
/>
|
||||
<TargetCharge
|
||||
|
|
@ -38,11 +38,11 @@
|
|||
@target-time-updated="setTargetTime"
|
||||
@target-time-removed="removeTargetTime"
|
||||
/>
|
||||
<TargetSoCSelect
|
||||
<TargetSocSelect
|
||||
v-if="socBasedCharging"
|
||||
class="flex-grow-1 text-end"
|
||||
:target-soc="displayTargetSoC"
|
||||
:range-per-soc="rangePerSoC"
|
||||
:target-soc="displayTargetSoc"
|
||||
:range-per-soc="rangePerSoc"
|
||||
@target-soc-updated="targetSocUpdated"
|
||||
/>
|
||||
<TargetEnergySelect
|
||||
|
|
@ -69,7 +69,7 @@ import VehicleTitle from "./VehicleTitle.vue";
|
|||
import VehicleSoc from "./VehicleSoc.vue";
|
||||
import VehicleStatus from "./VehicleStatus.vue";
|
||||
import TargetCharge from "./TargetCharge.vue";
|
||||
import TargetSoCSelect from "./TargetSoCSelect.vue";
|
||||
import TargetSocSelect from "./TargetSocSelect.vue";
|
||||
import TargetEnergySelect from "./TargetEnergySelect.vue";
|
||||
import { distanceUnit, distanceValue } from "../units";
|
||||
|
||||
|
|
@ -81,7 +81,7 @@ export default {
|
|||
VehicleStatus,
|
||||
LabelAndValue,
|
||||
TargetCharge,
|
||||
TargetSoCSelect,
|
||||
TargetSocSelect,
|
||||
TargetEnergySelect,
|
||||
},
|
||||
mixins: [collector, formatter],
|
||||
|
|
@ -89,11 +89,11 @@ export default {
|
|||
id: [String, Number],
|
||||
connected: Boolean,
|
||||
vehiclePresent: Boolean,
|
||||
vehicleSoC: Number,
|
||||
vehicleTargetSoC: Number,
|
||||
vehicleSoc: Number,
|
||||
vehicleTargetSoc: Number,
|
||||
enabled: Boolean,
|
||||
charging: Boolean,
|
||||
minSoC: Number,
|
||||
minSoc: Number,
|
||||
vehicleDetectionActive: Boolean,
|
||||
vehicleRange: Number,
|
||||
vehicleTitle: String,
|
||||
|
|
@ -103,7 +103,7 @@ export default {
|
|||
targetTimeActive: Boolean,
|
||||
targetTime: String,
|
||||
targetTimeProjectedStart: String,
|
||||
targetSoC: Number,
|
||||
targetSoc: Number,
|
||||
targetEnergy: Number,
|
||||
chargedEnergy: Number,
|
||||
mode: String,
|
||||
|
|
@ -124,7 +124,7 @@ export default {
|
|||
],
|
||||
data() {
|
||||
return {
|
||||
displayTargetSoC: this.targetSoC,
|
||||
displayTargetSoc: this.targetSoc,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -146,9 +146,9 @@ export default {
|
|||
rangeUnit: function () {
|
||||
return distanceUnit();
|
||||
},
|
||||
rangePerSoC: function () {
|
||||
if (this.vehicleSoC > 10 && this.range) {
|
||||
return this.range / this.vehicleSoC;
|
||||
rangePerSoc: function () {
|
||||
if (this.vehicleSoc > 10 && this.range) {
|
||||
return this.range / this.vehicleSoc;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
|
@ -158,7 +158,7 @@ export default {
|
|||
}
|
||||
return null;
|
||||
},
|
||||
chargedSoC: function () {
|
||||
chargedSoc: function () {
|
||||
const value = this.socPerKwh * (this.chargedEnergy / 1e3);
|
||||
return value > 1 ? `+${Math.round(value)}%` : null;
|
||||
},
|
||||
|
|
@ -167,17 +167,17 @@ export default {
|
|||
},
|
||||
},
|
||||
watch: {
|
||||
targetSoC: function () {
|
||||
this.displayTargetSoC = this.targetSoC;
|
||||
targetSoc: function () {
|
||||
this.displayTargetSoc = this.targetSoc;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
targetSocDrag: function (targetSoC) {
|
||||
this.displayTargetSoC = targetSoC;
|
||||
targetSocDrag: function (targetSoc) {
|
||||
this.displayTargetSoc = targetSoc;
|
||||
},
|
||||
targetSocUpdated: function (targetSoC) {
|
||||
this.displayTargetSoC = targetSoC;
|
||||
this.$emit("target-soc-updated", targetSoC);
|
||||
targetSocUpdated: function (targetSoc) {
|
||||
this.displayTargetSoc = targetSoc;
|
||||
this.$emit("target-soc-updated", targetSoc);
|
||||
},
|
||||
targetEnergyUpdated: function (targetEnergy) {
|
||||
this.$emit("target-energy-updated", targetEnergy);
|
||||
|
|
|
|||
|
|
@ -10,23 +10,23 @@
|
|||
'progress-bar-striped': charging,
|
||||
'progress-bar-animated': charging,
|
||||
}"
|
||||
:style="{ width: `${vehicleSoCDisplayWidth}%` }"
|
||||
:style="{ width: `${vehicleSocDisplayWidth}%` }"
|
||||
></div>
|
||||
<div
|
||||
v-if="remainingSoCWidth > 0 && enabled && connected"
|
||||
v-if="remainingSocWidth > 0 && enabled && connected"
|
||||
class="progress-bar bg-muted"
|
||||
role="progressbar"
|
||||
:class="progressColor"
|
||||
:style="{ width: `${remainingSoCWidth}%`, transition: 'none' }"
|
||||
:style="{ width: `${remainingSocWidth}%`, transition: 'none' }"
|
||||
></div>
|
||||
<div
|
||||
v-show="vehicleTargetSoC"
|
||||
ref="vehicleTargetSoC"
|
||||
v-show="vehicleTargetSoc"
|
||||
ref="vehicleTargetSoc"
|
||||
class="vehicle-target-soc"
|
||||
data-bs-toggle="tooltip"
|
||||
title=" "
|
||||
:class="{ 'vehicle-target-soc--active': vehicleTargetSoCActive }"
|
||||
:style="{ left: `${vehicleTargetSoC}%` }"
|
||||
:class="{ 'vehicle-target-soc--active': vehicleTargetSocActive }"
|
||||
:style="{ left: `${vehicleTargetSoc}%` }"
|
||||
/>
|
||||
</div>
|
||||
<div class="target">
|
||||
|
|
@ -36,14 +36,14 @@
|
|||
min="0"
|
||||
max="100"
|
||||
step="5"
|
||||
:value="visibleTargetSoC"
|
||||
:value="visibleTargetSoc"
|
||||
class="target-slider"
|
||||
:class="{ 'target-slider--active': targetSliderActive }"
|
||||
@mousedown="changeTargetSoCStart"
|
||||
@touchstart="changeTargetSoCStart"
|
||||
@input="movedTargetSoC"
|
||||
@mouseup="changeTargetSoCEnd"
|
||||
@touchend="changeTargetSoCEnd"
|
||||
@mousedown="changeTargetSocStart"
|
||||
@touchstart="changeTargetSocStart"
|
||||
@input="movedTargetSoc"
|
||||
@mouseup="changeTargetSocEnd"
|
||||
@touchend="changeTargetSocEnd"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -57,12 +57,12 @@ export default {
|
|||
props: {
|
||||
connected: Boolean,
|
||||
vehiclePresent: Boolean,
|
||||
vehicleSoC: Number,
|
||||
vehicleTargetSoC: Number,
|
||||
vehicleSoc: Number,
|
||||
vehicleTargetSoc: Number,
|
||||
enabled: Boolean,
|
||||
charging: Boolean,
|
||||
minSoC: Number,
|
||||
targetSoC: Number,
|
||||
minSoc: Number,
|
||||
targetSoc: Number,
|
||||
targetEnergy: Number,
|
||||
chargedEnergy: Number,
|
||||
socBasedCharging: Boolean,
|
||||
|
|
@ -70,16 +70,16 @@ export default {
|
|||
emits: ["target-soc-drag", "target-soc-updated"],
|
||||
data: function () {
|
||||
return {
|
||||
selectedTargetSoC: null,
|
||||
selectedTargetSoc: null,
|
||||
interactionStartScreenY: null,
|
||||
tooltip: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
vehicleSoCDisplayWidth: function () {
|
||||
vehicleSocDisplayWidth: function () {
|
||||
if (this.socBasedCharging) {
|
||||
if (this.vehicleSoC >= 0) {
|
||||
return this.vehicleSoC;
|
||||
if (this.vehicleSoc >= 0) {
|
||||
return this.vehicleSoc;
|
||||
}
|
||||
return 100;
|
||||
} else {
|
||||
|
|
@ -89,50 +89,50 @@ export default {
|
|||
return 100;
|
||||
}
|
||||
},
|
||||
vehicleTargetSoCActive: function () {
|
||||
return this.vehicleTargetSoC > 0 && this.vehicleTargetSoC > this.vehicleSoC;
|
||||
vehicleTargetSocActive: function () {
|
||||
return this.vehicleTargetSoc > 0 && this.vehicleTargetSoc > this.vehicleSoc;
|
||||
},
|
||||
targetSliderActive: function () {
|
||||
return !this.vehicleTargetSoC || this.visibleTargetSoC <= this.vehicleTargetSoC;
|
||||
return !this.vehicleTargetSoc || this.visibleTargetSoc <= this.vehicleTargetSoc;
|
||||
},
|
||||
progressColor: function () {
|
||||
if (this.minSoCActive) {
|
||||
if (this.minSocActive) {
|
||||
return "bg-danger";
|
||||
}
|
||||
return "bg-primary";
|
||||
},
|
||||
minSoCActive: function () {
|
||||
return this.minSoC > 0 && this.vehicleSoC < this.minSoC;
|
||||
minSocActive: function () {
|
||||
return this.minSoc > 0 && this.vehicleSoc < this.minSoc;
|
||||
},
|
||||
remainingSoCWidth: function () {
|
||||
remainingSocWidth: function () {
|
||||
if (this.socBasedCharging) {
|
||||
if (this.vehicleSoCDisplayWidth === 100) {
|
||||
if (this.vehicleSocDisplayWidth === 100) {
|
||||
return null;
|
||||
}
|
||||
if (this.minSoCActive) {
|
||||
return this.minSoC - this.vehicleSoC;
|
||||
if (this.minSocActive) {
|
||||
return this.minSoc - this.vehicleSoc;
|
||||
}
|
||||
let targetSoC = this.targetSliderActive
|
||||
? this.visibleTargetSoC
|
||||
: this.vehicleTargetSoC;
|
||||
if (targetSoC > this.vehicleSoC) {
|
||||
return targetSoC - this.vehicleSoC;
|
||||
let targetSoc = this.targetSliderActive
|
||||
? this.visibleTargetSoc
|
||||
: this.vehicleTargetSoc;
|
||||
if (targetSoc > this.vehicleSoc) {
|
||||
return targetSoc - this.vehicleSoc;
|
||||
}
|
||||
} else {
|
||||
return 100 - this.vehicleSoCDisplayWidth;
|
||||
return 100 - this.vehicleSocDisplayWidth;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
visibleTargetSoC: function () {
|
||||
return Number(this.selectedTargetSoC || this.targetSoC);
|
||||
visibleTargetSoc: function () {
|
||||
return Number(this.selectedTargetSoc || this.targetSoc);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
targetSoC: function () {
|
||||
this.selectedTargetSoC = this.targetSoC;
|
||||
targetSoc: function () {
|
||||
this.selectedTargetSoc = this.targetSoc;
|
||||
},
|
||||
vehicleTargetSoC: function () {
|
||||
vehicleTargetSoc: function () {
|
||||
this.updateTooltip();
|
||||
},
|
||||
},
|
||||
|
|
@ -140,37 +140,37 @@ export default {
|
|||
this.updateTooltip();
|
||||
},
|
||||
methods: {
|
||||
changeTargetSoCStart: function (e) {
|
||||
changeTargetSocStart: function (e) {
|
||||
e.stopPropagation();
|
||||
},
|
||||
changeTargetSoCEnd: function (e) {
|
||||
changeTargetSocEnd: function (e) {
|
||||
const value = parseInt(e.target.value, 10);
|
||||
// value changed
|
||||
if (value !== this.targetSoC) {
|
||||
if (value !== this.targetSoc) {
|
||||
this.$emit("target-soc-updated", value);
|
||||
}
|
||||
},
|
||||
movedTargetSoC: function (e) {
|
||||
movedTargetSoc: function (e) {
|
||||
let value = parseInt(e.target.value, 10);
|
||||
e.stopPropagation();
|
||||
const minTargetSoC = 20;
|
||||
if (value < minTargetSoC) {
|
||||
e.target.value = minTargetSoC;
|
||||
this.selectedTargetSoC = value;
|
||||
const minTargetSoc = 20;
|
||||
if (value < minTargetSoc) {
|
||||
e.target.value = minTargetSoc;
|
||||
this.selectedTargetSoc = value;
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
this.selectedTargetSoC = value;
|
||||
this.selectedTargetSoc = value;
|
||||
|
||||
this.$emit("target-soc-drag", this.selectedTargetSoC);
|
||||
this.$emit("target-soc-drag", this.selectedTargetSoc);
|
||||
return true;
|
||||
},
|
||||
updateTooltip: function () {
|
||||
if (!this.tooltip) {
|
||||
this.tooltip = new Tooltip(this.$refs.vehicleTargetSoC);
|
||||
this.tooltip = new Tooltip(this.$refs.vehicleTargetSoc);
|
||||
}
|
||||
const soc = this.vehicleTargetSoC;
|
||||
const content = this.$t("main.vehicleSoC.vehicleTarget", { soc });
|
||||
const soc = this.vehicleTargetSoc;
|
||||
const content = this.$t("main.vehicleSoc.vehicleTarget", { soc });
|
||||
this.tooltip.setContent({ ".tooltip-inner": content });
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ export default {
|
|||
name: "VehicleStatus",
|
||||
mixins: [formatter],
|
||||
props: {
|
||||
vehicleSoC: Number,
|
||||
vehicleTargetSoC: Number,
|
||||
minSoC: Number,
|
||||
vehicleSoc: Number,
|
||||
vehicleTargetSoc: Number,
|
||||
minSoc: Number,
|
||||
enabled: Boolean,
|
||||
connected: Boolean,
|
||||
charging: Boolean,
|
||||
|
|
@ -45,8 +45,8 @@ export default {
|
|||
}
|
||||
|
||||
// min charge active
|
||||
if (this.minSoC > 0 && this.vehicleSoC < this.minSoC) {
|
||||
return t("minCharge", { soc: this.minSoC });
|
||||
if (this.minSoc > 0 && this.vehicleSoc < this.minSoc) {
|
||||
return t("minCharge", { soc: this.minSoc });
|
||||
}
|
||||
|
||||
// target charage
|
||||
|
|
@ -71,8 +71,8 @@ export default {
|
|||
}
|
||||
|
||||
if (this.enabled && !this.charging) {
|
||||
if (this.vehicleTargetSoC > 0 && this.vehicleSoC >= this.vehicleTargetSoC - 1) {
|
||||
return t("vehicleTargetReached", { soc: this.vehicleTargetSoC });
|
||||
if (this.vehicleTargetSoc > 0 && this.vehicleSoc >= this.vehicleTargetSoc - 1) {
|
||||
return t("vehicleTargetReached", { soc: this.vehicleTargetSoc });
|
||||
}
|
||||
return t("waitForVehicle");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,24 +43,24 @@ export default {
|
|||
{
|
||||
vehiclePresent: true,
|
||||
vehicleTitle: "Ford Mustang Mach-E",
|
||||
vehicleSoC: 46,
|
||||
vehicleSoc: 46,
|
||||
vehicleRange: 182,
|
||||
targetSoC: 100,
|
||||
targetSoc: 100,
|
||||
},
|
||||
{
|
||||
vehiclePresent: true,
|
||||
vehicleTitle: "Renault Twingo Electric",
|
||||
vehicleSoC: 77,
|
||||
vehicleSoc: 77,
|
||||
vehicleRange: 98,
|
||||
targetSoC: 90,
|
||||
targetSoc: 90,
|
||||
},
|
||||
{
|
||||
vehiclePresent: true,
|
||||
vehicleTitle: "Blauer VW ID.4",
|
||||
vehicleSoC: 16,
|
||||
vehicleSoc: 16,
|
||||
vehicleRange: 52,
|
||||
minSoC: 35,
|
||||
targetSoC: 60,
|
||||
minSoc: 35,
|
||||
targetSoc: 60,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -447,8 +447,8 @@ func (c *EEBus) Identify() (string, error) {
|
|||
|
||||
var _ api.Battery = (*EEBus)(nil)
|
||||
|
||||
// SoC implements the api.Vehicle interface
|
||||
func (c *EEBus) SoC() (float64, error) {
|
||||
// Soc implements the api.Vehicle interface
|
||||
func (c *EEBus) Soc() (float64, error) {
|
||||
socSupported, err := c.emobility.EVSoCSupported()
|
||||
if err != nil {
|
||||
return 0, api.ErrNotAvailable
|
||||
|
|
|
|||
|
|
@ -502,7 +502,7 @@ paths:
|
|||
- id: "2238974987235987"
|
||||
name: My Tesla
|
||||
operationId: get-tesla-vehicles
|
||||
description: Once the Tesla account credentials have been configured via the `/config` API this endpoint will return a list of vehicles associated with the account. The ID of the vehicle to use for SoC/Range can then be set using `tesla_vehicle_id` on the `/config` endpoint.
|
||||
description: Once the Tesla account credentials have been configured via the `/config` API this endpoint will return a list of vehicles associated with the account. The ID of the vehicle to use for Soc/Range can then be set using `tesla_vehicle_id` on the `/config` endpoint.
|
||||
components:
|
||||
schemas:
|
||||
Message:
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ type OpenWB struct {
|
|||
authS func(string) error
|
||||
}
|
||||
|
||||
// go:generate go run ../cmd/tools/decorate.go -f decorateOpenWB -b *OpenWB -r api.Charger -t "api.PhaseSwitcher,Phases1p3p,func(int) (error)" -t "api.Battery,SoC,func() (float64, error)"
|
||||
// go:generate go run ../cmd/tools/decorate.go -f decorateOpenWB -b *OpenWB -r api.Charger -t "api.PhaseSwitcher,Phases1p3p,func(int) (error)" -t "api.Battery,Soc,func() (float64, error)"
|
||||
|
||||
// NewOpenWBFromConfig creates a new configurable charger
|
||||
func NewOpenWBFromConfig(other map[string]interface{}) (api.Charger, error) {
|
||||
|
|
@ -161,7 +161,7 @@ func NewOpenWB(log *util.Logger, mqttconf mqtt.Config, id int, topic string, p1p
|
|||
|
||||
var soc func() (float64, error)
|
||||
if dc {
|
||||
soc = floatG(fmt.Sprintf("%s/lp/%d/%s", topic, id, openwb.VehicleSoCTopic))
|
||||
soc = floatG(fmt.Sprintf("%s/lp/%d/%s", topic, id, openwb.VehicleSocTopic))
|
||||
}
|
||||
|
||||
return decorateOpenWB(c, phases, soc), nil
|
||||
|
|
|
|||
|
|
@ -29,11 +29,11 @@ const (
|
|||
ChargeTotalEnergyTopic = "kWhCounter"
|
||||
|
||||
// vehicle
|
||||
VehicleSoCTopic = "Soc"
|
||||
VehicleSocTopic = "Soc"
|
||||
|
||||
// general measurements
|
||||
PowerTopic = "W"
|
||||
SoCTopic = "%Soc"
|
||||
SocTopic = "%Soc"
|
||||
CurrentTopic = "APhase" // 1..3
|
||||
|
||||
// configuration
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ type decorateOpenWBBatteryImpl struct {
|
|||
battery func() (float64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateOpenWBBatteryImpl) SoC() (float64, error) {
|
||||
func (impl *decorateOpenWBBatteryImpl) Soc() (float64, error) {
|
||||
return impl.battery()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ type config struct {
|
|||
Vehicles []qualifiedConfig
|
||||
Tariffs tariffConfig
|
||||
Site map[string]interface{}
|
||||
LoadPoints []map[string]interface{}
|
||||
Loadpoints []map[string]interface{}
|
||||
}
|
||||
|
||||
type mqttConfig struct {
|
||||
|
|
|
|||
|
|
@ -126,11 +126,11 @@ func (d *DeviceTest) testMeter(deviceCategory DeviceCategory, v interface{}) (De
|
|||
return DeviceTestResultInvalid, errors.New("selected device is not a battery meter")
|
||||
}
|
||||
|
||||
_, err := b.SoC()
|
||||
_, err := b.Soc()
|
||||
|
||||
for err != nil && errors.Is(err, api.ErrMustRetry) {
|
||||
time.Sleep(3 * time.Second)
|
||||
_, err = b.SoC()
|
||||
_, err = b.Soc()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -148,7 +148,7 @@ func (d *DeviceTest) testVehicle(v interface{}) (DeviceTestResult, error) {
|
|||
return DeviceTestResultInvalid, errors.New("selected device is not a vehicle")
|
||||
}
|
||||
|
||||
if _, err := vv.SoC(); err != nil {
|
||||
if _, err := vv.Soc(); err != nil {
|
||||
return DeviceTestResultInvalid, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ javascript:
|
|||
residualpower: 500,
|
||||
pvpower: 5000,
|
||||
batterypower: -750,
|
||||
batterySoC: 55,
|
||||
batterySoc: 55,
|
||||
gridpower: -1000,
|
||||
loadpoints: [
|
||||
{ enabled: true, vehicleSoC: 62, maxcurrent: 6, phases: 1, chargepower: 0 },
|
||||
{ enabled: false, vehicleSoC: 22, maxcurrent: 0, phases: 3, chargepower: 0 }
|
||||
{ enabled: true, vehicleSoc: 62, maxcurrent: 6, phases: 1, chargepower: 0 },
|
||||
{ enabled: false, vehicleSoc: 22, maxcurrent: 0, phases: 3, chargepower: 0 }
|
||||
]
|
||||
};
|
||||
function get() {
|
||||
|
|
@ -56,10 +56,10 @@ meters:
|
|||
source: js
|
||||
vm: shared
|
||||
script: |
|
||||
if (state.batterypower < 0) state.batterySoC++; else state.batterySoC--;
|
||||
if (state.batterySoC < 10) state.batterySoC = 90;
|
||||
if (state.batterySoC > 90) state.batterySoC = 10;
|
||||
state.batterySoC;
|
||||
if (state.batterypower < 0) state.batterySoc++; else state.batterySoc--;
|
||||
if (state.batterySoc < 10) state.batterySoc = 90;
|
||||
if (state.batterySoc > 90) state.batterySoc = 10;
|
||||
state.batterySoc;
|
||||
|
||||
- name: meter_charger_1
|
||||
type: custom
|
||||
|
|
@ -150,16 +150,16 @@ vehicles:
|
|||
vm: shared
|
||||
script: |
|
||||
var lp = state.loadpoints[0];
|
||||
if (lp.chargepower > 0) lp.vehicleSoC++; else lp.vehicleSoC--;
|
||||
if (lp.vehicleSoC < 15) lp.vehicleSoC = 80;
|
||||
if (lp.vehicleSoC > 80) lp.vehicleSoC = 15;
|
||||
lp.vehicleSoC;
|
||||
if (lp.chargepower > 0) lp.vehicleSoc++; else lp.vehicleSoc--;
|
||||
if (lp.vehicleSoc < 15) lp.vehicleSoc = 80;
|
||||
if (lp.vehicleSoc > 80) lp.vehicleSoc = 15;
|
||||
lp.vehicleSoc;
|
||||
range:
|
||||
source: js
|
||||
vm: shared
|
||||
script: |
|
||||
var lp = state.loadpoints[0]
|
||||
var range = (44 * lp.vehicleSoC) / 15;
|
||||
var range = (44 * lp.vehicleSoc) / 15;
|
||||
range
|
||||
capacity: 44
|
||||
onidentify:
|
||||
|
|
@ -172,16 +172,16 @@ vehicles:
|
|||
vm: shared
|
||||
script: |
|
||||
var lp = state.loadpoints[1];
|
||||
if (lp.chargepower > 0) lp.vehicleSoC++; else lp.vehicleSoC--;
|
||||
if (lp.vehicleSoC < 15) lp.vehicleSoC = 75;
|
||||
if (lp.vehicleSoC > 75) lp.vehicleSoC = 15;
|
||||
lp.vehicleSoC;
|
||||
if (lp.chargepower > 0) lp.vehicleSoc++; else lp.vehicleSoc--;
|
||||
if (lp.vehicleSoc < 15) lp.vehicleSoc = 75;
|
||||
if (lp.vehicleSoc > 75) lp.vehicleSoc = 15;
|
||||
lp.vehicleSoc;
|
||||
range:
|
||||
source: js
|
||||
vm: shared
|
||||
script: |
|
||||
var lp = state.loadpoints[1]
|
||||
var range = (80 * lp.vehicleSoC) / 17;
|
||||
var range = (80 * lp.vehicleSoc) / 17;
|
||||
range
|
||||
status:
|
||||
source: js
|
||||
|
|
|
|||
|
|
@ -125,8 +125,8 @@ func runDump(cmd *cobra.Command, args []string) {
|
|||
d.DumpWithHeader(fmt.Sprintf("vehicle: %s", v.Title()), v)
|
||||
}
|
||||
|
||||
for id, lpI := range site.LoadPoints() {
|
||||
lp := lpI.(*core.LoadPoint)
|
||||
for id, lpI := range site.Loadpoints() {
|
||||
lp := lpI.(*core.Loadpoint)
|
||||
|
||||
d.Header(fmt.Sprintf("loadpoint %d", id+1), "=")
|
||||
fmt.Println("")
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ func (d *dumper) Dump(name string, v interface{}) {
|
|||
// wait up to 1m for the vehicle to wakeup
|
||||
start := time.Now()
|
||||
for err = api.ErrMustRetry; err != nil && errors.Is(err, api.ErrMustRetry); {
|
||||
if soc, err = v.SoC(); err != nil {
|
||||
if soc, err = v.Soc(); err != nil {
|
||||
if time.Since(start) > time.Minute {
|
||||
err = os.ErrDeadlineExceeded
|
||||
} else {
|
||||
|
|
@ -81,9 +81,9 @@ func (d *dumper) Dump(name string, v interface{}) {
|
|||
}
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "SoC:\t%v\n", err)
|
||||
fmt.Fprintf(w, "Soc:\t%v\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(w, "SoC:\t%.0f%%\n", soc)
|
||||
fmt.Fprintf(w, "Soc:\t%.0f%%\n", soc)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,10 +170,10 @@ func (d *dumper) Dump(name string, v interface{}) {
|
|||
}
|
||||
|
||||
if v, ok := v.(api.SocLimiter); ok {
|
||||
if targetSoC, err := v.TargetSoC(); err != nil {
|
||||
fmt.Fprintf(w, "Target SoC:\t%v\n", err)
|
||||
if targetSoc, err := v.TargetSoc(); err != nil {
|
||||
fmt.Fprintf(w, "Target Soc:\t%v\n", err)
|
||||
} else {
|
||||
fmt.Fprintf(w, "Target SoC:\t%.0f%%\n", targetSoC)
|
||||
fmt.Fprintf(w, "Target Soc:\t%.0f%%\n", targetSoc)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ func runRoot(cmd *cobra.Command, args []string) {
|
|||
|
||||
// setup database
|
||||
if err == nil && conf.Influx.URL != "" {
|
||||
configureInflux(conf.Influx, site.LoadPoints(), tee.Attach())
|
||||
configureInflux(conf.Influx, site.Loadpoints(), tee.Attach())
|
||||
}
|
||||
|
||||
// setup mqtt publisher
|
||||
|
|
|
|||
12
cmd/setup.go
12
cmd/setup.go
|
|
@ -133,7 +133,7 @@ func configureInflux(conf server.InfluxConfig, loadPoints []loadpoint.API, in <-
|
|||
)
|
||||
|
||||
// eliminate duplicate values
|
||||
dedupe := pipe.NewDeduplicator(30*time.Minute, "vehicleCapacity", "vehicleSoC", "vehicleRange", "vehicleOdometer", "chargedEnergy", "chargeRemainingEnergy")
|
||||
dedupe := pipe.NewDeduplicator(30*time.Minute, "vehicleCapacity", "vehicleSoc", "vehicleRange", "vehicleOdometer", "chargedEnergy", "chargeRemainingEnergy")
|
||||
in = dedupe.Pipe(in)
|
||||
|
||||
go influx.Run(loadPoints, in)
|
||||
|
|
@ -254,8 +254,8 @@ func configureTariffs(conf tariffConfig) (tariff.Tariffs, error) {
|
|||
|
||||
func configureSiteAndLoadpoints(conf config) (site *core.Site, err error) {
|
||||
if err = cp.configure(conf); err == nil {
|
||||
var loadPoints []*core.LoadPoint
|
||||
loadPoints, err = configureLoadPoints(conf, cp)
|
||||
var loadPoints []*core.Loadpoint
|
||||
loadPoints, err = configureLoadpoints(conf, cp)
|
||||
|
||||
var tariffs tariff.Tariffs
|
||||
if err == nil {
|
||||
|
|
@ -275,7 +275,7 @@ func configureSiteAndLoadpoints(conf config) (site *core.Site, err error) {
|
|||
return site, err
|
||||
}
|
||||
|
||||
func configureSite(conf map[string]interface{}, cp *ConfigProvider, loadPoints []*core.LoadPoint, vehicles []api.Vehicle, tariffs tariff.Tariffs) (*core.Site, error) {
|
||||
func configureSite(conf map[string]interface{}, cp *ConfigProvider, loadPoints []*core.Loadpoint, vehicles []api.Vehicle, tariffs tariff.Tariffs) (*core.Site, error) {
|
||||
site, err := core.NewSiteFromConfig(log, cp, conf, loadPoints, vehicles, tariffs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed configuring site: %w", err)
|
||||
|
|
@ -284,7 +284,7 @@ func configureSite(conf map[string]interface{}, cp *ConfigProvider, loadPoints [
|
|||
return site, nil
|
||||
}
|
||||
|
||||
func configureLoadPoints(conf config, cp *ConfigProvider) (loadPoints []*core.LoadPoint, err error) {
|
||||
func configureLoadpoints(conf config, cp *ConfigProvider) (loadPoints []*core.Loadpoint, err error) {
|
||||
lpInterfaces, ok := viper.AllSettings()["loadpoints"].([]interface{})
|
||||
if !ok || len(lpInterfaces) == 0 {
|
||||
return nil, errors.New("missing loadpoints")
|
||||
|
|
@ -297,7 +297,7 @@ func configureLoadPoints(conf config, cp *ConfigProvider) (loadPoints []*core.Lo
|
|||
}
|
||||
|
||||
log := util.NewLogger("lp-" + strconv.Itoa(id+1))
|
||||
lp, err := core.NewLoadPointFromConfig(log, cp, lpc)
|
||||
lp, err := core.NewLoadpointFromConfig(log, cp, lpc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed configuring loadpoint: %w", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ func TestYamlOff(t *testing.T) {
|
|||
t.Error(err)
|
||||
}
|
||||
|
||||
var lp core.LoadPoint
|
||||
if err := util.DecodeOther(conf.LoadPoints[0], &lp); err != nil {
|
||||
var lp core.Loadpoint
|
||||
if err := util.DecodeOther(conf.Loadpoints[0], &lp); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ func main() {
|
|||
|
||||
start := time.Now()
|
||||
for err = api.ErrMustRetry; err != nil && matchesError(err, api.ErrMustRetry); {
|
||||
if soc, err = v.SoC(); err != nil {
|
||||
if soc, err = v.Soc(); err != nil {
|
||||
if time.Since(start) > time.Minute {
|
||||
err = os.ErrDeadlineExceeded
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -9,5 +9,5 @@ const (
|
|||
|
||||
vehicleRange = "vehicleRange" // vehicle range
|
||||
vehicleOdometer = "vehicleOdometer" // vehicle odometer
|
||||
vehicleTargetSoC = "vehicleTargetSoC" // vehicle soc limit
|
||||
vehicleTargetSoc = "vehicleTargetSoc" // vehicle soc limit
|
||||
)
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ const (
|
|||
evChargePower = "power" // update chargeRater
|
||||
evVehicleConnect = "connect" // vehicle connected
|
||||
evVehicleDisconnect = "disconnect" // vehicle disconnected
|
||||
evVehicleSoC = "soc" // vehicle soc progress
|
||||
evVehicleSoc = "soc" // vehicle soc progress
|
||||
evVehicleUnidentified = "guest" // vehicle unidentified
|
||||
|
||||
pvTimer = "pv"
|
||||
|
|
@ -62,14 +62,14 @@ type PollConfig struct {
|
|||
Interval time.Duration `mapstructure:"interval"` // interval when not charging
|
||||
}
|
||||
|
||||
// SoCConfig defines soc settings, estimation and update behaviour
|
||||
type SoCConfig struct {
|
||||
// SocConfig defines soc settings, estimation and update behaviour
|
||||
type SocConfig struct {
|
||||
Poll PollConfig `mapstructure:"poll"`
|
||||
Estimate *bool `mapstructure:"estimate"`
|
||||
Min_ int `mapstructure:"min"` // TODO deprecated
|
||||
Target_ int `mapstructure:"target"` // TODO deprecated
|
||||
min int // Default minimum SoC, guarded by mutex
|
||||
target int // Default target SoC, guarded by mutex
|
||||
min int // Default minimum Soc, guarded by mutex
|
||||
target int // Default target Soc, guarded by mutex
|
||||
}
|
||||
|
||||
// Poll modes
|
||||
|
|
@ -90,14 +90,14 @@ type ThresholdConfig struct {
|
|||
// Task is the task type
|
||||
type Task = func()
|
||||
|
||||
// LoadPoint is responsible for controlling charge depending on
|
||||
// SoC needs and power availability.
|
||||
type LoadPoint struct {
|
||||
// Loadpoint is responsible for controlling charge depending on
|
||||
// Soc needs and power availability.
|
||||
type Loadpoint struct {
|
||||
clock clock.Clock // mockable time
|
||||
bus evbus.Bus // event bus
|
||||
pushChan chan<- push.Event // notifications
|
||||
uiChan chan<- util.Param // client push messages
|
||||
lpChan chan<- *LoadPoint // update requests
|
||||
lpChan chan<- *Loadpoint // update requests
|
||||
log *util.Logger
|
||||
|
||||
// exposed public configuration
|
||||
|
|
@ -110,7 +110,7 @@ type LoadPoint struct {
|
|||
VehicleRef string `mapstructure:"vehicle"` // Vehicle reference
|
||||
VehiclesRef_ []string `mapstructure:"vehicles"` // TODO deprecated
|
||||
MeterRef string `mapstructure:"meter"` // Charge meter reference
|
||||
SoC SoCConfig
|
||||
Soc SocConfig
|
||||
Enable, Disable ThresholdConfig
|
||||
ResetOnDisconnect bool `mapstructure:"resetOnDisconnect"`
|
||||
onDisconnect api.ActionConfig
|
||||
|
|
@ -125,7 +125,7 @@ type LoadPoint struct {
|
|||
measuredPhases int // Charger physically measured phases
|
||||
chargeCurrent float64 // Charger current limit
|
||||
guardUpdated time.Time // Charger enabled/disabled timestamp
|
||||
socUpdated time.Time // SoC updated timestamp (poll: connected)
|
||||
socUpdated time.Time // Soc updated timestamp (poll: connected)
|
||||
vehicleDetect time.Time // Vehicle connected timestamp
|
||||
vehicleDetectTicker *clock.Ticker
|
||||
vehicleIdentifier string
|
||||
|
|
@ -152,7 +152,7 @@ type LoadPoint struct {
|
|||
wakeUpTimer *Timer // Vehicle wake-up timeout
|
||||
|
||||
// charge progress
|
||||
vehicleSoc float64 // Vehicle SoC
|
||||
vehicleSoc float64 // Vehicle Soc
|
||||
chargeDuration time.Duration // Charge duration
|
||||
chargedEnergy float64 // Charged energy while connected in Wh
|
||||
chargeRemainingDuration time.Duration // Remaining charge duration
|
||||
|
|
@ -166,31 +166,31 @@ type LoadPoint struct {
|
|||
tasks *util.Queue[Task] // tasks to be executed
|
||||
}
|
||||
|
||||
// NewLoadPointFromConfig creates a new loadpoint
|
||||
func NewLoadPointFromConfig(log *util.Logger, cp configProvider, other map[string]interface{}) (*LoadPoint, error) {
|
||||
lp := NewLoadPoint(log)
|
||||
// NewLoadpointFromConfig creates a new loadpoint
|
||||
func NewLoadpointFromConfig(log *util.Logger, cp configProvider, other map[string]interface{}) (*Loadpoint, error) {
|
||||
lp := NewLoadpoint(log)
|
||||
if err := util.DecodeOther(other, lp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// set vehicle polling mode
|
||||
switch lp.SoC.Poll.Mode = strings.ToLower(lp.SoC.Poll.Mode); lp.SoC.Poll.Mode {
|
||||
switch lp.Soc.Poll.Mode = strings.ToLower(lp.Soc.Poll.Mode); lp.Soc.Poll.Mode {
|
||||
case pollCharging:
|
||||
case pollConnected, pollAlways:
|
||||
lp.log.WARN.Printf("poll mode '%s' may deplete your battery or lead to API misuse. USE AT YOUR OWN RISK.", lp.SoC.Poll)
|
||||
lp.log.WARN.Printf("poll mode '%s' may deplete your battery or lead to API misuse. USE AT YOUR OWN RISK.", lp.Soc.Poll)
|
||||
default:
|
||||
if lp.SoC.Poll.Mode != "" {
|
||||
lp.log.WARN.Printf("invalid poll mode: %s", lp.SoC.Poll.Mode)
|
||||
if lp.Soc.Poll.Mode != "" {
|
||||
lp.log.WARN.Printf("invalid poll mode: %s", lp.Soc.Poll.Mode)
|
||||
}
|
||||
lp.SoC.Poll.Mode = pollCharging
|
||||
lp.Soc.Poll.Mode = pollCharging
|
||||
}
|
||||
|
||||
// set vehicle polling interval
|
||||
if lp.SoC.Poll.Interval < pollInterval {
|
||||
if lp.SoC.Poll.Interval == 0 {
|
||||
lp.SoC.Poll.Interval = pollInterval
|
||||
if lp.Soc.Poll.Interval < pollInterval {
|
||||
if lp.Soc.Poll.Interval == 0 {
|
||||
lp.Soc.Poll.Interval = pollInterval
|
||||
} else {
|
||||
lp.log.WARN.Printf("poll interval '%v' is lower than %v and may deplete your battery or lead to API misuse. USE AT YOUR OWN RISK.", lp.SoC.Poll.Interval, pollInterval)
|
||||
lp.log.WARN.Printf("poll interval '%v' is lower than %v and may deplete your battery or lead to API misuse. USE AT YOUR OWN RISK.", lp.Soc.Poll.Interval, pollInterval)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -202,11 +202,11 @@ func NewLoadPointFromConfig(log *util.Logger, cp configProvider, other map[strin
|
|||
lp.log.WARN.Println("maxCurrent must be larger than minCurrent")
|
||||
}
|
||||
|
||||
if lp.SoC.Min_ != 0 {
|
||||
if lp.Soc.Min_ != 0 {
|
||||
lp.log.WARN.Println("Configuring soc.min at loadpoint is deprecated and must be applied per vehicle")
|
||||
}
|
||||
|
||||
if lp.SoC.Target_ != 0 {
|
||||
if lp.Soc.Target_ != 0 {
|
||||
lp.log.WARN.Println("Configuring soc.target at loadpoint is deprecated and must be applied per vehicle")
|
||||
}
|
||||
|
||||
|
|
@ -265,12 +265,12 @@ func NewLoadPointFromConfig(log *util.Logger, cp configProvider, other map[strin
|
|||
return lp, nil
|
||||
}
|
||||
|
||||
// NewLoadPoint creates a LoadPoint with sane defaults
|
||||
func NewLoadPoint(log *util.Logger) *LoadPoint {
|
||||
// NewLoadpoint creates a Loadpoint with sane defaults
|
||||
func NewLoadpoint(log *util.Logger) *Loadpoint {
|
||||
clock := clock.New()
|
||||
bus := evbus.New()
|
||||
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
log: log, // logger
|
||||
clock: clock, // mockable time
|
||||
bus: bus, // event bus
|
||||
|
|
@ -278,7 +278,7 @@ func NewLoadPoint(log *util.Logger) *LoadPoint {
|
|||
status: api.StatusNone,
|
||||
MinCurrent: 6, // A
|
||||
MaxCurrent: 16, // A
|
||||
SoC: SoCConfig{min: 0, target: 100}, // %
|
||||
Soc: SocConfig{min: 0, target: 100}, // %
|
||||
Enable: ThresholdConfig{Delay: time.Minute, Threshold: 0}, // t, W
|
||||
Disable: ThresholdConfig{Delay: 3 * time.Minute, Threshold: 0}, // t, W
|
||||
GuardDuration: 5 * time.Minute,
|
||||
|
|
@ -288,13 +288,13 @@ func NewLoadPoint(log *util.Logger) *LoadPoint {
|
|||
}
|
||||
|
||||
// allow target charge handler to access loadpoint
|
||||
lp.socTimer = soc.NewTimer(lp.log, &adapter{LoadPoint: lp})
|
||||
lp.socTimer = soc.NewTimer(lp.log, &adapter{Loadpoint: lp})
|
||||
|
||||
return lp
|
||||
}
|
||||
|
||||
// collectDefaults collects default values for use on disconnect
|
||||
func (lp *LoadPoint) collectDefaults() {
|
||||
func (lp *Loadpoint) collectDefaults() {
|
||||
// get reference to action config
|
||||
actionCfg := &lp.onDisconnect
|
||||
|
||||
|
|
@ -304,15 +304,15 @@ func (lp *LoadPoint) collectDefaults() {
|
|||
*actionCfg.Mode = lp.GetMode()
|
||||
*actionCfg.MinCurrent = lp.GetMinCurrent()
|
||||
*actionCfg.MaxCurrent = lp.GetMaxCurrent()
|
||||
*actionCfg.MinSoC = lp.GetMinSoC()
|
||||
*actionCfg.TargetSoC = lp.GetTargetSoC()
|
||||
*actionCfg.MinSoc = lp.GetMinSoc()
|
||||
*actionCfg.TargetSoc = lp.GetTargetSoc()
|
||||
} else {
|
||||
lp.log.ERROR.Printf("error allocating action config: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// requestUpdate requests site to update this loadpoint
|
||||
func (lp *LoadPoint) requestUpdate() {
|
||||
func (lp *Loadpoint) requestUpdate() {
|
||||
select {
|
||||
case lp.lpChan <- lp: // request loadpoint update
|
||||
default:
|
||||
|
|
@ -320,7 +320,7 @@ func (lp *LoadPoint) requestUpdate() {
|
|||
}
|
||||
|
||||
// configureChargerType ensures that chargeMeter, Rate and Timer can use charger capabilities
|
||||
func (lp *LoadPoint) configureChargerType(charger api.Charger) {
|
||||
func (lp *Loadpoint) configureChargerType(charger api.Charger) {
|
||||
var integrated bool
|
||||
|
||||
// ensure charge meter exists
|
||||
|
|
@ -367,19 +367,19 @@ func (lp *LoadPoint) configureChargerType(charger api.Charger) {
|
|||
}
|
||||
|
||||
// pushEvent sends push messages to clients
|
||||
func (lp *LoadPoint) pushEvent(event string) {
|
||||
func (lp *Loadpoint) pushEvent(event string) {
|
||||
lp.pushChan <- push.Event{Event: event}
|
||||
}
|
||||
|
||||
// publish sends values to UI and databases
|
||||
func (lp *LoadPoint) publish(key string, val interface{}) {
|
||||
func (lp *Loadpoint) publish(key string, val interface{}) {
|
||||
if lp.uiChan != nil {
|
||||
lp.uiChan <- util.Param{Key: key, Val: val}
|
||||
}
|
||||
}
|
||||
|
||||
// evChargeStartHandler sends external start event
|
||||
func (lp *LoadPoint) evChargeStartHandler() {
|
||||
func (lp *Loadpoint) evChargeStartHandler() {
|
||||
lp.log.INFO.Println("start charging ->")
|
||||
lp.pushEvent(evChargeStart)
|
||||
|
||||
|
|
@ -397,7 +397,7 @@ func (lp *LoadPoint) evChargeStartHandler() {
|
|||
}
|
||||
|
||||
// evChargeStopHandler sends external stop event
|
||||
func (lp *LoadPoint) evChargeStopHandler() {
|
||||
func (lp *Loadpoint) evChargeStopHandler() {
|
||||
lp.log.INFO.Println("stop charging <-")
|
||||
lp.pushEvent(evChargeStop)
|
||||
|
||||
|
|
@ -414,7 +414,7 @@ func (lp *LoadPoint) evChargeStopHandler() {
|
|||
}
|
||||
|
||||
// evVehicleConnectHandler sends external start event
|
||||
func (lp *LoadPoint) evVehicleConnectHandler() {
|
||||
func (lp *Loadpoint) evVehicleConnectHandler() {
|
||||
lp.log.INFO.Printf("car connected")
|
||||
|
||||
// energy
|
||||
|
|
@ -444,7 +444,7 @@ func (lp *LoadPoint) evVehicleConnectHandler() {
|
|||
}
|
||||
|
||||
// evVehicleDisconnectHandler sends external start event
|
||||
func (lp *LoadPoint) evVehicleDisconnectHandler() {
|
||||
func (lp *Loadpoint) evVehicleDisconnectHandler() {
|
||||
lp.log.INFO.Println("car disconnected")
|
||||
|
||||
// session is persisted during evChargeStopHandler which runs before
|
||||
|
|
@ -482,15 +482,15 @@ func (lp *LoadPoint) evVehicleDisconnectHandler() {
|
|||
lp.socTimer.Reset()
|
||||
}
|
||||
|
||||
// evVehicleSoCProgressHandler sends external start event
|
||||
func (lp *LoadPoint) evVehicleSoCProgressHandler(soc float64) {
|
||||
// evVehicleSocProgressHandler sends external start event
|
||||
func (lp *Loadpoint) evVehicleSocProgressHandler(soc float64) {
|
||||
if lp.progress.NextStep(soc) {
|
||||
lp.pushEvent(evVehicleSoC)
|
||||
lp.pushEvent(evVehicleSoc)
|
||||
}
|
||||
}
|
||||
|
||||
// evChargeCurrentHandler publishes the charge current
|
||||
func (lp *LoadPoint) evChargeCurrentHandler(current float64) {
|
||||
func (lp *Loadpoint) evChargeCurrentHandler(current float64) {
|
||||
if !lp.enabled {
|
||||
current = 0
|
||||
}
|
||||
|
|
@ -502,7 +502,7 @@ func (lp *LoadPoint) evChargeCurrentHandler(current float64) {
|
|||
// It assumes that the charge meter cannot consume more than total household consumption.
|
||||
// If physical charge meter is present this handler is not used.
|
||||
// The actual value is published by the evChargeCurrentHandler
|
||||
func (lp *LoadPoint) evChargeCurrentWrappedMeterHandler(current float64) {
|
||||
func (lp *Loadpoint) evChargeCurrentWrappedMeterHandler(current float64) {
|
||||
power := current * float64(lp.activePhases()) * Voltage
|
||||
|
||||
// if disabled we cannot be charging
|
||||
|
|
@ -515,7 +515,7 @@ func (lp *LoadPoint) evChargeCurrentWrappedMeterHandler(current float64) {
|
|||
}
|
||||
|
||||
// applyAction executes the action
|
||||
func (lp *LoadPoint) applyAction(actionCfg api.ActionConfig) {
|
||||
func (lp *Loadpoint) applyAction(actionCfg api.ActionConfig) {
|
||||
if actionCfg.Mode != nil {
|
||||
lp.SetMode(*actionCfg.Mode)
|
||||
}
|
||||
|
|
@ -525,21 +525,21 @@ func (lp *LoadPoint) applyAction(actionCfg api.ActionConfig) {
|
|||
if max := actionCfg.MaxCurrent; max != nil && *max <= *lp.onDisconnect.MaxCurrent {
|
||||
lp.SetMaxCurrent(*max)
|
||||
}
|
||||
if actionCfg.MinSoC != nil {
|
||||
lp.SetMinSoC(*actionCfg.MinSoC)
|
||||
if actionCfg.MinSoc != nil {
|
||||
lp.SetMinSoc(*actionCfg.MinSoc)
|
||||
}
|
||||
if actionCfg.TargetSoC != nil {
|
||||
lp.SetTargetSoC(*actionCfg.TargetSoC)
|
||||
if actionCfg.TargetSoc != nil {
|
||||
lp.SetTargetSoc(*actionCfg.TargetSoc)
|
||||
}
|
||||
}
|
||||
|
||||
// Name returns the human-readable loadpoint title
|
||||
func (lp *LoadPoint) Name() string {
|
||||
func (lp *Loadpoint) Name() string {
|
||||
return lp.Title
|
||||
}
|
||||
|
||||
// Prepare loadpoint configuration by adding missing helper elements
|
||||
func (lp *LoadPoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Event, lpChan chan<- *LoadPoint) {
|
||||
func (lp *Loadpoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Event, lpChan chan<- *Loadpoint) {
|
||||
lp.uiChan = uiChan
|
||||
lp.pushChan = pushChan
|
||||
lp.lpChan = lpChan
|
||||
|
|
@ -550,7 +550,7 @@ func (lp *LoadPoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Even
|
|||
_ = lp.bus.Subscribe(evVehicleConnect, lp.evVehicleConnectHandler)
|
||||
_ = lp.bus.Subscribe(evVehicleDisconnect, lp.evVehicleDisconnectHandler)
|
||||
_ = lp.bus.Subscribe(evChargeCurrent, lp.evChargeCurrentHandler)
|
||||
_ = lp.bus.Subscribe(evVehicleSoC, lp.evVehicleSoCProgressHandler)
|
||||
_ = lp.bus.Subscribe(evVehicleSoc, lp.evVehicleSocProgressHandler)
|
||||
|
||||
// publish initial values
|
||||
lp.publish("title", lp.Title)
|
||||
|
|
@ -570,8 +570,8 @@ func (lp *LoadPoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Even
|
|||
|
||||
lp.Lock()
|
||||
lp.publish("mode", lp.Mode)
|
||||
lp.publish("targetSoC", lp.SoC.target)
|
||||
lp.publish("minSoC", lp.SoC.min)
|
||||
lp.publish("targetSoc", lp.Soc.target)
|
||||
lp.publish("minSoc", lp.Soc.min)
|
||||
lp.Unlock()
|
||||
|
||||
// reset detection state
|
||||
|
|
@ -595,7 +595,7 @@ func (lp *LoadPoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Even
|
|||
}
|
||||
|
||||
// syncCharger updates charger status and synchronizes it with expectations
|
||||
func (lp *LoadPoint) syncCharger() {
|
||||
func (lp *Loadpoint) syncCharger() {
|
||||
enabled, err := lp.charger.Enabled()
|
||||
if err == nil {
|
||||
if enabled != lp.enabled {
|
||||
|
|
@ -616,7 +616,7 @@ func (lp *LoadPoint) syncCharger() {
|
|||
}
|
||||
|
||||
// setLimit applies charger current limits and enables/disables accordingly
|
||||
func (lp *LoadPoint) setLimit(chargeCurrent float64, force bool) error {
|
||||
func (lp *Loadpoint) setLimit(chargeCurrent float64, force bool) error {
|
||||
// set current
|
||||
if chargeCurrent != lp.chargeCurrent && chargeCurrent >= lp.GetMinCurrent() {
|
||||
var err error
|
||||
|
|
@ -685,25 +685,25 @@ func (lp *LoadPoint) setLimit(chargeCurrent float64, force bool) error {
|
|||
}
|
||||
|
||||
// connected returns the EVs connection state
|
||||
func (lp *LoadPoint) connected() bool {
|
||||
func (lp *Loadpoint) connected() bool {
|
||||
status := lp.GetStatus()
|
||||
return status == api.StatusB || status == api.StatusC
|
||||
}
|
||||
|
||||
// charging returns the EVs charging state
|
||||
func (lp *LoadPoint) charging() bool {
|
||||
func (lp *Loadpoint) charging() bool {
|
||||
return lp.GetStatus() == api.StatusC
|
||||
}
|
||||
|
||||
// charging returns the EVs charging state
|
||||
func (lp *LoadPoint) setStatus(status api.ChargeStatus) {
|
||||
func (lp *Loadpoint) setStatus(status api.ChargeStatus) {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
lp.status = status
|
||||
}
|
||||
|
||||
// targetEnergyReached checks if target is configured and reached
|
||||
func (lp *LoadPoint) targetEnergyReached() bool {
|
||||
func (lp *Loadpoint) targetEnergyReached() bool {
|
||||
return (lp.vehicle == nil || lp.vehicleHasFeature(api.Offline)) &&
|
||||
lp.targetEnergy > 0 &&
|
||||
lp.getChargedEnergy()/1e3 >= float64(lp.targetEnergy)
|
||||
|
|
@ -711,23 +711,23 @@ func (lp *LoadPoint) targetEnergyReached() bool {
|
|||
|
||||
// targetSocReached checks if target is configured and reached.
|
||||
// If vehicle is not configured this will always return false
|
||||
func (lp *LoadPoint) targetSocReached() bool {
|
||||
func (lp *Loadpoint) targetSocReached() bool {
|
||||
return lp.vehicle != nil &&
|
||||
lp.SoC.target > 0 &&
|
||||
lp.SoC.target < 100 &&
|
||||
lp.vehicleSoc >= float64(lp.SoC.target)
|
||||
lp.Soc.target > 0 &&
|
||||
lp.Soc.target < 100 &&
|
||||
lp.vehicleSoc >= float64(lp.Soc.target)
|
||||
}
|
||||
|
||||
// minSocNotReached checks if minimum is configured and not reached.
|
||||
// If vehicle is not configured this will always return true
|
||||
func (lp *LoadPoint) minSocNotReached() bool {
|
||||
func (lp *Loadpoint) minSocNotReached() bool {
|
||||
return lp.vehicle != nil &&
|
||||
lp.SoC.min > 0 &&
|
||||
lp.vehicleSoc < float64(lp.SoC.min)
|
||||
lp.Soc.min > 0 &&
|
||||
lp.vehicleSoc < float64(lp.Soc.min)
|
||||
}
|
||||
|
||||
// climateActive checks if vehicle has active climate request
|
||||
func (lp *LoadPoint) climateActive() bool {
|
||||
func (lp *Loadpoint) climateActive() bool {
|
||||
if cl, ok := lp.vehicle.(api.VehicleClimater); ok {
|
||||
active, outsideTemp, targetTemp, err := cl.Climater()
|
||||
if err == nil {
|
||||
|
|
@ -758,18 +758,18 @@ func (lp *LoadPoint) climateActive() bool {
|
|||
}
|
||||
|
||||
// disableUnlessClimater disables the charger unless climate is active
|
||||
func (lp *LoadPoint) disableUnlessClimater() error {
|
||||
func (lp *Loadpoint) disableUnlessClimater() error {
|
||||
var current float64 // zero disables
|
||||
if lp.climateActive() {
|
||||
lp.log.DEBUG.Println("climater active")
|
||||
current = lp.GetMinCurrent()
|
||||
}
|
||||
lp.socTimer.Reset() // once SoC is reached, the target charge request is removed
|
||||
lp.socTimer.Reset() // once Soc is reached, the target charge request is removed
|
||||
return lp.setLimit(current, true)
|
||||
}
|
||||
|
||||
// remoteControlled returns true if remote control status is active
|
||||
func (lp *LoadPoint) remoteControlled(demand loadpoint.RemoteDemand) bool {
|
||||
func (lp *Loadpoint) remoteControlled(demand loadpoint.RemoteDemand) bool {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
|
||||
|
|
@ -777,7 +777,7 @@ func (lp *LoadPoint) remoteControlled(demand loadpoint.RemoteDemand) bool {
|
|||
}
|
||||
|
||||
// setVehicleIdentifier updated the vehicle id as read from the charger
|
||||
func (lp *LoadPoint) setVehicleIdentifier(id string) {
|
||||
func (lp *Loadpoint) setVehicleIdentifier(id string) {
|
||||
if lp.vehicleIdentifier != id {
|
||||
lp.vehicleIdentifier = id
|
||||
lp.publish("vehicleIdentity", id)
|
||||
|
|
@ -785,7 +785,7 @@ func (lp *LoadPoint) setVehicleIdentifier(id string) {
|
|||
}
|
||||
|
||||
// identifyVehicle reads vehicle identification from charger
|
||||
func (lp *LoadPoint) identifyVehicle() {
|
||||
func (lp *Loadpoint) identifyVehicle() {
|
||||
identifier, ok := lp.charger.(api.Identifier)
|
||||
if !ok {
|
||||
return
|
||||
|
|
@ -815,7 +815,7 @@ func (lp *LoadPoint) identifyVehicle() {
|
|||
}
|
||||
|
||||
// selectVehicleByID selects the vehicle with the given ID
|
||||
func (lp *LoadPoint) selectVehicleByID(id string) api.Vehicle {
|
||||
func (lp *Loadpoint) selectVehicleByID(id string) api.Vehicle {
|
||||
vehicles := lp.coordinatedVehicles()
|
||||
|
||||
// find exact match
|
||||
|
|
@ -848,7 +848,7 @@ func (lp *LoadPoint) selectVehicleByID(id string) api.Vehicle {
|
|||
|
||||
// setActiveVehicle assigns currently active vehicle, configures soc estimator
|
||||
// and adds an odometer task
|
||||
func (lp *LoadPoint) setActiveVehicle(vehicle api.Vehicle) {
|
||||
func (lp *Loadpoint) setActiveVehicle(vehicle api.Vehicle) {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
|
||||
|
|
@ -868,16 +868,16 @@ func (lp *LoadPoint) setActiveVehicle(vehicle api.Vehicle) {
|
|||
}
|
||||
lp.log.INFO.Printf("vehicle updated: %s -> %s", from, to)
|
||||
|
||||
// reset minSoC and targetSoC before change
|
||||
lp.setMinSoC(0)
|
||||
lp.setTargetSoC(100)
|
||||
// reset minSoc and targetSoc before change
|
||||
lp.setMinSoc(0)
|
||||
lp.setTargetSoc(100)
|
||||
|
||||
if lp.vehicle = vehicle; vehicle != nil {
|
||||
lp.socUpdated = time.Time{}
|
||||
|
||||
// resolve optional config
|
||||
var estimate bool
|
||||
if lp.SoC.Estimate == nil || *lp.SoC.Estimate {
|
||||
if lp.Soc.Estimate == nil || *lp.Soc.Estimate {
|
||||
estimate = true
|
||||
}
|
||||
lp.socEstimator = soc.NewEstimator(lp.log, lp.charger, vehicle, estimate)
|
||||
|
|
@ -925,7 +925,7 @@ func (lp *LoadPoint) setActiveVehicle(vehicle api.Vehicle) {
|
|||
})
|
||||
}
|
||||
|
||||
func (lp *LoadPoint) wakeUpVehicle() {
|
||||
func (lp *Loadpoint) wakeUpVehicle() {
|
||||
// charger
|
||||
if c, ok := lp.charger.(api.Resurrector); ok {
|
||||
if err := c.WakeUp(); err != nil {
|
||||
|
|
@ -943,12 +943,12 @@ func (lp *LoadPoint) wakeUpVehicle() {
|
|||
}
|
||||
|
||||
// unpublishVehicle resets published vehicle data
|
||||
func (lp *LoadPoint) unpublishVehicle() {
|
||||
func (lp *Loadpoint) unpublishVehicle() {
|
||||
lp.vehicleSoc = 0
|
||||
|
||||
lp.publish("vehicleSoC", 0.0)
|
||||
lp.publish("vehicleSoc", 0.0)
|
||||
lp.publish(vehicleRange, int64(0))
|
||||
lp.publish(vehicleTargetSoC, 0.0)
|
||||
lp.publish(vehicleTargetSoc, 0.0)
|
||||
|
||||
lp.setRemainingDuration(-1)
|
||||
|
||||
|
|
@ -956,7 +956,7 @@ func (lp *LoadPoint) unpublishVehicle() {
|
|||
}
|
||||
|
||||
// vehicleHasFeature checks availability of vehicle feature
|
||||
func (lp *LoadPoint) vehicleHasFeature(f api.Feature) bool {
|
||||
func (lp *Loadpoint) vehicleHasFeature(f api.Feature) bool {
|
||||
v, ok := lp.vehicle.(api.FeatureDescriber)
|
||||
if ok {
|
||||
ok = v.Has(f)
|
||||
|
|
@ -965,14 +965,14 @@ func (lp *LoadPoint) vehicleHasFeature(f api.Feature) bool {
|
|||
}
|
||||
|
||||
// vehiclePublishFeature availability of vehicle features
|
||||
func (lp *LoadPoint) vehiclePublishFeature(f api.Feature) {
|
||||
func (lp *Loadpoint) vehiclePublishFeature(f api.Feature) {
|
||||
lp.publish("vehicleFeature"+f.String(), lp.vehicleHasFeature(f))
|
||||
}
|
||||
|
||||
// vehicleUnidentified returns true if there are associated vehicles and detection is running.
|
||||
// It will also reset the api cache at regular intervals.
|
||||
// Detection is stopped after maximum duration and the "guest vehicle" message dispatched.
|
||||
func (lp *LoadPoint) vehicleUnidentified() bool {
|
||||
func (lp *Loadpoint) vehicleUnidentified() bool {
|
||||
if lp.vehicle != nil || lp.vehicleDetect.IsZero() || len(lp.coordinatedVehicles()) == 0 {
|
||||
return false
|
||||
}
|
||||
|
|
@ -996,7 +996,7 @@ func (lp *LoadPoint) vehicleUnidentified() bool {
|
|||
}
|
||||
|
||||
// vehicleDefaultOrDetect will assign and update default vehicle or start detection
|
||||
func (lp *LoadPoint) vehicleDefaultOrDetect() {
|
||||
func (lp *Loadpoint) vehicleDefaultOrDetect() {
|
||||
if lp.defaultVehicle != nil {
|
||||
if lp.vehicle != lp.defaultVehicle {
|
||||
lp.setActiveVehicle(lp.defaultVehicle)
|
||||
|
|
@ -1011,7 +1011,7 @@ func (lp *LoadPoint) vehicleDefaultOrDetect() {
|
|||
}
|
||||
|
||||
// startVehicleDetection reset connection timer and starts api refresh timer
|
||||
func (lp *LoadPoint) startVehicleDetection() {
|
||||
func (lp *Loadpoint) startVehicleDetection() {
|
||||
// flush all vehicles before detection starts
|
||||
lp.log.DEBUG.Println("vehicle api refresh")
|
||||
provider.ResetCached()
|
||||
|
|
@ -1022,7 +1022,7 @@ func (lp *LoadPoint) startVehicleDetection() {
|
|||
}
|
||||
|
||||
// stopVehicleDetection expires the connection timer and ticker
|
||||
func (lp *LoadPoint) stopVehicleDetection() {
|
||||
func (lp *Loadpoint) stopVehicleDetection() {
|
||||
lp.vehicleDetect = time.Time{}
|
||||
if lp.vehicleDetectTicker != nil {
|
||||
lp.vehicleDetectTicker.Stop()
|
||||
|
|
@ -1031,7 +1031,7 @@ func (lp *LoadPoint) stopVehicleDetection() {
|
|||
}
|
||||
|
||||
// identifyVehicleByStatus validates if the active vehicle is still connected to the loadpoint
|
||||
func (lp *LoadPoint) identifyVehicleByStatus() {
|
||||
func (lp *Loadpoint) identifyVehicleByStatus() {
|
||||
if len(lp.coordinatedVehicles()) == 0 {
|
||||
return
|
||||
}
|
||||
|
|
@ -1049,7 +1049,7 @@ func (lp *LoadPoint) identifyVehicleByStatus() {
|
|||
}
|
||||
|
||||
// vehicleOdometer updates odometer
|
||||
func (lp *LoadPoint) vehicleOdometer() {
|
||||
func (lp *Loadpoint) vehicleOdometer() {
|
||||
if vs, ok := lp.vehicle.(api.VehicleOdometer); ok {
|
||||
if odo, err := vs.Odometer(); err == nil {
|
||||
lp.log.DEBUG.Printf("vehicle odometer: %.0fkm", odo)
|
||||
|
|
@ -1093,7 +1093,7 @@ func statusEvents(prevStatus, status api.ChargeStatus) []string {
|
|||
}
|
||||
|
||||
// updateChargerStatus updates charger status and detects car connected/disconnected events
|
||||
func (lp *LoadPoint) updateChargerStatus() error {
|
||||
func (lp *Loadpoint) updateChargerStatus() error {
|
||||
status, err := lp.charger.Status()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -1126,7 +1126,7 @@ func (lp *LoadPoint) updateChargerStatus() error {
|
|||
}
|
||||
|
||||
// effectiveCurrent returns the currently effective charging current
|
||||
func (lp *LoadPoint) effectiveCurrent() float64 {
|
||||
func (lp *Loadpoint) effectiveCurrent() float64 {
|
||||
if !lp.charging() {
|
||||
return 0
|
||||
}
|
||||
|
|
@ -1141,7 +1141,7 @@ func (lp *LoadPoint) effectiveCurrent() float64 {
|
|||
}
|
||||
|
||||
// elapsePVTimer puts the pv enable/disable timer into elapsed state
|
||||
func (lp *LoadPoint) elapsePVTimer() {
|
||||
func (lp *Loadpoint) elapsePVTimer() {
|
||||
lp.log.DEBUG.Printf("pv timer elapse")
|
||||
|
||||
lp.pvTimer = elapsed
|
||||
|
|
@ -1151,7 +1151,7 @@ func (lp *LoadPoint) elapsePVTimer() {
|
|||
}
|
||||
|
||||
// resetPVTimerIfRunning resets the pv enable/disable timer to disabled state
|
||||
func (lp *LoadPoint) resetPVTimerIfRunning(typ ...string) {
|
||||
func (lp *Loadpoint) resetPVTimerIfRunning(typ ...string) {
|
||||
if lp.pvTimer.IsZero() {
|
||||
return
|
||||
}
|
||||
|
|
@ -1167,19 +1167,19 @@ func (lp *LoadPoint) resetPVTimerIfRunning(typ ...string) {
|
|||
}
|
||||
|
||||
// resetPhaseTimer resets the phase switch timer to disabled state
|
||||
func (lp *LoadPoint) resetPhaseTimer() {
|
||||
func (lp *Loadpoint) resetPhaseTimer() {
|
||||
lp.phaseTimer = time.Time{}
|
||||
lp.publishTimer(phaseTimer, 0, timerInactive)
|
||||
}
|
||||
|
||||
// scalePhasesRequired validates if fixed phase configuration matches enabled phases
|
||||
func (lp *LoadPoint) scalePhasesRequired() bool {
|
||||
func (lp *Loadpoint) scalePhasesRequired() bool {
|
||||
_, ok := lp.charger.(api.PhaseSwitcher)
|
||||
return ok && lp.ConfiguredPhases != 0 && lp.ConfiguredPhases != lp.GetPhases()
|
||||
}
|
||||
|
||||
// scalePhasesIfAvailable scales if api.PhaseSwitcher is available
|
||||
func (lp *LoadPoint) scalePhasesIfAvailable(phases int) error {
|
||||
func (lp *Loadpoint) scalePhasesIfAvailable(phases int) error {
|
||||
if lp.ConfiguredPhases != 0 {
|
||||
phases = lp.ConfiguredPhases
|
||||
}
|
||||
|
|
@ -1192,7 +1192,7 @@ func (lp *LoadPoint) scalePhasesIfAvailable(phases int) error {
|
|||
}
|
||||
|
||||
// setConfiguredPhases sets the default phase configuration
|
||||
func (lp *LoadPoint) setConfiguredPhases(phases int) {
|
||||
func (lp *Loadpoint) setConfiguredPhases(phases int) {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
|
||||
|
|
@ -1207,7 +1207,7 @@ func (lp *LoadPoint) setConfiguredPhases(phases int) {
|
|||
}
|
||||
|
||||
// setPhases sets the number of enabled phases without modifying the charger
|
||||
func (lp *LoadPoint) setPhases(phases int) {
|
||||
func (lp *Loadpoint) setPhases(phases int) {
|
||||
if lp.GetPhases() != phases {
|
||||
lp.Lock()
|
||||
lp.phases = phases
|
||||
|
|
@ -1223,7 +1223,7 @@ func (lp *LoadPoint) setPhases(phases int) {
|
|||
|
||||
// scalePhases adjusts the number of active phases and returns the appropriate charging current.
|
||||
// Returns api.ErrNotAvailable if api.PhaseSwitcher is not available.
|
||||
func (lp *LoadPoint) scalePhases(phases int) error {
|
||||
func (lp *Loadpoint) scalePhases(phases int) error {
|
||||
cp, ok := lp.charger.(api.PhaseSwitcher)
|
||||
if !ok {
|
||||
panic("charger does not implement api.PhaseSwitcher")
|
||||
|
|
@ -1251,7 +1251,7 @@ func (lp *LoadPoint) scalePhases(phases int) error {
|
|||
}
|
||||
|
||||
// pvScalePhases switches phases if necessary and returns if switch occurred
|
||||
func (lp *LoadPoint) pvScalePhases(availablePower, minCurrent, maxCurrent float64) bool {
|
||||
func (lp *Loadpoint) pvScalePhases(availablePower, minCurrent, maxCurrent float64) bool {
|
||||
phases := lp.GetPhases()
|
||||
|
||||
// observed phase state inconsistency
|
||||
|
|
@ -1327,7 +1327,7 @@ func (lp *LoadPoint) pvScalePhases(availablePower, minCurrent, maxCurrent float6
|
|||
}
|
||||
|
||||
// coordinatedVehicles is the slice of vehicles from the coordinator
|
||||
func (lp *LoadPoint) coordinatedVehicles() []api.Vehicle {
|
||||
func (lp *Loadpoint) coordinatedVehicles() []api.Vehicle {
|
||||
if lp.coordinator == nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1335,7 +1335,7 @@ func (lp *LoadPoint) coordinatedVehicles() []api.Vehicle {
|
|||
}
|
||||
|
||||
// TODO move up to timer functions
|
||||
func (lp *LoadPoint) publishTimer(name string, delay time.Duration, action string) {
|
||||
func (lp *Loadpoint) publishTimer(name string, delay time.Duration, action string) {
|
||||
timer := lp.pvTimer
|
||||
if name == phaseTimer {
|
||||
timer = lp.phaseTimer
|
||||
|
|
@ -1357,7 +1357,7 @@ func (lp *LoadPoint) publishTimer(name string, delay time.Duration, action strin
|
|||
}
|
||||
|
||||
// pvMaxCurrent calculates the maximum target current for PV mode
|
||||
func (lp *LoadPoint) pvMaxCurrent(mode api.ChargeMode, sitePower float64, batteryBuffered bool) float64 {
|
||||
func (lp *Loadpoint) pvMaxCurrent(mode api.ChargeMode, sitePower float64, batteryBuffered bool) float64 {
|
||||
// read only once to simplify testing
|
||||
minCurrent := lp.GetMinCurrent()
|
||||
maxCurrent := lp.GetMaxCurrent()
|
||||
|
|
@ -1458,7 +1458,7 @@ func (lp *LoadPoint) pvMaxCurrent(mode api.ChargeMode, sitePower float64, batter
|
|||
}
|
||||
|
||||
// UpdateChargePower updates charge meter power
|
||||
func (lp *LoadPoint) UpdateChargePower() {
|
||||
func (lp *Loadpoint) UpdateChargePower() {
|
||||
err := retry.Do(func() error {
|
||||
value, err := lp.chargeMeter.CurrentPower()
|
||||
if err != nil {
|
||||
|
|
@ -1485,7 +1485,7 @@ func (lp *LoadPoint) UpdateChargePower() {
|
|||
}
|
||||
|
||||
// updateChargeCurrents uses MeterCurrent interface to count phases with current >=1A
|
||||
func (lp *LoadPoint) updateChargeCurrents() {
|
||||
func (lp *Loadpoint) updateChargeCurrents() {
|
||||
lp.chargeCurrents = nil
|
||||
|
||||
phaseMeter, ok := lp.chargeMeter.(api.MeterCurrent)
|
||||
|
|
@ -1528,7 +1528,7 @@ func (lp *LoadPoint) updateChargeCurrents() {
|
|||
}
|
||||
|
||||
// publish charged energy and duration
|
||||
func (lp *LoadPoint) publishChargeProgress() {
|
||||
func (lp *Loadpoint) publishChargeProgress() {
|
||||
if f, err := lp.chargeRater.ChargedEnergy(); err == nil {
|
||||
// workaround for Go-E resetting during disconnect, see
|
||||
// https://github.com/evcc-io/evcc/issues/5092
|
||||
|
|
@ -1553,12 +1553,12 @@ func (lp *LoadPoint) publishChargeProgress() {
|
|||
}
|
||||
|
||||
// socPollAllowed validates charging state against polling mode
|
||||
func (lp *LoadPoint) socPollAllowed() bool {
|
||||
remaining := lp.SoC.Poll.Interval - lp.clock.Since(lp.socUpdated)
|
||||
func (lp *Loadpoint) socPollAllowed() bool {
|
||||
remaining := lp.Soc.Poll.Interval - lp.clock.Since(lp.socUpdated)
|
||||
|
||||
honourUpdateInterval := lp.SoC.Poll.Mode == pollAlways ||
|
||||
lp.SoC.Poll.Mode == pollConnected && lp.connected() ||
|
||||
lp.SoC.Poll.Mode == pollCharging && lp.connected() && (lp.vehicleSoc < float64(lp.SoC.target))
|
||||
honourUpdateInterval := lp.Soc.Poll.Mode == pollAlways ||
|
||||
lp.Soc.Poll.Mode == pollConnected && lp.connected() ||
|
||||
lp.Soc.Poll.Mode == pollCharging && lp.connected() && (lp.vehicleSoc < float64(lp.Soc.target))
|
||||
|
||||
if honourUpdateInterval && remaining > 0 {
|
||||
lp.log.DEBUG.Printf("next soc poll remaining time: %v", remaining.Truncate(time.Second))
|
||||
|
|
@ -1567,10 +1567,10 @@ func (lp *LoadPoint) socPollAllowed() bool {
|
|||
return lp.charging() || honourUpdateInterval && (remaining <= 0) || lp.connected() && lp.socUpdated.IsZero()
|
||||
}
|
||||
|
||||
// checks if the connected charger can provide SoC to the connected vehicle
|
||||
func (lp *LoadPoint) socProvidedByCharger() bool {
|
||||
// checks if the connected charger can provide Soc to the connected vehicle
|
||||
func (lp *Loadpoint) socProvidedByCharger() bool {
|
||||
if charger, ok := lp.charger.(api.Battery); ok {
|
||||
if _, err := charger.SoC(); err == nil {
|
||||
if _, err := charger.Soc(); err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -1578,7 +1578,7 @@ func (lp *LoadPoint) socProvidedByCharger() bool {
|
|||
}
|
||||
|
||||
// publish state of charge, remaining charge duration and range
|
||||
func (lp *LoadPoint) publishSoCAndRange() {
|
||||
func (lp *Loadpoint) publishSocAndRange() {
|
||||
if lp.socEstimator == nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -1590,7 +1590,7 @@ func (lp *LoadPoint) publishSoCAndRange() {
|
|||
// guard for socEstimator removed by api
|
||||
if se := lp.socEstimator; se != nil {
|
||||
lp.socUpdated = lp.clock.Now()
|
||||
f, err = se.SoC(lp.getChargedEnergy())
|
||||
f, err = se.Soc(lp.getChargedEnergy())
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
|
@ -1607,22 +1607,22 @@ func (lp *LoadPoint) publishSoCAndRange() {
|
|||
|
||||
lp.vehicleSoc = math.Trunc(f)
|
||||
lp.log.DEBUG.Printf("vehicle soc: %.0f%%", lp.vehicleSoc)
|
||||
lp.publish("vehicleSoC", lp.vehicleSoc)
|
||||
lp.publish("vehicleSoc", lp.vehicleSoc)
|
||||
|
||||
// vehicle target soc
|
||||
targetSoC := 100.0
|
||||
targetSoc := 100.0
|
||||
if vs, ok := lp.vehicle.(api.SocLimiter); ok {
|
||||
var err error
|
||||
if targetSoC, err = vs.TargetSoC(); err == nil {
|
||||
lp.log.DEBUG.Printf("vehicle target soc: %.0f%%", targetSoC)
|
||||
lp.publish(vehicleTargetSoC, targetSoC)
|
||||
if targetSoc, err = vs.TargetSoc(); err == nil {
|
||||
lp.log.DEBUG.Printf("vehicle target soc: %.0f%%", targetSoc)
|
||||
lp.publish(vehicleTargetSoc, targetSoc)
|
||||
}
|
||||
}
|
||||
|
||||
// use minimum of vehicle and loadpoint
|
||||
socLimit := int(math.Round(targetSoC))
|
||||
if lp.SoC.target < socLimit {
|
||||
socLimit = lp.SoC.target
|
||||
socLimit := int(math.Round(targetSoc))
|
||||
if lp.Soc.target < socLimit {
|
||||
socLimit = lp.Soc.target
|
||||
}
|
||||
|
||||
if se := lp.socEstimator; se != nil {
|
||||
|
|
@ -1646,12 +1646,12 @@ func (lp *LoadPoint) publishSoCAndRange() {
|
|||
}
|
||||
|
||||
// trigger message after variables are updated
|
||||
lp.bus.Publish(evVehicleSoC, f)
|
||||
lp.bus.Publish(evVehicleSoc, f)
|
||||
}
|
||||
}
|
||||
|
||||
// addTask adds a single task to the queue
|
||||
func (lp *LoadPoint) addTask(task func()) {
|
||||
func (lp *Loadpoint) addTask(task func()) {
|
||||
// test guard
|
||||
if lp.tasks != nil {
|
||||
// don't add twice
|
||||
|
|
@ -1664,7 +1664,7 @@ func (lp *LoadPoint) addTask(task func()) {
|
|||
}
|
||||
|
||||
// processTasks executes a single task from the queue
|
||||
func (lp *LoadPoint) processTasks() {
|
||||
func (lp *Loadpoint) processTasks() {
|
||||
// test guard
|
||||
if lp.tasks != nil {
|
||||
if task, ok := lp.tasks.Dequeue(); ok {
|
||||
|
|
@ -1674,7 +1674,7 @@ func (lp *LoadPoint) processTasks() {
|
|||
}
|
||||
|
||||
// Update is the main control function. It reevaluates meters and charger state
|
||||
func (lp *LoadPoint) Update(sitePower float64, cheap, batteryBuffered bool) {
|
||||
func (lp *Loadpoint) Update(sitePower float64, cheap, batteryBuffered bool) {
|
||||
lp.processTasks()
|
||||
|
||||
mode := lp.GetMode()
|
||||
|
|
@ -1713,7 +1713,7 @@ func (lp *LoadPoint) Update(sitePower float64, cheap, batteryBuffered bool) {
|
|||
|
||||
// publish soc after updating charger status to make sure
|
||||
// initial update of connected state matches charger status
|
||||
lp.publishSoCAndRange()
|
||||
lp.publishSocAndRange()
|
||||
|
||||
// sync settings with charger
|
||||
lp.syncCharger()
|
||||
|
|
@ -1744,7 +1744,7 @@ func (lp *LoadPoint) Update(sitePower float64, cheap, batteryBuffered bool) {
|
|||
err = lp.disableUnlessClimater()
|
||||
|
||||
case lp.targetSocReached():
|
||||
lp.log.DEBUG.Printf("targetSoC reached: %.1f%% > %d%%", lp.vehicleSoc, lp.SoC.target)
|
||||
lp.log.DEBUG.Printf("targetSoc reached: %.1f%% > %d%%", lp.vehicleSoc, lp.Soc.target)
|
||||
err = lp.disableUnlessClimater()
|
||||
|
||||
// OCPP has priority over target charging
|
||||
|
|
@ -1805,7 +1805,7 @@ func (lp *LoadPoint) Update(sitePower float64, cheap, batteryBuffered bool) {
|
|||
|
||||
// Wake-up checks
|
||||
if lp.enabled && lp.status == api.StatusB &&
|
||||
int(lp.vehicleSoc) < lp.SoC.target && lp.wakeUpTimer.Expired() {
|
||||
int(lp.vehicleSoc) < lp.Soc.target && lp.wakeUpTimer.Expired() {
|
||||
lp.wakeUpVehicle()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,20 +35,20 @@ type API interface {
|
|||
GetTargetEnergy() float64
|
||||
// SetTargetEnergy sets the charge target energy
|
||||
SetTargetEnergy(float64)
|
||||
// GetTargetSoC returns the charge target soc
|
||||
GetTargetSoC() int
|
||||
// SetTargetSoC sets the charge target soc
|
||||
SetTargetSoC(int)
|
||||
// GetMinSoC returns the charge minimum soc
|
||||
GetMinSoC() int
|
||||
// SetMinSoC sets the charge minimum soc
|
||||
SetMinSoC(int)
|
||||
// GetTargetSoc returns the charge target soc
|
||||
GetTargetSoc() int
|
||||
// SetTargetSoc sets the charge target soc
|
||||
SetTargetSoc(int)
|
||||
// GetMinSoc returns the charge minimum soc
|
||||
GetMinSoc() int
|
||||
// SetMinSoc sets the charge minimum soc
|
||||
SetMinSoc(int)
|
||||
// GetPhases returns the enabled phases
|
||||
GetPhases() int
|
||||
// SetPhases sets the enabled phases
|
||||
SetPhases(int) error
|
||||
|
||||
// SetTargetCharge sets the charge targetSoC
|
||||
// SetTargetCharge sets the charge targetSoc
|
||||
SetTargetCharge(time.Time, int) error
|
||||
// RemoteControl sets remote status demand
|
||||
RemoteControl(string, RemoteDemand)
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@ import (
|
|||
var _ soc.Adapter = (*adapter)(nil)
|
||||
|
||||
type adapter struct {
|
||||
*LoadPoint
|
||||
*Loadpoint
|
||||
}
|
||||
|
||||
func (a *adapter) Publish(key string, val interface{}) {
|
||||
a.LoadPoint.publish(key, val)
|
||||
a.Loadpoint.publish(key, val)
|
||||
}
|
||||
|
||||
func (a *adapter) SocEstimator() *soc.Estimator {
|
||||
return a.LoadPoint.socEstimator
|
||||
return a.Loadpoint.socEstimator
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,24 +10,24 @@ import (
|
|||
"github.com/evcc-io/evcc/core/wrapper"
|
||||
)
|
||||
|
||||
var _ loadpoint.API = (*LoadPoint)(nil)
|
||||
var _ loadpoint.API = (*Loadpoint)(nil)
|
||||
|
||||
// GetStatus returns the charging status
|
||||
func (lp *LoadPoint) GetStatus() api.ChargeStatus {
|
||||
func (lp *Loadpoint) GetStatus() api.ChargeStatus {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
return lp.status
|
||||
}
|
||||
|
||||
// GetMode returns loadpoint charge mode
|
||||
func (lp *LoadPoint) GetMode() api.ChargeMode {
|
||||
func (lp *Loadpoint) GetMode() api.ChargeMode {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
return lp.Mode
|
||||
}
|
||||
|
||||
// SetMode sets loadpoint charge mode
|
||||
func (lp *LoadPoint) SetMode(mode api.ChargeMode) {
|
||||
func (lp *Loadpoint) SetMode(mode api.ChargeMode) {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
|
||||
|
|
@ -51,28 +51,28 @@ func (lp *LoadPoint) SetMode(mode api.ChargeMode) {
|
|||
}
|
||||
|
||||
// getChargedEnergy returns loadpoint charge target energy
|
||||
func (lp *LoadPoint) getChargedEnergy() float64 {
|
||||
func (lp *Loadpoint) getChargedEnergy() float64 {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
return lp.chargedEnergy
|
||||
}
|
||||
|
||||
// setChargedEnergy returns loadpoint charge target energy
|
||||
func (lp *LoadPoint) setChargedEnergy(energy float64) {
|
||||
func (lp *Loadpoint) setChargedEnergy(energy float64) {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
lp.chargedEnergy = energy
|
||||
}
|
||||
|
||||
// GetTargetEnergy returns loadpoint charge target energy
|
||||
func (lp *LoadPoint) GetTargetEnergy() float64 {
|
||||
func (lp *Loadpoint) GetTargetEnergy() float64 {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
return lp.targetEnergy
|
||||
}
|
||||
|
||||
// setTargetEnergy sets loadpoint charge target energy (no mutex)
|
||||
func (lp *LoadPoint) setTargetEnergy(energy float64) {
|
||||
func (lp *Loadpoint) setTargetEnergy(energy float64) {
|
||||
lp.targetEnergy = energy
|
||||
// TODO soctimer
|
||||
// if lp.socTimer != nil {
|
||||
|
|
@ -82,7 +82,7 @@ func (lp *LoadPoint) setTargetEnergy(energy float64) {
|
|||
}
|
||||
|
||||
// SetTargetEnergy sets loadpoint charge target energy
|
||||
func (lp *LoadPoint) SetTargetEnergy(energy float64) {
|
||||
func (lp *Loadpoint) SetTargetEnergy(energy float64) {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
|
||||
|
|
@ -95,73 +95,73 @@ func (lp *LoadPoint) SetTargetEnergy(energy float64) {
|
|||
}
|
||||
}
|
||||
|
||||
// GetTargetSoC returns loadpoint charge target soc
|
||||
func (lp *LoadPoint) GetTargetSoC() int {
|
||||
// GetTargetSoc returns loadpoint charge target soc
|
||||
func (lp *Loadpoint) GetTargetSoc() int {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
return lp.SoC.target
|
||||
return lp.Soc.target
|
||||
}
|
||||
|
||||
// setTargetSoC sets loadpoint charge target soc (no mutex)
|
||||
func (lp *LoadPoint) setTargetSoC(soc int) {
|
||||
lp.SoC.target = soc
|
||||
// setTargetSoc sets loadpoint charge target soc (no mutex)
|
||||
func (lp *Loadpoint) setTargetSoc(soc int) {
|
||||
lp.Soc.target = soc
|
||||
// test guard
|
||||
if lp.socTimer != nil {
|
||||
lp.socTimer.SoC = soc
|
||||
lp.socTimer.Soc = soc
|
||||
}
|
||||
lp.publish("targetSoC", soc)
|
||||
lp.publish("targetSoc", soc)
|
||||
}
|
||||
|
||||
// SetTargetSoC sets loadpoint charge target soc
|
||||
func (lp *LoadPoint) SetTargetSoC(soc int) {
|
||||
// SetTargetSoc sets loadpoint charge target soc
|
||||
func (lp *Loadpoint) SetTargetSoc(soc int) {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
|
||||
lp.log.DEBUG.Println("set target soc:", soc)
|
||||
|
||||
// apply immediately
|
||||
if lp.SoC.target != soc {
|
||||
lp.setTargetSoC(soc)
|
||||
if lp.Soc.target != soc {
|
||||
lp.setTargetSoc(soc)
|
||||
lp.requestUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
// GetMinSoC returns loadpoint charge minimum soc
|
||||
func (lp *LoadPoint) GetMinSoC() int {
|
||||
// GetMinSoc returns loadpoint charge minimum soc
|
||||
func (lp *Loadpoint) GetMinSoc() int {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
return lp.SoC.min
|
||||
return lp.Soc.min
|
||||
}
|
||||
|
||||
// setMinSoC sets loadpoint charge min soc (no mutex)
|
||||
func (lp *LoadPoint) setMinSoC(soc int) {
|
||||
lp.SoC.min = soc
|
||||
lp.publish("minSoC", soc)
|
||||
// setMinSoc sets loadpoint charge min soc (no mutex)
|
||||
func (lp *Loadpoint) setMinSoc(soc int) {
|
||||
lp.Soc.min = soc
|
||||
lp.publish("minSoc", soc)
|
||||
}
|
||||
|
||||
// SetMinSoC sets loadpoint charge minimum soc
|
||||
func (lp *LoadPoint) SetMinSoC(soc int) {
|
||||
// SetMinSoc sets loadpoint charge minimum soc
|
||||
func (lp *Loadpoint) SetMinSoc(soc int) {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
|
||||
lp.log.DEBUG.Println("set min soc:", soc)
|
||||
|
||||
// apply immediately
|
||||
if lp.SoC.min != soc {
|
||||
lp.setMinSoC(soc)
|
||||
if lp.Soc.min != soc {
|
||||
lp.setMinSoc(soc)
|
||||
lp.requestUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
// GetPhases returns loadpoint enabled phases
|
||||
func (lp *LoadPoint) GetPhases() int {
|
||||
func (lp *Loadpoint) GetPhases() int {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
return lp.phases
|
||||
}
|
||||
|
||||
// SetPhases sets loadpoint enabled phases
|
||||
func (lp *LoadPoint) SetPhases(phases int) error {
|
||||
func (lp *Loadpoint) SetPhases(phases int) error {
|
||||
// limit auto mode (phases=0) to scalable charger
|
||||
if _, ok := lp.charger.(api.PhaseSwitcher); !ok && phases == 0 {
|
||||
return fmt.Errorf("invalid number of phases: %d", phases)
|
||||
|
|
@ -185,8 +185,8 @@ func (lp *LoadPoint) SetPhases(phases int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// SetTargetCharge sets loadpoint charge targetSoC
|
||||
func (lp *LoadPoint) SetTargetCharge(finishAt time.Time, soc int) error {
|
||||
// SetTargetCharge sets loadpoint charge targetSoc
|
||||
func (lp *Loadpoint) SetTargetCharge(finishAt time.Time, soc int) error {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
|
||||
|
|
@ -197,12 +197,12 @@ func (lp *LoadPoint) SetTargetCharge(finishAt time.Time, soc int) error {
|
|||
lp.log.DEBUG.Printf("set target charge: %d @ %v", soc, finishAt)
|
||||
|
||||
// apply immediately
|
||||
if lp.socTimer.Time != finishAt || lp.SoC.target != soc {
|
||||
if lp.socTimer.Time != finishAt || lp.Soc.target != soc {
|
||||
lp.socTimer.Set(finishAt)
|
||||
|
||||
// don't remove soc
|
||||
if !finishAt.IsZero() {
|
||||
lp.setTargetSoC(soc)
|
||||
lp.setTargetSoc(soc)
|
||||
lp.requestUpdate()
|
||||
}
|
||||
}
|
||||
|
|
@ -211,7 +211,7 @@ func (lp *LoadPoint) SetTargetCharge(finishAt time.Time, soc int) error {
|
|||
}
|
||||
|
||||
// RemoteControl sets remote status demand
|
||||
func (lp *LoadPoint) RemoteControl(source string, demand loadpoint.RemoteDemand) {
|
||||
func (lp *Loadpoint) RemoteControl(source string, demand loadpoint.RemoteDemand) {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
|
||||
|
|
@ -229,27 +229,27 @@ func (lp *LoadPoint) RemoteControl(source string, demand loadpoint.RemoteDemand)
|
|||
}
|
||||
|
||||
// HasChargeMeter determines if a physical charge meter is attached
|
||||
func (lp *LoadPoint) HasChargeMeter() bool {
|
||||
func (lp *Loadpoint) HasChargeMeter() bool {
|
||||
_, isWrapped := lp.chargeMeter.(*wrapper.ChargeMeter)
|
||||
return lp.chargeMeter != nil && !isWrapped
|
||||
}
|
||||
|
||||
// GetChargePower returns the current charge power
|
||||
func (lp *LoadPoint) GetChargePower() float64 {
|
||||
func (lp *Loadpoint) GetChargePower() float64 {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
return lp.chargePower
|
||||
}
|
||||
|
||||
// GetMinCurrent returns the min loadpoint current
|
||||
func (lp *LoadPoint) GetMinCurrent() float64 {
|
||||
func (lp *Loadpoint) GetMinCurrent() float64 {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
return lp.MinCurrent
|
||||
}
|
||||
|
||||
// SetMinCurrent sets the min loadpoint current
|
||||
func (lp *LoadPoint) SetMinCurrent(current float64) {
|
||||
func (lp *Loadpoint) SetMinCurrent(current float64) {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
|
||||
|
|
@ -262,14 +262,14 @@ func (lp *LoadPoint) SetMinCurrent(current float64) {
|
|||
}
|
||||
|
||||
// GetMaxCurrent returns the max loadpoint current
|
||||
func (lp *LoadPoint) GetMaxCurrent() float64 {
|
||||
func (lp *Loadpoint) GetMaxCurrent() float64 {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
return lp.MaxCurrent
|
||||
}
|
||||
|
||||
// SetMaxCurrent sets the max loadpoint current
|
||||
func (lp *LoadPoint) SetMaxCurrent(current float64) {
|
||||
func (lp *Loadpoint) SetMaxCurrent(current float64) {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
|
||||
|
|
@ -282,17 +282,17 @@ func (lp *LoadPoint) SetMaxCurrent(current float64) {
|
|||
}
|
||||
|
||||
// GetMinPower returns the min loadpoint power for a single phase
|
||||
func (lp *LoadPoint) GetMinPower() float64 {
|
||||
func (lp *Loadpoint) GetMinPower() float64 {
|
||||
return Voltage * lp.GetMinCurrent()
|
||||
}
|
||||
|
||||
// GetMaxPower returns the max loadpoint power taking vehicle capabilities and phase scaling into account
|
||||
func (lp *LoadPoint) GetMaxPower() float64 {
|
||||
func (lp *Loadpoint) GetMaxPower() float64 {
|
||||
return Voltage * lp.GetMaxCurrent() * float64(lp.maxActivePhases())
|
||||
}
|
||||
|
||||
// setRemainingDuration sets the estimated remaining charging duration
|
||||
func (lp *LoadPoint) setRemainingDuration(chargeRemainingDuration time.Duration) {
|
||||
func (lp *Loadpoint) setRemainingDuration(chargeRemainingDuration time.Duration) {
|
||||
if lp.chargeRemainingDuration != chargeRemainingDuration {
|
||||
lp.chargeRemainingDuration = chargeRemainingDuration
|
||||
lp.publish("chargeRemainingDuration", chargeRemainingDuration)
|
||||
|
|
@ -300,14 +300,14 @@ func (lp *LoadPoint) setRemainingDuration(chargeRemainingDuration time.Duration)
|
|||
}
|
||||
|
||||
// GetRemainingDuration is the estimated remaining charging duration
|
||||
func (lp *LoadPoint) GetRemainingDuration() time.Duration {
|
||||
func (lp *Loadpoint) GetRemainingDuration() time.Duration {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
return lp.chargeRemainingDuration
|
||||
}
|
||||
|
||||
// setRemainingEnergy sets the remaining charge energy in Wh
|
||||
func (lp *LoadPoint) setRemainingEnergy(chargeRemainingEnergy float64) {
|
||||
func (lp *Loadpoint) setRemainingEnergy(chargeRemainingEnergy float64) {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
|
||||
|
|
@ -318,21 +318,21 @@ func (lp *LoadPoint) setRemainingEnergy(chargeRemainingEnergy float64) {
|
|||
}
|
||||
|
||||
// GetRemainingEnergy is the remaining charge energy in Wh
|
||||
func (lp *LoadPoint) GetRemainingEnergy() float64 {
|
||||
func (lp *Loadpoint) GetRemainingEnergy() float64 {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
return lp.chargeRemainingEnergy
|
||||
}
|
||||
|
||||
// GetVehicle gets the active vehicle
|
||||
func (lp *LoadPoint) GetVehicle() api.Vehicle {
|
||||
func (lp *Loadpoint) GetVehicle() api.Vehicle {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
return lp.vehicle
|
||||
}
|
||||
|
||||
// SetVehicle sets the active vehicle
|
||||
func (lp *LoadPoint) SetVehicle(vehicle api.Vehicle) {
|
||||
func (lp *Loadpoint) SetVehicle(vehicle api.Vehicle) {
|
||||
// TODO develop universal locking approach
|
||||
// setActiveVehicle is protected by lock, hence no locking here
|
||||
|
||||
|
|
@ -347,7 +347,7 @@ func (lp *LoadPoint) SetVehicle(vehicle api.Vehicle) {
|
|||
}
|
||||
|
||||
// StartVehicleDetection allows triggering vehicle detection for debugging purposes
|
||||
func (lp *LoadPoint) StartVehicleDetection() {
|
||||
func (lp *Loadpoint) StartVehicleDetection() {
|
||||
// reset vehicle
|
||||
lp.setActiveVehicle(nil)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
// resetMeasuredPhases resets measured phases to unknown on vehicle disconnect, phase switch or phase api call
|
||||
func (lp *LoadPoint) resetMeasuredPhases() {
|
||||
func (lp *Loadpoint) resetMeasuredPhases() {
|
||||
lp.Lock()
|
||||
lp.measuredPhases = 0
|
||||
lp.Unlock()
|
||||
|
|
@ -16,7 +16,7 @@ func (lp *LoadPoint) resetMeasuredPhases() {
|
|||
}
|
||||
|
||||
// getMeasuredPhases provides synchronized access to measuredPhases
|
||||
func (lp *LoadPoint) getMeasuredPhases() int {
|
||||
func (lp *Loadpoint) getMeasuredPhases() int {
|
||||
lp.Lock()
|
||||
defer lp.Unlock()
|
||||
return lp.measuredPhases
|
||||
|
|
@ -44,7 +44,7 @@ func expect(phases int) int {
|
|||
|
||||
// activePhases returns the number of expectedly active phases for the meter.
|
||||
// If unknown for 1p3p chargers during startup it will assume 3p.
|
||||
func (lp *LoadPoint) activePhases() int {
|
||||
func (lp *Loadpoint) activePhases() int {
|
||||
physical := lp.GetPhases()
|
||||
vehicle := lp.getVehiclePhases()
|
||||
measured := lp.getMeasuredPhases()
|
||||
|
|
@ -53,7 +53,7 @@ func (lp *LoadPoint) activePhases() int {
|
|||
}
|
||||
|
||||
// maxActivePhases returns the maximum number of active phases for the meter.
|
||||
func (lp *LoadPoint) maxActivePhases() int {
|
||||
func (lp *Loadpoint) maxActivePhases() int {
|
||||
physical := lp.GetPhases()
|
||||
measured := lp.getMeasuredPhases()
|
||||
vehicle := lp.getVehiclePhases()
|
||||
|
|
@ -74,7 +74,7 @@ func (lp *LoadPoint) maxActivePhases() int {
|
|||
return min(expect(vehicle), expect(physical), expect(measured))
|
||||
}
|
||||
|
||||
func (lp *LoadPoint) getVehiclePhases() int {
|
||||
func (lp *Loadpoint) getVehiclePhases() int {
|
||||
if lp.vehicle != nil {
|
||||
return lp.vehicle.Phases()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ func TestMaxActivePhases(t *testing.T) {
|
|||
vehicle := mock.NewMockVehicle(ctrl)
|
||||
vehicle.EXPECT().Phases().Return(tc.vehicle).MinTimes(1)
|
||||
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
ConfiguredPhases: dflt, // fixed phases or default
|
||||
vehicle: vehicle,
|
||||
phases: tc.physical,
|
||||
|
|
@ -125,7 +125,7 @@ func TestMaxActivePhases(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func testScale(t *testing.T, lp *LoadPoint, power float64, direction string, tc testCase) {
|
||||
func testScale(t *testing.T, lp *Loadpoint, power float64, direction string, tc testCase) {
|
||||
act := lp.activePhases()
|
||||
max := lp.maxActivePhases()
|
||||
|
||||
|
|
@ -177,7 +177,7 @@ func TestPvScalePhases(t *testing.T) {
|
|||
vehicle := mock.NewMockVehicle(ctrl)
|
||||
vehicle.EXPECT().Phases().Return(tc.vehicle).MinTimes(1)
|
||||
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
log: util.NewLogger("foo"),
|
||||
bus: evbus.New(),
|
||||
clock: clock,
|
||||
|
|
@ -276,65 +276,65 @@ func TestPvScalePhasesTimer(t *testing.T) {
|
|||
availablePower float64
|
||||
toPhases int
|
||||
res bool
|
||||
prepare func(lp *LoadPoint)
|
||||
prepare func(lp *Loadpoint)
|
||||
}{
|
||||
// switch up from 1p/1p configured/active
|
||||
{"1/1->3, not enough power", 1, 1, 0, 1, false, nil},
|
||||
{"1/1->3, kickoff", 1, 1, 3 * Voltage * minA, 1, false, func(lp *LoadPoint) {
|
||||
{"1/1->3, kickoff", 1, 1, 3 * Voltage * minA, 1, false, func(lp *Loadpoint) {
|
||||
lp.phaseTimer = time.Time{}
|
||||
}},
|
||||
{"1/1->3, timer running", 1, 1, 3 * Voltage * minA, 1, false, func(lp *LoadPoint) {
|
||||
{"1/1->3, timer running", 1, 1, 3 * Voltage * minA, 1, false, func(lp *Loadpoint) {
|
||||
lp.phaseTimer = lp.clock.Now()
|
||||
}},
|
||||
{"1/1->3, timer elapsed", 1, 1, 3 * Voltage * minA, 3, true, func(lp *LoadPoint) {
|
||||
{"1/1->3, timer elapsed", 1, 1, 3 * Voltage * minA, 3, true, func(lp *Loadpoint) {
|
||||
lp.phaseTimer = lp.clock.Now().Add(-dt)
|
||||
}},
|
||||
|
||||
// omit to switch up (again) from 3p/1p configured/active
|
||||
{"3/1->3, not enough power", 3, 1, 0, 3, false, nil},
|
||||
{"3/1->3, kickoff", 3, 1, 3 * Voltage * minA, 3, false, func(lp *LoadPoint) {
|
||||
{"3/1->3, kickoff", 3, 1, 3 * Voltage * minA, 3, false, func(lp *Loadpoint) {
|
||||
lp.phaseTimer = time.Time{}
|
||||
}},
|
||||
{"3/1->3, timer running", 3, 1, 3 * Voltage * minA, 3, false, func(lp *LoadPoint) {
|
||||
{"3/1->3, timer running", 3, 1, 3 * Voltage * minA, 3, false, func(lp *Loadpoint) {
|
||||
lp.phaseTimer = lp.clock.Now()
|
||||
}},
|
||||
{"3/1->3, timer elapsed", 3, 1, 3 * Voltage * minA, 3, false, func(lp *LoadPoint) {
|
||||
{"3/1->3, timer elapsed", 3, 1, 3 * Voltage * minA, 3, false, func(lp *Loadpoint) {
|
||||
lp.phaseTimer = lp.clock.Now().Add(-dt)
|
||||
}},
|
||||
|
||||
// omit to switch down from 3p/1p configured/active
|
||||
{"3/1->1, not enough power", 3, 1, 0, 3, false, nil},
|
||||
{"3/1->1, kickoff", 3, 1, 1 * Voltage * minA, 3, false, func(lp *LoadPoint) {
|
||||
{"3/1->1, kickoff", 3, 1, 1 * Voltage * minA, 3, false, func(lp *Loadpoint) {
|
||||
lp.phaseTimer = time.Time{}
|
||||
}},
|
||||
{"3/1->1, timer running", 3, 1, 1 * Voltage * minA, 3, false, func(lp *LoadPoint) {
|
||||
{"3/1->1, timer running", 3, 1, 1 * Voltage * minA, 3, false, func(lp *Loadpoint) {
|
||||
lp.phaseTimer = lp.clock.Now()
|
||||
}},
|
||||
{"3/1->1, timer elapsed", 3, 1, 1 * Voltage * minA, 3, false, func(lp *LoadPoint) {
|
||||
{"3/1->1, timer elapsed", 3, 1, 1 * Voltage * minA, 3, false, func(lp *Loadpoint) {
|
||||
lp.phaseTimer = lp.clock.Now().Add(-dt)
|
||||
}},
|
||||
|
||||
// switch down from 3p/3p configured/active
|
||||
{"3/3->1, enough power", 3, 3, 1 * Voltage * maxA, 3, false, nil},
|
||||
{"3/3->1, kickoff", 3, 3, 1 * Voltage * maxA, 3, false, func(lp *LoadPoint) {
|
||||
{"3/3->1, kickoff", 3, 3, 1 * Voltage * maxA, 3, false, func(lp *Loadpoint) {
|
||||
lp.phaseTimer = time.Time{}
|
||||
}},
|
||||
{"3/3->1, timer running", 3, 3, 1 * Voltage * maxA, 3, false, func(lp *LoadPoint) {
|
||||
{"3/3->1, timer running", 3, 3, 1 * Voltage * maxA, 3, false, func(lp *Loadpoint) {
|
||||
lp.phaseTimer = lp.clock.Now()
|
||||
}},
|
||||
{"3/3->1, timer elapsed", 3, 3, 1 * Voltage * maxA, 1, true, func(lp *LoadPoint) {
|
||||
{"3/3->1, timer elapsed", 3, 3, 1 * Voltage * maxA, 1, true, func(lp *Loadpoint) {
|
||||
lp.phaseTimer = lp.clock.Now().Add(-dt)
|
||||
}},
|
||||
|
||||
// error states from 1p/3p misconfig - no correction for time being (stay at 1p)
|
||||
{"1/3->1, enough power", 1, 3, 1 * Voltage * maxA, 1, false, nil},
|
||||
{"1/3->1, kickoff, correct phase setting", 1, 3, 1 * Voltage * maxA, 1, false, func(lp *LoadPoint) {
|
||||
{"1/3->1, kickoff, correct phase setting", 1, 3, 1 * Voltage * maxA, 1, false, func(lp *Loadpoint) {
|
||||
lp.phaseTimer = time.Time{}
|
||||
}},
|
||||
{"1/3->1, timer running, correct phase setting", 1, 3, 1 * Voltage * maxA, 1, false, func(lp *LoadPoint) {
|
||||
{"1/3->1, timer running, correct phase setting", 1, 3, 1 * Voltage * maxA, 1, false, func(lp *Loadpoint) {
|
||||
lp.phaseTimer = lp.clock.Now()
|
||||
}},
|
||||
{"1/3->1, switch not executed", 1, 3, 1 * Voltage * maxA, 1, false, func(lp *LoadPoint) {
|
||||
{"1/3->1, switch not executed", 1, 3, 1 * Voltage * maxA, 1, false, func(lp *Loadpoint) {
|
||||
lp.phaseTimer = lp.clock.Now().Add(-dt)
|
||||
}},
|
||||
}
|
||||
|
|
@ -344,7 +344,7 @@ func TestPvScalePhasesTimer(t *testing.T) {
|
|||
clock := clock.NewMock()
|
||||
clock.Add(time.Hour) // avoid time.IsZero
|
||||
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
log: util.NewLogger("foo"),
|
||||
clock: clock,
|
||||
charger: charger,
|
||||
|
|
@ -402,7 +402,7 @@ func TestScalePhasesIfAvailable(t *testing.T) {
|
|||
plainCharger := mock.NewMockCharger(ctrl)
|
||||
phaseCharger := mock.NewMockPhaseSwitcher(ctrl)
|
||||
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
log: util.NewLogger("foo"),
|
||||
clock: clock.NewMock(),
|
||||
charger: struct {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"github.com/evcc-io/evcc/core/db"
|
||||
)
|
||||
|
||||
func (lp *LoadPoint) chargeMeterTotal() float64 {
|
||||
func (lp *Loadpoint) chargeMeterTotal() float64 {
|
||||
m, ok := lp.chargeMeter.(api.MeterEnergy)
|
||||
if !ok {
|
||||
return 0
|
||||
|
|
@ -24,7 +24,7 @@ func (lp *LoadPoint) chargeMeterTotal() float64 {
|
|||
|
||||
// createSession creates a charging session. The created timestamp is empty until set by evChargeStartHandler.
|
||||
// The session is not persisted yet. That will only happen when stopSession is called.
|
||||
func (lp *LoadPoint) createSession() {
|
||||
func (lp *Loadpoint) createSession() {
|
||||
// test guard
|
||||
if lp.db == nil || lp.session != nil {
|
||||
return
|
||||
|
|
@ -44,7 +44,7 @@ func (lp *LoadPoint) createSession() {
|
|||
}
|
||||
|
||||
// stopSession ends a charging session segment and persists the session.
|
||||
func (lp *LoadPoint) stopSession() {
|
||||
func (lp *Loadpoint) stopSession() {
|
||||
// test guard
|
||||
if lp.db == nil || lp.session == nil {
|
||||
return
|
||||
|
|
@ -68,7 +68,7 @@ func (lp *LoadPoint) stopSession() {
|
|||
type sessionOption func(*db.Session)
|
||||
|
||||
// updateSession updates any parameter of a charging session and persists the session.
|
||||
func (lp *LoadPoint) updateSession(opts ...sessionOption) {
|
||||
func (lp *Loadpoint) updateSession(opts ...sessionOption) {
|
||||
// test guard
|
||||
if lp.db == nil || lp.session == nil {
|
||||
return
|
||||
|
|
@ -84,7 +84,7 @@ func (lp *LoadPoint) updateSession(opts ...sessionOption) {
|
|||
}
|
||||
|
||||
// clearSession clears the charging session without persisting it.
|
||||
func (lp *LoadPoint) clearSession() {
|
||||
func (lp *Loadpoint) clearSession() {
|
||||
// test guard
|
||||
if lp.db == nil {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -33,10 +33,10 @@ func (n *Null) ChargingTime() (time.Duration, error) {
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
func createChannels(t *testing.T) (chan util.Param, chan push.Event, chan *LoadPoint) {
|
||||
func createChannels(t *testing.T) (chan util.Param, chan push.Event, chan *Loadpoint) {
|
||||
uiChan := make(chan util.Param)
|
||||
pushChan := make(chan push.Event)
|
||||
lpChan := make(chan *LoadPoint)
|
||||
lpChan := make(chan *Loadpoint)
|
||||
|
||||
log := false
|
||||
go func() {
|
||||
|
|
@ -61,13 +61,13 @@ func createChannels(t *testing.T) (chan util.Param, chan push.Event, chan *LoadP
|
|||
return uiChan, pushChan, lpChan
|
||||
}
|
||||
|
||||
func attachChannels(lp *LoadPoint, uiChan chan util.Param, pushChan chan push.Event, lpChan chan *LoadPoint) {
|
||||
func attachChannels(lp *Loadpoint, uiChan chan util.Param, pushChan chan push.Event, lpChan chan *Loadpoint) {
|
||||
lp.uiChan = uiChan
|
||||
lp.pushChan = pushChan
|
||||
lp.lpChan = lpChan
|
||||
}
|
||||
|
||||
func attachListeners(t *testing.T, lp *LoadPoint) {
|
||||
func attachListeners(t *testing.T, lp *Loadpoint) {
|
||||
Voltage = 230 // V
|
||||
|
||||
if charger, ok := lp.charger.(*mock.MockCharger); ok && charger != nil {
|
||||
|
|
@ -80,7 +80,7 @@ func attachListeners(t *testing.T, lp *LoadPoint) {
|
|||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
lp := NewLoadPoint(util.NewLogger("foo"))
|
||||
lp := NewLoadpoint(util.NewLogger("foo"))
|
||||
|
||||
if lp.phases != 0 {
|
||||
t.Errorf("Phases %v", lp.phases)
|
||||
|
|
@ -154,7 +154,7 @@ func TestUpdatePowerZero(t *testing.T) {
|
|||
ctrl := gomock.NewController(t)
|
||||
charger := mock.NewMockCharger(ctrl)
|
||||
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
log: util.NewLogger("foo"),
|
||||
bus: evbus.New(),
|
||||
clock: clck,
|
||||
|
|
@ -306,7 +306,7 @@ func TestPVHysteresis(t *testing.T) {
|
|||
charger := mock.NewMockCharger(ctrl)
|
||||
|
||||
Voltage = 100
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
log: util.NewLogger("foo"),
|
||||
clock: clck,
|
||||
charger: charger,
|
||||
|
|
@ -355,7 +355,7 @@ func TestPVHysteresisForStatusOtherThanC(t *testing.T) {
|
|||
ctrl := gomock.NewController(t)
|
||||
|
||||
Voltage = 100
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
log: util.NewLogger("foo"),
|
||||
clock: clck,
|
||||
MinCurrent: minA,
|
||||
|
|
@ -378,7 +378,7 @@ func TestPVHysteresisForStatusOtherThanC(t *testing.T) {
|
|||
ctrl.Finish()
|
||||
}
|
||||
|
||||
func TestDisableAndEnableAtTargetSoC(t *testing.T) {
|
||||
func TestDisableAndEnableAtTargetSoc(t *testing.T) {
|
||||
clock := clock.NewMock()
|
||||
ctrl := gomock.NewController(t)
|
||||
charger := mock.NewMockCharger(ctrl)
|
||||
|
|
@ -389,7 +389,7 @@ func TestDisableAndEnableAtTargetSoC(t *testing.T) {
|
|||
vehicle.EXPECT().Phases().Return(0).AnyTimes()
|
||||
socEstimator := soc.NewEstimator(util.NewLogger("foo"), charger, vehicle, false)
|
||||
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
log: util.NewLogger("foo"),
|
||||
bus: evbus.New(),
|
||||
clock: clock,
|
||||
|
|
@ -402,10 +402,10 @@ func TestDisableAndEnableAtTargetSoC(t *testing.T) {
|
|||
// coordinator: coordinator.NewDummy(), // silence nil panics
|
||||
MinCurrent: minA,
|
||||
MaxCurrent: maxA,
|
||||
vehicle: vehicle, // needed for targetSoC check
|
||||
vehicle: vehicle, // needed for targetSoc check
|
||||
socEstimator: socEstimator, // instead of vehicle: vehicle,
|
||||
Mode: api.ModeNow,
|
||||
SoC: SoCConfig{
|
||||
Soc: SocConfig{
|
||||
target: 90,
|
||||
Poll: PollConfig{
|
||||
Mode: pollConnected, // allow polling when connected
|
||||
|
|
@ -421,7 +421,7 @@ func TestDisableAndEnableAtTargetSoC(t *testing.T) {
|
|||
lp.status = api.StatusC
|
||||
|
||||
t.Log("charging below soc target")
|
||||
vehicle.EXPECT().SoC().Return(85.0, nil)
|
||||
vehicle.EXPECT().Soc().Return(85.0, nil)
|
||||
charger.EXPECT().Status().Return(api.StatusC, nil)
|
||||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
charger.EXPECT().MaxCurrent(int64(maxA)).Return(nil)
|
||||
|
|
@ -429,7 +429,7 @@ func TestDisableAndEnableAtTargetSoC(t *testing.T) {
|
|||
|
||||
t.Log("charging above target - soc deactivates charger")
|
||||
clock.Add(5 * time.Minute)
|
||||
vehicle.EXPECT().SoC().Return(90.0, nil)
|
||||
vehicle.EXPECT().Soc().Return(90.0, nil)
|
||||
charger.EXPECT().Status().Return(api.StatusC, nil)
|
||||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
charger.EXPECT().Enable(false).Return(nil)
|
||||
|
|
@ -437,7 +437,7 @@ func TestDisableAndEnableAtTargetSoC(t *testing.T) {
|
|||
|
||||
t.Log("deactivated charger changes status to B")
|
||||
clock.Add(5 * time.Minute)
|
||||
vehicle.EXPECT().SoC().Return(95.0, nil)
|
||||
vehicle.EXPECT().Soc().Return(95.0, nil)
|
||||
charger.EXPECT().Status().Return(api.StatusB, nil)
|
||||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
lp.Update(-5000, false, false)
|
||||
|
|
@ -450,7 +450,7 @@ func TestDisableAndEnableAtTargetSoC(t *testing.T) {
|
|||
|
||||
t.Log("soc has fallen below target - soc update timer expired")
|
||||
clock.Add(pollInterval)
|
||||
vehicle.EXPECT().SoC().Return(85.0, nil)
|
||||
vehicle.EXPECT().Soc().Return(85.0, nil)
|
||||
charger.EXPECT().Status().Return(api.StatusB, nil)
|
||||
charger.EXPECT().Enabled().Return(lp.enabled, nil)
|
||||
charger.EXPECT().Enable(true).Return(nil)
|
||||
|
|
@ -464,7 +464,7 @@ func TestSetModeAndSocAtDisconnect(t *testing.T) {
|
|||
ctrl := gomock.NewController(t)
|
||||
charger := mock.NewMockCharger(ctrl)
|
||||
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
log: util.NewLogger("foo"),
|
||||
bus: evbus.New(),
|
||||
clock: clock,
|
||||
|
|
@ -477,7 +477,7 @@ func TestSetModeAndSocAtDisconnect(t *testing.T) {
|
|||
MaxCurrent: maxA,
|
||||
status: api.StatusC,
|
||||
Mode: api.ModeOff,
|
||||
SoC: SoCConfig{
|
||||
Soc: SocConfig{
|
||||
target: 70,
|
||||
},
|
||||
ResetOnDisconnect: true,
|
||||
|
|
@ -511,7 +511,7 @@ func TestSetModeAndSocAtDisconnect(t *testing.T) {
|
|||
}
|
||||
|
||||
// cacheExpecter can be used to verify asynchronously written values from cache
|
||||
func cacheExpecter(t *testing.T, lp *LoadPoint) (*util.Cache, func(key string, val interface{})) {
|
||||
func cacheExpecter(t *testing.T, lp *Loadpoint) (*util.Cache, func(key string, val interface{})) {
|
||||
// attach cache for verifying values
|
||||
paramC := make(chan util.Param)
|
||||
lp.uiChan = paramC
|
||||
|
|
@ -536,7 +536,7 @@ func TestChargedEnergyAtDisconnect(t *testing.T) {
|
|||
charger := mock.NewMockCharger(ctrl)
|
||||
rater := mock.NewMockChargeRater(ctrl)
|
||||
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
log: util.NewLogger("foo"),
|
||||
bus: evbus.New(),
|
||||
clock: clock,
|
||||
|
|
@ -608,7 +608,7 @@ func TestChargedEnergyAtDisconnect(t *testing.T) {
|
|||
ctrl.Finish()
|
||||
}
|
||||
|
||||
func TestTargetSoC(t *testing.T) {
|
||||
func TestTargetSoc(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
vhc := mock.NewMockVehicle(ctrl)
|
||||
|
||||
|
|
@ -634,9 +634,9 @@ func TestTargetSoC(t *testing.T) {
|
|||
for _, tc := range tc {
|
||||
t.Logf("%+v", tc)
|
||||
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
vehicle: tc.vehicle,
|
||||
SoC: SoCConfig{
|
||||
Soc: SocConfig{
|
||||
target: tc.target,
|
||||
},
|
||||
vehicleSoc: tc.soc,
|
||||
|
|
@ -648,15 +648,15 @@ func TestTargetSoC(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSoCPoll(t *testing.T) {
|
||||
func TestSocPoll(t *testing.T) {
|
||||
clock := clock.NewMock()
|
||||
tRefresh := pollInterval
|
||||
tNoRefresh := pollInterval / 2
|
||||
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
clock: clock,
|
||||
log: util.NewLogger("foo"),
|
||||
SoC: SoCConfig{
|
||||
Soc: SocConfig{
|
||||
Poll: PollConfig{
|
||||
Interval: time.Hour,
|
||||
},
|
||||
|
|
@ -718,7 +718,7 @@ func TestSoCPoll(t *testing.T) {
|
|||
clock.Add(tc.dt)
|
||||
}
|
||||
|
||||
lp.SoC.Poll.Mode = tc.mode
|
||||
lp.Soc.Poll.Mode = tc.mode
|
||||
lp.status = tc.status
|
||||
|
||||
res := lp.socPollAllowed()
|
||||
|
|
@ -733,7 +733,7 @@ func TestSoCPoll(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestMinSoC(t *testing.T) {
|
||||
func TestMinSoc(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
vhc := mock.NewMockVehicle(ctrl)
|
||||
|
||||
|
|
@ -758,9 +758,9 @@ func TestMinSoC(t *testing.T) {
|
|||
for _, tc := range tc {
|
||||
t.Logf("%+v", tc)
|
||||
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
vehicle: tc.vehicle,
|
||||
SoC: SoCConfig{
|
||||
Soc: SocConfig{
|
||||
min: tc.min,
|
||||
},
|
||||
vehicleSoc: tc.soc,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPublishSoCAndRange(t *testing.T) {
|
||||
func TestPublishSocAndRange(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
clck := clock.NewMock()
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ func TestPublishSoCAndRange(t *testing.T) {
|
|||
vehicle.EXPECT().OnIdentified().AnyTimes()
|
||||
|
||||
log := util.NewLogger("foo")
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
log: log,
|
||||
bus: evbus.New(),
|
||||
clock: clck,
|
||||
|
|
@ -66,15 +66,15 @@ func TestPublishSoCAndRange(t *testing.T) {
|
|||
lp.status = tc.status
|
||||
|
||||
assert.True(t, lp.socPollAllowed())
|
||||
vehicle.EXPECT().SoC().Return(0.0, errors.New("foo"))
|
||||
lp.publishSoCAndRange()
|
||||
vehicle.EXPECT().Soc().Return(0.0, errors.New("foo"))
|
||||
lp.publishSocAndRange()
|
||||
|
||||
clck.Add(time.Second)
|
||||
assert.Equal(t, tc.allowed, lp.socPollAllowed())
|
||||
if tc.allowed {
|
||||
vehicle.EXPECT().SoC().Return(0.0, errors.New("foo"))
|
||||
vehicle.EXPECT().Soc().Return(0.0, errors.New("foo"))
|
||||
}
|
||||
lp.publishSoCAndRange()
|
||||
lp.publishSocAndRange()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ func TestVehicleDetectByID(t *testing.T) {
|
|||
for _, tc := range tc {
|
||||
t.Logf("%+v", tc)
|
||||
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
log: util.NewLogger("foo"),
|
||||
}
|
||||
|
||||
|
|
@ -157,8 +157,8 @@ func TestDefaultVehicle(t *testing.T) {
|
|||
dflt.EXPECT().Phases().AnyTimes()
|
||||
dflt.EXPECT().OnIdentified().Return(api.ActionConfig{
|
||||
Mode: &mode,
|
||||
MinSoC: &minsoc,
|
||||
TargetSoC: &targetsoc,
|
||||
MinSoc: &minsoc,
|
||||
TargetSoc: &targetsoc,
|
||||
}).AnyTimes()
|
||||
|
||||
vehicle := mock.NewMockVehicle(ctrl)
|
||||
|
|
@ -168,7 +168,7 @@ func TestDefaultVehicle(t *testing.T) {
|
|||
vehicle.EXPECT().Phases().AnyTimes()
|
||||
vehicle.EXPECT().OnIdentified().AnyTimes()
|
||||
|
||||
lp := NewLoadPoint(util.NewLogger("foo"))
|
||||
lp := NewLoadpoint(util.NewLogger("foo"))
|
||||
lp.defaultVehicle = dflt
|
||||
lp.collectDefaults()
|
||||
|
||||
|
|
@ -201,10 +201,10 @@ func TestDefaultVehicle(t *testing.T) {
|
|||
if m := lp.GetMode(); m != mode {
|
||||
t.Errorf("expected mode %v, got %v", mode, m)
|
||||
}
|
||||
if s := lp.GetMinSoC(); s != minsoc {
|
||||
if s := lp.GetMinSoc(); s != minsoc {
|
||||
t.Errorf("expected minsoc %v, got %v", minsoc, s)
|
||||
}
|
||||
if s := lp.GetTargetSoC(); s != targetsoc {
|
||||
if s := lp.GetTargetSoc(); s != targetsoc {
|
||||
t.Errorf("expected targetsoc %v, got %v", targetsoc, s)
|
||||
}
|
||||
if m := lp.onDisconnect.Mode; *m != api.ModeOff {
|
||||
|
|
@ -231,17 +231,17 @@ func TestDefaultVehicle(t *testing.T) {
|
|||
func TestApplyVehicleDefaults(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
|
||||
newConfig := func(mode api.ChargeMode, minCurrent, maxCurrent float64, minSoC, targetSoC int) api.ActionConfig {
|
||||
newConfig := func(mode api.ChargeMode, minCurrent, maxCurrent float64, minSoc, targetSoc int) api.ActionConfig {
|
||||
return api.ActionConfig{
|
||||
Mode: &mode,
|
||||
MinCurrent: &minCurrent,
|
||||
MaxCurrent: &maxCurrent,
|
||||
MinSoC: &minSoC,
|
||||
TargetSoC: &targetSoC,
|
||||
MinSoc: &minSoc,
|
||||
TargetSoc: &targetSoc,
|
||||
}
|
||||
}
|
||||
|
||||
assertConfig := func(lp *LoadPoint, conf api.ActionConfig) {
|
||||
assertConfig := func(lp *Loadpoint, conf api.ActionConfig) {
|
||||
if lp.Mode != *conf.Mode {
|
||||
t.Errorf("expected mode %v, got %v", *conf.Mode, lp.Mode)
|
||||
}
|
||||
|
|
@ -251,11 +251,11 @@ func TestApplyVehicleDefaults(t *testing.T) {
|
|||
if lp.MaxCurrent != *conf.MaxCurrent {
|
||||
t.Errorf("expected maxCurrent %v, got %v", *conf.MaxCurrent, lp.MaxCurrent)
|
||||
}
|
||||
if lp.SoC.min != *conf.MinSoC {
|
||||
t.Errorf("expected minSoC %v, got %v", *conf.MinSoC, lp.SoC.min)
|
||||
if lp.Soc.min != *conf.MinSoc {
|
||||
t.Errorf("expected minSoc %v, got %v", *conf.MinSoc, lp.Soc.min)
|
||||
}
|
||||
if lp.SoC.target != *conf.TargetSoC {
|
||||
t.Errorf("expected targetSoC %v, got %v", *conf.TargetSoC, lp.SoC.target)
|
||||
if lp.Soc.target != *conf.TargetSoc {
|
||||
t.Errorf("expected targetSoc %v, got %v", *conf.TargetSoc, lp.Soc.target)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -272,7 +272,7 @@ func TestApplyVehicleDefaults(t *testing.T) {
|
|||
vehicle.EXPECT().Phases().AnyTimes()
|
||||
vehicle.EXPECT().OnIdentified().Return(oi).AnyTimes()
|
||||
|
||||
lp := NewLoadPoint(util.NewLogger("foo"))
|
||||
lp := NewLoadpoint(util.NewLogger("foo"))
|
||||
|
||||
// populate channels
|
||||
x, y, z := createChannels(t)
|
||||
|
|
@ -282,7 +282,7 @@ func TestApplyVehicleDefaults(t *testing.T) {
|
|||
lp.ResetOnDisconnect = true
|
||||
|
||||
// check loadpoint default currents can't be violated
|
||||
lp.applyAction(newConfig(*od.Mode, 5, 17, *od.MinSoC, *od.TargetSoC))
|
||||
lp.applyAction(newConfig(*od.Mode, 5, 17, *od.MinSoc, *od.TargetSoc))
|
||||
assertConfig(lp, od)
|
||||
|
||||
// vehicle identified
|
||||
|
|
@ -340,12 +340,12 @@ func TestReconnectVehicle(t *testing.T) {
|
|||
vehicle.MockVehicle.EXPECT().Phases().AnyTimes()
|
||||
vehicle.MockVehicle.EXPECT().OnIdentified().AnyTimes()
|
||||
vehicle.MockVehicle.EXPECT().Identifiers().AnyTimes().Return(tc.vehicleId)
|
||||
vehicle.MockVehicle.EXPECT().SoC().Return(0.0, nil).AnyTimes()
|
||||
vehicle.MockVehicle.EXPECT().Soc().Return(0.0, nil).AnyTimes()
|
||||
|
||||
charger := mock.NewMockCharger(ctrl)
|
||||
charger.EXPECT().Status().Return(api.StatusB, nil).AnyTimes()
|
||||
|
||||
lp := &LoadPoint{
|
||||
lp := &Loadpoint{
|
||||
log: util.NewLogger("foo"),
|
||||
bus: evbus.New(),
|
||||
clock: clck,
|
||||
|
|
|
|||
34
core/site.go
34
core/site.go
|
|
@ -22,7 +22,7 @@ import (
|
|||
|
||||
const standbyPower = 10 // consider less than 10W as charger in standby
|
||||
|
||||
// Updater abstracts the LoadPoint implementation for testing
|
||||
// Updater abstracts the Loadpoint implementation for testing
|
||||
type Updater interface {
|
||||
Update(availablePower float64, cheapRate, batteryBuffered bool)
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ type batteryMeasurement struct {
|
|||
// Site is the main configuration container. A site can host multiple loadpoints.
|
||||
type Site struct {
|
||||
uiChan chan<- util.Param // client push messages
|
||||
lpUpdateChan chan *LoadPoint
|
||||
lpUpdateChan chan *Loadpoint
|
||||
|
||||
*Health
|
||||
|
||||
|
|
@ -53,8 +53,8 @@ type Site struct {
|
|||
Voltage float64 `mapstructure:"voltage"` // Operating voltage. 230V for Germany.
|
||||
ResidualPower float64 `mapstructure:"residualPower"` // PV meter only: household usage. Grid meter: household safety margin
|
||||
Meters MetersConfig // Meter references
|
||||
PrioritySoC float64 `mapstructure:"prioritySoC"` // prefer battery up to this SoC
|
||||
BufferSoC float64 `mapstructure:"bufferSoC"` // ignore battery above this SoC
|
||||
PrioritySoc float64 `mapstructure:"prioritySoc"` // prefer battery up to this Soc
|
||||
BufferSoc float64 `mapstructure:"bufferSoc"` // ignore battery above this Soc
|
||||
MaxGridSupplyWhileBatteryCharging float64 `mapstructure:"maxGridSupplyWhileBatteryCharging"` // ignore battery charging if AC consumption is above this value
|
||||
|
||||
// meters
|
||||
|
|
@ -63,7 +63,7 @@ type Site struct {
|
|||
batteryMeters []api.Meter // Battery charging meters
|
||||
|
||||
tariffs tariff.Tariffs // Tariff
|
||||
loadpoints []*LoadPoint // Loadpoints
|
||||
loadpoints []*Loadpoint // Loadpoints
|
||||
coordinator *coordinator.Coordinator // Savings
|
||||
savings *Savings // Savings
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ func NewSiteFromConfig(
|
|||
log *util.Logger,
|
||||
cp configProvider,
|
||||
other map[string]interface{},
|
||||
loadpoints []*LoadPoint,
|
||||
loadpoints []*Loadpoint,
|
||||
vehicles []api.Vehicle,
|
||||
tariffs tariff.Tariffs,
|
||||
) (*Site, error) {
|
||||
|
|
@ -208,8 +208,8 @@ func NewSite() *Site {
|
|||
return lp
|
||||
}
|
||||
|
||||
// LoadPoints returns the array of associated loadpoints
|
||||
func (site *Site) LoadPoints() []loadpoint.API {
|
||||
// Loadpoints returns the array of associated loadpoints
|
||||
func (site *Site) Loadpoints() []loadpoint.API {
|
||||
res := make([]loadpoint.API, len(site.loadpoints))
|
||||
for id, lp := range site.loadpoints {
|
||||
res[id] = lp
|
||||
|
|
@ -401,7 +401,7 @@ func (site *Site) updateMeters() error {
|
|||
site.log.ERROR.Printf("battery %d power: %v", i+1, err)
|
||||
}
|
||||
|
||||
soc, err := meter.(api.Battery).SoC()
|
||||
soc, err := meter.(api.Battery).Soc()
|
||||
|
||||
if err == nil {
|
||||
site.batterySoc += soc
|
||||
|
|
@ -482,14 +482,14 @@ func (site *Site) sitePower(totalChargePower float64) (float64, error) {
|
|||
site.Lock()
|
||||
defer site.Unlock()
|
||||
|
||||
// if battery is charging below prioritySoC give it priority
|
||||
if site.batterySoc < site.PrioritySoC && batteryPower < 0 {
|
||||
// if battery is charging below prioritySoc give it priority
|
||||
if site.batterySoc < site.PrioritySoc && batteryPower < 0 {
|
||||
site.log.DEBUG.Printf("giving priority to battery charging at soc: %.0f%%", site.batterySoc)
|
||||
batteryPower = 0
|
||||
}
|
||||
|
||||
// if battery is discharging above bufferSoC ignore it
|
||||
site.batteryBuffered = batteryPower > 0 && site.BufferSoC > 0 && site.batterySoc > site.BufferSoC
|
||||
site.batteryBuffered = batteryPower > 0 && site.BufferSoc > 0 && site.batterySoc > site.BufferSoc
|
||||
}
|
||||
|
||||
sitePower := sitePower(site.log, site.MaxGridSupplyWhileBatteryCharging, site.gridPower, batteryPower, site.ResidualPower)
|
||||
|
|
@ -544,8 +544,8 @@ func (site *Site) prepare() {
|
|||
site.publish("gridConfigured", site.gridMeter != nil)
|
||||
site.publish("pvConfigured", len(site.pvMeters) > 0)
|
||||
site.publish("batteryConfigured", len(site.batteryMeters) > 0)
|
||||
site.publish("bufferSoC", site.BufferSoC)
|
||||
site.publish("prioritySoC", site.PrioritySoC)
|
||||
site.publish("bufferSoc", site.BufferSoc)
|
||||
site.publish("prioritySoc", site.PrioritySoc)
|
||||
site.publish("residualPower", site.ResidualPower)
|
||||
|
||||
site.publish("currency", site.tariffs.Currency.String())
|
||||
|
|
@ -557,7 +557,7 @@ func (site *Site) prepare() {
|
|||
// Prepare attaches communication channels to site and loadpoints
|
||||
func (site *Site) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Event) {
|
||||
site.uiChan = uiChan
|
||||
site.lpUpdateChan = make(chan *LoadPoint, 1) // 1 capacity to avoid deadlock
|
||||
site.lpUpdateChan = make(chan *Loadpoint, 1) // 1 capacity to avoid deadlock
|
||||
|
||||
site.prepare()
|
||||
|
||||
|
|
@ -570,10 +570,10 @@ func (site *Site) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Event)
|
|||
for {
|
||||
select {
|
||||
case param := <-lpUIChan:
|
||||
param.LoadPoint = &id
|
||||
param.Loadpoint = &id
|
||||
uiChan <- param
|
||||
case ev := <-lpPushChan:
|
||||
ev.LoadPoint = &id
|
||||
ev.Loadpoint = &id
|
||||
pushChan <- ev
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,16 +8,16 @@ import (
|
|||
// API is the external site API
|
||||
type API interface {
|
||||
Healthy() bool
|
||||
LoadPoints() []loadpoint.API
|
||||
Loadpoints() []loadpoint.API
|
||||
|
||||
//
|
||||
// battery
|
||||
//
|
||||
|
||||
GetBufferSoC() float64
|
||||
SetBufferSoC(float64) error
|
||||
GetPrioritySoC() float64
|
||||
SetPrioritySoC(float64) error
|
||||
GetBufferSoc() float64
|
||||
SetBufferSoc(float64) error
|
||||
GetPrioritySoc() float64
|
||||
SetPrioritySoc(float64) error
|
||||
|
||||
//
|
||||
// power and energy
|
||||
|
|
|
|||
|
|
@ -9,15 +9,15 @@ import (
|
|||
|
||||
var _ site.API = (*Site)(nil)
|
||||
|
||||
// GetPrioritySoC returns the PrioritySoC
|
||||
func (site *Site) GetPrioritySoC() float64 {
|
||||
// GetPrioritySoc returns the PrioritySoc
|
||||
func (site *Site) GetPrioritySoc() float64 {
|
||||
site.Lock()
|
||||
defer site.Unlock()
|
||||
return site.PrioritySoC
|
||||
return site.PrioritySoc
|
||||
}
|
||||
|
||||
// SetPrioritySoC sets the PrioritySoC
|
||||
func (site *Site) SetPrioritySoC(soc float64) error {
|
||||
// SetPrioritySoc sets the PrioritySoc
|
||||
func (site *Site) SetPrioritySoc(soc float64) error {
|
||||
site.Lock()
|
||||
defer site.Unlock()
|
||||
|
||||
|
|
@ -25,21 +25,21 @@ func (site *Site) SetPrioritySoC(soc float64) error {
|
|||
return errors.New("battery not configured")
|
||||
}
|
||||
|
||||
site.PrioritySoC = soc
|
||||
site.publish("prioritySoC", site.PrioritySoC)
|
||||
site.PrioritySoc = soc
|
||||
site.publish("prioritySoc", site.PrioritySoc)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBufferSoC returns the BufferSoC
|
||||
func (site *Site) GetBufferSoC() float64 {
|
||||
// GetBufferSoc returns the BufferSoc
|
||||
func (site *Site) GetBufferSoc() float64 {
|
||||
site.Lock()
|
||||
defer site.Unlock()
|
||||
return site.BufferSoC
|
||||
return site.BufferSoc
|
||||
}
|
||||
|
||||
// SetBufferSoC sets the BufferSoC
|
||||
func (site *Site) SetBufferSoC(soc float64) error {
|
||||
// SetBufferSoc sets the BufferSoc
|
||||
func (site *Site) SetBufferSoc(soc float64) error {
|
||||
site.Lock()
|
||||
defer site.Unlock()
|
||||
|
||||
|
|
@ -47,8 +47,8 @@ func (site *Site) SetBufferSoC(soc float64) error {
|
|||
return errors.New("battery not configured")
|
||||
}
|
||||
|
||||
site.BufferSoC = soc
|
||||
site.publish("bufferSoC", site.BufferSoC)
|
||||
site.BufferSoc = soc
|
||||
site.publish("bufferSoc", site.BufferSoc)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import (
|
|||
const chargeEfficiency = 0.9 // assume charge 90% efficiency
|
||||
|
||||
// Estimator provides vehicle soc and charge duration
|
||||
// Vehicle SoC can be estimated to provide more granularity
|
||||
// Vehicle Soc can be estimated to provide more granularity
|
||||
type Estimator struct {
|
||||
log *util.Logger
|
||||
charger api.Charger
|
||||
|
|
@ -21,12 +21,12 @@ type Estimator struct {
|
|||
|
||||
capacity float64 // vehicle capacity in Wh cached to simplify testing
|
||||
virtualCapacity float64 // estimated virtual vehicle capacity in Wh
|
||||
vehicleSoc float64 // estimated vehicle SoC
|
||||
initialSoc float64 // first received valid vehicle SoC
|
||||
initialEnergy float64 // energy counter at first valid SoC
|
||||
prevSoc float64 // previous vehicle SoC in %
|
||||
vehicleSoc float64 // estimated vehicle Soc
|
||||
initialSoc float64 // first received valid vehicle Soc
|
||||
initialEnergy float64 // energy counter at first valid Soc
|
||||
prevSoc float64 // previous vehicle Soc in %
|
||||
prevChargedEnergy float64 // previous charged energy in Wh
|
||||
energyPerSocStep float64 // Energy per SoC percent in Wh
|
||||
energyPerSocStep float64 // Energy per Soc percent in Wh
|
||||
}
|
||||
|
||||
// NewEstimator creates new estimator
|
||||
|
|
@ -53,9 +53,9 @@ func (s *Estimator) Reset() {
|
|||
s.energyPerSocStep = s.virtualCapacity / 100
|
||||
}
|
||||
|
||||
// AssumedChargeDuration estimates charge duration up to targetSoC based on virtual capacity
|
||||
func (s *Estimator) AssumedChargeDuration(targetSoC int, chargePower float64) time.Duration {
|
||||
percentRemaining := float64(targetSoC) - s.vehicleSoc
|
||||
// AssumedChargeDuration estimates charge duration up to targetSoc based on virtual capacity
|
||||
func (s *Estimator) AssumedChargeDuration(targetSoc int, chargePower float64) time.Duration {
|
||||
percentRemaining := float64(targetSoc) - s.vehicleSoc
|
||||
|
||||
if percentRemaining <= 0 || s.virtualCapacity <= 0 {
|
||||
return 0
|
||||
|
|
@ -65,10 +65,10 @@ func (s *Estimator) AssumedChargeDuration(targetSoC int, chargePower float64) ti
|
|||
return time.Duration(float64(time.Hour) * whRemaining / chargePower).Round(time.Second)
|
||||
}
|
||||
|
||||
// RemainingChargeDuration returns the remaining duration estimate based on SoC, target and charge power
|
||||
func (s *Estimator) RemainingChargeDuration(chargePower float64, targetSoC int) time.Duration {
|
||||
// RemainingChargeDuration returns the remaining duration estimate based on Soc, target and charge power
|
||||
func (s *Estimator) RemainingChargeDuration(chargePower float64, targetSoc int) time.Duration {
|
||||
if chargePower > 0 {
|
||||
percentRemaining := float64(targetSoC) - s.vehicleSoc
|
||||
percentRemaining := float64(targetSoc) - s.vehicleSoc
|
||||
if percentRemaining <= 0 {
|
||||
return 0
|
||||
}
|
||||
|
|
@ -86,15 +86,15 @@ func (s *Estimator) RemainingChargeDuration(chargePower float64, targetSoC int)
|
|||
}
|
||||
}
|
||||
|
||||
return s.AssumedChargeDuration(targetSoC, chargePower)
|
||||
return s.AssumedChargeDuration(targetSoc, chargePower)
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// RemainingChargeEnergy returns the remaining charge energy in kWh
|
||||
func (s *Estimator) RemainingChargeEnergy(targetSoC int) float64 {
|
||||
percentRemaining := float64(targetSoC) - s.vehicleSoc
|
||||
func (s *Estimator) RemainingChargeEnergy(targetSoc int) float64 {
|
||||
percentRemaining := float64(targetSoc) - s.vehicleSoc
|
||||
if percentRemaining <= 0 || s.virtualCapacity <= 0 {
|
||||
return 0
|
||||
}
|
||||
|
|
@ -104,14 +104,14 @@ func (s *Estimator) RemainingChargeEnergy(targetSoC int) float64 {
|
|||
return whRemaining / 1e3
|
||||
}
|
||||
|
||||
// SoC replaces the api.Vehicle.SoC interface to take charged energy into account
|
||||
func (s *Estimator) SoC(chargedEnergy float64) (float64, error) {
|
||||
var fetchedSoC *float64
|
||||
// Soc replaces the api.Vehicle.Soc interface to take charged energy into account
|
||||
func (s *Estimator) Soc(chargedEnergy float64) (float64, error) {
|
||||
var fetchedSoc *float64
|
||||
|
||||
if charger, ok := s.charger.(api.Battery); ok {
|
||||
f, err := charger.SoC()
|
||||
f, err := charger.Soc()
|
||||
|
||||
// if the charger does or could provide SoC, we always use it instead of using the vehicle API
|
||||
// if the charger does or could provide Soc, we always use it instead of using the vehicle API
|
||||
if err == nil || !errors.Is(err, api.ErrNotAvailable) {
|
||||
if err != nil {
|
||||
// never received a soc value
|
||||
|
|
@ -124,13 +124,13 @@ func (s *Estimator) SoC(chargedEnergy float64) (float64, error) {
|
|||
s.log.WARN.Printf("vehicle soc (charger): %v (ignored by estimator)", err)
|
||||
}
|
||||
|
||||
fetchedSoC = &f
|
||||
fetchedSoc = &f
|
||||
s.vehicleSoc = f
|
||||
}
|
||||
}
|
||||
|
||||
if fetchedSoC == nil {
|
||||
f, err := s.vehicle.SoC()
|
||||
if fetchedSoc == nil {
|
||||
f, err := s.vehicle.Soc()
|
||||
if err != nil {
|
||||
// required for online APIs with refreshkey
|
||||
if errors.Is(err, api.ErrMustRetry) {
|
||||
|
|
@ -147,7 +147,7 @@ func (s *Estimator) SoC(chargedEnergy float64) (float64, error) {
|
|||
s.log.WARN.Printf("vehicle soc: %v (ignored by estimator)", err)
|
||||
}
|
||||
|
||||
fetchedSoC = &f
|
||||
fetchedSoc = &f
|
||||
s.vehicleSoc = f
|
||||
}
|
||||
|
||||
|
|
@ -194,8 +194,8 @@ func (s *Estimator) SoC(chargedEnergy float64) (float64, error) {
|
|||
s.prevChargedEnergy = math.Max(chargedEnergy, 0)
|
||||
s.prevSoc = s.vehicleSoc
|
||||
} else {
|
||||
s.vehicleSoc = math.Min(*fetchedSoC+energyDelta/s.energyPerSocStep, 100)
|
||||
s.log.DEBUG.Printf("soc estimated: %.2f%% (vehicle: %.2f%%)", s.vehicleSoc, *fetchedSoC)
|
||||
s.vehicleSoc = math.Min(*fetchedSoc+energyDelta/s.energyPerSocStep, 100)
|
||||
s.log.DEBUG.Printf("soc estimated: %.2f%% (vehicle: %.2f%%)", s.vehicleSoc, *fetchedSoc)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,14 +22,14 @@ func TestRemainingChargeDuration(t *testing.T) {
|
|||
ce.vehicleSoc = 20.0
|
||||
|
||||
chargePower := 1000.0
|
||||
targetSoC := 80
|
||||
targetSoc := 80
|
||||
|
||||
if remaining := ce.RemainingChargeDuration(chargePower, targetSoC); remaining != 6*time.Hour {
|
||||
if remaining := ce.RemainingChargeDuration(chargePower, targetSoc); remaining != 6*time.Hour {
|
||||
t.Error("wrong remaining charge duration")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSoCEstimation(t *testing.T) {
|
||||
func TestSocEstimation(t *testing.T) {
|
||||
type chargerStruct struct {
|
||||
*mock.MockCharger
|
||||
*mock.MockBattery
|
||||
|
|
@ -48,8 +48,8 @@ func TestSoCEstimation(t *testing.T) {
|
|||
|
||||
tc := []struct {
|
||||
chargedEnergy float64
|
||||
vehicleSoC float64
|
||||
estimatedSoC float64
|
||||
vehicleSoc float64
|
||||
estimatedSoc float64
|
||||
virtualCapacity float64
|
||||
}{
|
||||
{0, 0.0, 0.0, 10000},
|
||||
|
|
@ -72,27 +72,27 @@ func TestSoCEstimation(t *testing.T) {
|
|||
}
|
||||
|
||||
for i := 1; i < 3; i++ {
|
||||
useVehicleSoC := true
|
||||
useVehicleSoc := true
|
||||
if i == 2 {
|
||||
useVehicleSoC = false
|
||||
useVehicleSoc = false
|
||||
}
|
||||
for _, tc := range tc {
|
||||
t.Logf("%+v", tc)
|
||||
if useVehicleSoC {
|
||||
charger.MockBattery.EXPECT().SoC().Return(tc.vehicleSoC, nil)
|
||||
if useVehicleSoc {
|
||||
charger.MockBattery.EXPECT().Soc().Return(tc.vehicleSoc, nil)
|
||||
} else {
|
||||
charger.MockBattery.EXPECT().SoC().Return(0.0, api.ErrNotAvailable)
|
||||
vehicle.EXPECT().SoC().Return(tc.vehicleSoC, nil)
|
||||
charger.MockBattery.EXPECT().Soc().Return(0.0, api.ErrNotAvailable)
|
||||
vehicle.EXPECT().Soc().Return(tc.vehicleSoc, nil)
|
||||
}
|
||||
|
||||
soc, err := ce.SoC(tc.chargedEnergy)
|
||||
soc, err := ce.Soc(tc.chargedEnergy)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// validate soc estimate
|
||||
if tc.estimatedSoC != soc {
|
||||
t.Errorf("expected estimated soc: %g, got: %g", tc.estimatedSoC, soc)
|
||||
if tc.estimatedSoc != soc {
|
||||
t.Errorf("expected estimated soc: %g, got: %g", tc.estimatedSoc, soc)
|
||||
}
|
||||
|
||||
// validate capacity estimate
|
||||
|
|
@ -102,18 +102,18 @@ func TestSoCEstimation(t *testing.T) {
|
|||
|
||||
// validate duration estimate
|
||||
chargePower := 1e3
|
||||
targetSoC := 100
|
||||
remainingHours := (float64(targetSoC) - soc) / 100 * tc.virtualCapacity / chargePower
|
||||
targetSoc := 100
|
||||
remainingHours := (float64(targetSoc) - soc) / 100 * tc.virtualCapacity / chargePower
|
||||
remainingDuration := time.Duration(float64(time.Hour) * remainingHours).Round(time.Second)
|
||||
|
||||
if rm := ce.RemainingChargeDuration(chargePower, targetSoC); rm != remainingDuration {
|
||||
if rm := ce.RemainingChargeDuration(chargePower, targetSoc); rm != remainingDuration {
|
||||
t.Errorf("expected estimated duration: %v, got: %v", remainingDuration, rm)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSoCFromChargerAndVehicleWithErrors(t *testing.T) {
|
||||
func TestSocFromChargerAndVehicleWithErrors(t *testing.T) {
|
||||
type chargerStruct struct {
|
||||
*mock.MockCharger
|
||||
*mock.MockBattery
|
||||
|
|
@ -132,14 +132,14 @@ func TestSoCFromChargerAndVehicleWithErrors(t *testing.T) {
|
|||
|
||||
tc := []struct {
|
||||
chargedEnergy float64
|
||||
vehicleSoC float64
|
||||
estimatedSoC float64
|
||||
vehicleSoc float64
|
||||
estimatedSoc float64
|
||||
virtualCapacity float64
|
||||
expectVehicle bool
|
||||
chargerError error
|
||||
vehicleError error
|
||||
}{
|
||||
// start with SoC from charger and errors
|
||||
// start with Soc from charger and errors
|
||||
{0, 0.0, 0.0, 10000, false, errors.New("some error"), nil},
|
||||
{0, 0.0, 20.0, 10000, false, api.ErrMustRetry, nil},
|
||||
{0, 20.0, 20.0, 10000, false, nil, nil},
|
||||
|
|
@ -150,7 +150,7 @@ func TestSoCFromChargerAndVehicleWithErrors(t *testing.T) {
|
|||
{1200, 32.0, 32.0, 10000, false, nil, nil},
|
||||
{1900, 39.0, 39.0, 10000, false, nil, nil},
|
||||
{2000, 40.0, 40.0, 10000, false, nil, nil},
|
||||
// move to SoC from vehicle
|
||||
// move to Soc from vehicle
|
||||
{3000, 0.0, 50.0, 10000, true, api.ErrNotAvailable, errors.New("some error")},
|
||||
{3100, 0.0, 51.0, 10000, true, api.ErrNotAvailable, api.ErrMustRetry},
|
||||
{5100, 71.0, 71.0, 10000, true, api.ErrNotAvailable, nil},
|
||||
|
|
@ -164,7 +164,7 @@ func TestSoCFromChargerAndVehicleWithErrors(t *testing.T) {
|
|||
{0, 50.0, 50.0, 10000, true, api.ErrNotAvailable, nil}, // -10000
|
||||
{4990, 50.0, 99.9, 10000, true, api.ErrNotAvailable, nil},
|
||||
{5000, 50.0, 100.0, 10000, true, api.ErrNotAvailable, nil},
|
||||
// back to SoC from charger
|
||||
// back to Soc from charger
|
||||
{5001, 50.0, 100.0, 10000, false, nil, nil},
|
||||
{0, 20.0, 20.0, 10000, false, nil, nil},
|
||||
{1000, 30.0, 30.0, 10000, false, nil, nil},
|
||||
|
|
@ -172,12 +172,12 @@ func TestSoCFromChargerAndVehicleWithErrors(t *testing.T) {
|
|||
|
||||
for _, tc := range tc {
|
||||
t.Logf("%+v", tc)
|
||||
charger.MockBattery.EXPECT().SoC().Return(tc.vehicleSoC, tc.chargerError)
|
||||
charger.MockBattery.EXPECT().Soc().Return(tc.vehicleSoc, tc.chargerError)
|
||||
if tc.expectVehicle {
|
||||
vehicle.EXPECT().SoC().Return(tc.vehicleSoC, tc.vehicleError)
|
||||
vehicle.EXPECT().Soc().Return(tc.vehicleSoc, tc.vehicleError)
|
||||
}
|
||||
|
||||
soc, err := ce.SoC(tc.chargedEnergy)
|
||||
soc, err := ce.Soc(tc.chargedEnergy)
|
||||
if err != nil {
|
||||
if (!tc.expectVehicle && err != tc.chargerError) || (tc.expectVehicle && err != tc.vehicleError) {
|
||||
t.Error(err)
|
||||
|
|
@ -187,8 +187,8 @@ func TestSoCFromChargerAndVehicleWithErrors(t *testing.T) {
|
|||
}
|
||||
|
||||
// validate soc estimate
|
||||
if tc.estimatedSoC != soc {
|
||||
t.Errorf("expected estimated soc: %g, got: %g", tc.estimatedSoC, soc)
|
||||
if tc.estimatedSoc != soc {
|
||||
t.Errorf("expected estimated soc: %g, got: %g", tc.estimatedSoc, soc)
|
||||
}
|
||||
|
||||
// validate capacity estimate
|
||||
|
|
@ -198,11 +198,11 @@ func TestSoCFromChargerAndVehicleWithErrors(t *testing.T) {
|
|||
|
||||
// validate duration estimate
|
||||
chargePower := 1e3
|
||||
targetSoC := 100
|
||||
remainingHours := (float64(targetSoC) - soc) / 100 * tc.virtualCapacity / chargePower
|
||||
targetSoc := 100
|
||||
remainingHours := (float64(targetSoc) - soc) / 100 * tc.virtualCapacity / chargePower
|
||||
remainingDuration := time.Duration(float64(time.Hour) * remainingHours).Round(time.Second)
|
||||
|
||||
if rm := ce.RemainingChargeDuration(chargePower, targetSoC); rm != remainingDuration {
|
||||
if rm := ce.RemainingChargeDuration(chargePower, targetSoc); rm != remainingDuration {
|
||||
t.Errorf("expected estimated duration: %v, got: %v", remainingDuration, rm)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ type Timer struct {
|
|||
Adapter
|
||||
log *util.Logger
|
||||
current float64
|
||||
SoC int
|
||||
Soc int
|
||||
Time time.Time
|
||||
finishAt time.Time
|
||||
active bool
|
||||
|
|
@ -113,10 +113,10 @@ func (lp *Timer) DemandActive() bool {
|
|||
}
|
||||
|
||||
// time
|
||||
remainingDuration := time.Duration(float64(se.AssumedChargeDuration(lp.SoC, power)) / chargeEfficiency)
|
||||
remainingDuration := time.Duration(float64(se.AssumedChargeDuration(lp.Soc, power)) / chargeEfficiency)
|
||||
lp.finishAt = time.Now().Add(remainingDuration).Round(time.Minute)
|
||||
|
||||
lp.log.DEBUG.Printf("estimated charge duration: %v to %d%% at %.0fW", remainingDuration.Round(time.Minute), lp.SoC, power)
|
||||
lp.log.DEBUG.Printf("estimated charge duration: %v to %d%% at %.0fW", remainingDuration.Round(time.Minute), lp.Soc, power)
|
||||
if lp.active {
|
||||
lp.log.DEBUG.Printf("projected end: %v", lp.finishAt)
|
||||
lp.log.DEBUG.Printf("desired finish time: %v", lp.Time)
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
dist/index.html
vendored
4
dist/index.html
vendored
|
|
@ -20,8 +20,8 @@
|
|||
<meta name="theme-color" content="#020318" />
|
||||
|
||||
<title>evcc</title>
|
||||
<script type="module" crossorigin src="./assets/index.3fb53f91.js"></script>
|
||||
<link rel="stylesheet" href="./assets/index.a0d2f98d.css">
|
||||
<script type="module" crossorigin src="./assets/index.286a4950.js"></script>
|
||||
<link rel="stylesheet" href="./assets/index.b7c1c9f8.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -88,8 +88,8 @@ vehicles:
|
|||
vin: WREN...
|
||||
onIdentify: # set defaults when vehicle is identified
|
||||
mode: pv # enable PV-charging when vehicle is identified
|
||||
minSoC: 20 # immediately charge to 0% regardless of mode unless "off" (disabled)
|
||||
targetSoC: 90 # limit charge to 90%
|
||||
minSoc: 20 # immediately charge to 0% regardless of mode unless "off" (disabled)
|
||||
targetSoc: 90 # limit charge to 90%
|
||||
|
||||
# site describes the EVU connection, PV and home battery
|
||||
site:
|
||||
|
|
@ -99,8 +99,8 @@ site:
|
|||
pvs:
|
||||
- pv # list of pv inverters/ meters
|
||||
battery: battery # battery meter
|
||||
prioritySoC: # give home battery priority up to this soc (empty to disable)
|
||||
bufferSoC: # ignore home battery discharge above soc (empty to disable)
|
||||
prioritySoc: # give home battery priority up to this soc (empty to disable)
|
||||
bufferSoc: # ignore home battery discharge above soc (empty to disable)
|
||||
|
||||
# loadpoint describes the charger, charge meter and connected vehicle
|
||||
loadpoints:
|
||||
|
|
@ -197,8 +197,8 @@ messaging:
|
|||
title: Car disconnected
|
||||
msg: Car disconnected after ${connectedDuration}
|
||||
soc: # vehicle soc update event
|
||||
title: SoC updated
|
||||
msg: Battery charged to ${vehicleSoC:%.0f}%
|
||||
title: Soc updated
|
||||
msg: Battery charged to ${vehicleSoc:%.0f}%
|
||||
guest: # vehicle could not be identified
|
||||
title: Unknown vehicle
|
||||
msg: Unknown vehicle, guest connected?
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ func (s *OCPP) errorHandler(errC <-chan error) {
|
|||
// Run executes the OCPP chargepoint client
|
||||
func (s *OCPP) Run() {
|
||||
for {
|
||||
for id, lp := range s.site.LoadPoints() {
|
||||
for id, lp := range s.site.Loadpoints() {
|
||||
connector := id + 1
|
||||
|
||||
status := ocppcore.ChargePointStatusAvailable
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ func (s *SEMP) deviceInfoQuery(w http.ResponseWriter, r *http.Request) {
|
|||
if did == "" {
|
||||
msg.DeviceInfo = append(msg.DeviceInfo, s.allDeviceInfo()...)
|
||||
} else {
|
||||
for id, lp := range s.site.LoadPoints() {
|
||||
for id, lp := range s.site.Loadpoints() {
|
||||
if did != s.deviceID(id) {
|
||||
continue
|
||||
}
|
||||
|
|
@ -294,7 +294,7 @@ func (s *SEMP) deviceStatusQuery(w http.ResponseWriter, r *http.Request) {
|
|||
if did == "" {
|
||||
msg.DeviceStatus = append(msg.DeviceStatus, s.allDeviceStatus()...)
|
||||
} else {
|
||||
for id, lp := range s.site.LoadPoints() {
|
||||
for id, lp := range s.site.Loadpoints() {
|
||||
if did != s.deviceID(id) {
|
||||
continue
|
||||
}
|
||||
|
|
@ -319,7 +319,7 @@ func (s *SEMP) devicePlanningQuery(w http.ResponseWriter, r *http.Request) {
|
|||
if did == "" {
|
||||
msg.PlanningRequest = append(msg.PlanningRequest, s.allPlanningRequest()...)
|
||||
} else {
|
||||
for id, lp := range s.site.LoadPoints() {
|
||||
for id, lp := range s.site.Loadpoints() {
|
||||
if did != s.deviceID(id) {
|
||||
continue
|
||||
}
|
||||
|
|
@ -397,7 +397,7 @@ func (s *SEMP) deviceInfo(id int, lp loadpoint.API) DeviceInfo {
|
|||
}
|
||||
|
||||
func (s *SEMP) allDeviceInfo() (res []DeviceInfo) {
|
||||
for id, lp := range s.site.LoadPoints() {
|
||||
for id, lp := range s.site.Loadpoints() {
|
||||
res = append(res, s.deviceInfo(id, lp))
|
||||
}
|
||||
|
||||
|
|
@ -432,7 +432,7 @@ func (s *SEMP) deviceStatus(id int, lp loadpoint.API) DeviceStatus {
|
|||
}
|
||||
|
||||
func (s *SEMP) allDeviceStatus() (res []DeviceStatus) {
|
||||
for id, lp := range s.site.LoadPoints() {
|
||||
for id, lp := range s.site.Loadpoints() {
|
||||
res = append(res, s.deviceStatus(id, lp))
|
||||
}
|
||||
|
||||
|
|
@ -489,7 +489,7 @@ func (s *SEMP) planningRequest(id int, lp loadpoint.API) (res PlanningRequest) {
|
|||
}
|
||||
|
||||
func (s *SEMP) allPlanningRequest() (res []PlanningRequest) {
|
||||
for id, lp := range s.site.LoadPoints() {
|
||||
for id, lp := range s.site.Loadpoints() {
|
||||
if pr := s.planningRequest(id, lp); len(pr.Timeframe) > 0 {
|
||||
res = append(res, pr)
|
||||
}
|
||||
|
|
@ -512,7 +512,7 @@ func (s *SEMP) deviceControlHandler(w http.ResponseWriter, r *http.Request) {
|
|||
for _, dev := range msg.DeviceControl {
|
||||
did := dev.DeviceID
|
||||
|
||||
for id, lp := range s.site.LoadPoints() {
|
||||
for id, lp := range s.site.Loadpoints() {
|
||||
if did != s.deviceID(id) {
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
10
i18n/de.toml
10
i18n/de.toml
|
|
@ -126,7 +126,7 @@ default = "default"
|
|||
disclaimerHint = "Hinweis:"
|
||||
disclaimerText = "Änderungen gehen nach einem Neustart des Servers verloren."
|
||||
|
||||
[main.loadpointSettings.minSoC]
|
||||
[main.loadpointSettings.minSoc]
|
||||
label = "Min. Ladung %"
|
||||
description = "Reichweite für Notfälle. Fahrzeug wird im PV-Modus „schnell” auf {0}% geladen. Danach weiter mit PV-Überschuss."
|
||||
|
||||
|
|
@ -146,14 +146,14 @@ label = "Min. Ladestrom"
|
|||
|
||||
[main.vehicle]
|
||||
fallbackName = "Fahrzeug"
|
||||
vehicleSoC = "Ladestand"
|
||||
targetSoC = "Ladeziel"
|
||||
vehicleSoc = "Ladestand"
|
||||
targetSoc = "Ladeziel"
|
||||
none = "Kein Fahrzeug"
|
||||
unknown = "Gastfahrzeug"
|
||||
changeVehicle = "Fahrzeug ändern"
|
||||
detectionActive = "Fahrzeugerkennung läuft..."
|
||||
|
||||
[main.vehicleSoC]
|
||||
[main.vehicleSoc]
|
||||
disconnected = "getrennt"
|
||||
charging = "lädt"
|
||||
ready = "bereit"
|
||||
|
|
@ -186,7 +186,7 @@ inactiveLabel = "Zielzeit"
|
|||
activeLabel = "{time}"
|
||||
modalTitle = "Zielzeit festlegen"
|
||||
setTargetTime = "keine"
|
||||
description = "Wann soll das Fahrzeug auf {targetSoC}% geladen sein?"
|
||||
description = "Wann soll das Fahrzeug auf {targetSoc}% geladen sein?"
|
||||
today = "heute"
|
||||
tomorrow = "morgen"
|
||||
targetIsInThePast = "Wähle einen Zeitpunkt in der Zukunft, Marty."
|
||||
|
|
|
|||
10
i18n/en.toml
10
i18n/en.toml
|
|
@ -126,7 +126,7 @@ default = "default"
|
|||
disclaimerHint = "Note:"
|
||||
disclaimerText = "Changes will be lost after the server is restarted."
|
||||
|
||||
[main.loadpointSettings.minSoC]
|
||||
[main.loadpointSettings.minSoc]
|
||||
label = "Min. charge %"
|
||||
description = "For emergencies. The vehicle gets „fast” charged to {0}% from all available solar, and then continues with only the solar surplus."
|
||||
|
||||
|
|
@ -146,14 +146,14 @@ label = "Min. Current"
|
|||
|
||||
[main.vehicle]
|
||||
fallbackName = "Vehicle"
|
||||
vehicleSoC = "Charge"
|
||||
targetSoC = "Limit"
|
||||
vehicleSoc = "Charge"
|
||||
targetSoc = "Limit"
|
||||
none = "No vehicle"
|
||||
unknown = "Guest vehicle"
|
||||
changeVehicle = "Change Vehicle"
|
||||
detectionActive = "Detecting vehicle…"
|
||||
|
||||
[main.vehicleSoC]
|
||||
[main.vehicleSoc]
|
||||
disconnected = "disconnected"
|
||||
charging = "charging"
|
||||
ready = "ready"
|
||||
|
|
@ -186,7 +186,7 @@ inactiveLabel = "Target time"
|
|||
activeLabel = "{time}"
|
||||
modalTitle = "Set Target Time"
|
||||
setTargetTime = "none"
|
||||
description = "When should the vehicle be charged to {targetSoC}%?"
|
||||
description = "When should the vehicle be charged to {targetSoc}%?"
|
||||
today = "today"
|
||||
tomorrow = "tomorrow"
|
||||
targetIsInThePast = "Pick a time in the future, Marty."
|
||||
|
|
|
|||
10
i18n/es.toml
10
i18n/es.toml
|
|
@ -110,7 +110,7 @@ default = "default"
|
|||
disclaimerHint = "Pipa:"
|
||||
disclaimerText = "Actualmente, los cambios aún no son persistentes y se restablecen después de un inicio de servicio."
|
||||
|
||||
[main.loadpointSettings.minSoC]
|
||||
[main.loadpointSettings.minSoc]
|
||||
label = "Cargo de min. %"
|
||||
description = "Rango para emergencias. El vehículo se carga „rápidamente” a {0}% en modo solar. Luego continuar con el excedente fotovoltaico."
|
||||
|
||||
|
|
@ -130,14 +130,14 @@ label = "Corriente de carga mínima"
|
|||
|
||||
[main.vehicle]
|
||||
fallbackName = "Vehículo"
|
||||
vehicleSoC = "Nivel de carga"
|
||||
targetSoC = "Objetivo de carga"
|
||||
vehicleSoc = "Nivel de carga"
|
||||
targetSoc = "Objetivo de carga"
|
||||
none = "Ningún vehículo"
|
||||
unknown = "Vehículo de invitados"
|
||||
changeVehicle = "Cambia vehículo"
|
||||
detectionActive = "Reconocer vehículo ..."
|
||||
|
||||
[main.vehicleSoC]
|
||||
[main.vehicleSoc]
|
||||
disconnected = "separado"
|
||||
charging = "cargando"
|
||||
ready = "listo"
|
||||
|
|
@ -170,7 +170,7 @@ inactiveLabel = "tiempo objetivo"
|
|||
activeLabel = "{time}"
|
||||
modalTitle = "Establecer el tiempo objetivo"
|
||||
setTargetTime = "niguna"
|
||||
description = "¿Cuándo se debe cargar el vehículo al {targetSoC}%?"
|
||||
description = "¿Cuándo se debe cargar el vehículo al {targetSoc}%?"
|
||||
today = "hoy"
|
||||
tomorrow = "mañana"
|
||||
targetIsInThePast = "Elegir un tiempo en el futuro, Marty."
|
||||
|
|
|
|||
10
i18n/fr.toml
10
i18n/fr.toml
|
|
@ -110,7 +110,7 @@ default = "Défaut"
|
|||
disclaimerHint = "A noter:"
|
||||
disclaimerText = "Les modifications seront perdues après le redémarrage du serveur."
|
||||
|
||||
[main.loadpointSettings.minSoC]
|
||||
[main.loadpointSettings.minSoc]
|
||||
label = "Min. charger %"
|
||||
description = "Pour les urgences. Le véhicule est chargé „rapidement” à {0} % de tout l'énergie solaire disponible, puis continue avec uniquement l'excédent solaire."
|
||||
|
||||
|
|
@ -130,14 +130,14 @@ label = "Min. courant de charge"
|
|||
|
||||
[main.vehicle]
|
||||
fallbackName = "Véhicule"
|
||||
vehicleSoC = "Charger"
|
||||
targetSoC = "Cible de chargement"
|
||||
vehicleSoc = "Charger"
|
||||
targetSoc = "Cible de chargement"
|
||||
none = "Pas de véhicule"
|
||||
unknown = "Véhicule invité"
|
||||
changeVehicle = "Changer de véhicule"
|
||||
detectionActive = "Détection de véhicule…"
|
||||
|
||||
[main.vehicleSoC]
|
||||
[main.vehicleSoc]
|
||||
disconnected = "Détaché"
|
||||
charging = "Mise en charge"
|
||||
ready = "Prêt"
|
||||
|
|
@ -170,7 +170,7 @@ inactiveLabel = "Temps visé"
|
|||
activeLabel = "{time}"
|
||||
modalTitle = "Définir l'heure cible"
|
||||
setTargetTime = "Non"
|
||||
description = "Quand le véhicule doit-il être rechargé à {targetSoC}%?"
|
||||
description = "Quand le véhicule doit-il être rechargé à {targetSoc}%?"
|
||||
today = "Aujourd'hui"
|
||||
tomorrow = "Matin"
|
||||
targetIsInThePast = "Choisissez une heure dans le futur, Marty."
|
||||
|
|
|
|||
10
i18n/hr.toml
10
i18n/hr.toml
|
|
@ -133,7 +133,7 @@ default = "zadano"
|
|||
disclaimerHint = "Bilješka:"
|
||||
disclaimerText = "Promjene će biti izgubljene nakon ponovnog pokretanja poslužitelja."
|
||||
|
||||
[main.loadpointSettings.minSoC]
|
||||
[main.loadpointSettings.minSoc]
|
||||
label = "Min. naknada %"
|
||||
description = "Za hitne slučajeve. Vozilo se „brzo” puni na {0}% od svih dostupnih solarnih, a zatim nastavlja samo sa solarnim viškom."
|
||||
|
||||
|
|
@ -153,14 +153,14 @@ label = "Min. struja"
|
|||
|
||||
[main.vehicle]
|
||||
fallbackName = "Vozilo"
|
||||
vehicleSoC = "Naboj"
|
||||
targetSoC = "Granica"
|
||||
vehicleSoc = "Naboj"
|
||||
targetSoc = "Granica"
|
||||
none = "Bez vozila"
|
||||
unknown = "Vozilo za goste"
|
||||
changeVehicle = "Promijeni vozilo"
|
||||
detectionActive = "Otkrivanje vozila..."
|
||||
|
||||
[main.vehicleSoC]
|
||||
[main.vehicleSoc]
|
||||
disconnected = "odspojeno"
|
||||
charging = "punjenje"
|
||||
ready = "spreman"
|
||||
|
|
@ -184,7 +184,7 @@ scale1p = "Smanjenje na 1-fazno napajanje za {preostalo}…"
|
|||
scale3p = "Povećanje na 3-fazno napajanje za {remaining}…"
|
||||
|
||||
[main.targetCharge]
|
||||
description = "Kada bi se vozilo trebalo napuniti na {targetSoC}%?"
|
||||
description = "Kada bi se vozilo trebalo napuniti na {targetSoc}%?"
|
||||
today = "danas"
|
||||
targetIsInThePast = "Odaberi vrijeme u budućnosti, Marty."
|
||||
activate = "Upaliti"
|
||||
|
|
|
|||
10
i18n/it.toml
10
i18n/it.toml
|
|
@ -51,14 +51,14 @@ remoteDisabledHard = "{source}: Disabilitato"
|
|||
|
||||
[main.vehicle]
|
||||
fallbackName = "Veicolo"
|
||||
vehicleSoC = "Caricato"
|
||||
targetSoC = "Limite"
|
||||
vehicleSoc = "Caricato"
|
||||
targetSoc = "Limite"
|
||||
none = "Nessun veicolo"
|
||||
unknown = "Veicolo ospite"
|
||||
changeVehicle = "Cambia veicolo"
|
||||
detectionActive = "Rilevamento del veicolo..."
|
||||
|
||||
[main.vehicleSoC]
|
||||
[main.vehicleSoc]
|
||||
disconnected = "disconesso"
|
||||
charging = "carica"
|
||||
ready = "pronto"
|
||||
|
|
@ -125,7 +125,7 @@ disclaimerHint = "Nota:"
|
|||
title = "Impostazioni {0}"
|
||||
disclaimerText = "Le modifiche andranno perse dopo il riavvio del server."
|
||||
|
||||
[main.loadpointSettings.minSoC]
|
||||
[main.loadpointSettings.minSoc]
|
||||
label = "Min. carica %"
|
||||
description = "Per le emergenze. Il veicolo viene „veloce” caricato a {0}% da tutto il solare disponibile, e poi continua con solo il surplus solare."
|
||||
|
||||
|
|
@ -187,7 +187,7 @@ inactiveLabel = "Tempo obiettivo"
|
|||
activeLabel = "{time}"
|
||||
modalTitle = "Imposta orario target"
|
||||
setTargetTime = "nessuno"
|
||||
description = "Quando dovrebbe essere addebitato il veicolo a {targetSoC}%?"
|
||||
description = "Quando dovrebbe essere addebitato il veicolo a {targetSoc}%?"
|
||||
tomorrow = "domani"
|
||||
targetIsInThePast = "Scegli un momento nel futuro, Marty."
|
||||
experimentalLabel = "Sperimentale"
|
||||
|
|
|
|||
10
i18n/lb.toml
10
i18n/lb.toml
|
|
@ -82,7 +82,7 @@ batteryCharge = "Batterie gelueden"
|
|||
pvProduction = "Produktioun"
|
||||
loadpoints = "Opluedpunkt | Opluedpunkt | {count} Opluedstatiounen"
|
||||
|
||||
[main.loadpointSettings.minSoC]
|
||||
[main.loadpointSettings.minSoc]
|
||||
label = "Minimum Staat vun charge"
|
||||
description = "Fir Noutfäll. D'Gefier gëtt „séier” gelueden op {0}% vun all verfügbare Solarenergie, a geet dann mat nëmmen dem Solariwwerschoss weider."
|
||||
|
||||
|
|
@ -95,15 +95,15 @@ phases_1 = "1 Phase"
|
|||
phases_3 = "3 Phasen"
|
||||
|
||||
[main.vehicle]
|
||||
vehicleSoC = "Staat vun charge"
|
||||
targetSoC = "Limite"
|
||||
vehicleSoc = "Staat vun charge"
|
||||
targetSoc = "Limite"
|
||||
none = "Keng Gefier"
|
||||
unknown = "Gäscht Gefier"
|
||||
changeVehicle = "Gefier änneren"
|
||||
detectionActive = "Erkennung vum Gefier ..."
|
||||
fallbackName = "Gefier"
|
||||
|
||||
[main.vehicleSoC]
|
||||
[main.vehicleSoc]
|
||||
connected = "verbonne"
|
||||
disconnected = "deconnectéiert"
|
||||
charging = "oplueden"
|
||||
|
|
@ -116,7 +116,7 @@ activate = "Aktivéieren"
|
|||
title = "Zilzäit"
|
||||
setTargetTime = "keen"
|
||||
experimentalLabel = "Experimentell"
|
||||
description = "Wéini soll d'Gefier op {targetSoC}% gelueden ginn?"
|
||||
description = "Wéini soll d'Gefier op {targetSoc}% gelueden ginn?"
|
||||
modalTitle = "Zielzäit setzen"
|
||||
today = "haut"
|
||||
tomorrow = "muer"
|
||||
|
|
|
|||
10
i18n/lt.toml
10
i18n/lt.toml
|
|
@ -110,7 +110,7 @@ default = "standartiškai"
|
|||
disclaimerHint = "Pastaba:"
|
||||
disclaimerText = "Šie pakeitimai neišlieka po evcc serverio restarto."
|
||||
|
||||
[main.loadpointSettings.minSoC]
|
||||
[main.loadpointSettings.minSoc]
|
||||
label = "Minimali įkrova"
|
||||
description = "Minimali įkrova. Automobilis įkraunamas „Greitai” iki {0}% nustatyme „Saulė”, toliau įkraunamas tik saulės energijos pertekliumi."
|
||||
|
||||
|
|
@ -130,14 +130,14 @@ label = "Min. Srovė"
|
|||
|
||||
[main.vehicle]
|
||||
fallbackName = "Automobilis"
|
||||
vehicleSoC = "Akumuliatorius"
|
||||
targetSoC = "Limitas"
|
||||
vehicleSoc = "Akumuliatorius"
|
||||
targetSoc = "Limitas"
|
||||
none = "Nėra automobilio"
|
||||
unknown = "Nežinomas automobilis"
|
||||
changeVehicle = "Pakeisti automobilį"
|
||||
detectionActive = "Bandome atpažinti automobilį ..."
|
||||
|
||||
[main.vehicleSoC]
|
||||
[main.vehicleSoc]
|
||||
disconnected = "neprijungtas"
|
||||
charging = "vyksta įkrovimas"
|
||||
ready = "leidžiama įkrauti"
|
||||
|
|
@ -170,7 +170,7 @@ inactiveLabel = "Suplanuotas įkrovimas"
|
|||
activeLabel = "{time}"
|
||||
modalTitle = "Nustatyti įkrovimo pabaigos laiką"
|
||||
setTargetTime = "nenustatytas"
|
||||
description = "Kada automobilis turėtų būti įkrautas iki {targetSoC}%?"
|
||||
description = "Kada automobilis turėtų būti įkrautas iki {targetSoc}%?"
|
||||
today = "šiandien"
|
||||
tomorrow = "rytoj"
|
||||
targetIsInThePast = "Pasirinkite laiką ateityje."
|
||||
|
|
|
|||
10
i18n/nl.toml
10
i18n/nl.toml
|
|
@ -110,7 +110,7 @@ default = "standaard"
|
|||
disclaimerHint = "Letop:"
|
||||
disclaimerText = "Changes zal verloren zijn nadat de server is herstart."
|
||||
|
||||
[main.loadpointSettings.minSoC]
|
||||
[main.loadpointSettings.minSoc]
|
||||
label = "Min. lading %"
|
||||
description = "Voor noodgevallen. Het voertuig wordt „snel” opgeladen tot {0}% van alle beschikbare zonne-energie, en gaat dan verder met alleen het zonneoverschot."
|
||||
|
||||
|
|
@ -130,14 +130,14 @@ label = "Min. vermogen"
|
|||
|
||||
[main.vehicle]
|
||||
fallbackName = "Voertuig"
|
||||
vehicleSoC = "Opladen"
|
||||
targetSoC = "Limiet"
|
||||
vehicleSoc = "Opladen"
|
||||
targetSoc = "Limiet"
|
||||
none = "Geen voertuig"
|
||||
unknown = "Gast"
|
||||
changeVehicle = "Wijzig voertuig"
|
||||
detectionActive = "Detecteer voertuig..."
|
||||
|
||||
[main.vehicleSoC]
|
||||
[main.vehicleSoc]
|
||||
disconnected = "niet verbonden"
|
||||
charging = "laden"
|
||||
ready = "klaar"
|
||||
|
|
@ -170,7 +170,7 @@ inactiveLabel = "Ingestelde tijd"
|
|||
activeLabel = "{time}"
|
||||
modalTitle = "Stel tijd in"
|
||||
setTargetTime = "geen"
|
||||
description = "Tegen wanneer moet het voertuig geladen zijn naar {targetSoC}%?"
|
||||
description = "Tegen wanneer moet het voertuig geladen zijn naar {targetSoc}%?"
|
||||
today = "Vandaag"
|
||||
tomorrow = "Morgen"
|
||||
targetIsInThePast = "Pick een tijd in de toekomst, Marty."
|
||||
|
|
|
|||
10
i18n/no.toml
10
i18n/no.toml
|
|
@ -95,7 +95,7 @@ phases_1 = "1-fas"
|
|||
phases_1_hint = "({min} til {max})"
|
||||
phases_3_hint = "({min} til {max})"
|
||||
|
||||
[main.loadpointSettings.minSoC]
|
||||
[main.loadpointSettings.minSoc]
|
||||
description = "For nødstilfeller. Kjøretøyet blir „raskt” ladet til {0} % fra all tilgjengelig solenergi, og fortsetter deretter med bare solenergioverskuddet."
|
||||
label = "Min. kostnad%"
|
||||
|
||||
|
|
@ -103,15 +103,15 @@ label = "Min. kostnad%"
|
|||
label = "Maks. ladestrøm"
|
||||
|
||||
[main.vehicle]
|
||||
targetSoC = "Grense"
|
||||
targetSoc = "Grense"
|
||||
none = "Ingen kjøretøy"
|
||||
changeVehicle = "Lad kjøretøy"
|
||||
detectionActive = "Oppdager kjøretøy ..."
|
||||
vehicleSoC = "Lade"
|
||||
vehicleSoc = "Lade"
|
||||
fallbackName = "Kjøretøy"
|
||||
unknown = "Gjestekjøretøy"
|
||||
|
||||
[main.vehicleSoC]
|
||||
[main.vehicleSoc]
|
||||
disconnected = "frakoblet"
|
||||
charging = "lader"
|
||||
vehicleTarget = "Kjøretøygrense: {soc}%"
|
||||
|
|
@ -137,7 +137,7 @@ targetChargeWaitForVehicle = "Mållading klar. Venter på kjøretøy..."
|
|||
[main.targetCharge]
|
||||
inactiveLabel = "Mål-tid"
|
||||
activeLabel = "{time}"
|
||||
description = "Når skal kjøretøyet lades til {targetSoC}%?"
|
||||
description = "Når skal kjøretøyet lades til {targetSoc}%?"
|
||||
tomorrow = "i morgen"
|
||||
experimentalLabel = "Eksperimentelt"
|
||||
activate = "Slå på"
|
||||
|
|
|
|||
10
i18n/pl.toml
10
i18n/pl.toml
|
|
@ -105,7 +105,7 @@ default = "domyślny"
|
|||
disclaimerHint = "Uwaga:"
|
||||
disclaimerText = "Zmiany zostaną zresetowane po restarcie serwera."
|
||||
|
||||
[main.loadpointSettings.minSoC]
|
||||
[main.loadpointSettings.minSoc]
|
||||
label = "Minimalne naładowanie %"
|
||||
description = "Zasięg w sytuacjach awaryjnych. Pojazd jest „szybko” ładowany do {0}% w trybie „Słońce”. Następnie kontynuuje tylko z nadwyżką fotowoltaiczną."
|
||||
|
||||
|
|
@ -125,14 +125,14 @@ label = "Min. Prąd"
|
|||
|
||||
[main.vehicle]
|
||||
fallbackName = "Pojazd"
|
||||
vehicleSoC = "Bateria"
|
||||
targetSoC = "Ograniczenie"
|
||||
vehicleSoc = "Bateria"
|
||||
targetSoc = "Ograniczenie"
|
||||
none = "Brak pojazdu"
|
||||
unknown = "Pojazd gościa"
|
||||
changeVehicle = "Zmień pojazd"
|
||||
detectionActive = "Wykrywanie pojazdu..."
|
||||
|
||||
[main.vehicleSoC]
|
||||
[main.vehicleSoc]
|
||||
disconnected = "odłączony"
|
||||
charging = "ładuje się"
|
||||
ready = "gotowy"
|
||||
|
|
@ -165,7 +165,7 @@ inactiveLabel = "Czas docelowy"
|
|||
activeLabel = "{time}"
|
||||
modalTitle = "Ustaw docelowy czas"
|
||||
setTargetTime = "brak"
|
||||
description = "Kiedy pojazd powinien zostać naladowany do {targetSoC}%?"
|
||||
description = "Kiedy pojazd powinien zostać naladowany do {targetSoc}%?"
|
||||
today = "dzisiaj"
|
||||
tomorrow = "jutro"
|
||||
targetIsInThePast = "Wybierz czas w przyszłości."
|
||||
|
|
|
|||
10
i18n/pt.toml
10
i18n/pt.toml
|
|
@ -109,7 +109,7 @@ default = "predefinado"
|
|||
disclaimerHint = "Aviso:"
|
||||
disclaimerText = "As alterações não são persistentes no momento e serão redefinidas após a reinicialização do servidor."
|
||||
|
||||
[main.loadpointSettings.minSoC]
|
||||
[main.loadpointSettings.minSoc]
|
||||
label = "Nível mínimo de carga"
|
||||
description = "Alcance para emergências. O veículo carrega até {0}% no modo „Rápido”. Em seguida, continue com o modo „PV”."
|
||||
|
||||
|
|
@ -129,14 +129,14 @@ label = "Corrente de carga mínima"
|
|||
|
||||
[main.vehicle]
|
||||
fallbackName = "Veículo"
|
||||
vehicleSoC = "Nível de carga"
|
||||
targetSoC = "Alvo de carregamento"
|
||||
vehicleSoc = "Nível de carga"
|
||||
targetSoc = "Alvo de carregamento"
|
||||
none = "Nenhum veículo"
|
||||
unknown = "Veículo convidado"
|
||||
changeVehicle = "Troca de veículo"
|
||||
detectionActive = "Detecção de veículo em obra..."
|
||||
|
||||
[main.vehicleSoC]
|
||||
[main.vehicleSoc]
|
||||
disconnected = "separado"
|
||||
charging = "a carregar"
|
||||
ready = "esperando"
|
||||
|
|
@ -169,7 +169,7 @@ inactiveLabel = "Tempo alvo"
|
|||
activeLabel = "{time}"
|
||||
modalTitle = "Definir hora-alvo"
|
||||
setTargetTime = "sem"
|
||||
description = "Quando o veículo deve ser carregado para {targetSoC}%?"
|
||||
description = "Quando o veículo deve ser carregado para {targetSoc}%?"
|
||||
today = "hoje"
|
||||
tomorrow = "amanhã"
|
||||
targetIsInThePast = "O ponto do tempo está no passado."
|
||||
|
|
|
|||
10
i18n/uk.toml
10
i18n/uk.toml
|
|
@ -157,7 +157,7 @@ phases_3 = "3 фаза"
|
|||
label = "Фази"
|
||||
phases_3_hint = "({min} до {max})"
|
||||
|
||||
[main.loadpointSettings.minSoC]
|
||||
[main.loadpointSettings.minSoc]
|
||||
description = "Для надзвичайних ситуацій. Транспортний засіб «швидко» заряджається до {0}% від усіх доступних сонячних батарей, а потім продовжує працювати лише з сонячним надлишком."
|
||||
label = "Мін. заряд %"
|
||||
|
||||
|
|
@ -194,8 +194,8 @@ login = "увійти"
|
|||
|
||||
[main.vehicle]
|
||||
fallbackName = "Транспортний засіб"
|
||||
targetSoC = "Ліміт"
|
||||
vehicleSoC = "Заряд"
|
||||
targetSoc = "Ліміт"
|
||||
vehicleSoc = "Заряд"
|
||||
none = "Без транспортного засобу"
|
||||
unknown = "Гостьовий транспортний засіб"
|
||||
detectionActive = "Виявлення транспортного засобу…"
|
||||
|
|
@ -213,7 +213,7 @@ activate = "Увімкніть"
|
|||
inactiveLabel = "Запланований час"
|
||||
experimentalLabel = "Експериментальний"
|
||||
experimentalText = "Це працює, але не ідеально. Будь ласка, повідомте про несподівану поведінку"
|
||||
description = "Коли транспортний засіб повинен бути заряджений на {targetSoC}%?"
|
||||
description = "Коли транспортний засіб повинен бути заряджений на {targetSoc}%?"
|
||||
|
||||
[main.loadpoint]
|
||||
fallbackName = "Точка зарядки"
|
||||
|
|
@ -224,7 +224,7 @@ duration = "Тривалість"
|
|||
[main.loadpointSettings.minCurrent]
|
||||
label = "Мін. струм"
|
||||
|
||||
[main.vehicleSoC]
|
||||
[main.vehicleSoc]
|
||||
charging = "зарядка"
|
||||
connected = "підключено"
|
||||
vehicleTarget = "Ліміт транспортного засобу: {soc}%"
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ func init() {
|
|||
registry.Add("bosch-bpt", NewBoschBpts5HybridFromConfig)
|
||||
}
|
||||
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateBoschBpts5Hybrid -b api.Meter -t "api.Battery,SoC,func() (float64, error)"
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateBoschBpts5Hybrid -b api.Meter -t "api.Battery,Soc,func() (float64, error)"
|
||||
|
||||
// NewBoschBpts5HybridFromConfig creates a Bosch BPT-S 5 Hybrid Meter from generic config
|
||||
func NewBoschBpts5HybridFromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
|
|
@ -83,13 +83,13 @@ func NewBoschBpts5Hybrid(uri, usage string, cache time.Duration) (api.Meter, err
|
|||
usage: strings.ToLower(usage),
|
||||
}
|
||||
|
||||
// decorate api.BatterySoC
|
||||
var batterySoC func() (float64, error)
|
||||
// decorate api.BatterySoc
|
||||
var batterySoc func() (float64, error)
|
||||
if usage == "battery" {
|
||||
batterySoC = m.batterySoC
|
||||
batterySoc = m.batterySoc
|
||||
}
|
||||
|
||||
return decorateBoschBpts5Hybrid(m, batterySoC), nil
|
||||
return decorateBoschBpts5Hybrid(m, batterySoc), nil
|
||||
}
|
||||
|
||||
// CurrentPower implements the api.Meter interface
|
||||
|
|
@ -108,8 +108,8 @@ func (m *BoschBpts5Hybrid) CurrentPower() (float64, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// batterySoC implements the api.Battery interface
|
||||
func (m *BoschBpts5Hybrid) batterySoC() (float64, error) {
|
||||
// batterySoc implements the api.Battery interface
|
||||
func (m *BoschBpts5Hybrid) batterySoc() (float64, error) {
|
||||
status, err := m.api.Status()
|
||||
return status.CurrentBatterySoc, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,6 @@ type decorateBoschBpts5HybridBatteryImpl struct {
|
|||
battery func() (float64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateBoschBpts5HybridBatteryImpl) SoC() (float64, error) {
|
||||
func (impl *decorateBoschBpts5HybridBatteryImpl) Soc() (float64, error) {
|
||||
return impl.battery()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ func init() {
|
|||
registry.Add("lgess", NewLgEssFromConfig)
|
||||
}
|
||||
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateLgEss -b *LgEss -r api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Battery,SoC,func() (float64, error)"
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateLgEss -b *LgEss -r api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Battery,Soc,func() (float64, error)"
|
||||
|
||||
// NewLgEssFromConfig creates an LgEss Meter from generic config
|
||||
func NewLgEssFromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
|
|
@ -89,13 +89,13 @@ func NewLgEss(uri, usage, password string, cache time.Duration) (api.Meter, erro
|
|||
totalEnergy = m.totalEnergy
|
||||
}
|
||||
|
||||
// decorate api.BatterySoC
|
||||
var batterySoC func() (float64, error)
|
||||
// decorate api.BatterySoc
|
||||
var batterySoc func() (float64, error)
|
||||
if usage == "battery" {
|
||||
batterySoC = m.batterySoC
|
||||
batterySoc = m.batterySoc
|
||||
}
|
||||
|
||||
return decorateLgEss(m, totalEnergy, batterySoC), nil
|
||||
return decorateLgEss(m, totalEnergy, batterySoc), nil
|
||||
}
|
||||
|
||||
// CurrentPower implements the api.Meter interface
|
||||
|
|
@ -132,8 +132,8 @@ func (m *LgEss) totalEnergy() (float64, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// batterySoC implements the api.Battery interface
|
||||
func (m *LgEss) batterySoC() (float64, error) {
|
||||
// batterySoc implements the api.Battery interface
|
||||
func (m *LgEss) batterySoc() (float64, error) {
|
||||
data, err := m.lp.Data()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ type decorateLgEssBatteryImpl struct {
|
|||
battery func() (float64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateLgEssBatteryImpl) SoC() (float64, error) {
|
||||
func (impl *decorateLgEssBatteryImpl) Soc() (float64, error) {
|
||||
return impl.battery()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ func init() {
|
|||
registry.Add(api.Custom, NewConfigurableFromConfig)
|
||||
}
|
||||
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateMeter -b api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.MeterCurrent,Currents,func() (float64, float64, float64, error)" -t "api.Battery,SoC,func() (float64, error)"
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateMeter -b api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.MeterCurrent,Currents,func() (float64, float64, float64, error)" -t "api.Battery,Soc,func() (float64, error)"
|
||||
|
||||
// NewConfigurableFromConfig creates api.Meter from config
|
||||
func NewConfigurableFromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
var cc struct {
|
||||
Power provider.Config
|
||||
Energy *provider.Config // optional
|
||||
SoC *provider.Config // optional
|
||||
Soc *provider.Config // optional
|
||||
Currents []provider.Config // optional
|
||||
}
|
||||
|
||||
|
|
@ -64,16 +64,16 @@ func NewConfigurableFromConfig(other map[string]interface{}) (api.Meter, error)
|
|||
currentsG = collectCurrentProviders(curr)
|
||||
}
|
||||
|
||||
// decorate Meter with BatterySoC
|
||||
var batterySoCG func() (float64, error)
|
||||
if cc.SoC != nil {
|
||||
batterySoCG, err = provider.NewFloatGetterFromConfig(*cc.SoC)
|
||||
// decorate Meter with BatterySoc
|
||||
var batterySocG func() (float64, error)
|
||||
if cc.Soc != nil {
|
||||
batterySocG, err = provider.NewFloatGetterFromConfig(*cc.Soc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("battery: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
res := m.Decorate(totalEnergyG, currentsG, batterySoCG)
|
||||
res := m.Decorate(totalEnergyG, currentsG, batterySocG)
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
|
@ -112,9 +112,9 @@ type Meter struct {
|
|||
func (m *Meter) Decorate(
|
||||
totalEnergy func() (float64, error),
|
||||
currents func() (float64, float64, float64, error),
|
||||
batterySoC func() (float64, error),
|
||||
batterySoc func() (float64, error),
|
||||
) api.Meter {
|
||||
return decorateMeter(m, totalEnergy, currents, batterySoC)
|
||||
return decorateMeter(m, totalEnergy, currents, batterySoc)
|
||||
}
|
||||
|
||||
// CurrentPower implements the api.Meter interface
|
||||
|
|
|
|||
|
|
@ -44,9 +44,9 @@ func NewMovingAverageFromConfig(other map[string]interface{}) (api.Meter, error)
|
|||
}
|
||||
|
||||
// decorate battery reading
|
||||
var batterySoC func() (float64, error)
|
||||
var batterySoc func() (float64, error)
|
||||
if m, ok := m.(api.Battery); ok {
|
||||
batterySoC = m.SoC
|
||||
batterySoc = m.Soc
|
||||
}
|
||||
|
||||
// decorate currents reading
|
||||
|
|
@ -55,7 +55,7 @@ func NewMovingAverageFromConfig(other map[string]interface{}) (api.Meter, error)
|
|||
currents = m.Currents
|
||||
}
|
||||
|
||||
res := meter.Decorate(totalEnergy, currents, batterySoC)
|
||||
res := meter.Decorate(totalEnergy, currents, batterySoc)
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ type decorateMeterBatteryImpl struct {
|
|||
battery func() (float64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateMeterBatteryImpl) SoC() (float64, error) {
|
||||
func (impl *decorateMeterBatteryImpl) Soc() (float64, error) {
|
||||
return impl.battery()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,21 +19,21 @@ type Modbus struct {
|
|||
device meters.Device
|
||||
opPower modbus.Operation
|
||||
opEnergy modbus.Operation
|
||||
opSoC modbus.Operation
|
||||
opSoc modbus.Operation
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.Add("modbus", NewModbusFromConfig)
|
||||
}
|
||||
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateModbus -b api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.MeterCurrent,Currents,func() (float64, float64, float64, error)" -t "api.Battery,SoC,func() (float64, error)"
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateModbus -b api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.MeterCurrent,Currents,func() (float64, float64, float64, error)" -t "api.Battery,Soc,func() (float64, error)"
|
||||
|
||||
// NewModbusFromConfig creates api.Meter from config
|
||||
func NewModbusFromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
cc := struct {
|
||||
Model string
|
||||
modbus.Settings `mapstructure:",squash"`
|
||||
Power, Energy, SoC string
|
||||
Power, Energy, Soc string
|
||||
Currents []string
|
||||
Delay time.Duration
|
||||
Timeout time.Duration
|
||||
|
|
@ -134,9 +134,9 @@ func NewModbusFromConfig(other map[string]interface{}) (api.Meter, error) {
|
|||
|
||||
// decorate soc reading
|
||||
var soc func() (float64, error)
|
||||
if cc.SoC != "" {
|
||||
if err := modbus.ParseOperation(device, cc.SoC, &m.opSoC); err != nil {
|
||||
return nil, fmt.Errorf("invalid measurement for soc: %s", cc.SoC)
|
||||
if cc.Soc != "" {
|
||||
if err := modbus.ParseOperation(device, cc.Soc, &m.opSoc); err != nil {
|
||||
return nil, fmt.Errorf("invalid measurement for soc: %s", cc.Soc)
|
||||
}
|
||||
|
||||
soc = m.soc
|
||||
|
|
@ -188,5 +188,5 @@ func (m *Modbus) totalEnergy() (float64, error) {
|
|||
|
||||
// soc implements the api.Battery interface
|
||||
func (m *Modbus) soc() (float64, error) {
|
||||
return m.floatGetter(m.opSoC)
|
||||
return m.floatGetter(m.opSoc)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ type decorateModbusBatteryImpl struct {
|
|||
battery func() (float64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateModbusBatteryImpl) SoC() (float64, error) {
|
||||
func (impl *decorateModbusBatteryImpl) Soc() (float64, error) {
|
||||
return impl.battery()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ func NewOpenWBFromConfig(other map[string]interface{}) (api.Meter, error) {
|
|||
}
|
||||
|
||||
power = floatG(fmt.Sprintf("%s/housebattery/%s", cc.Topic, openwb.PowerTopic))
|
||||
soc = floatG(fmt.Sprintf("%s/housebattery/%s", cc.Topic, openwb.SoCTopic))
|
||||
soc = floatG(fmt.Sprintf("%s/housebattery/%s", cc.Topic, openwb.SocTopic))
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid usage: %s", cc.Usage)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ func init() {
|
|||
registry.Add("powerwall", NewPowerWallFromConfig)
|
||||
}
|
||||
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decoratePowerWall -b *PowerWall -r api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Battery,SoC,func() (float64, error)"
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decoratePowerWall -b *PowerWall -r api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Battery,Soc,func() (float64, error)"
|
||||
|
||||
// NewPowerWallFromConfig creates a PowerWall Powerwall Meter from generic config
|
||||
func NewPowerWallFromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
|
|
@ -80,13 +80,13 @@ func NewPowerWall(uri, usage, user, password string) (api.Meter, error) {
|
|||
totalEnergy = m.totalEnergy
|
||||
}
|
||||
|
||||
// decorate api.BatterySoC
|
||||
var batterySoC func() (float64, error)
|
||||
// decorate api.BatterySoc
|
||||
var batterySoc func() (float64, error)
|
||||
if usage == "battery" {
|
||||
batterySoC = m.batterySoC
|
||||
batterySoc = m.batterySoc
|
||||
}
|
||||
|
||||
return decoratePowerWall(m, totalEnergy, batterySoC), nil
|
||||
return decoratePowerWall(m, totalEnergy, batterySoc), nil
|
||||
}
|
||||
|
||||
var _ api.Meter = (*PowerWall)(nil)
|
||||
|
|
@ -124,8 +124,8 @@ func (m *PowerWall) totalEnergy() (float64, error) {
|
|||
return 0, fmt.Errorf("invalid usage: %s", m.usage)
|
||||
}
|
||||
|
||||
// batterySoC implements the api.Battery interface
|
||||
func (m *PowerWall) batterySoC() (float64, error) {
|
||||
// batterySoc implements the api.Battery interface
|
||||
func (m *PowerWall) batterySoc() (float64, error) {
|
||||
res, err := m.client.GetSOE()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ type decoratePowerWallBatteryImpl struct {
|
|||
battery func() (float64, error)
|
||||
}
|
||||
|
||||
func (impl *decoratePowerWallBatteryImpl) SoC() (float64, error) {
|
||||
func (impl *decoratePowerWallBatteryImpl) Soc() (float64, error) {
|
||||
return impl.battery()
|
||||
}
|
||||
|
||||
|
|
|
|||
14
meter/rct.go
14
meter/rct.go
|
|
@ -50,7 +50,7 @@ func init() {
|
|||
registry.Add("rct", NewRCTFromConfig)
|
||||
}
|
||||
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateRCT -b *RCT -r api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Battery,SoC,func() (float64, error)"
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateRCT -b *RCT -r api.Meter -t "api.MeterEnergy,TotalEnergy,func() (float64, error)" -t "api.Battery,Soc,func() (float64, error)"
|
||||
|
||||
// NewRCTFromConfig creates an RCT from generic config
|
||||
func NewRCTFromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
|
|
@ -90,13 +90,13 @@ func NewRCT(uri, usage string, cache time.Duration) (api.Meter, error) {
|
|||
totalEnergy = m.totalEnergy
|
||||
}
|
||||
|
||||
// decorate api.BatterySoC
|
||||
var batterySoC func() (float64, error)
|
||||
// decorate api.BatterySoc
|
||||
var batterySoc func() (float64, error)
|
||||
if usage == "battery" {
|
||||
batterySoC = m.batterySoC
|
||||
batterySoc = m.batterySoc
|
||||
}
|
||||
|
||||
return decorateRCT(m, totalEnergy, batterySoC), nil
|
||||
return decorateRCT(m, totalEnergy, batterySoc), nil
|
||||
}
|
||||
|
||||
// CurrentPower implements the api.Meter interface
|
||||
|
|
@ -151,8 +151,8 @@ func (m *RCT) totalEnergy() (float64, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// batterySoC implements the api.Battery interface
|
||||
func (m *RCT) batterySoC() (float64, error) {
|
||||
// batterySoc implements the api.Battery interface
|
||||
func (m *RCT) batterySoc() (float64, error) {
|
||||
res, err := m.conn.QueryFloat32(rct.BatterySoC)
|
||||
return float64(res * 100), err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ type decorateRCTBatteryImpl struct {
|
|||
battery func() (float64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateRCTBatteryImpl) SoC() (float64, error) {
|
||||
func (impl *decorateRCTBatteryImpl) Soc() (float64, error) {
|
||||
return impl.battery()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ func init() {
|
|||
registry.Add("sma", NewSMAFromConfig)
|
||||
}
|
||||
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateSMA -b *SMA -r api.Meter -t "api.Battery,SoC,func() (float64, error)"
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateSMA -b *SMA -r api.Meter -t "api.Battery,Soc,func() (float64, error)"
|
||||
|
||||
// NewSMAFromConfig creates an SMA meter from generic config
|
||||
func NewSMAFromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,6 @@ type decorateSMABatteryImpl struct {
|
|||
battery func() (float64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateSMABatteryImpl) SoC() (float64, error) {
|
||||
func (impl *decorateSMABatteryImpl) Soc() (float64, error) {
|
||||
return impl.battery()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -374,19 +374,19 @@ func (mr *MockVehicleMockRecorder) Phases() *gomock.Call {
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Phases", reflect.TypeOf((*MockVehicle)(nil).Phases))
|
||||
}
|
||||
|
||||
// SoC mocks base method.
|
||||
func (m *MockVehicle) SoC() (float64, error) {
|
||||
// Soc mocks base method.
|
||||
func (m *MockVehicle) Soc() (float64, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SoC")
|
||||
ret := m.ctrl.Call(m, "Soc")
|
||||
ret0, _ := ret[0].(float64)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// SoC indicates an expected call of SoC.
|
||||
func (mr *MockVehicleMockRecorder) SoC() *gomock.Call {
|
||||
// Soc indicates an expected call of Soc.
|
||||
func (mr *MockVehicleMockRecorder) Soc() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SoC", reflect.TypeOf((*MockVehicle)(nil).SoC))
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Soc", reflect.TypeOf((*MockVehicle)(nil).Soc))
|
||||
}
|
||||
|
||||
// Title mocks base method.
|
||||
|
|
@ -464,17 +464,17 @@ func (m *MockBattery) EXPECT() *MockBatteryMockRecorder {
|
|||
return m.recorder
|
||||
}
|
||||
|
||||
// SoC mocks base method.
|
||||
func (m *MockBattery) SoC() (float64, error) {
|
||||
// Soc mocks base method.
|
||||
func (m *MockBattery) Soc() (float64, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SoC")
|
||||
ret := m.ctrl.Call(m, "Soc")
|
||||
ret0, _ := ret[0].(float64)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// SoC indicates an expected call of SoC.
|
||||
func (mr *MockBatteryMockRecorder) SoC() *gomock.Call {
|
||||
// Soc indicates an expected call of Soc.
|
||||
func (mr *MockBatteryMockRecorder) Soc() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SoC", reflect.TypeOf((*MockBattery)(nil).SoC))
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Soc", reflect.TypeOf((*MockBattery)(nil).Soc))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
// Event is a notification event
|
||||
type Event struct {
|
||||
LoadPoint *int // optional loadpoint id
|
||||
Loadpoint *int // optional loadpoint id
|
||||
Event string
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ func (h *Hub) apply(ev Event, tmpl *template.Template) (string, error) {
|
|||
|
||||
// get all values from cache
|
||||
for _, p := range h.cache.All() {
|
||||
if p.LoadPoint == nil || ev.LoadPoint == p.LoadPoint {
|
||||
if p.Loadpoint == nil || ev.Loadpoint == p.Loadpoint {
|
||||
attr[p.Key] = p.Val
|
||||
}
|
||||
}
|
||||
|
|
|
|||
42
schema.json
42
schema.json
|
|
@ -71,10 +71,10 @@
|
|||
"mode": {
|
||||
"$ref": "#/definitions/mode"
|
||||
},
|
||||
"minSoC": {
|
||||
"minSoc": {
|
||||
"type": "integer"
|
||||
},
|
||||
"targetSoC": {
|
||||
"targetSoc": {
|
||||
"type": "integer"
|
||||
},
|
||||
"minCurrent": {
|
||||
|
|
@ -90,7 +90,9 @@
|
|||
},
|
||||
"site": {
|
||||
"type": "object",
|
||||
"required": ["meters"],
|
||||
"required": [
|
||||
"meters"
|
||||
],
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string"
|
||||
|
|
@ -127,8 +129,8 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"prioritySoC": {},
|
||||
"bufferSoC": {}
|
||||
"prioritySoc": {},
|
||||
"bufferSoc": {}
|
||||
}
|
||||
},
|
||||
"loadpoints": {
|
||||
|
|
@ -136,7 +138,9 @@
|
|||
"description": "List of loadpoints",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["charger"],
|
||||
"required": [
|
||||
"charger"
|
||||
],
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string"
|
||||
|
|
@ -220,7 +224,10 @@
|
|||
},
|
||||
"namedObject": {
|
||||
"type": "object",
|
||||
"required": ["name", "type"],
|
||||
"required": [
|
||||
"name",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
|
|
@ -234,13 +241,28 @@
|
|||
}
|
||||
},
|
||||
"loglevel": {
|
||||
"enum": ["trace", "debug", "info", "error", "fatal"]
|
||||
"enum": [
|
||||
"trace",
|
||||
"debug",
|
||||
"info",
|
||||
"error",
|
||||
"fatal"
|
||||
]
|
||||
},
|
||||
"mode": {
|
||||
"enum": ["off", "now", "pv", "minpv"]
|
||||
"enum": [
|
||||
"off",
|
||||
"now",
|
||||
"pv",
|
||||
"minpv"
|
||||
]
|
||||
},
|
||||
"pollMode": {
|
||||
"enum": ["always", "charging", "connected"]
|
||||
"enum": [
|
||||
"always",
|
||||
"charging",
|
||||
"connected"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,8 +97,8 @@ func (s *HTTPd) RegisterSiteHandlers(site site.API, cache *util.Cache) {
|
|||
routes := map[string]route{
|
||||
"health": {[]string{"GET"}, "/health", healthHandler(site)},
|
||||
"state": {[]string{"GET"}, "/state", stateHandler(cache)},
|
||||
"buffersoc": {[]string{"POST", "OPTIONS"}, "/buffersoc/{value:[0-9.]+}", floatHandler(site.SetBufferSoC, site.GetBufferSoC)},
|
||||
"prioritysoc": {[]string{"POST", "OPTIONS"}, "/prioritysoc/{value:[0-9.]+}", floatHandler(site.SetPrioritySoC, site.GetPrioritySoC)},
|
||||
"buffersoc": {[]string{"POST", "OPTIONS"}, "/buffersoc/{value:[0-9.]+}", floatHandler(site.SetBufferSoc, site.GetBufferSoc)},
|
||||
"prioritysoc": {[]string{"POST", "OPTIONS"}, "/prioritysoc/{value:[0-9.]+}", floatHandler(site.SetPrioritySoc, site.GetPrioritySoc)},
|
||||
"residualpower": {[]string{"POST", "OPTIONS"}, "/residualpower/{value:[-0-9.]+}", floatHandler(site.SetResidualPower, site.GetResidualPower)},
|
||||
"sessions": {[]string{"GET"}, "/sessions", sessionHandler},
|
||||
"telemetry": {[]string{"GET"}, "/settings/telemetry", boolGetHandler(telemetry.Enabled)},
|
||||
|
|
@ -110,14 +110,14 @@ func (s *HTTPd) RegisterSiteHandlers(site site.API, cache *util.Cache) {
|
|||
}
|
||||
|
||||
// loadpoint api
|
||||
for id, lp := range site.LoadPoints() {
|
||||
for id, lp := range site.Loadpoints() {
|
||||
loadpoint := api.PathPrefix(fmt.Sprintf("/loadpoints/%d", id+1)).Subrouter()
|
||||
|
||||
routes := map[string]route{
|
||||
"mode": {[]string{"POST", "OPTIONS"}, "/mode/{value:[a-z]+}", chargeModeHandler(lp)},
|
||||
"targetenergy": {[]string{"POST", "OPTIONS"}, "/targetenergy/{value:[0-9.]+}", floatHandler(pass(lp.SetTargetEnergy), lp.GetTargetEnergy)},
|
||||
"targetsoc": {[]string{"POST", "OPTIONS"}, "/targetsoc/{value:[0-9]+}", intHandler(pass(lp.SetTargetSoC), lp.GetTargetSoC)},
|
||||
"minsoc": {[]string{"POST", "OPTIONS"}, "/minsoc/{value:[0-9]+}", intHandler(pass(lp.SetMinSoC), lp.GetMinSoC)},
|
||||
"targetsoc": {[]string{"POST", "OPTIONS"}, "/targetsoc/{value:[0-9]+}", intHandler(pass(lp.SetTargetSoc), lp.GetTargetSoc)},
|
||||
"minsoc": {[]string{"POST", "OPTIONS"}, "/minsoc/{value:[0-9]+}", intHandler(pass(lp.SetMinSoc), lp.GetMinSoc)},
|
||||
"mincurrent": {[]string{"POST", "OPTIONS"}, "/mincurrent/{value:[0-9.]+}", floatHandler(pass(lp.SetMinCurrent), lp.GetMinCurrent)},
|
||||
"maxcurrent": {[]string{"POST", "OPTIONS"}, "/maxcurrent/{value:[0-9.]+}", floatHandler(pass(lp.SetMaxCurrent), lp.GetMaxCurrent)},
|
||||
"phases": {[]string{"POST", "OPTIONS"}, "/phases/{value:[0-9]+}", phasesHandler(lp)},
|
||||
|
|
|
|||
|
|
@ -303,10 +303,10 @@ func targetChargeHandler(loadpoint targetCharger) http.HandlerFunc {
|
|||
}
|
||||
|
||||
res := struct {
|
||||
SoC int `json:"soc"`
|
||||
Soc int `json:"soc"`
|
||||
Time time.Time `json:"time"`
|
||||
}{
|
||||
SoC: socV,
|
||||
Soc: socV,
|
||||
Time: timeV,
|
||||
}
|
||||
|
||||
|
|
@ -380,6 +380,6 @@ func socketHandler(hub *SocketHub) http.HandlerFunc {
|
|||
|
||||
// TargetCharger defines target charge related loadpoint operations
|
||||
type targetCharger interface {
|
||||
// SetTargetCharge sets the charge targetSoC
|
||||
// SetTargetCharge sets the charge targetSoc
|
||||
SetTargetCharge(time.Time, int) error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,22 +11,22 @@ import (
|
|||
)
|
||||
|
||||
type mockLoadpoint struct {
|
||||
SoC int
|
||||
Soc int
|
||||
TargetTime time.Time
|
||||
}
|
||||
|
||||
func (lp *mockLoadpoint) SetTargetCharge(time time.Time, soc int) error {
|
||||
lp.SoC = soc
|
||||
lp.Soc = soc
|
||||
lp.TargetTime = time
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestTargetChargeHandler(t *testing.T) {
|
||||
tc := []struct {
|
||||
inSoC string
|
||||
inSoc string
|
||||
inTime string
|
||||
statusCode int
|
||||
outSoC int
|
||||
outSoc int
|
||||
outTime time.Time
|
||||
}{
|
||||
{
|
||||
|
|
@ -42,13 +42,13 @@ func TestTargetChargeHandler(t *testing.T) {
|
|||
|
||||
handler := http.HandlerFunc(targetChargeHandler(mockLp))
|
||||
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("/targetcharge/%s/%s", tc.inSoC, tc.inTime), nil)
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("/targetcharge/%s/%s", tc.inSoc, tc.inTime), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
vars := map[string]string{
|
||||
"soc": tc.inSoC,
|
||||
"soc": tc.inSoc,
|
||||
"time": tc.inTime,
|
||||
}
|
||||
|
||||
|
|
@ -62,8 +62,8 @@ func TestTargetChargeHandler(t *testing.T) {
|
|||
status, tc.statusCode)
|
||||
}
|
||||
|
||||
if mockLp.SoC != tc.outSoC {
|
||||
t.Errorf("wrong target soc: got %v want %v", mockLp.SoC, tc.outSoC)
|
||||
if mockLp.Soc != tc.outSoc {
|
||||
t.Errorf("wrong target soc: got %v want %v", mockLp.Soc, tc.outSoc)
|
||||
}
|
||||
|
||||
isoFormat := "2006-01-02T15:04:05.999Z07:00"
|
||||
|
|
|
|||
|
|
@ -90,9 +90,9 @@ func (m *Influx) Run(loadPoints []loadpoint.API, in <-chan util.Param) {
|
|||
// add points to batch for async writing
|
||||
for param := range in {
|
||||
// vehicle name
|
||||
if param.LoadPoint != nil {
|
||||
if param.Loadpoint != nil {
|
||||
if name, ok := param.Val.(string); ok && param.Key == "vehicleTitle" {
|
||||
vehicles[*param.LoadPoint] = name
|
||||
vehicles[*param.Loadpoint] = name
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
@ -102,9 +102,9 @@ func (m *Influx) Run(loadPoints []loadpoint.API, in <-chan util.Param) {
|
|||
}
|
||||
|
||||
tags := map[string]string{}
|
||||
if param.LoadPoint != nil {
|
||||
tags["loadpoint"] = loadPoints[*param.LoadPoint].Name()
|
||||
tags["vehicle"] = vehicles[*param.LoadPoint]
|
||||
if param.Loadpoint != nil {
|
||||
tags["loadpoint"] = loadPoints[*param.Loadpoint].Name()
|
||||
tags["vehicle"] = vehicles[*param.Loadpoint]
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{}
|
||||
|
|
|
|||
|
|
@ -109,14 +109,14 @@ func (m *MQTT) listenSetters(topic string, site site.API, lp loadpoint.API) {
|
|||
m.Handler.ListenSetter(topic+"/mode/set", func(payload string) {
|
||||
lp.SetMode(api.ChargeMode(payload))
|
||||
})
|
||||
m.Handler.ListenSetter(topic+"/minSoC/set", func(payload string) {
|
||||
m.Handler.ListenSetter(topic+"/minSoc/set", func(payload string) {
|
||||
if soc, err := strconv.Atoi(payload); err == nil {
|
||||
lp.SetMinSoC(soc)
|
||||
lp.SetMinSoc(soc)
|
||||
}
|
||||
})
|
||||
m.Handler.ListenSetter(topic+"/targetSoC/set", func(payload string) {
|
||||
m.Handler.ListenSetter(topic+"/targetSoc/set", func(payload string) {
|
||||
if soc, err := strconv.Atoi(payload); err == nil {
|
||||
lp.SetTargetSoC(soc)
|
||||
lp.SetTargetSoc(soc)
|
||||
}
|
||||
})
|
||||
m.Handler.ListenSetter(topic+"/minCurrent/set", func(payload string) {
|
||||
|
|
@ -154,15 +154,15 @@ func (m *MQTT) Run(site site.API, in <-chan util.Param) {
|
|||
m.publish(topic, true, "online")
|
||||
|
||||
// site setters
|
||||
m.Handler.ListenSetter(fmt.Sprintf("%s/site/prioritySoC/set", m.root), func(payload string) {
|
||||
m.Handler.ListenSetter(fmt.Sprintf("%s/site/prioritySoc/set", m.root), func(payload string) {
|
||||
if soc, err := strconv.Atoi(payload); err == nil {
|
||||
_ = site.SetPrioritySoC(float64(soc))
|
||||
_ = site.SetPrioritySoc(float64(soc))
|
||||
}
|
||||
})
|
||||
|
||||
m.Handler.ListenSetter(fmt.Sprintf("%s/site/bufferSoC/set", m.root), func(payload string) {
|
||||
m.Handler.ListenSetter(fmt.Sprintf("%s/site/bufferSoc/set", m.root), func(payload string) {
|
||||
if soc, err := strconv.Atoi(payload); err == nil {
|
||||
_ = site.SetBufferSoC(float64(soc))
|
||||
_ = site.SetBufferSoc(float64(soc))
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -174,16 +174,16 @@ func (m *MQTT) Run(site site.API, in <-chan util.Param) {
|
|||
|
||||
// number of loadpoints
|
||||
topic = fmt.Sprintf("%s/loadpoints", m.root)
|
||||
m.publish(topic, true, len(site.LoadPoints()))
|
||||
m.publish(topic, true, len(site.Loadpoints()))
|
||||
|
||||
// loadpoint setters
|
||||
for id, lp := range site.LoadPoints() {
|
||||
for id, lp := range site.Loadpoints() {
|
||||
topic := fmt.Sprintf("%s/loadpoints/%d", m.root, id+1)
|
||||
m.listenSetters(topic, site, lp)
|
||||
}
|
||||
|
||||
// TODO remove deprecated topics
|
||||
for id := range site.LoadPoints() {
|
||||
for id := range site.Loadpoints() {
|
||||
topic := fmt.Sprintf("%s/loadpoints/%d", m.root, id+1)
|
||||
for _, dep := range []string{"activePhases", "range", "socCharge", "vehicleSoc"} {
|
||||
m.publish(fmt.Sprintf("%s/%s", topic, dep), true, nil)
|
||||
|
|
@ -202,8 +202,8 @@ func (m *MQTT) Run(site site.API, in <-chan util.Param) {
|
|||
// publish
|
||||
for p := range in {
|
||||
topic := fmt.Sprintf("%s/site", m.root)
|
||||
if p.LoadPoint != nil {
|
||||
id := *p.LoadPoint + 1
|
||||
if p.Loadpoint != nil {
|
||||
id := *p.Loadpoint + 1
|
||||
topic = fmt.Sprintf("%s/loadpoints/%d", m.root, id)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -122,8 +122,8 @@ func kv(p util.Param) string {
|
|||
|
||||
var msg strings.Builder
|
||||
msg.WriteString("\"")
|
||||
if p.LoadPoint != nil {
|
||||
msg.WriteString(fmt.Sprintf("loadpoints.%d.", *p.LoadPoint))
|
||||
if p.Loadpoint != nil {
|
||||
msg.WriteString(fmt.Sprintf("loadpoints.%d.", *p.Loadpoint))
|
||||
}
|
||||
msg.WriteString(p.Key)
|
||||
msg.WriteString("\":")
|
||||
|
|
|
|||
|
|
@ -166,8 +166,8 @@ params:
|
|||
valuetype: chargemodes
|
||||
- name: minsoc
|
||||
description:
|
||||
de: Minimaler Ladestand (SoC) in %
|
||||
en: Minimum state of charge (SoC) in %
|
||||
de: Minimaler Ladestand (Soc) in %
|
||||
en: Minimum state of charge (Soc) in %
|
||||
help:
|
||||
de: Lade sofort mit maximaler Geschwindigkeit bis zu dem angegeben Ladestand, wenn der Lademodus nicht auf 'Aus' steht
|
||||
en: Charge immediately with maximum power up to the defined state of charge, if the charge mode is not set to 'OFF'
|
||||
|
|
@ -175,11 +175,11 @@ params:
|
|||
valuetype: number
|
||||
- name: targetsoc
|
||||
description:
|
||||
de: Ziel-Ladestand (SoC) in %
|
||||
en: Target state of charge (SoC) in %
|
||||
de: Ziel-Ladestand (Soc) in %
|
||||
en: Target state of charge (Soc) in %
|
||||
help:
|
||||
de: Bis zu welchem Ladestand (SoC) soll das Fahrzeug geladen werden
|
||||
en: Until which state of charge (SoC) should the vehicle be charged
|
||||
de: Bis zu welchem Ladestand (Soc) soll das Fahrzeug geladen werden
|
||||
en: Until which state of charge (Soc) should the vehicle be charged
|
||||
example: 80
|
||||
valuetype: number
|
||||
- name: mincurrent
|
||||
|
|
@ -300,9 +300,9 @@ presets:
|
|||
params:
|
||||
- name: mode
|
||||
advanced: true
|
||||
- name: minSoC
|
||||
- name: minSoc
|
||||
advanced: true
|
||||
- name: targetSoC
|
||||
- name: targetSoc
|
||||
advanced: true
|
||||
- name: minCurrent
|
||||
advanced: true
|
||||
|
|
@ -313,16 +313,16 @@ presets:
|
|||
valuetype: stringlist
|
||||
render: |
|
||||
{{define "vehicle-identify"}}
|
||||
{{- if or (ne .mode "") (ne .minSoC "") (ne .targetSoC "") (ne .minCurrent "") (ne .maxCurrent "") }}
|
||||
{{- if or (ne .mode "") (ne .minSoc "") (ne .targetSoc "") (ne .minCurrent "") (ne .maxCurrent "") }}
|
||||
onIdentify:
|
||||
{{- if (ne .mode "") }}
|
||||
mode: {{ .mode }}
|
||||
{{- end }}
|
||||
{{- if (ne .minSoC "") }}
|
||||
minSoC: {{ .minSoC }}
|
||||
{{- if (ne .minSoc "") }}
|
||||
minSoc: {{ .minSoc }}
|
||||
{{- end }}
|
||||
{{- if (ne .targetSoC "") }}
|
||||
targetSoC: {{ .targetSoC }}
|
||||
{{- if (ne .targetSoc "") }}
|
||||
targetSoc: {{ .targetSoc }}
|
||||
{{- end }}
|
||||
{{- if (ne .minCurrent "") }}
|
||||
minCurrent: {{ .minCurrent }}
|
||||
|
|
|
|||
|
|
@ -41,5 +41,5 @@ render: |
|
|||
{{- include "modbus" . }}
|
||||
model: sunspec
|
||||
power: 802:W # sunspec model 802 battery
|
||||
soc: 802:SoC # sunspec model 802 battery
|
||||
soc: 802:Soc # sunspec model 802 battery
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ render: |
|
|||
uri: {{ .host }}:{{ .port }} # Port 502
|
||||
id: 2
|
||||
register:
|
||||
address: 30845 # Battery SoC, %
|
||||
address: 30845 # Battery Soc, %
|
||||
type: holding
|
||||
decode: uint32nan
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ render: |
|
|||
source: modbus
|
||||
{{- include "modbus" . | indent 2 }}
|
||||
register: # manual non-sunspec register configuration
|
||||
address: 122 # Batterie SoC
|
||||
address: 122 # Batterie Soc
|
||||
type: input
|
||||
decode: int16
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -26,5 +26,5 @@ render: |
|
|||
soc:
|
||||
source: http
|
||||
uri: http://{{ .host }}:{{ .port }}/
|
||||
jq: .SData.SoC
|
||||
jq: .SData.Soc
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ render: |
|
|||
uri: {{ .host }}:{{ .port }}
|
||||
id: 100 # com.victronenergy.system
|
||||
register:
|
||||
address: 843 # SoC
|
||||
address: 843 # Soc
|
||||
type: input
|
||||
decode: uint16
|
||||
{{- end }}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue