diff --git a/.drone.yml b/.drone.yml
new file mode 100644
index 0000000..0c5d267
--- /dev/null
+++ b/.drone.yml
@@ -0,0 +1,105 @@
+---
+kind: pipeline
+type: docker
+name: Build
+
+platform:
+ os: linux
+ arch: amd64
+
+volumes:
+ - name: cache
+ host:
+ path: /tmp/cache
+
+trigger:
+ event:
+ include:
+ - push
+
+steps:
+ - name: Load cached frontend packages
+ image: sinlead/drone-cache:1.0.0
+ settings:
+ action: load
+ key: yarn.lock
+ mount: node_modules
+ prefix: yarn-modules-${DRONE_REPO_NAME}
+ volumes:
+ - name: cache
+ path: /cache
+
+ - name: Frontend install
+ image: node:current-alpine
+ commands:
+ - node -v
+ - yarn --version
+ - yarn
+
+ - name: Cache frontend packages
+ image: sinlead/drone-cache:1.0.0
+ settings:
+ action: save
+ key: yarn.lock
+ mount: node_modules
+ prefix: yarn-modules-${DRONE_REPO_NAME}
+ volumes:
+ - name: cache
+ path: /cache
+
+ - name: Frontend build
+ image: node:current-alpine
+ commands:
+ - yarn build
+
+ - name: Lint project using eslint
+ image: node:current-alpine
+ commands:
+ - yarn lint
+
+---
+kind: pipeline
+type: docker
+name: Publish
+
+platform:
+ os: linux
+ arch: amd64
+
+steps:
+ - name: Publish to ghcr
+ image: plugins/docker
+ environment:
+ PUBLIC_MQTT_BROKER_WS_URL:
+ from_secret: MQTT_BROKER_WS_URL
+ settings:
+ registry: ghcr.io
+ repo: ghcr.io/kevinmidboe/${DRONE_REPO_NAME}
+ dockerfile: Dockerfile
+ username:
+ from_secret: GITHUB_USERNAME
+ password:
+ from_secret: GHCR_UPLOAD_TOKEN
+ build_args:
+ - PUBLIC_MQTT_BROKER_WS_URL=wss://hive.schleppe.cloud/mqtt
+ build_args_from_env:
+ - PUBLIC_MQTT_BROKER_WS_URL
+ tags:
+ - latest
+ - ${DRONE_COMMIT_SHA}
+
+trigger:
+ event:
+ include:
+ - push
+ exclude:
+ - pull_request
+ branch:
+ - main
+
+depends_on:
+ - Build
+
+---
+kind: signature
+hmac: 41d26f0c24afccaea20bfa929d51e7ac8bdaf5ebbc523349ed6934411af026c1
diff --git a/Dockerfile b/Dockerfile
index a007b76..cb90705 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -4,12 +4,14 @@ WORKDIR /app
COPY package*.json .
COPY *config* .
-COPY .env .
+COPY yarn.lock .
COPY src/ src
COPY static/ static
-RUN npm install
-RUN npm run build
+ARG PUBLIC_MQTT_BROKER_WS_URL
+
+RUN yarn install
+RUN yarn run build
FROM nginx:alpine
diff --git a/README.md b/README.md
index b0881a6..a0d43a4 100644
--- a/README.md
+++ b/README.md
@@ -3,10 +3,12 @@
Hive monitor is a frontend for displaying data from MQTT server receiving events from bee hives. The complementary project [hivemonitor-ESP32-firmware]() reads sensor values and publishes on MQTT topic.
Complemetary hivemonitor repositories:
+
- [Hive monitor ESP32 Firmware](https://github.com/kevinmidboe/hivemonitor-esp32-firmware)
- [Hive monitor ESP32 PCB design](https://github.com/kevinmidboe/hivemonitor-pcb)
## Preview
+
[Preview site](https://hive.schleppe.cloud)

@@ -74,3 +76,32 @@ Build and run using nginx in docker:
docker build -t hivemonitor .
docker run -p 8080:8080 --name hivemonitor-frontend hivemonitor
```
+
+## Example mqtt payloads
+
+should automate generating interfaces or grpc.
+
+telemetry/gateway/[id]:
+
+```json
+{
+ "gateway_name": "Elisabeth",
+ "t": "2025-02-08 15:03:30",
+ "temperature": "22",
+ "battery_level": "20"
+}
+```
+
+telemetry/hive/[id]:
+
+```json
+{
+ "hive_name": "Elisabeth",
+ "t": "2025-02-08 15:03:30",
+ "temperature": "22",
+ "humidity": "30",
+ "pressure": "30",
+ "weight": "30",
+ "battery_level": "20"
+}
+```
diff --git a/nginx.conf b/nginx.conf
index 23c9966..05422b1 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -18,5 +18,25 @@ http {
root /app/;
gzip_static on;
+
+
+ # what file to server as index
+ index index.html;
+
+ location / {
+ # First attempt to serve request as file, then
+ # as directory, then fall back to redirecting to index.html
+ try_files $uri $uri/ $uri.html /index.html;
+ }
+
+ location ~* \.(?:css|js|jpg|svg)$ {
+ expires 30d;
+ add_header Cache-Control "public";
+ }
+
+ location ~* \.(?:json)$ {
+ expires 1d;
+ add_header Cache-Control "public";
+ }
}
-}
\ No newline at end of file
+}
diff --git a/package.json b/package.json
index 71470aa..d03b136 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
},
"type": "module",
"dependencies": {
+ "chart.js": "^4.4.7",
"mqtt": "^5.0.0",
"svelte-gestures": "^1.5.2"
}
diff --git a/src/lib/components/Badge.svelte b/src/lib/components/Badge.svelte
new file mode 100644
index 0000000..9623291
--- /dev/null
+++ b/src/lib/components/Badge.svelte
@@ -0,0 +1,55 @@
+
+
+
+
+
+ {dataType}
+ {lastValue}
+
+
+
{lastUpdated} mins
+
+
+
+ Up from last 2 days
+
+
+
+
diff --git a/src/lib/components/Calendar.svelte b/src/lib/components/Calendar.svelte
new file mode 100644
index 0000000..98a71f2
--- /dev/null
+++ b/src/lib/components/Calendar.svelte
@@ -0,0 +1,110 @@
+
+
+
+
{monthString}
+
+
+ {#each Array(firstDay).fill(null) as _}
+
+ {/each}
+
+ {#each Array(daysInMonth)
+ .fill(0)
+ .map((_, i) => i + 1) as _}
+
+
+
+ {/each}
+
+
+
+
diff --git a/src/lib/components/Card.svelte b/src/lib/components/Card.svelte
index bd6389e..3478c7a 100644
--- a/src/lib/components/Card.svelte
+++ b/src/lib/components/Card.svelte
@@ -4,8 +4,8 @@
export let title = '';
export let iconBackground = 'var(--background)';
- export let opens: any = null;
- export let data: any = {};
+ export let opens: any = null; // eslint-disable-line
+ export let data: any = {}; // eslint-disable-line
export let borderLess = false;
function openModal() {
@@ -128,8 +128,8 @@
}
.metric .icon {
- max-width: 22px;
- height: 22px;
+ max-width: 28px;
+ height: 28px;
margin-right: 0.25rem;
}
}
diff --git a/src/lib/components/DateSeparator.svelte b/src/lib/components/DateSeparator.svelte
index bffe6a1..12c5975 100644
--- a/src/lib/components/DateSeparator.svelte
+++ b/src/lib/components/DateSeparator.svelte
@@ -2,7 +2,7 @@
export let date: Date;
function convertDate() {
- let [_, time] = date.toLocaleString('nb-NO').split(', ');
+ let [_, time] = date.toLocaleString('nb-NO').split(', '); // eslint-disable-line
time = time.slice(0, time.length - 3);
const diffDays = Math.floor((new Date().getTime() - date.getTime()) / 86400000);
diff --git a/src/lib/components/Dropdown.svelte b/src/lib/components/Dropdown.svelte
new file mode 100644
index 0000000..9eb465d
--- /dev/null
+++ b/src/lib/components/Dropdown.svelte
@@ -0,0 +1,99 @@
+
+
+
+
+
+ {#if open}
+
+ {/if}
+
+
+
diff --git a/src/lib/components/FooterNavigation.svelte b/src/lib/components/FooterNavigation.svelte
index 3a8b133..f741d76 100644
--- a/src/lib/components/FooterNavigation.svelte
+++ b/src/lib/components/FooterNavigation.svelte
@@ -43,6 +43,7 @@
display: flex;
flex-direction: column;
align-items: center;
+ width: 100%;
cursor: pointer;
padding: 0.6rem 0.5rem 0.2rem;
color: var(--color);
@@ -50,6 +51,10 @@
transition: all 200ms ease;
will-change: font-weight;
+ &:hover {
+ background-color: var(--background-60);
+ }
+
.icon {
width: 20px;
height: 20px;
diff --git a/src/lib/components/Graph.svelte b/src/lib/components/Graph.svelte
new file mode 100644
index 0000000..c041574
--- /dev/null
+++ b/src/lib/components/Graph.svelte
@@ -0,0 +1,159 @@
+
+
+
+
+
+ {dataType}
+ {lastValue}
+
+
+
{date} days
+
+
+
+
+
+
+
+
diff --git a/src/lib/components/HiveSummary.svelte b/src/lib/components/HiveSummary.svelte
index 13aced7..1c12dd1 100644
--- a/src/lib/components/HiveSummary.svelte
+++ b/src/lib/components/HiveSummary.svelte
@@ -53,4 +53,6 @@
{/each}
+
diff --git a/src/lib/components/Modal.svelte b/src/lib/components/Modal.svelte
index a371fc3..6b901a3 100644
--- a/src/lib/components/Modal.svelte
+++ b/src/lib/components/Modal.svelte
@@ -16,17 +16,17 @@
function toggleHandler(isOpen: boolean) {
if (!browser || !modalElement) return;
const app = document.getElementById('app');
- if (!app) return
+ if (!app) return;
if (isOpen) {
app.classList.add('no-scroll');
app.inert = true;
- app.setAttribute('aria-hidden', 'true')
+ app.setAttribute('aria-hidden', 'true');
} else {
setTimeout(() => (modalElement.scrollTop = 0), 500);
app.classList.remove('no-scroll');
app.inert = false;
- app.setAttribute('aria-hidden', 'false')
+ app.setAttribute('aria-hidden', 'false');
}
}
@@ -52,7 +52,7 @@
diff --git a/src/lib/components/cards/HighTemperature.svelte b/src/lib/components/cards/HighTemperature.svelte
index 0ca0ccd..a2068bc 100644
--- a/src/lib/components/cards/HighTemperature.svelte
+++ b/src/lib/components/cards/HighTemperature.svelte
@@ -1,14 +1,13 @@
-
+
- Over: { temperature }°C
+ Over: {temperature}°C
-
diff --git a/src/lib/components/cards/LowBattery.svelte b/src/lib/components/cards/LowBattery.svelte
index 9ca210f..2e8b4c8 100644
--- a/src/lib/components/cards/LowBattery.svelte
+++ b/src/lib/components/cards/LowBattery.svelte
@@ -1,15 +1,14 @@
-
+
- Under: { battery }%
+ Under: {battery}%
-
diff --git a/src/lib/components/cards/LowTemperature.svelte b/src/lib/components/cards/LowTemperature.svelte
index 286a353..a7bc6d7 100644
--- a/src/lib/components/cards/LowTemperature.svelte
+++ b/src/lib/components/cards/LowTemperature.svelte
@@ -1,14 +1,13 @@
-
+
- Under: { temperature }°C
+ Under: {temperature}°C
-
diff --git a/src/lib/components/cards/NoData.svelte b/src/lib/components/cards/NoData.svelte
index 62bb768..30d0b53 100644
--- a/src/lib/components/cards/NoData.svelte
+++ b/src/lib/components/cards/NoData.svelte
@@ -1,14 +1,13 @@
-
+
- More than: { time } hours
+ More than: {time} hours
-
diff --git a/src/lib/components/cards/WeightChanged.svelte b/src/lib/components/cards/WeightChanged.svelte
index 63d579d..37ef510 100644
--- a/src/lib/components/cards/WeightChanged.svelte
+++ b/src/lib/components/cards/WeightChanged.svelte
@@ -1,15 +1,14 @@
-
+
- From { from }kg to { to }kg
+ From {from}kg to {to}kg
-
diff --git a/src/lib/components/displays/BatteryDisplay.svelte b/src/lib/components/displays/BatteryDisplay.svelte
index 22d54a7..dcb75aa 100644
--- a/src/lib/components/displays/BatteryDisplay.svelte
+++ b/src/lib/components/displays/BatteryDisplay.svelte
@@ -1,18 +1,17 @@
-
+
- Battery
- { formatBattery(battery) }% remaining
+ Battery
+ {formatBattery(battery)}% remaining
-
\ No newline at end of file
+
diff --git a/src/lib/components/displays/HumidityDisplay.svelte b/src/lib/components/displays/HumidityDisplay.svelte
index 0f60b2c..143feb5 100644
--- a/src/lib/components/displays/HumidityDisplay.svelte
+++ b/src/lib/components/displays/HumidityDisplay.svelte
@@ -1,20 +1,18 @@
-
+
- Humidity
- { formatHumidity(humidity) }%
- Min 24 - Max 48
+ Humidity
+ {formatHumidity(humidity)}%
+ Min 24 - Max 48
-
\ No newline at end of file
+
diff --git a/src/lib/components/displays/QueenDisplay.svelte b/src/lib/components/displays/QueenDisplay.svelte
index b8f6712..8f7aca6 100644
--- a/src/lib/components/displays/QueenDisplay.svelte
+++ b/src/lib/components/displays/QueenDisplay.svelte
@@ -1,18 +1,15 @@
-
+
- Resident Queen
- Elisabeth
- Age 453 days
+ Resident Queen
+ Elisabeth
+ Age 453 days
-
\ No newline at end of file
+
diff --git a/src/lib/components/displays/SyncDisplay.svelte b/src/lib/components/displays/SyncDisplay.svelte
index b31c728..34442b1 100644
--- a/src/lib/components/displays/SyncDisplay.svelte
+++ b/src/lib/components/displays/SyncDisplay.svelte
@@ -1,33 +1,31 @@
-
+
- Last synced
- { sinceUpdate }s ago
+ Last synced
+ {sinceUpdate}s ago
-
\ No newline at end of file
+
diff --git a/src/lib/components/displays/TemperatureDisplay.svelte b/src/lib/components/displays/TemperatureDisplay.svelte
index 06733bc..f61641c 100644
--- a/src/lib/components/displays/TemperatureDisplay.svelte
+++ b/src/lib/components/displays/TemperatureDisplay.svelte
@@ -1,22 +1,20 @@
-
+
- Temperature
- { formatTemperature(temperature) }°C
+ Temperature
+ {formatTemperature(temperature)}°C
- {change}
+ {change}
-
\ No newline at end of file
+
diff --git a/src/lib/components/displays/WeightDisplay.svelte b/src/lib/components/displays/WeightDisplay.svelte
index 3bbd515..0891c4c 100644
--- a/src/lib/components/displays/WeightDisplay.svelte
+++ b/src/lib/components/displays/WeightDisplay.svelte
@@ -1,19 +1,18 @@
-
+
- Weight
- { formatWeight(weight) }kg
- Min 22.4 - Max 24.4
+ Weight
+ {formatWeight(weight)}kg
+ Min 22.4 - Max 24.4
\ No newline at end of file
+
diff --git a/src/lib/components/modals/GatewayModal.svelte b/src/lib/components/modals/GatewayModal.svelte
index 8aaf3d1..c28e7ce 100644
--- a/src/lib/components/modals/GatewayModal.svelte
+++ b/src/lib/components/modals/GatewayModal.svelte
@@ -36,8 +36,8 @@
{#if selectedSection === segments[0]}
diff --git a/src/lib/components/modals/ModalHeader.svelte b/src/lib/components/modals/ModalHeader.svelte
index 7602661..13c6191 100644
--- a/src/lib/components/modals/ModalHeader.svelte
+++ b/src/lib/components/modals/ModalHeader.svelte
@@ -5,7 +5,7 @@
export let title: string;
export let subtitle: string;
- export let icon: any;
+ export let icon: any; // eslint-disable-line
let bodyTitleEl: HTMLElement;
let headerTitleEl: HTMLElement;
diff --git a/src/lib/icons/AlarmIcon.svelte b/src/lib/icons/AlarmIcon.svelte
index cdd8c1e..2b8537c 100644
--- a/src/lib/icons/AlarmIcon.svelte
+++ b/src/lib/icons/AlarmIcon.svelte
@@ -20,11 +20,18 @@
-->
-
-