Replace combinatorial decorators with capability registry (BC) (#28565)
This commit is contained in:
parent
33d2de24c1
commit
00967eb57e
101 changed files with 2416 additions and 44670 deletions
|
|
@ -121,7 +121,7 @@ func New(log *util.Logger, title string, maxCurrent, maxPower float64, meter api
|
|||
|
||||
if maxCurrent == 0 {
|
||||
c.log.DEBUG.Printf("validation of max phase current disabled")
|
||||
} else if _, ok := meter.(api.PhaseCurrents); meter != nil && !ok {
|
||||
} else if meter != nil && !api.HasCap[api.PhaseCurrents](meter) {
|
||||
return nil, errors.New("meter does not support phase currents")
|
||||
}
|
||||
|
||||
|
|
@ -263,7 +263,7 @@ func (c *Circuit) updateMeters() error {
|
|||
return fmt.Errorf("circuit power: %w", err)
|
||||
}
|
||||
|
||||
if phaseMeter, ok := c.meter.(api.PhaseCurrents); ok {
|
||||
if phaseMeter, ok := api.Cap[api.PhaseCurrents](c.meter); ok {
|
||||
var i1, i2, i3 float64
|
||||
if err := backoff.Retry(func() error {
|
||||
var err error
|
||||
|
|
@ -275,7 +275,7 @@ func (c *Circuit) updateMeters() error {
|
|||
}
|
||||
|
||||
var p1, p2, p3 float64
|
||||
if phaseMeter, ok := c.meter.(api.PhasePowers); ok {
|
||||
if phaseMeter, ok := api.Cap[api.PhasePowers](c.meter); ok {
|
||||
var err error // phases needed for signed currents
|
||||
if p1, p2, p3, err = phaseMeter.Powers(); err != nil {
|
||||
return fmt.Errorf("circuit powers: %w", err)
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ func (c *Coordinator) availableDetectibleVehicles(owner loadpoint.API) []api.Veh
|
|||
|
||||
for _, vv := range c.vehicles {
|
||||
// status api available
|
||||
if _, ok := vv.(api.ChargeState); ok {
|
||||
if api.HasCap[api.ChargeState](vv) {
|
||||
// available or associated to current loadpoint
|
||||
if o, ok := c.tracked[vv]; o == owner || !ok {
|
||||
res = append(res, vv)
|
||||
|
|
@ -135,7 +135,7 @@ func (c *Coordinator) identifyVehicleByStatus(available []api.Vehicle) api.Vehic
|
|||
defer c.mu.RUnlock()
|
||||
|
||||
for _, vehicle := range available {
|
||||
if vs, ok := vehicle.(api.ChargeState); ok {
|
||||
if vs, ok := api.Cap[api.ChargeState](vehicle); ok {
|
||||
status, err := vs.Status()
|
||||
if err != nil {
|
||||
if !loadpoint.AcceptableError(err) {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ func ptrValueEqual[T comparable](a, b *T) bool {
|
|||
|
||||
// hasFeature returns true if features are supported and given feature present
|
||||
func hasFeature(a any, f api.Feature) bool {
|
||||
c, ok := a.(api.FeatureDescriber)
|
||||
c, ok := api.Cap[api.FeatureDescriber](a)
|
||||
return ok && slices.Contains(c.Features(), f)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -403,7 +403,7 @@ func (lp *Loadpoint) configureChargerType(charger api.Charger) {
|
|||
if lp.chargeMeter == nil {
|
||||
integrated = true
|
||||
|
||||
if mt, ok := charger.(api.Meter); ok {
|
||||
if mt, ok := api.Cap[api.Meter](charger); ok {
|
||||
lp.chargeMeter = mt
|
||||
} else {
|
||||
mt := new(wrapper.ChargeMeter)
|
||||
|
|
@ -416,7 +416,7 @@ func (lp *Loadpoint) configureChargerType(charger api.Charger) {
|
|||
// ensure charge rater exists
|
||||
// measurement are obtained from separate charge meter if defined
|
||||
// (https://github.com/evcc-io/evcc/issues/2469)
|
||||
if rt, ok := charger.(api.ChargeRater); ok && integrated {
|
||||
if rt, ok := api.Cap[api.ChargeRater](charger); ok && integrated {
|
||||
lp.chargeRater = rt
|
||||
|
||||
// when restarting in the middle of charging session, use this as negative offset
|
||||
|
|
@ -433,7 +433,7 @@ func (lp *Loadpoint) configureChargerType(charger api.Charger) {
|
|||
}
|
||||
|
||||
// ensure charge timer exists
|
||||
if ct, ok := charger.(api.ChargeTimer); ok {
|
||||
if ct, ok := api.Cap[api.ChargeTimer](charger); ok {
|
||||
lp.chargeTimer = ct
|
||||
} else {
|
||||
ct := wrapper.NewChargeTimer()
|
||||
|
|
@ -674,7 +674,7 @@ func (lp *Loadpoint) Prepare(site site.API, uiChan chan<- util.Param, pushChan c
|
|||
}
|
||||
|
||||
// charger icon
|
||||
if c, ok := lp.charger.(api.IconDescriber); ok {
|
||||
if c, ok := api.Cap[api.IconDescriber](lp.charger); ok {
|
||||
lp.publish(keys.ChargerIcon, c.Icon())
|
||||
} else {
|
||||
lp.publish(keys.ChargerIcon, nil)
|
||||
|
|
@ -777,7 +777,7 @@ func (lp *Loadpoint) syncCharger() error {
|
|||
)
|
||||
|
||||
// use chargers actual set current if available
|
||||
cg, isCg := lp.charger.(api.CurrentGetter)
|
||||
cg, isCg := api.Cap[api.CurrentGetter](lp.charger)
|
||||
if isCg {
|
||||
if current, err = cg.GetMaxCurrent(); err == nil {
|
||||
// smallest adjustment most PWM-Controllers can do is: 100%÷256×0,6A = 0.234A
|
||||
|
|
@ -806,7 +806,7 @@ func (lp *Loadpoint) syncCharger() error {
|
|||
}
|
||||
|
||||
// sync phases
|
||||
_, isPs := lp.charger.(api.PhaseSwitcher)
|
||||
_, isPs := api.Cap[api.PhaseSwitcher](lp.charger)
|
||||
if phases := lp.GetPhases(); isPs && shouldBeConsistent && phases > 0 {
|
||||
// fallback to active phases from measured phases
|
||||
chargerPhases := lp.measuredPhases
|
||||
|
|
@ -814,7 +814,7 @@ func (lp *Loadpoint) syncCharger() error {
|
|||
chargerPhases = 3
|
||||
}
|
||||
|
||||
pg, isPg := lp.charger.(api.PhaseGetter)
|
||||
pg, isPg := api.Cap[api.PhaseGetter](lp.charger)
|
||||
if isPg {
|
||||
if chargerPhases, err = pg.GetPhases(); err == nil {
|
||||
if chargerPhases > 0 && chargerPhases != phases {
|
||||
|
|
@ -858,8 +858,7 @@ func (lp *Loadpoint) syncCharger() error {
|
|||
|
||||
// coarseCurrent returns true if charger or vehicle require full amp steps
|
||||
func (lp *Loadpoint) coarseCurrent() bool {
|
||||
_, ok := lp.charger.(api.ChargerEx)
|
||||
return !ok || lp.vehicleHasFeature(api.CoarseCurrent)
|
||||
return !api.HasCap[api.ChargerEx](lp.charger) || lp.vehicleHasFeature(api.CoarseCurrent)
|
||||
}
|
||||
|
||||
// roundedCurrent rounds current down to full amps if charger or vehicle require it
|
||||
|
|
@ -902,7 +901,7 @@ func (lp *Loadpoint) setLimit(current float64) error {
|
|||
// set current
|
||||
if current != lp.offeredCurrent && current >= effMinCurrent {
|
||||
var err error
|
||||
if charger, ok := lp.charger.(api.ChargerEx); ok {
|
||||
if charger, ok := api.Cap[api.ChargerEx](lp.charger); ok {
|
||||
err = charger.MaxCurrentMillis(current)
|
||||
} else {
|
||||
err = lp.charger.MaxCurrent(int64(current))
|
||||
|
|
@ -910,7 +909,7 @@ func (lp *Loadpoint) setLimit(current float64) error {
|
|||
|
||||
if err != nil {
|
||||
v := lp.GetVehicle()
|
||||
if vv, ok := v.(api.Resurrector); ok && errors.Is(err, api.ErrAsleep) {
|
||||
if vv, ok := api.Cap[api.Resurrector](v); ok && errors.Is(err, api.ErrAsleep) {
|
||||
// https://github.com/evcc-io/evcc/issues/8254
|
||||
// wakeup vehicle
|
||||
lp.log.DEBUG.Printf("set charge current limit: waking up vehicle")
|
||||
|
|
@ -931,7 +930,7 @@ func (lp *Loadpoint) setLimit(current float64) error {
|
|||
if enabled := current >= effMinCurrent; enabled != lp.enabled {
|
||||
if err := lp.charger.Enable(enabled); err != nil {
|
||||
v := lp.GetVehicle()
|
||||
if vv, ok := v.(api.Resurrector); enabled && ok && errors.Is(err, api.ErrAsleep) {
|
||||
if vv, ok := api.Cap[api.Resurrector](v); enabled && ok && errors.Is(err, api.ErrAsleep) {
|
||||
// https://github.com/evcc-io/evcc/issues/8254
|
||||
// wakeup vehicle
|
||||
lp.log.DEBUG.Printf("charger %s: waking up vehicle", status[enabled])
|
||||
|
|
@ -1140,7 +1139,7 @@ func (lp *Loadpoint) getStatusChanges() ([]api.ChargeStatus, error) {
|
|||
}
|
||||
|
||||
// check charger connection duration
|
||||
if ct, ok := lp.charger.(api.ConnectionTimer); ok {
|
||||
if ct, ok := api.Cap[api.ConnectionTimer](lp.charger); ok {
|
||||
d, err := ct.ConnectionDuration()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connection duration: %w", err)
|
||||
|
|
@ -1252,7 +1251,7 @@ func (lp *Loadpoint) scalePhasesIfAvailable(phases int) error {
|
|||
// 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 {
|
||||
cp, ok := lp.charger.(api.PhaseSwitcher)
|
||||
cp, ok := api.Cap[api.PhaseSwitcher](lp.charger)
|
||||
if !ok {
|
||||
panic("charger does not implement api.PhaseSwitcher")
|
||||
}
|
||||
|
|
@ -1589,7 +1588,7 @@ func (lp *Loadpoint) UpdateChargePowerAndCurrents() float64 {
|
|||
// update charge currents
|
||||
lp.chargeCurrents = nil
|
||||
|
||||
if phaseMeter, ok := lp.chargeMeter.(api.PhaseCurrents); ok {
|
||||
if phaseMeter, ok := api.Cap[api.PhaseCurrents](lp.chargeMeter); ok {
|
||||
if err := backoff.Retry(func() error {
|
||||
i1, i2, i3, err := phaseMeter.Currents()
|
||||
if err != nil {
|
||||
|
|
@ -1642,7 +1641,7 @@ func (lp *Loadpoint) phasesFromChargeCurrents() {
|
|||
|
||||
// updateChargeVoltages uses PhaseVoltages interface to count phases with nominal grid voltage
|
||||
func (lp *Loadpoint) updateChargeVoltages() {
|
||||
phaseMeter, ok := lp.chargeMeter.(api.PhaseVoltages)
|
||||
phaseMeter, ok := api.Cap[api.PhaseVoltages](lp.chargeMeter)
|
||||
if !ok {
|
||||
return // don't guess
|
||||
}
|
||||
|
|
@ -1716,7 +1715,7 @@ func (lp *Loadpoint) publishChargeProgress() {
|
|||
// TODO deprecated: use sessionEnergy instead
|
||||
lp.publish(keys.ChargedEnergy, lp.GetChargedEnergy())
|
||||
lp.publish(keys.ChargeDuration, lp.chargeDuration)
|
||||
if _, ok := lp.chargeMeter.(api.MeterEnergy); ok {
|
||||
if api.HasCap[api.MeterEnergy](lp.chargeMeter) {
|
||||
lp.publish(keys.ChargeTotalImport, lp.chargeMeterTotal())
|
||||
}
|
||||
}
|
||||
|
|
@ -1739,14 +1738,14 @@ func (lp *Loadpoint) publishSocAndRange() {
|
|||
var socR *float64
|
||||
var limitR *int64
|
||||
|
||||
if battery, ok := dev.(api.Battery); ok {
|
||||
if battery, ok := api.Cap[api.Battery](dev); ok {
|
||||
if soc, err := soc.Guard(battery.Soc()); err == nil {
|
||||
socR = &soc
|
||||
|
||||
// don't publish here in case it needs be updated by the estimator
|
||||
lp.log.DEBUG.Printf("%s soc: %.0f%%", typ, soc)
|
||||
|
||||
if socLimiter, ok := dev.(api.SocLimiter); ok {
|
||||
if socLimiter, ok := api.Cap[api.SocLimiter](dev); ok {
|
||||
if limit, err := socLimiter.GetLimitSoc(); err == nil {
|
||||
limitR = &limit
|
||||
|
||||
|
|
@ -1771,7 +1770,7 @@ func (lp *Loadpoint) publishSocAndRange() {
|
|||
socR, limitR = socAndLimit("vehicle", lp.GetVehicle())
|
||||
|
||||
// range
|
||||
if vs, ok := lp.GetVehicle().(api.VehicleRange); ok {
|
||||
if vs, ok := api.Cap[api.VehicleRange](lp.GetVehicle()); ok {
|
||||
if rng, err := vs.Range(); err == nil {
|
||||
lp.log.DEBUG.Printf("vehicle range: %dkm", rng)
|
||||
lp.publish(keys.VehicleRange, rng)
|
||||
|
|
@ -1915,7 +1914,7 @@ func (lp *Loadpoint) Update(sitePower, batteryBoostPower float64, consumption, f
|
|||
lp.PublishEffectiveValues()
|
||||
|
||||
// §14a
|
||||
if dimmer, ok := lp.charger.(api.Dimmer); ok {
|
||||
if dimmer, ok := api.Cap[api.Dimmer](lp.charger); ok {
|
||||
dimmed, err := dimmer.Dimmed()
|
||||
if err != nil {
|
||||
lp.log.ERROR.Printf("dimmed: %v", err)
|
||||
|
|
@ -1952,7 +1951,7 @@ func (lp *Loadpoint) Update(sitePower, batteryBoostPower float64, consumption, f
|
|||
|
||||
lp.resetHeatingSession()
|
||||
|
||||
if sr, ok := lp.charger.(api.StatusReasoner); ok && lp.GetStatus() == api.StatusB {
|
||||
if sr, ok := api.Cap[api.StatusReasoner](lp.charger); ok && lp.GetStatus() == api.StatusB {
|
||||
if r, err := sr.StatusReason(); err == nil {
|
||||
lp.publish(keys.ChargerStatusReason, r)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ func (lp *Loadpoint) effectiveMinCurrent() float64 {
|
|||
}
|
||||
}
|
||||
|
||||
if c, ok := lp.charger.(api.CurrentLimiter); ok {
|
||||
if c, ok := api.Cap[api.CurrentLimiter](lp.charger); ok {
|
||||
if res, _, err := c.GetMinMaxCurrent(); err == nil {
|
||||
chargerMin = res
|
||||
}
|
||||
|
|
@ -181,7 +181,7 @@ func (lp *Loadpoint) effectiveMaxCurrent() float64 {
|
|||
}
|
||||
}
|
||||
|
||||
if c, ok := lp.charger.(api.CurrentLimiter); ok {
|
||||
if c, ok := api.Cap[api.CurrentLimiter](lp.charger); ok {
|
||||
if _, res, err := c.GetMinMaxCurrent(); err == nil && res > 0 {
|
||||
maxCurrent = min(maxCurrent, res)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ func (lp *Loadpoint) getVehiclePhases() int {
|
|||
}
|
||||
|
||||
func (lp *Loadpoint) getChargerPhysicalPhases() int {
|
||||
if cc, ok := lp.charger.(api.PhaseDescriber); ok {
|
||||
if cc, ok := api.Cap[api.PhaseDescriber](lp.charger); ok {
|
||||
return cc.Phases()
|
||||
}
|
||||
|
||||
|
|
@ -159,6 +159,5 @@ func (lp *Loadpoint) getChargerPhysicalPhases() int {
|
|||
}
|
||||
|
||||
func (lp *Loadpoint) hasPhaseSwitching() bool {
|
||||
_, ok := lp.charger.(api.PhaseSwitcher)
|
||||
return ok
|
||||
return api.HasCap[api.PhaseSwitcher](lp.charger)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
func (lp *Loadpoint) chargeMeterTotal() float64 {
|
||||
m, ok := lp.chargeMeter.(api.MeterEnergy)
|
||||
m, ok := api.Cap[api.MeterEnergy](lp.chargeMeter)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ func (lp *Loadpoint) createSession() {
|
|||
lp.session.Vehicle = lp.GetTitle()
|
||||
}
|
||||
|
||||
if c, ok := lp.charger.(api.Identifier); ok {
|
||||
if c, ok := api.Cap[api.Identifier](lp.charger); ok {
|
||||
if id, err := c.Identify(); err == nil {
|
||||
lp.session.Identifier = id
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ func (lp *Loadpoint) setVehicleIdentifier(id string) {
|
|||
|
||||
// identifyVehicle reads vehicle identification from charger
|
||||
func (lp *Loadpoint) identifyVehicle() {
|
||||
identifier, ok := lp.charger.(api.Identifier)
|
||||
identifier, ok := api.Cap[api.Identifier](lp.charger)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
|
@ -176,8 +176,8 @@ func (lp *Loadpoint) setActiveVehicle(v api.Vehicle) {
|
|||
|
||||
func (lp *Loadpoint) wakeUpVehicle() {
|
||||
// wake up charger or vehicle. First wakeupAttemptsLeft will be odd.
|
||||
charger, chargerCanWakeUp := lp.charger.(api.Resurrector)
|
||||
vehicle, vehicleCanWakeUp := lp.GetVehicle().(api.Resurrector)
|
||||
charger, chargerCanWakeUp := api.Cap[api.Resurrector](lp.charger)
|
||||
vehicle, vehicleCanWakeUp := api.Cap[api.Resurrector](lp.GetVehicle())
|
||||
|
||||
if lp.wakeUpTimer.wakeupAttemptsLeft%2 != 0 {
|
||||
if chargerCanWakeUp {
|
||||
|
|
@ -296,14 +296,14 @@ func (lp *Loadpoint) identifyVehicleByStatus() {
|
|||
}
|
||||
|
||||
// remove previous vehicle if status was not confirmed
|
||||
if _, ok := lp.GetVehicle().(api.ChargeState); ok {
|
||||
if api.HasCap[api.ChargeState](lp.GetVehicle()) {
|
||||
lp.setActiveVehicle(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// vehicleOdometer updates odometer
|
||||
func (lp *Loadpoint) vehicleOdometer() {
|
||||
if vs, ok := lp.GetVehicle().(api.VehicleOdometer); ok {
|
||||
if vs, ok := api.Cap[api.VehicleOdometer](lp.GetVehicle()); ok {
|
||||
if odo, err := vs.Odometer(); err == nil {
|
||||
lp.log.DEBUG.Printf("vehicle odometer: %.0fkm", odo)
|
||||
lp.publish(keys.VehicleOdometer, odo)
|
||||
|
|
@ -361,7 +361,7 @@ func (lp *Loadpoint) vehicleSocPollAllowed() bool {
|
|||
|
||||
// vehicleClimateActive checks if vehicle has active climate request
|
||||
func (lp *Loadpoint) vehicleClimateActive() bool {
|
||||
if cl, ok := lp.GetVehicle().(api.VehicleClimater); ok && lp.vehicleClimatePollAllowed() {
|
||||
if cl, ok := api.Cap[api.VehicleClimater](lp.GetVehicle()); ok && lp.vehicleClimatePollAllowed() {
|
||||
active, err := cl.Climater()
|
||||
if err == nil {
|
||||
if active {
|
||||
|
|
|
|||
55
core/site.go
55
core/site.go
|
|
@ -346,13 +346,14 @@ func (site *Site) restoreSettings() error {
|
|||
}
|
||||
|
||||
func meterCapabilities(name string, meter any) string {
|
||||
if _, ok := meter.(api.Meter); !ok {
|
||||
power := api.HasCap[api.Meter](meter)
|
||||
|
||||
if !power {
|
||||
panic("not a meter: " + name)
|
||||
}
|
||||
|
||||
_, power := meter.(api.Meter)
|
||||
_, energy := meter.(api.MeterEnergy)
|
||||
_, currents := meter.(api.PhaseCurrents)
|
||||
energy := api.HasCap[api.MeterEnergy](meter)
|
||||
currents := api.HasCap[api.PhaseCurrents](meter)
|
||||
|
||||
name += ":"
|
||||
return fmt.Sprintf(" %-10s power %s energy %s currents %s",
|
||||
|
|
@ -368,7 +369,7 @@ func (site *Site) DumpConfig() {
|
|||
// verify vehicle detection
|
||||
if vehicles := site.Vehicles().Instances(); len(vehicles) > 1 {
|
||||
for _, v := range vehicles {
|
||||
if _, ok := v.(api.ChargeState); !ok && len(v.Identifiers()) == 0 {
|
||||
if !api.HasCap[api.ChargeState](v) && len(v.Identifiers()) == 0 {
|
||||
site.log.INFO.Printf("vehicle '%s' does not support automatic detection", v.GetTitle())
|
||||
}
|
||||
}
|
||||
|
|
@ -394,12 +395,12 @@ func (site *Site) DumpConfig() {
|
|||
if len(site.batteryMeters) > 0 {
|
||||
for i, dev := range site.batteryMeters {
|
||||
battery := dev.Instance()
|
||||
_, ok := battery.(api.Battery)
|
||||
_, hasCapacity := battery.(api.BatteryCapacity)
|
||||
isBattery := api.HasCap[api.Battery](battery)
|
||||
hasCapacity := api.HasCap[api.BatteryCapacity](battery)
|
||||
|
||||
site.log.INFO.Println(
|
||||
meterCapabilities(fmt.Sprintf("battery %d", i+1), battery),
|
||||
fmt.Sprintf("soc %s capacity %s", presence[ok], presence[hasCapacity]),
|
||||
fmt.Sprintf("soc %s capacity %s", presence[isBattery], presence[hasCapacity]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -408,11 +409,11 @@ func (site *Site) DumpConfig() {
|
|||
site.log.INFO.Println(" vehicles:")
|
||||
|
||||
for i, v := range vehicles {
|
||||
_, rng := v.(api.VehicleRange)
|
||||
_, finish := v.(api.VehicleFinishTimer)
|
||||
_, status := v.(api.ChargeState)
|
||||
_, climate := v.(api.VehicleClimater)
|
||||
_, wakeup := v.(api.Resurrector)
|
||||
_, rng := api.Cap[api.VehicleRange](v)
|
||||
_, finish := api.Cap[api.VehicleFinishTimer](v)
|
||||
_, status := api.Cap[api.ChargeState](v)
|
||||
_, climate := api.Cap[api.VehicleClimater](v)
|
||||
_, wakeup := api.Cap[api.Resurrector](v)
|
||||
site.log.INFO.Printf(" vehicle %d: range %s finish %s status %s climate %s wakeup %s",
|
||||
i+1, presence[rng], presence[finish], presence[status], presence[climate], presence[wakeup],
|
||||
)
|
||||
|
|
@ -435,11 +436,11 @@ func (site *Site) DumpConfig() {
|
|||
lp.log.INFO.Printf("loadpoint %d:", i+1)
|
||||
lp.log.INFO.Printf(" mode: %s", lp.GetMode())
|
||||
|
||||
_, power := lp.charger.(api.Meter)
|
||||
_, energy := lp.charger.(api.MeterEnergy)
|
||||
_, currents := lp.charger.(api.PhaseCurrents)
|
||||
_, phases := lp.charger.(api.PhaseSwitcher)
|
||||
_, wakeup := lp.charger.(api.Resurrector)
|
||||
_, power := api.Cap[api.Meter](lp.charger)
|
||||
_, energy := api.Cap[api.MeterEnergy](lp.charger)
|
||||
_, currents := api.Cap[api.PhaseCurrents](lp.charger)
|
||||
_, phases := api.Cap[api.PhaseSwitcher](lp.charger)
|
||||
_, wakeup := api.Cap[api.Resurrector](lp.charger)
|
||||
|
||||
lp.log.INFO.Printf(" charger: power %s energy %s currents %s phases %s wakeup %s",
|
||||
presence[power],
|
||||
|
|
@ -499,7 +500,7 @@ func (site *Site) collectMeters(key string, meters []config.Device[api.Meter]) [
|
|||
|
||||
// energy (production)
|
||||
var energy float64
|
||||
if m, ok := meter.(api.MeterEnergy); err == nil && ok {
|
||||
if m, ok := api.Cap[api.MeterEnergy](meter); err == nil && ok {
|
||||
energy, err = m.TotalEnergy()
|
||||
if err != nil {
|
||||
site.log.ERROR.Printf("%s %d energy: %v", key, i+1, err)
|
||||
|
|
@ -543,7 +544,7 @@ func (site *Site) updatePvMeters() {
|
|||
site.log.WARN.Printf("pv %d power: %.0fW is negative - check configuration if sign is correct", i+1, power)
|
||||
}
|
||||
|
||||
if m, ok := meter.(api.MaxACPowerGetter); ok {
|
||||
if m, ok := api.Cap[api.MaxACPowerGetter](meter); ok {
|
||||
if dc := power - m.MaxACPower(); dc > 0 && power > 0 {
|
||||
mm[i].ExcessDCPower = dc
|
||||
site.log.DEBUG.Printf("pv %d excess DC: %.0fW", i+1, dc)
|
||||
|
|
@ -611,13 +612,13 @@ func (site *Site) updateBatteryMeters() {
|
|||
meter := dev.Instance()
|
||||
|
||||
// battery soc and capacity
|
||||
if m, ok := meter.(api.Battery); ok {
|
||||
if m, ok := api.Cap[api.Battery](meter); ok {
|
||||
batSoc, err := soc.Guard(m.Soc())
|
||||
if err == nil {
|
||||
mm[i].Soc = new(batSoc)
|
||||
|
||||
if m, ok := m.(api.BatteryCapacity); ok {
|
||||
mm[i].Capacity = new(m.Capacity())
|
||||
if bc, ok := api.Cap[api.BatteryCapacity](meter); ok {
|
||||
mm[i].Capacity = new(bc.Capacity())
|
||||
}
|
||||
|
||||
site.log.DEBUG.Printf("battery %d soc: %.0f%%", i+1, batSoc)
|
||||
|
|
@ -626,7 +627,7 @@ func (site *Site) updateBatteryMeters() {
|
|||
}
|
||||
}
|
||||
|
||||
_, controllable := meter.(api.BatteryController)
|
||||
_, controllable := api.Cap[api.BatteryController](meter)
|
||||
mm[i].Controllable = new(controllable)
|
||||
}
|
||||
|
||||
|
|
@ -717,10 +718,10 @@ func (site *Site) updateGridMeter() error {
|
|||
}
|
||||
|
||||
// grid phase currents (signed)
|
||||
if phaseMeter, ok := site.gridMeter.(api.PhaseCurrents); ok {
|
||||
if phaseMeter, ok := api.Cap[api.PhaseCurrents](site.gridMeter); ok {
|
||||
// grid phase powers
|
||||
var p1, p2, p3 float64
|
||||
if phaseMeter, ok := site.gridMeter.(api.PhasePowers); ok {
|
||||
if phaseMeter, ok := api.Cap[api.PhasePowers](site.gridMeter); ok {
|
||||
var err error // phases needed for signed currents
|
||||
if p1, p2, p3, err = phaseMeter.Powers(); err == nil {
|
||||
mm.Powers = []float64{p1, p2, p3}
|
||||
|
|
@ -739,7 +740,7 @@ func (site *Site) updateGridMeter() error {
|
|||
}
|
||||
|
||||
// grid energy (import)
|
||||
if energyMeter, ok := site.gridMeter.(api.MeterEnergy); ok {
|
||||
if energyMeter, ok := api.Cap[api.MeterEnergy](site.gridMeter); ok {
|
||||
if f, err := energyMeter.TotalEnergy(); err == nil {
|
||||
mm.Energy = f
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ func (site *Site) hasBatteryControl() bool {
|
|||
for _, dev := range site.batteryMeters {
|
||||
meter := dev.Instance()
|
||||
|
||||
if _, ok := meter.(api.BatteryController); ok {
|
||||
if api.HasCap[api.BatteryController](meter) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -117,12 +117,12 @@ func (site *Site) requiredBatteryMode(batteryGridChargeActive bool, rate api.Rat
|
|||
func (site *Site) batteryMaxSocReached(dev config.Device[api.Meter]) (bool, error) {
|
||||
meter := dev.Instance()
|
||||
|
||||
batLimiter, ok := meter.(api.BatterySocLimiter)
|
||||
batLimiter, ok := api.Cap[api.BatterySocLimiter](meter)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
batSoc, ok := meter.(api.Battery)
|
||||
batSoc, ok := api.Cap[api.Battery](meter)
|
||||
if !ok {
|
||||
return false, errors.New("battery with soc limits must have soc")
|
||||
}
|
||||
|
|
@ -152,7 +152,7 @@ func (site *Site) applyBatteryMode(mode api.BatteryMode) error {
|
|||
for _, dev := range site.batteryMeters {
|
||||
meter := dev.Instance()
|
||||
|
||||
batCtrl, ok := meter.(api.BatteryController)
|
||||
batCtrl, ok := api.Cap[api.BatteryController](meter)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ func (site *Site) dimMeters(dim bool) error {
|
|||
var errs error
|
||||
|
||||
for _, dev := range slices.Concat(site.auxMeters, site.extMeters) {
|
||||
m, ok := dev.Instance().(api.Dimmer)
|
||||
m, ok := api.Cap[api.Dimmer](dev.Instance())
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
|
@ -92,7 +92,7 @@ func (site *Site) curtailPV(curtail bool) error {
|
|||
var errs error
|
||||
|
||||
for _, dev := range site.pvMeters {
|
||||
m, ok := dev.Instance().(api.Curtailer)
|
||||
m, ok := api.Cap[api.Curtailer](dev.Instance())
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -422,17 +422,17 @@ func (site *Site) batteryRequest(dev config.Device[api.Meter], b types.Measureme
|
|||
|
||||
instance := dev.Instance()
|
||||
|
||||
if _, ok := instance.(api.BatteryController); ok {
|
||||
if api.HasCap[api.BatteryController](instance) {
|
||||
bat.ChargeFromGrid = true
|
||||
}
|
||||
|
||||
if m, ok := instance.(api.BatteryPowerLimiter); ok {
|
||||
if m, ok := api.Cap[api.BatteryPowerLimiter](instance); ok {
|
||||
charge, discharge := m.GetPowerLimits()
|
||||
bat.CMax = float32(charge)
|
||||
bat.DMax = float32(discharge)
|
||||
}
|
||||
|
||||
if m, ok := instance.(api.BatterySocLimiter); ok {
|
||||
if m, ok := api.Cap[api.BatterySocLimiter](instance); ok {
|
||||
minSoc, maxSoc := m.GetSocLimits()
|
||||
bat.SMin = float32(*b.Capacity * minSoc * 10) // Wh
|
||||
bat.SMax = float32(*b.Capacity * maxSoc * 10) // Wh
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ func (cr *ChargeRater) StartCharge(continued bool) {
|
|||
cr.start = cr.clck.Now()
|
||||
|
||||
// get end energy amount
|
||||
if m, ok := cr.meter.(api.MeterEnergy); ok {
|
||||
if m, ok := api.Cap[api.MeterEnergy](cr.meter); ok {
|
||||
if f, err := m.TotalEnergy(); err == nil {
|
||||
cr.startEnergy = f
|
||||
cr.log.DEBUG.Printf("charge start energy: %.3fkWh", f)
|
||||
|
|
@ -74,7 +74,7 @@ func (cr *ChargeRater) StopCharge() {
|
|||
cr.charging = false
|
||||
|
||||
// get end energy amount
|
||||
if m, ok := cr.meter.(api.MeterEnergy); ok {
|
||||
if m, ok := api.Cap[api.MeterEnergy](cr.meter); ok {
|
||||
if f, err := m.TotalEnergy(); err == nil {
|
||||
cr.chargedEnergy += f - cr.startEnergy
|
||||
cr.log.DEBUG.Printf("charge final energy: %.3fkWh", cr.chargedEnergy)
|
||||
|
|
@ -92,7 +92,7 @@ func (cr *ChargeRater) ResetCharge() {
|
|||
defer cr.Unlock()
|
||||
|
||||
// get end energy amount
|
||||
if m, ok := cr.meter.(api.MeterEnergy); ok {
|
||||
if m, ok := api.Cap[api.MeterEnergy](cr.meter); ok {
|
||||
if f, err := m.TotalEnergy(); err == nil {
|
||||
cr.chargedEnergy += f - cr.startEnergy
|
||||
cr.log.DEBUG.Printf("charge final energy: %.3fkWh", cr.chargedEnergy)
|
||||
|
|
@ -117,7 +117,7 @@ func (cr *ChargeRater) SetChargePower(power float64) {
|
|||
}
|
||||
|
||||
// update energy amount if not provided by meter
|
||||
if _, ok := cr.meter.(api.MeterEnergy); !ok {
|
||||
if !api.HasCap[api.MeterEnergy](cr.meter) {
|
||||
// convert power to energy in kWh
|
||||
cr.chargedEnergy += power / 1e3 * float64(cr.clck.Since(cr.start)) / float64(time.Hour)
|
||||
// move timestamp
|
||||
|
|
@ -137,7 +137,7 @@ func (cr *ChargeRater) ChargedEnergy() (float64, error) {
|
|||
}
|
||||
|
||||
// get current energy amount
|
||||
if m, ok := cr.meter.(api.MeterEnergy); ok {
|
||||
if m, ok := api.Cap[api.MeterEnergy](cr.meter); ok {
|
||||
f, err := m.TotalEnergy()
|
||||
if err == nil {
|
||||
return cr.chargedEnergy + f - cr.startEnergy, nil
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue