Skip to content

Commit

Permalink
Merge pull request #3 from scs/feature/improvements
Browse files Browse the repository at this point in the history
Feature/improvements
* Rename reader to meter (only code, not .ini config)
* Web frontend enhancements (SMT-27)
* Frontend: Restart datacollector when pressing deploy button
  • Loading branch information
raymar9 authored Sep 9, 2021
2 parents dfc6851 + df78aa1 commit 5b4eb20
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 59 deletions.
8 changes: 4 additions & 4 deletions backend/smartmeter_datacollector_configurator/configurator.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import configparser
import logging

from .dto import ConfigDto, LoggerSinkDto, MqttSinkDto, ReaderDto, SinkType
from .dto import ConfigDto, LoggerSinkDto, MeterDto, MqttSinkDto, SinkType

CA_FILE_NAME = "ca.crt"
CONFIG_FILE_NAME = "datacollector.ini"
Expand All @@ -18,7 +18,7 @@ def retrieve_config(config_dir: str) -> ConfigDto:
dto = ConfigDto()
for sec in parser.sections():
if sec.startswith("reader"):
dto.readers.append(ReaderDto.parse_obj(
dto.meters.append(MeterDto.parse_obj(
dict(parser.items(sec))
))
elif sec.startswith("sink"):
Expand All @@ -42,10 +42,10 @@ def retrieve_config(config_dir: str) -> ConfigDto:

def write_config_from_dto(config_dir: str, config: ConfigDto) -> None:
parser = configparser.ConfigParser()
for i, reader in enumerate(config.readers):
for i, meter in enumerate(config.meters):
sec_name = f"reader{i}"
parser.add_section(sec_name)
parser[sec_name] = reader.dict(exclude_none=True)
parser[sec_name] = meter.dict(exclude_none=True)
sinks = (config.mqtt_sink, config.logger_sink)
for i, sink in enumerate(sinks):
if not sink:
Expand Down
8 changes: 4 additions & 4 deletions backend/smartmeter_datacollector_configurator/dto.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
LOGGER_LEVEL = ["DEBUG", "INFO", "WARNING", "ERROR", "FATAL", "CRITICAL"]


class ReaderType(str, Enum):
class MeterType(str, Enum):
LGE450 = "lge450"


Expand All @@ -23,8 +23,8 @@ class Config:
use_enum_values = True


class ReaderDto(BaseModel):
type: ReaderType
class MeterDto(BaseModel):
type: MeterType
port: str
key: Optional[str]

Expand Down Expand Up @@ -82,7 +82,7 @@ def name_not_empty(cls, val: str):

class ConfigDto(BaseModel):
log_level: str = "WARNING"
readers: List[ReaderDto] = []
meters: List[MeterDto] = []
mqtt_sink: Optional[MqttSinkDto]
logger_sink: Optional[LoggerSinkDto]

Expand Down
12 changes: 11 additions & 1 deletion frontend/smartmeter-datacollector-configurator/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,27 @@
</div>
</header>
<configurator />
<footer class="footer has-background-white">
<p class="content has-text-centered">
<a :href="grafanaUrl" target="_blank">Grafana Web</a> -
<a href="https://github.com/scs/smartmeter-datacollector/wiki" target="_blank">Documentation</a> -
<a href="https://github.com/scs/smartmeter-datacollector" target="_blank">Source Code</a>
</p>
</footer>
</div>
</template>

<script>
import Configurator from "./components/Configurator.vue";
import { getGrafanaUrl } from "./utils";
export default {
name: "App",
components: {
Configurator,
},
computed: {
grafanaUrl: getGrafanaUrl,
},
};
</script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,35 @@
<div>
<div class="level">
<div class="level-left">
<b-button class="level-item" icon-left="upload" @click="checkCredentials(confirmLoad)"
>Load Configuration</b-button
>
<b-button class="level-item" icon-left="download" @click="checkCredentials(confirmDeploy)"
>Deploy Configuration</b-button
>
<b-button class="level-item" icon-left="sync-alt" @click="checkCredentials(restartDatacollector)"
>Restart Data Collector</b-button
>
<b-button class="level-item" icon-left="sync-alt" @click="checkCredentials(restartDemo)">Restart Demo</b-button>
<b-button
class="level-item"
icon-left="key"
@click="checkCredentials(changePasswordModal, 'Please enter current password.')"
>Change Password</b-button
>
icon-left="upload"
@click="checkCredentials(confirmLoad)"
label="Load Configuration"
/>
<b-tooltip label="Deploy configuration and restart data collector">
<b-button
class="level-item"
icon-left="download"
@click="checkCredentials(confirmDeploy)"
label="Deploy Configuration"
/>
</b-tooltip>
<b-button
class="level-item"
icon-left="sync-alt"
@click="checkCredentials(restartDatacollector)"
label="Restart Data Collector"
/>
<b-button class="level-item" icon-left="sync-alt" @click="checkCredentials(restartDemo)" label="Restart Demo" />
<b-tooltip label="Set new configurator password">
<b-button
class="level-item"
icon-left="key"
@click="checkCredentials(changePasswordModal, 'Please enter current configurator password.')"
label="Change Password"
/>
</b-tooltip>
</div>
<div class="level-right">
<b-button class="level-item is-danger" icon-left="trash" @click="confirmDiscard"
Expand All @@ -39,14 +52,14 @@
<div class="column">
<p class="title is-4">Smart Meters</p>
<div class="block buttons">
<b-button type="is-success" icon-left="plus" @click="addReader">Smart Meter</b-button>
<b-button type="is-success" icon-left="plus" @click="addMeter">Smart Meter</b-button>
</div>
<smart-meter
v-for="(r, r_i) in readers"
v-for="(r, r_i) in meters"
:key="r.id"
:initConfig="r.config"
@remove="removeReader(r_i)"
@update="updateReader(r_i, $event)"
@remove="removeMeter(r_i)"
@update="updateMeter(r_i, $event)"
/>
</div>
<div class="column">
Expand All @@ -71,7 +84,7 @@

<script>
import axios from "axios";
import { getBaseHostUrl } from "../utils";
import { getApiUrl } from "../utils";
import LoggerSink from "./LoggerSink.vue";
import MqttSink from "./MqttSink.vue";
import SmartMeter from "./SmartMeter.vue";
Expand All @@ -81,7 +94,7 @@ export default {
data() {
return {
loggerLevel: "WARNING",
readers: [],
meters: [],
loggerSink: null,
mqttSink: null,
credentials: null,
Expand All @@ -92,28 +105,28 @@ export default {
this.USERNAME = "admin";
},
methods: {
addReader() {
this.readers.push({
id: this.getReaderId(),
addMeter() {
this.meters.push({
id: this.getMeterId(),
config: {},
});
},
getReaderId() {
if (this.readers.length == 0) {
getMeterId() {
if (this.meters.length == 0) {
return 1;
}
return Math.max(...this.readers.map((r) => r.id)) + 1;
return Math.max(...this.meters.map((r) => r.id)) + 1;
},
updateReader(index, newConfig) {
this.readers[index].config = newConfig;
updateMeter(index, newConfig) {
this.meters[index].config = newConfig;
},
removeReader(index) {
this.readers.splice(index, 1);
removeMeter(index) {
this.meters.splice(index, 1);
},
checkCredentials(action, message = null) {
if (!this.credentials) {
this.$buefy.dialog.prompt({
message: message || "Please enter password.",
message: message || "Please enter configurator password.",
inputAttrs: {
placeholder: "Password",
type: "password",
Expand All @@ -137,7 +150,7 @@ export default {
},
resetConfig() {
this.loggerLevel = "WARNING";
this.readers = [];
this.meters = [];
this.loggerSink = null;
this.mqttSink = null;
},
Expand All @@ -164,7 +177,8 @@ export default {
confirmDeploy() {
this.$buefy.dialog.confirm({
title: "Deploy Configuration",
message: "Do you want to upload and deploy the configuration?",
message:
"Do you want to upload and deploy the configuration?<br />The Data Collector service will be restarted.",
confirmText: "Upload",
type: "is-warning",
hasIcon: true,
Expand All @@ -185,7 +199,7 @@ export default {
},
loadConfig() {
axios
.get(`${getBaseHostUrl()}/config`, {
.get(`${getApiUrl()}/config`, {
timeout: 3000,
responseType: "json",
auth: this.getAuthentication(),
Expand All @@ -206,7 +220,7 @@ export default {
deployConfig() {
const configJson = JSON.stringify(this.packConfig());
axios
.post(`${getBaseHostUrl()}/config`, configJson, {
.post(`${getApiUrl()}/config`, configJson, {
timeout: 4000,
auth: this.getAuthentication(),
})
Expand All @@ -218,6 +232,9 @@ export default {
duration: 4000,
});
})
.then(() => {
this.restartDatacollector();
})
.catch((error) => {
const message = this.parseError(error);
this.$buefy.toast.open({
Expand All @@ -230,7 +247,7 @@ export default {
},
restartDatacollector() {
axios
.post(`${getBaseHostUrl()}/restart`, null, {
.post(`${getApiUrl()}/restart`, null, {
timeout: 6000,
auth: this.getAuthentication(),
})
Expand All @@ -254,7 +271,7 @@ export default {
},
restartDemo() {
axios
.post(`${getBaseHostUrl()}/restart-demo`, null, {
.post(`${getApiUrl()}/restart-demo`, null, {
timeout: 8000,
auth: this.getAuthentication(),
})
Expand All @@ -278,7 +295,7 @@ export default {
},
extractConfig(cfg) {
this.loggerLevel = cfg["log_level"] || "WARNING";
this.readers = cfg["readers"].map((r, index) => {
this.meters = cfg["meters"].map((r, index) => {
return { id: index, config: r };
});
this.mqttSink = cfg["mqtt_sink"] || null;
Expand All @@ -287,7 +304,7 @@ export default {
packConfig() {
return {
log_level: this.loggerLevel,
readers: this.readers.map((r) => r.config),
meters: this.meters.map((r) => r.config),
mqtt_sink: this.mqttSink,
logger_sink: this.loggerSink,
};
Expand All @@ -302,7 +319,7 @@ export default {
},
changePassword(newPassword) {
axios
.post(`${getBaseHostUrl()}/credentials`, newPassword, {
.post(`${getApiUrl()}/credentials`, newPassword, {
timeout: 4000,
auth: this.getAuthentication(),
})
Expand All @@ -329,4 +346,8 @@ export default {
};
</script>

<style scoped></style>
<style lang="css" scoped>
.level-left .b-tooltip {
margin-right: 0.75rem;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<button type="button" class="delete" @click="$emit('close')" />
</header>
<section class="modal-card-body">
<b-field label="New Password">
<b-field label="New configurator password">
<b-input
type="password"
v-model="newPassword"
Expand All @@ -16,7 +16,7 @@
password-reveal
></b-input>
</b-field>
<b-field label="Repeat new Password" :message="validationText">
<b-field label="Repeat password" :message="validationText">
<b-input type="password" v-model="newPasswordRepeat" lazy password-reveal @input="checkInput"></b-input>
</b-field>
</section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

<script>
import axios from "axios";
import { getBaseHostUrl } from "../utils";
import { getApiUrl } from "../utils";
export default {
props: {
initConfig: {
Expand Down Expand Up @@ -63,7 +63,7 @@ export default {
},
loadPorts() {
axios
.get(`${getBaseHostUrl()}/ttydevices`, {
.get(`${getApiUrl()}/ttydevices`, {
timeout: 3000,
responseType: "json",
})
Expand Down
17 changes: 12 additions & 5 deletions frontend/smartmeter-datacollector-configurator/src/utils.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
const devMode = process.env.NODE_ENV === "development";
const devBaseUrl = "http://localhost:8000/api";
const devApiPort = 8000;
const prodApiPort = 8000;
const apiPath = "/api";
const grafanaPort = 3000;

function getBaseHostUrl() {
function getApiUrl() {
if (devMode) {
return devBaseUrl;
return `http://${location.hostname}:${devApiPort}${apiPath}`;
}
return `http://${location.host}/api`;
return `http://${location.hostname}:${prodApiPort}${apiPath}`;
}

export { getBaseHostUrl };
function getGrafanaUrl() {
return `http://${location.hostname}:${grafanaPort}`;
}

export { getApiUrl, getGrafanaUrl };

0 comments on commit 5b4eb20

Please sign in to comment.