Add sunrise/sunset document
This commit is contained in:
parent
14b48c3c21
commit
a24532d9cc
11 changed files with 277 additions and 2 deletions
19
Cargo.lock
generated
19
Cargo.lock
generated
|
|
@ -397,6 +397,15 @@ version = "1.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7588475145507237ded760e52bf2f1085495245502033756d28ea72ade0e498b"
|
checksum = "7588475145507237ded760e52bf2f1085495245502033756d28ea72ade0e498b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ciborium"
|
name = "ciborium"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
|
@ -2628,6 +2637,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"showbits-assets",
|
"showbits-assets",
|
||||||
"showbits-typst",
|
"showbits-typst",
|
||||||
|
"sunrise",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -2782,6 +2792,15 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sunrise"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3217c5830416956b1f2dc731f526150a82c144ebe83d2f0e78853c8356a22ada"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "svgtypes"
|
name = "svgtypes"
|
||||||
version = "0.15.3"
|
version = "0.15.3"
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ rust-embed = "8.6.0"
|
||||||
serde = { version = "1.0.218", features = ["derive"] }
|
serde = { version = "1.0.218", features = ["derive"] }
|
||||||
showbits-assets.path = "./showbits-assets"
|
showbits-assets.path = "./showbits-assets"
|
||||||
showbits-typst.path = "./showbits-typst"
|
showbits-typst.path = "./showbits-typst"
|
||||||
|
sunrise = "1.0.1"
|
||||||
tokio = "1.43.0"
|
tokio = "1.43.0"
|
||||||
typst = "0.13.0"
|
typst = "0.13.0"
|
||||||
typst-assets = { version = "0.13.0", features = ["fonts"] }
|
typst-assets = { version = "0.13.0", features = ["fonts"] }
|
||||||
|
|
|
||||||
100
showbits-thermal-printer-ui/src/components/CDocumentSunrise.vue
Normal file
100
showbits-thermal-printer-ui/src/components/CDocumentSunrise.vue
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useApiRequest } from "@/apiRequest";
|
||||||
|
import { ref } from "vue";
|
||||||
|
import CError from "./CError.vue";
|
||||||
|
|
||||||
|
const { disabled, error, makeRequest } = useApiRequest();
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
const latitude = ref<number>();
|
||||||
|
const longitude = ref<number>();
|
||||||
|
const year = ref(today.getFullYear());
|
||||||
|
const month = ref(today.getMonth() + 1);
|
||||||
|
const feed = ref(true);
|
||||||
|
|
||||||
|
function submit() {
|
||||||
|
if (typeof latitude.value !== "number") return;
|
||||||
|
if (typeof longitude.value !== "number") return;
|
||||||
|
const data = new URLSearchParams();
|
||||||
|
data.append("latitude", latitude.value.toFixed(5));
|
||||||
|
data.append("longitude", longitude.value.toFixed(5));
|
||||||
|
data.append("year", year.value.toFixed());
|
||||||
|
data.append("month", month.value.toFixed());
|
||||||
|
data.append("feed", String(feed.value));
|
||||||
|
void makeRequest("api/sunrise", data);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<h2>Sunrise and Sunset</h2>
|
||||||
|
|
||||||
|
<label class="wide">
|
||||||
|
Latitude:
|
||||||
|
<input
|
||||||
|
v-model="latitude"
|
||||||
|
type="number"
|
||||||
|
step="any"
|
||||||
|
min="-90"
|
||||||
|
max="90"
|
||||||
|
required
|
||||||
|
:disabled
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="wide">
|
||||||
|
Longitude:
|
||||||
|
<input
|
||||||
|
v-model="longitude"
|
||||||
|
type="number"
|
||||||
|
step="any"
|
||||||
|
min="-180"
|
||||||
|
max="180"
|
||||||
|
required
|
||||||
|
:disabled
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="wide">
|
||||||
|
Year:
|
||||||
|
<input
|
||||||
|
v-model="year"
|
||||||
|
type="number"
|
||||||
|
min="-9999"
|
||||||
|
max="9999"
|
||||||
|
required
|
||||||
|
:disabled
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="wide">
|
||||||
|
Month:
|
||||||
|
<input
|
||||||
|
v-model="month"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="12"
|
||||||
|
required
|
||||||
|
:disabled
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label><input v-model="feed" type="checkbox" :disabled /> Feed</label>
|
||||||
|
|
||||||
|
<button :disabled>Print</button>
|
||||||
|
<CError :message="error" />
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wide {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -5,11 +5,19 @@ import CDocumentCells from "./CDocumentCells.vue";
|
||||||
import CDocumentChat from "./CDocumentChat.vue";
|
import CDocumentChat from "./CDocumentChat.vue";
|
||||||
import CDocumentEgg from "./CDocumentEgg.vue";
|
import CDocumentEgg from "./CDocumentEgg.vue";
|
||||||
import CDocumentImage from "./CDocumentImage.vue";
|
import CDocumentImage from "./CDocumentImage.vue";
|
||||||
|
import CDocumentSunrise from "./CDocumentSunrise.vue";
|
||||||
import CDocumentText from "./CDocumentText.vue";
|
import CDocumentText from "./CDocumentText.vue";
|
||||||
import CDocumentTictactoe from "./CDocumentTictactoe.vue";
|
import CDocumentTictactoe from "./CDocumentTictactoe.vue";
|
||||||
|
|
||||||
const mode = ref<
|
const mode = ref<
|
||||||
"calendar" | "chat" | "cells" | "egg" | "image" | "text" | "tictactoe"
|
| "calendar"
|
||||||
|
| "cells"
|
||||||
|
| "chat"
|
||||||
|
| "egg"
|
||||||
|
| "image"
|
||||||
|
| "sunrise"
|
||||||
|
| "text"
|
||||||
|
| "tictactoe"
|
||||||
>();
|
>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -17,10 +25,11 @@ const mode = ref<
|
||||||
<div class="outer">
|
<div class="outer">
|
||||||
<button v-if="mode" class="close" @click="mode = undefined">X</button>
|
<button v-if="mode" class="close" @click="mode = undefined">X</button>
|
||||||
<CDocumentCalendar v-if="mode === 'calendar'" />
|
<CDocumentCalendar v-if="mode === 'calendar'" />
|
||||||
<CDocumentChat v-if="mode === 'chat'" />
|
|
||||||
<CDocumentCells v-if="mode === 'cells'" />
|
<CDocumentCells v-if="mode === 'cells'" />
|
||||||
|
<CDocumentChat v-if="mode === 'chat'" />
|
||||||
<CDocumentEgg v-if="mode === 'egg'" />
|
<CDocumentEgg v-if="mode === 'egg'" />
|
||||||
<CDocumentImage v-if="mode === 'image'" />
|
<CDocumentImage v-if="mode === 'image'" />
|
||||||
|
<CDocumentSunrise v-if="mode === 'sunrise'" />
|
||||||
<CDocumentText v-if="mode === 'text'" />
|
<CDocumentText v-if="mode === 'text'" />
|
||||||
<CDocumentTictactoe v-if="mode === 'tictactoe'" />
|
<CDocumentTictactoe v-if="mode === 'tictactoe'" />
|
||||||
<hr v-if="mode !== undefined" />
|
<hr v-if="mode !== undefined" />
|
||||||
|
|
@ -32,6 +41,7 @@ const mode = ref<
|
||||||
<button @click="mode = 'chat'">Chat Message</button>
|
<button @click="mode = 'chat'">Chat Message</button>
|
||||||
<button @click="mode = 'egg'">Easter Egg</button>
|
<button @click="mode = 'egg'">Easter Egg</button>
|
||||||
<button @click="mode = 'image'">Image</button>
|
<button @click="mode = 'image'">Image</button>
|
||||||
|
<button @click="mode = 'sunrise'">Sunrise and Sunset</button>
|
||||||
<button @click="mode = 'text'">Text</button>
|
<button @click="mode = 'text'">Text</button>
|
||||||
<button @click="mode = 'tictactoe'">Tic Tac Toe</button>
|
<button @click="mode = 'tictactoe'">Tic Tac Toe</button>
|
||||||
<a href="photo.html">Take a photo</a>
|
<a href="photo.html">Take a photo</a>
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ rust-embed = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
showbits-assets = { workspace = true }
|
showbits-assets = { workspace = true }
|
||||||
showbits-typst = { workspace = true }
|
showbits-typst = { workspace = true }
|
||||||
|
sunrise = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["full"] }
|
tokio = { workspace = true, features = ["full"] }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ pub mod cells;
|
||||||
pub mod chat;
|
pub mod chat;
|
||||||
pub mod egg;
|
pub mod egg;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
|
pub mod sunrise;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
pub mod tictactoe;
|
pub mod tictactoe;
|
||||||
|
|
||||||
|
|
|
||||||
38
showbits-thermal-printer/src/documents/sunrise/data.json
Normal file
38
showbits-thermal-printer/src/documents/sunrise/data.json
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"year": 2025,
|
||||||
|
"month": 3,
|
||||||
|
"times": [
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"],
|
||||||
|
["01:23", "45:67"]
|
||||||
|
],
|
||||||
|
"feed": false
|
||||||
|
}
|
||||||
1
showbits-thermal-printer/src/documents/sunrise/lib
Symbolic link
1
showbits-thermal-printer/src/documents/sunrise/lib
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../lib
|
||||||
46
showbits-thermal-printer/src/documents/sunrise/main.typ
Normal file
46
showbits-thermal-printer/src/documents/sunrise/main.typ
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#import "@preview/oxifmt:0.2.1": strfmt
|
||||||
|
#import "lib/main.typ" as lib;
|
||||||
|
#show: it => lib.init(it)
|
||||||
|
|
||||||
|
#let data = json("data.json")
|
||||||
|
#let date = datetime(year: data.year, month: data.month, day: 1)
|
||||||
|
|
||||||
|
#let month_length = 32 - (date + duration(days: 31)).day()
|
||||||
|
|
||||||
|
#let head(name) = text(size: 32pt, name)
|
||||||
|
#let empty = box()
|
||||||
|
#let day(content) = box(
|
||||||
|
width: 100%,
|
||||||
|
height: 100%,
|
||||||
|
stroke: 2pt + black,
|
||||||
|
content,
|
||||||
|
)
|
||||||
|
|
||||||
|
#align(center + horizon)[
|
||||||
|
#set par(spacing: 8pt)
|
||||||
|
|
||||||
|
Sonnenauf- und Untergang
|
||||||
|
#strfmt("{:04}-{:02}", date.year(), date.month())
|
||||||
|
|
||||||
|
#set par(leading: 5pt)
|
||||||
|
#grid(
|
||||||
|
columns: (50pt,) * 7,
|
||||||
|
rows: 50pt,
|
||||||
|
gutter: 4pt,
|
||||||
|
head[Mo], head[Di], head[Mi], head[Do], head[Fr], head[Sa], head[So],
|
||||||
|
..for _ in range(date.weekday() - 1) { (empty,) },
|
||||||
|
..for (i, (sunrise, sunset)) in data.times.enumerate() {
|
||||||
|
(
|
||||||
|
day[
|
||||||
|
#strfmt("{:02}", i + 1) \
|
||||||
|
#sunrise \
|
||||||
|
#sunset
|
||||||
|
],
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
#if data.feed {
|
||||||
|
lib.feed
|
||||||
|
}
|
||||||
57
showbits-thermal-printer/src/documents/sunrise/mod.rs
Normal file
57
showbits-thermal-printer/src/documents/sunrise/mod.rs
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
use axum::{Form, extract::State};
|
||||||
|
use jiff::{Timestamp, ToSpan, Zoned, civil};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::server::{Server, somehow};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Data {
|
||||||
|
year: i16,
|
||||||
|
month: i8,
|
||||||
|
times: Vec<(String, String)>,
|
||||||
|
feed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct FormData {
|
||||||
|
pub latitude: f64,
|
||||||
|
pub longitude: f64,
|
||||||
|
pub year: Option<i16>,
|
||||||
|
pub month: Option<i8>,
|
||||||
|
pub feed: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn post(server: State<Server>, Form(form): Form<FormData>) -> somehow::Result<()> {
|
||||||
|
let now = Zoned::now().date();
|
||||||
|
let year = form.year.unwrap_or(now.year());
|
||||||
|
let month = form.month.unwrap_or(now.month());
|
||||||
|
|
||||||
|
let first = civil::Date::new(year, month, 1)?;
|
||||||
|
let mut times = vec![];
|
||||||
|
for day in 1..=first.days_in_month() {
|
||||||
|
let date = first + day.days();
|
||||||
|
let (rise, set) = sunrise::sunrise_sunset(
|
||||||
|
form.latitude,
|
||||||
|
form.longitude,
|
||||||
|
date.year() as i32,
|
||||||
|
date.month() as u32,
|
||||||
|
date.day() as u32,
|
||||||
|
);
|
||||||
|
let rise = Timestamp::new(rise, 0)?.strftime("%H:%M").to_string();
|
||||||
|
let set = Timestamp::new(set, 0)?.strftime("%H:%M").to_string();
|
||||||
|
times.push((rise, set));
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = Data {
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
times,
|
||||||
|
feed: form.feed.unwrap_or(true),
|
||||||
|
};
|
||||||
|
|
||||||
|
let typst = super::typst_with_lib()
|
||||||
|
.with_json("/data.json", &data)
|
||||||
|
.with_main_file(include_str!("main.typ"));
|
||||||
|
|
||||||
|
server.print_typst(typst).await
|
||||||
|
}
|
||||||
|
|
@ -41,6 +41,7 @@ pub async fn run(tx: mpsc::Sender<Command>, addr: String) -> anyhow::Result<()>
|
||||||
.route("/api/chat", post(documents::chat::post))
|
.route("/api/chat", post(documents::chat::post))
|
||||||
.route("/api/egg", post(documents::egg::post))
|
.route("/api/egg", post(documents::egg::post))
|
||||||
.route("/api/image", post(documents::image::post))
|
.route("/api/image", post(documents::image::post))
|
||||||
|
.route("/api/sunrise", post(documents::sunrise::post))
|
||||||
.route("/api/text", post(documents::text::post))
|
.route("/api/text", post(documents::text::post))
|
||||||
.route("/api/tictactoe", post(documents::tictactoe::post))
|
.route("/api/tictactoe", post(documents::tictactoe::post))
|
||||||
// Rest
|
// Rest
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue