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"
|
||||
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]]
|
||||
name = "ciborium"
|
||||
version = "0.2.2"
|
||||
|
|
@ -2628,6 +2637,7 @@ dependencies = [
|
|||
"serde",
|
||||
"showbits-assets",
|
||||
"showbits-typst",
|
||||
"sunrise",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
|
@ -2782,6 +2792,15 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sunrise"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3217c5830416956b1f2dc731f526150a82c144ebe83d2f0e78853c8356a22ada"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "svgtypes"
|
||||
version = "0.15.3"
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ rust-embed = "8.6.0"
|
|||
serde = { version = "1.0.218", features = ["derive"] }
|
||||
showbits-assets.path = "./showbits-assets"
|
||||
showbits-typst.path = "./showbits-typst"
|
||||
sunrise = "1.0.1"
|
||||
tokio = "1.43.0"
|
||||
typst = "0.13.0"
|
||||
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 CDocumentEgg from "./CDocumentEgg.vue";
|
||||
import CDocumentImage from "./CDocumentImage.vue";
|
||||
import CDocumentSunrise from "./CDocumentSunrise.vue";
|
||||
import CDocumentText from "./CDocumentText.vue";
|
||||
import CDocumentTictactoe from "./CDocumentTictactoe.vue";
|
||||
|
||||
const mode = ref<
|
||||
"calendar" | "chat" | "cells" | "egg" | "image" | "text" | "tictactoe"
|
||||
| "calendar"
|
||||
| "cells"
|
||||
| "chat"
|
||||
| "egg"
|
||||
| "image"
|
||||
| "sunrise"
|
||||
| "text"
|
||||
| "tictactoe"
|
||||
>();
|
||||
</script>
|
||||
|
||||
|
|
@ -17,10 +25,11 @@ const mode = ref<
|
|||
<div class="outer">
|
||||
<button v-if="mode" class="close" @click="mode = undefined">X</button>
|
||||
<CDocumentCalendar v-if="mode === 'calendar'" />
|
||||
<CDocumentChat v-if="mode === 'chat'" />
|
||||
<CDocumentCells v-if="mode === 'cells'" />
|
||||
<CDocumentChat v-if="mode === 'chat'" />
|
||||
<CDocumentEgg v-if="mode === 'egg'" />
|
||||
<CDocumentImage v-if="mode === 'image'" />
|
||||
<CDocumentSunrise v-if="mode === 'sunrise'" />
|
||||
<CDocumentText v-if="mode === 'text'" />
|
||||
<CDocumentTictactoe v-if="mode === 'tictactoe'" />
|
||||
<hr v-if="mode !== undefined" />
|
||||
|
|
@ -32,6 +41,7 @@ const mode = ref<
|
|||
<button @click="mode = 'chat'">Chat Message</button>
|
||||
<button @click="mode = 'egg'">Easter Egg</button>
|
||||
<button @click="mode = 'image'">Image</button>
|
||||
<button @click="mode = 'sunrise'">Sunrise and Sunset</button>
|
||||
<button @click="mode = 'text'">Text</button>
|
||||
<button @click="mode = 'tictactoe'">Tic Tac Toe</button>
|
||||
<a href="photo.html">Take a photo</a>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ rust-embed = { workspace = true }
|
|||
serde = { workspace = true }
|
||||
showbits-assets = { workspace = true }
|
||||
showbits-typst = { workspace = true }
|
||||
sunrise = { workspace = true }
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
|
||||
[lints]
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ pub mod cells;
|
|||
pub mod chat;
|
||||
pub mod egg;
|
||||
pub mod image;
|
||||
pub mod sunrise;
|
||||
pub mod text;
|
||||
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/egg", post(documents::egg::post))
|
||||
.route("/api/image", post(documents::image::post))
|
||||
.route("/api/sunrise", post(documents::sunrise::post))
|
||||
.route("/api/text", post(documents::text::post))
|
||||
.route("/api/tictactoe", post(documents::tictactoe::post))
|
||||
// Rest
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue