Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
andig committed Jul 10, 2020
1 parent 5c6431b commit 655783d
Show file tree
Hide file tree
Showing 14 changed files with 218 additions and 252 deletions.
15 changes: 7 additions & 8 deletions assets/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,10 @@ <h1 class="display-4 pt-3" v-if="title">{{title}}</h1>

<div class="row mt-4 border-bottom">
<div class="col-12 col-md-4">
<p class="h1" v-if="state.title">{{state.title}}</p>
<p class="h1" v-else>Home</p>
<p class="h1">{{state.title||"Home"}}</p>
</div>
<div class="d-none col-12 col-md-8 pt-1"
v-bind:class="{'d-md-block': state.loadPoints.length>1}">
v-bind:class="{'d-md-block': state.loadpoints.length>1}">
<modeswitch></modeswitch>
</div>
</div>
Expand Down Expand Up @@ -172,12 +171,12 @@ <h2>
</h2>
</div>
<div class="d-block col-12 text-center pt-4 pb-4 mb-4 bg-light"
v-bind:class="{ 'd-md-none': state.loadPoints.length>1 }">
v-bind:class="{ 'd-md-none': state.loadpoints.length>1 }">
<modeswitch></modeswitch>
</div>
</div>

<loadpoint v-for="loadpoint in state.loadPoints" v-bind:loadpoint="loadpoint" :id="'loadpoint-'+loadpoint.name">
<loadpoint v-for="(loadpoint,id) in state.loadpoints" v-bind:loadpoint="loadpoint" :id="'loadpoint-'+id">
</loadpoint>

</div>
Expand All @@ -187,7 +186,7 @@ <h2>
<div>
<div class="row border-bottom">
<div class="col-12">
<p class="h1">{{loadpoint.title||loadpoint.name||"Ladepunkt"}}</p>
<p class="h1">{{loadpoint.title||"Ladepunkt"}}</p>
</div>
</div>
<div class="row">
Expand Down Expand Up @@ -283,11 +282,11 @@ <h2>
</div>
</div>

<div v-for="loadpoint in state.loadPoints" v-bind:loadpoint="loadpoint" :id="'loadpoint-'+loadpoint.name">
<div v-for="(loadpoint,id) in state.loadpoints" v-bind:loadpoint="loadpoint" :id="'loadpoint-'+id">

<div class="row mt-4 border-bottom">
<div class="col-12">
<p class="h1">{{loadpoint.title||loadpoint.name||"Ladepunkt"}}</p>
<p class="h1">{{loadpoint.title||"Ladepunkt"}}</p>
</div>
</div>

Expand Down
81 changes: 16 additions & 65 deletions assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,81 +70,32 @@ let formatter = {
//

let store = {
initialized: false,
state: {
// configuration
mode: null,
gridMeter: null,
pvMeter: null,
batteryMeter: null,
// runtime
gridPower: null,
pvPower: null,
batteryPower: null,
loadPoints: {
// configuration
// soc: null,
// socCapacity: null,
// socTitle: null,
// chargeMeter: true,
// phases: null,
// minCurrent: null,
// maxCurrent: null,
// runtime
// connected: null,
// charging: null,
// gridPower: null,
// pvPower: null,
// chargeCurrent: null,
// chargePower: null,
// chargeDuration: null,
// chargeEstimate: -1,
// chargedEnergy: null,
// socCharge: "—"
},
loadpoints: [],
},
update: function(msg) {
// update loadpoints array
let target = this.state;
if (msg.loadpoint !== undefined) {
for (var i=0; i<this.state.loadPoints.length; i++) {
if (this.state.loadPoints[i].name != msg.loadpoint) {
continue;
}

Object.keys(msg.data).forEach(function (k) {
let v = msg.data[k];
Vue.set(this.state.loadPoints[i], k, v)
}, this);
while (this.state.loadpoints.length <= msg.loadpoint) {
this.state.loadpoints.push({});
}

return;
target = this.state.loadpoints[msg.loadpoint];
}

Object.keys(msg).forEach(function(k) {
if (k == "error") {
toasts.error({message: msg[k]});
} else if (k == "warn") {
toasts.warn({message: msg[k]});
} else {
if (!(k in this.state)) {
console.log("invalid key: " + k);
}
Vue.set(this.state, k, msg[k])
}
}, this);
Object.keys(msg).forEach(function (k) {
Vue.set(target, k, msg[k]);
});
},
init: function() {
if (!store.initialized) {
axios.get("config").then(function(msg) {
Object.keys(msg.data).forEach(function (k) {
let data = {};
data[k] = msg.data[k];
store.update(data);
});
axios.get("config").then(function(msg) {
for (let i=0; i<msg.data.loadpoints.length; i++) {
let data = Object.assign(msg.data.loadpoints[i], { loadpoint: i });
this.update(data);
}

store.initialized = true;
}).catch(toasts.error);
}
delete msg.data.loadpoints;
this.update(msg.data);
}.bind(this)).catch(toasts.error);
}
};

Expand Down
5 changes: 3 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,13 @@ func run(cmd *cobra.Command, args []string) {
// start broadcasting values
tee := &Tee{}

cache := util.NewCache()
// value cache
cache := util.NewCache(len(site.LoadPoints()))
go cache.Run(tee.Attach())

// setup database
if conf.Influx.URL != "" {
configureDatabase(tee.Attach(), conf.Influx)
configureDatabase(conf.Influx, site.LoadPoints(), tee.Attach())
}

// create webserver
Expand Down
8 changes: 4 additions & 4 deletions cmd/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func init() {
}

// setup influx databases
func configureDatabase(in <-chan util.Param, conf server.InfluxConfig) {
func configureDatabase(conf server.InfluxConfig, loadPoints []*core.LoadPoint, in <-chan util.Param) {
influx := server.NewInfluxClient(
conf.URL,
conf.Token,
Expand All @@ -35,7 +35,7 @@ func configureDatabase(in <-chan util.Param, conf server.InfluxConfig) {
limiter := server.NewLimiter(5 * time.Second)
in = limiter.Pipe(in)

go influx.Run(in)
go influx.Run(loadPoints, in)
}

func configureMessengers(conf messagingConfig) chan push.Event {
Expand Down Expand Up @@ -82,8 +82,8 @@ func configureLoadPoints(conf config, cp *ConfigProvider) (loadPoints []*core.Lo
var lpc []map[string]interface{}
util.DecodeOther(log, lps, &lpc)

for _, lpc := range lpc {
lp := core.NewLoadPointFromConfig(log, cp, lpc)
for i, lpc := range lpc {
lp := core.NewLoadPointFromConfig(log, cp, lpc, i)
loadPoints = append(loadPoints, lp)
}

Expand Down
49 changes: 29 additions & 20 deletions core/loadpoint.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package core

import (
"strings"
"strconv"
"time"

"github.com/andig/evcc/api"
Expand Down Expand Up @@ -43,7 +43,7 @@ type Handler interface {
// LoadPoint is responsible for controlling charge depending on
// SoC needs and power availability.
type LoadPoint struct {
ID int
id int // Loadpoint ID starting at 0 for first loadpoint

clock clock.Clock // mockable time
bus evbus.Bus // event bus
Expand All @@ -52,12 +52,13 @@ type LoadPoint struct {
log *util.Logger

// exposed public configuration
Name string `mapstructure:"name"` // Name
Title string `mapstructure:"title"` // UI title
Phases int64 `mapstructure:"phases"` // Phases- required for converting power and current
ChargerRef string `mapstructure:"charger"` // Charger reference
VehicleRef string `mapstructure:"vehicle"` // Vehicle reference
ChargeMeterRef string `mapstructure:"charge"` // Charge meter reference
Title string `mapstructure:"title"` // UI title
Phases int64 `mapstructure:"phases"` // Phases- required for converting power and current
ChargerRef string `mapstructure:"charger"` // Charger reference
VehicleRef string `mapstructure:"vehicle"` // Vehicle reference
Meters struct {
ChargeMeterRef string `mapstructure:"charge"` // Charge meter reference
}
Enable, Disable ThresholdConfig

handler Handler
Expand All @@ -79,17 +80,15 @@ type LoadPoint struct {
}

// NewLoadPointFromConfig creates a new loadpoint
func NewLoadPointFromConfig(log *util.Logger, cp configProvider, other map[string]interface{}) *LoadPoint {
lp := NewLoadPoint()
func NewLoadPointFromConfig(log *util.Logger, cp configProvider, other map[string]interface{}, id int) *LoadPoint {
lp := NewLoadPoint(id)
util.DecodeOther(log, other, &lp)

// promote logger
if lp.Name != "" {
lp.log = util.NewLogger(strings.ToLower(lp.Name))
}
// logger
lp.log = util.NewLogger("lp-" + strconv.Itoa(id+1))

if lp.ChargeMeterRef != "" {
lp.chargeMeter = cp.Meter(lp.ChargeMeterRef)
if lp.Meters.ChargeMeterRef != "" {
lp.chargeMeter = cp.Meter(lp.Meters.ChargeMeterRef)
}
if lp.VehicleRef != "" {
lp.vehicle = cp.Vehicle(lp.VehicleRef)
Expand All @@ -113,13 +112,14 @@ func NewLoadPointFromConfig(log *util.Logger, cp configProvider, other map[strin
}

// NewLoadPoint creates a LoadPoint with sane defaults
func NewLoadPoint() *LoadPoint {
func NewLoadPoint(id int) *LoadPoint {
log := util.NewLogger("core")
clock := clock.New()
bus := evbus.New()

lp := &LoadPoint{
log: log, //logger
id: id, // loadpoint id
log: log, // logger
clock: clock, // mockable time
bus: bus, // event bus
Phases: 1,
Expand Down Expand Up @@ -175,8 +175,9 @@ func (lp *LoadPoint) configureChargerType(charger api.Charger) {

// notify sends push messages to clients
func (lp *LoadPoint) notify(event string, attributes map[string]interface{}) {
attributes["loadpoint"] = lp.Name
panic("foo") // add cache
lp.pushChan <- push.Event{
LoadPoint: &lp.id,
Event: event,
Attributes: attributes,
}
Expand All @@ -185,7 +186,7 @@ func (lp *LoadPoint) notify(event string, attributes map[string]interface{}) {
// publish sends values to UI and databases
func (lp *LoadPoint) publish(key string, val interface{}) {
lp.uiChan <- util.Param{
LoadPoint: lp.Name,
LoadPoint: &lp.id,
Key: key,
Val: val,
}
Expand Down Expand Up @@ -242,6 +243,14 @@ func (lp *LoadPoint) evChargeCurrentHandler(current int64) {
lp.publish("chargeCurrent", current)
}

// Name returns the human-readable loadpoint title
func (lp *LoadPoint) Name() string {
if lp.Title == "" {
return "Loadpoint " + strconv.Itoa(lp.id+1)
}
return lp.Title
}

// Prepare loadpoint configuration by adding missing helper elements
func (lp *LoadPoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Event) {
lp.pushChan = pushChan
Expand Down
18 changes: 13 additions & 5 deletions core/site.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func NewSiteFromConfig(
site.Mode = api.ModeOff
}

// configure meter from references
// if site.Meters.PVMeterRef == "" && site.Meters.GridMeterRef == "" {
// site.log.FATAL.Fatal("config: missing either pv or grid meter")
// }
Expand All @@ -89,6 +90,7 @@ func NewSiteFromConfig(
site.loadPoints = append(site.loadPoints, lp)
}

// validate single voltage
if Voltage != 0 && Voltage != site.VoltageRef {
site.log.FATAL.Fatal("config: only single voltage allowed")
}
Expand Down Expand Up @@ -116,12 +118,11 @@ type SiteConfiguration struct {
GridMeter bool `json:"gridMeter"`
PVMeter bool `json:"pvMeter"`
BatteryMeter bool `json:"batteryMeter"`
LoadPoints []LoadpointConfiguration `json:"loadPoints"`
LoadPoints []LoadpointConfiguration `json:"loadpoints"`
}

// LoadpointConfiguration is the loadpoint feature structure
type LoadpointConfiguration struct {
Name string `json:"name"`
Title string `json:"title"`
Phases int64 `json:"phases"`
MinCurrent int64 `json:"minCurrent"`
Expand All @@ -137,6 +138,15 @@ func (lp *LoadPoint) hasChargeMeter() bool {
return lp.chargeMeter != nil && !isWrapped
}

// LoadPoints returns the array of associated loadpoints
func (lp *Site) LoadPoints() []*LoadPoint {
res := make([]*LoadPoint, 0, len(lp.loadPoints))
for _, lp := range lp.loadPoints {
res = append(res, lp.(*LoadPoint))
}
return res
}

// Configuration returns meter configuration
func (lp *Site) Configuration() SiteConfiguration {
c := SiteConfiguration{
Expand All @@ -150,8 +160,7 @@ func (lp *Site) Configuration() SiteConfiguration {
lp := lptr.(*LoadPoint)

lpc := LoadpointConfiguration{
Name: lp.Name,
Title: lp.Title,
Title: lp.Name(),
Phases: lp.Phases,
MinCurrent: lp.MinCurrent,
MaxCurrent: lp.MaxCurrent,
Expand Down Expand Up @@ -182,7 +191,6 @@ func (lp *Site) DumpConfig() {
lp := lptr.(*LoadPoint)
lp.log.INFO.Printf("loadpoint %d config:", i)

lp.log.INFO.Printf(" name %s", lp.Name)
lp.log.INFO.Printf(" vehicle %s", presence[lp.vehicle != nil])
lp.log.INFO.Printf(" charge %s", presence[lp.hasChargeMeter()])

Expand Down
1 change: 1 addition & 0 deletions push/hub.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import "github.com/andig/evcc/util"

// Event is a notification event
type Event struct {
LoadPoint *int // optional loadpoint id
Event string
Attributes map[string]interface{}
}
Expand Down

0 comments on commit 655783d

Please sign in to comment.