Compare commits

...

17 Commits

Author SHA1 Message Date
Thomas Klaehn
f71c15b4d2 Update module dependencies
Signed-off-by: Thomas Klaehn <thomas.klaehn@perinet.io>
2024-07-09 17:13:17 +02:00
Thomas Klaehn
d3b5b7d9ba Deactivate plot in sauna
Signed-off-by: Thomas Klaehn <thomas.klaehn@perinet.io>
2024-07-04 07:04:52 +02:00
Thomas Klaehn
d26f92a0f1 Fix: exchange test data with measurement in plot
Signed-off-by: Thomas Klaehn <thomas.klaehn@perinet.io>
2024-07-03 11:32:47 +02:00
Thomas Klaehn
5822328d9d Add data plot to sauna route
Signed-off-by: Thomas Klaehn <thomas.klaehn@perinet.io>
2024-07-03 11:25:54 +02:00
Thomas Klaehn
59cb6d1380 Update go module dependencies 2024-04-03 07:51:20 +02:00
Thomas Klaehn
149a6ddefe Change menu 2024-03-25 07:46:16 +01:00
tkl
0a2b75f4b5 Update webui/src/routes/bicycle/+page.svelte 2024-02-20 08:26:56 +00:00
tkl
ca7ff8e154 Update webui/src/routes/bicycle/+page.svelte 2024-02-20 08:26:12 +00:00
tkl
7e36c36ec0 Fix year format 2024-02-13 12:52:57 +00:00
tkl
2a1925dba9 Change display yeat 2023 -> 2024 2024-02-13 08:18:23 +00:00
tkl
bd7af78cba Merge pull request 'add_bicycle' (#3) from add_bicycle into main
Reviewed-on: #3
2023-03-05 08:15:57 +00:00
Thomas Klaehn
6c6e405c92 Update module dependencies 2023-03-05 09:14:34 +01:00
Thomas Klaehn
99d0cb478a Add bicycle page 2023-03-05 09:10:17 +01:00
Thomas Klaehn
52dbdb0b65 Webserver: Add bicycle api 2023-03-01 08:21:56 +01:00
Thomas Klaehn
137b342629 Merge branch 'add_time_to_sauna_api' 2023-03-01 08:20:16 +01:00
Thomas Klaehn
4eae02cd0c Separate sauna api into single module 2023-03-01 08:17:32 +01:00
Thomas Klaehn
84d84b4313 Sauna: Add update timestamp to sauna api 2023-02-21 08:06:05 +01:00
13 changed files with 858 additions and 667 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
/node_modules/
/build/
/vendor/
.DS_Store

21
go.mod
View File

@@ -1,11 +1,20 @@
module webserver
go 1.19
require github.com/eclipse/paho.mqtt.golang v1.4.2
go 1.20
require (
github.com/gorilla/websocket v1.4.2 // indirect
golang.org/x/net v0.4.0 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
git.blackfinn.de/apiservice/bicycle v0.0.2
git.blackfinn.de/apiservice/sauna v1.1.0
)
require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/influxdata/influxdb-client-go/v2 v2.13.0 // indirect
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // indirect
github.com/oapi-codegen/runtime v1.0.0 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/sync v0.6.0 // indirect
)

47
go.sum
View File

@@ -1,13 +1,34 @@
github.com/eclipse/paho.mqtt.golang v1.4.2 h1:66wOzfUHSSI1zamx7jR6yMEI5EuHnT1G6rNA5PM12m4=
github.com/eclipse/paho.mqtt.golang v1.4.2/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
git.blackfinn.de/apiservice/bicycle v0.0.2 h1:q+DMnWzxa4ZWxwMf03OPhQyDGRii7l7YmrEKYLgwn2A=
git.blackfinn.de/apiservice/bicycle v0.0.2/go.mod h1:ygL9Ax8JreS+taIsqNq0NNlmhGnrHE+v1NE8Solihls=
git.blackfinn.de/apiservice/sauna v1.1.0 h1:F2qysKjOld5lwTT7VJCY1YIEz6rXtE8CLpEfkGmeM3o=
git.blackfinn.de/apiservice/sauna v1.1.0/go.mod h1:yM5lSlCApQrrZ7a+Y8ZxzBkLV2Frx6k1cw219XeSEG4=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik=
github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/influxdata/influxdb-client-go/v2 v2.13.0 h1:ioBbLmR5NMbAjP4UVA5r9b5xGjpABD7j65pI8kFphDM=
github.com/influxdata/influxdb-client-go/v2 v2.13.0/go.mod h1:k+spCbt9hcvqvUiz0sr5D8LolXHqAAOfPw9v/RIRHl4=
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf h1:7JTmneyiNEwVBOHSjoMxiWAqB992atOeepeFYegn5RU=
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo=
github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

80
main.go
View File

@@ -1,75 +1,22 @@
package main
import (
"encoding/json"
"flag"
"log"
"net/http"
"sync"
mqtt "github.com/eclipse/paho.mqtt.golang"
bicycle "git.blackfinn.de/apiservice/bicycle"
sauna "git.blackfinn.de/apiservice/sauna"
)
type temperature struct {
Value float64 `json:"value"`
Unit string `json:"unit"`
}
var (
logger log.Logger = *log.Default()
sauna_mutex sync.Mutex
sauna_temperature = temperature{
Value: 0.0,
Unit: "°C",
}
)
var messagePubHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
logger.Printf("Received message: %s from topic: %s\n", msg.Payload(), msg.Topic())
}
var saunaHandler mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
log.Printf("Received message: %s from topic: %s\n", msg.Payload(), msg.Topic())
sauna_mutex.Lock()
err := json.Unmarshal(msg.Payload(), &sauna_temperature)
sauna_mutex.Unlock()
if err != nil {
logger.Print(err)
return
}
}
var connectHandler mqtt.OnConnectHandler = func(client mqtt.Client) {
logger.Println("Connected")
}
var connectLostHandler mqtt.ConnectionLostHandler = func(client mqtt.Client, err error) {
logger.Printf("Connect lost: %v", err)
}
func init() {
logger.SetPrefix("Homeservice: ")
}
func http_endpoint_sauna(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-type", "application/json; charset=utf-8;")
if r.Method == http.MethodGet {
sauna_mutex.Lock()
data, err := json.Marshal(sauna_temperature)
sauna_mutex.Unlock()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write(json.RawMessage(`{"error": "cannot marshal object to json"}`))
} else {
w.WriteHeader(http.StatusOK)
w.Write(data)
}
} else {
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
func main() {
logger.Println("starting")
@@ -77,30 +24,13 @@ func main() {
flag.StringVar(&webui_path, "d", "./build/webui", "Specify path to serve the web ui. Default is ./static")
flag.Parse()
// MQTT connection
opts := mqtt.NewClientOptions()
opts.AddBroker("tcp://nuc:1883")
opts.SetClientID("homeservice")
opts.SetDefaultPublishHandler(messagePubHandler)
opts.OnConnect = connectHandler
opts.OnConnectionLost = connectLostHandler
client := mqtt.NewClient(opts)
if token := client.Connect(); token.Wait() && token.Error() != nil {
panic(token.Error())
}
// Start apis
bicycle.Start()
sauna.Start()
// MQTT subscribtion
topic := "sauna/temperature"
token := client.Subscribe(topic, 1, saunaHandler)
token.Wait()
logger.Printf("Subscribed to topic %s", topic)
// API routes
// Serve files from static folder
http.Handle("/", http.FileServer(http.Dir(webui_path)))
http.HandleFunc("/sauna/sample", http_endpoint_sauna)
port := ":5000"
logger.Println("Server is running on port" + port)

925
webui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -32,5 +32,9 @@
"vite": "^4.0.4",
"vitest": "^0.25.3"
},
"type": "module"
"type": "module",
"dependencies": {
"hamburger-menu-svelte": "^2.2.0",
"uplot": "^1.6.30"
}
}

View File

@@ -141,175 +141,20 @@ textarea {
margin-left: 2%;
}
header {
width: 100%;
height: 50px;
line-height: 50px;
text-align: center;
background-color: #282929;
}
/*logo placement*/
header figure {
float: left;
color: #b6b6b6;
}
header figure.logo {
position: absolute;
right: 0px;
top: -15px;
}
/*Navigation*/
.header {
background-color: #282929;
/*position: fixed;*/
width: 100%;
z-index: 510;
}
.header ul {
margin: 0;
padding: 0;
list-style: none;
overflow: hidden;
/* background-color: #282929; */
background-color: transparent;
}
.header li a {
display: block;
padding: 30px 5px 10px 5px;
font-size: 1.5rem;
border-right: 1px solid #282929;
text-decoration: none;
/* background: rgba(255, 255, 255, 0.9) */
background-color: #282929;
}
.header li a:hover,
.header .menu-btn:hover {
background-color: #282929;
}
.header .logo {
display: block;
float: left;
font-size: 2em;
padding: 5px;
text-decoration: none;
}
/* menu */
.header .menu {
clear: both;
max-height: 0;
transition: max-height .2s ease-out;
}
/* menu icon */
.header .menu-icon {
cursor: pointer;
float: right;
margin: 2%;
header {
height: 40px;
display: flex;
align-items: center;
padding:10px 20px;
position: relative;
user-select: none;
}
.header .menu-icon .nav-icon {
background: #000;
display: block;
height: 5px;
position: relative;
transition: background .2s ease-out;
width: 30px;
}
.header .menu-icon .nav-icon:before,
.header .menu-icon .nav-icon:after {
background: #000;
content: '';
display: block;
height: 100%;
position: absolute;
transition: all .2s ease-out;
width: 100%;
}
.header .menu-icon .nav-icon:before {
top: 20px;
}
.header .menu-icon .nav-icon:after {
top: 10px;
}
/* menu btn */
.header .menu-btn {
display: none;
}
.header .menu-btn:checked~.menu {
max-height: 1200px;
}
.header .menu-btn:checked~.menu-icon .nav-icon {
background: transparent;
}
.header .menu-btn:checked~.menu-icon .nav-icon:before {
transform: rotate(-45deg);
top: 0;
}
.header .menu-btn:checked~.menu-icon .nav-icon:after {
transform: rotate(45deg);
top: 0;
}
/* menu content - larger screen*/
@media (min-width: 1132px) {
.header li {
float: left;
}
.header li a {
padding: 20px 15px 5px 0px;
font-size: 1rem;
border: none;
}
.header .menu {
clear: none;
float: right;
max-height: none;
}
.header .menu-icon {
display: none;
}
}
/* smaller screen*/
@media (max-width: 924px) {
.header li {
padding: 30px 5px 10px 5px;
}
.header li a {
font-size: 1.2rem;
@media (min-width: 768px) {
header {
justify-content: space-between;
}
}

View File

@@ -1,21 +1,27 @@
<script>
import favicon from '$lib/images/haus.svg'
import Hamburger from "hamburger-menu-svelte";
const menu_list = [
{ name: "Home", url: "/" },
{ name: "Bicycle", url: "/bicycle" },
{ name: "Sauna", url: "/sauna" }
];
</script>
<header class="header">
<!-- <a href="https://www.perinet.io" target="_blank" class="logo">
<img src={perinetLogo} alt="Perinet Logo" width="180">
</a> -->
<link rel="shortcut icon" type="image/svg" href={favicon} />
<input class="menu-btn" type="checkbox" id="menu-btn" />
<label class="menu-icon" for="menu-btn"><span class="nav-icon"></span></label>
<ul class="menu">
<li><a href="/">Home</a></li>
<li><a href="/sauna">Sauna</a></li>
<!-- <li><a href="/chicken">Chicken</a></li> -->
</ul>
<Hamburger {menu_list}/>
</header>
<style>
/* header {
height: 40px;
display: flex;
align-items: center;
padding:10px 20px;
}
@media (min-width: 768px) {
header {
justify-content: space-between;
}
} */
</style>

110
webui/src/lib/Uplot.svelte Normal file
View File

@@ -0,0 +1,110 @@
<script lang="ts">
import 'uplot/dist/uPlot.min.css';
export let data_label = ""; // data label in legend
export let unit = ""; // unit in legend
export let last_for_seconds = 20; // Period of time [s] the data are shifted out on the left
let plotContainer;
let data = [];
data[0] = [Date.now()];
data[1] = [null];
const PRECISION = 1;
export async function create(legend_label, legend_unit, duration) {
data_label = legend_label;
unit = legend_unit;
last_for_seconds = duration;
const uplotModule = await import('uplot');
const uPlot = uplotModule.default;
redraw(uPlot);
}
export async function add_data(new_data) {
data[0].push(Date.now());
data[1].push(new_data);
}
function redraw(uPlot) {
function getSize() {
let max_height = window.innerHeight;
let max_width = window.innerWidth;
let current_posY = plotContainer.getBoundingClientRect().top;
let height = Math.max(max_height - current_posY - 200, 250);
let width = Math.max(max_width - (2 * 50), 600);
return {
width: width,
height: height,
}
}
const opts = {
...getSize(),
ms: 1,
cursor: {drag: {setScale: false}},
points: {show: false},
pxSnap: false,
pxAlign: 0,
series: [
{
label: "time"
},
{
label: data_label,
stroke: "#b6b6b6",
paths: uPlot.paths.spline(),
value: (u, v, sidx, didx) => {
if (didx == null) {
let d = u.data[sidx];
v = d[d.length - 1];
}
let value = v;
if ( value != null) {
value = value.toFixed(PRECISION) + unit;
}
return value;
},
pxAlign: 0,
points: { show: false },
}
],
axes: [{grid: { show: false}}, {grid: { show: false}}],
// hooks are used here to filter out the time series from the legend
hooks: {
init: [
u => {
[...u.root.querySelectorAll('.u-legend .u-series')].forEach((el, i) => {
if (u.series[i].label ==="time") {
el.style.display = 'none';
}
});
}
]
}
};
let uplot = new uPlot(opts, data, plotContainer);
function update() {
const now = Date.now();
const scale = { min: now - (last_for_seconds * 1000), max: now };
// shift out data earlier than chart origin
let last = Date.now();
let first = last - (last_for_seconds * 1000);
while(data[0][0] < first) {
for(let i = 0; i < data.length; i++) {
data[i].shift();
}
}
uplot.setData(data, false);
uplot.setScale('x', scale);
uplot.setLegend({idx: data[0].length - 1}, false);
requestAnimationFrame(update);
}
requestAnimationFrame(update);
}
</script>
<div bind:this={plotContainer}></div>

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 397.6 397.6" style="enable-background:new 0 0 397.6 397.6;" xml:space="preserve">
<path d="M313.1,147.875c-6.057,0-11.962,0.654-17.661,1.871l-15.805-53.435l37.842-2.565c4.447-0.301,8.487,2.613,9.602,6.932
c0.689,2.671,0.161,5.488-1.45,7.728c-1.611,2.24-4.113,3.638-6.865,3.834l-9.126,0.652c-3.581,0.256-6.276,3.366-6.02,6.947
s3.361,6.289,6.947,6.02l9.126-0.652c6.611-0.472,12.623-3.829,16.493-9.21c3.87-5.382,5.14-12.148,3.484-18.566
c-2.677-10.375-12.376-17.364-23.069-16.654l-45.936,3.114c-1.969,0.134-3.77,1.154-4.896,2.773
c-1.127,1.62-1.457,3.663-0.897,5.556l6.405,21.656H145.477l-6.316-12H155.5c3.59,0,6.5-2.91,6.5-6.5s-2.91-6.5-6.5-6.5h-47
c-3.59,0-6.5,2.91-6.5,6.5s2.91,6.5,6.5,6.5h16.086l9.542,18.349l-18.836,33.485c-9.549-3.751-19.929-5.834-30.792-5.834
c-46.593,0-84.5,37.906-84.5,84.5s37.907,84.5,84.5,84.5c44.404,0,80.892-34.436,84.225-78c0,0,31.695,0,31.776,0
c2.235,0,4.32-1.15,5.511-3.055l68.779-110.047l8.185,27.672c-31.758,12.162-54.376,42.945-54.376,78.93
c0,46.594,37.907,84.5,84.5,84.5s84.5-37.906,84.5-84.5S359.693,147.875,313.1,147.875z M84.5,303.875
c-39.425,0-71.5-32.075-71.5-71.5s32.075-71.5,71.5-71.5c8.549,0,16.75,1.513,24.355,4.276l-31.482,55.968
c-3.726,2.365-6.206,6.516-6.206,11.256c0,7.363,5.969,13.333,13.333,13.333c5.002,0,9.354-2.759,11.636-6.833h59.556
C152.395,275.263,121.733,303.875,84.5,303.875z M96.136,225.875c-0.99-1.769-2.37-3.285-4.025-4.439l28.528-50.717
c19.37,11.397,32.922,31.647,35.052,55.156H96.136z M168.725,225.875c-2.17-28.365-18.393-52.845-41.715-66.482l14.327-25.471
l48.396,91.953H168.725z M200.983,219.337l-48.665-92.462h106.454L200.983,219.337z M313.1,303.875c-39.425,0-71.5-32.075-71.5-71.5
c0-30.093,18.697-55.885,45.077-66.418l16.89,57.105c-2.348,2.403-3.8,5.687-3.8,9.313c0,7.363,5.969,13.333,13.333,13.333
s13.333-5.97,13.333-13.333c0-6.354-4.449-11.661-10.399-12.999l-16.895-57.123c4.518-0.897,9.184-1.378,13.962-1.378
c39.425,0,71.5,32.075,71.5,71.5S352.525,303.875,313.1,303.875z"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,2 @@
export const prerender = true;
export const trailingSlash = 'always';

View File

@@ -0,0 +1,49 @@
<script>
import { onMount } from "../../../node_modules/svelte/internal";
import icon from "$lib/images/wheels.svg"
let backend_url = "https://home.blackfinn.de/api/bicycle";
// let backend_url = "http://localhost:5000/api/bicycle";
let year = "2024";
let total_distance_value = 0.0;
let total_distance_unit = "m"
let year_result_value = 0.0;
let year_result_unit = "m"
function get_api() {
fetch(backend_url)
.then(response => response.json())
.then(data => {
total_distance_value = data.Years[year].total_distance.value
total_distance_unit = data.Years[year].total_distance.unit
year_result_value = data.Years[year].year_result.value
year_result_unit = data.Years[year].year_result.unit
}).catch(error => {
console.log(error);
return [];
});
}
setInterval(() => {
get_api();
}, 1000)
onMount(async () => {
get_api();
});
</script>
<svelte:head>
<title>Bicycle</title>
<meta name="description" content="Bicycle"/>
</svelte:head>
<section id='content_id' class='content'>
<h1>Bicycle</h1>
<figure>
<img src={icon} alt="Bicycle" width=150/>
</figure>
<h1>Total distance: {total_distance_value} {total_distance_unit}</h1>
<h1>Year result: {year_result_value} {year_result_unit}</h1>
</section>

View File

@@ -1,11 +1,15 @@
<script>
import { onMount } from "../../../node_modules/svelte/internal";
// import Plot from "$lib/Uplot.svelte"
import icon from "$lib/images/sauna.svg"
let backend_url = "https://home.blackfinn.de/sauna/sample";
let backend_url = "https://home.blackfinn.de/api/sauna";
let temperature_value = 0.0
let temperature_unit = "°C"
let time = "00:00:00";
let updated = false;
let plot;
function get_temperature() {
fetch(backend_url)
@@ -13,6 +17,11 @@
.then(data => {
temperature_unit = data.unit
temperature_value = data.value
time = data.time.slice(11, 19)
// if(plot) {
// plot.add_data(temperature_value);
// }
updated = JSON.parse(data.valid);
}).catch(error => {
console.log(error);
return [];
@@ -24,6 +33,9 @@
}, 1000)
onMount(async () => {
// if(plot) {
// plot.create("Temperature", "°C", 4 * 60 * 60 /* 4h */);
// }
get_temperature();
});
</script>
@@ -38,5 +50,11 @@
<figure>
<img src={icon} alt="Sauna" width=150/>
</figure>
{#if updated}
<div>{time} Uhr:</div>
<h1>{temperature_value} {temperature_unit}</h1>
<!-- <div><Plot/></div> -->
{/if}
</section>
<!-- <Plot bind:this={plot}/> -->