Rename SoC to Soc and LoadPoint to Loadpoint (#5549)

This commit is contained in:
andig 2022-12-23 10:19:23 +01:00 • committed by GitHub
parent 54f204c0f8
commit 03d6d1418a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
165 changed files with 1054 additions and 1032 deletions

View file

@ -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

View file

@ -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:

View file

@ -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{},

View file

@ -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;
}

View file

@ -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>

View file

@ -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: () => {

View file

@ -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);
},
},

View file

@ -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 },
},

View file

@ -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,

View file

@ -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"));

View file

@ -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 = [];

View file

@ -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,

View file

@ -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"],

View file

@ -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)}%`;
},
},

View file

@ -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) {

View file

@ -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" />

View file

@ -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);

View file

@ -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 });
},
},

View file

@ -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");
}

View file

@ -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,
},
],
};

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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()
}

View file

@ -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 {

View file

@ -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
}

View file

@ -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

View file

@ -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("")

View file

@ -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)
}
}

View file

@ -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

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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 {

View file

@ -9,5 +9,5 @@ const (
vehicleRange = "vehicleRange" // vehicle range
vehicleOdometer = "vehicleOdometer" // vehicle odometer
vehicleTargetSoC = "vehicleTargetSoC" // vehicle soc limit
vehicleTargetSoc = "vehicleTargetSoc" // vehicle soc limit
)

View file

@ -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()
}

View file

@ -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)

View file

@ -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
}

View file

@ -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)

View file

@ -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()
}

View file

@ -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 {

View file

@ -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

View file

@ -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,

View file

@ -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,

View file

@ -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
}
}

View file

@ -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

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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)
}
}

View file

@ -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
View file

@ -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>

View file

@ -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?

View file

@ -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

View file

@ -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
}

View file

@ -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."

View file

@ -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."

View file

@ -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."

View file

@ -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."

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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."

View file

@ -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."

View file

@ -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å"

View file

@ -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."

View file

@ -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."

View file

@ -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}%"

View file

@ -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
}

View file

@ -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()
}

View file

@ -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

View file

@ -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()
}

View file

@ -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

View file

@ -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
}

View file

@ -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()
}

View file

@ -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)
}

View file

@ -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()
}

View file

@ -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)

View file

@ -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

View file

@ -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()
}

View file

@ -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
}

View file

@ -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()
}

View file

@ -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) {

View file

@ -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()
}

View file

@ -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))
}

View file

@ -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
}
}

View file

@ -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"
]
}
}
}

View file

@ -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)},

View file

@ -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
}

View file

@ -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"

View file

@ -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{}{}

View file

@ -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)
}

View file

@ -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("\":")

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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 }}

View file

@ -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 }}

View file

@ -26,5 +26,5 @@ render: |
soc:
source: http
uri: http://{{ .host }}:{{ .port }}/
jq: .SData.SoC
jq: .SData.Soc
{{- end }}

View file

@ -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