1 package net.pterodactylus.rhynodge.webpages.weather
3 import kotlinx.html.body
4 import kotlinx.html.div
6 import kotlinx.html.head
7 import kotlinx.html.html
8 import kotlinx.html.img
9 import kotlinx.html.stream.createHTML
10 import kotlinx.html.style
11 import kotlinx.html.unsafe
12 import net.pterodactylus.rhynodge.Reaction
13 import net.pterodactylus.rhynodge.State
14 import net.pterodactylus.rhynodge.Trigger
15 import net.pterodactylus.rhynodge.output.DefaultOutput
16 import net.pterodactylus.rhynodge.output.Output
17 import java.text.DateFormat
18 import java.time.temporal.ChronoUnit
19 import java.util.Locale
22 * Detects changes in the weather and creates email texts.
24 * @author [David ‘Bombe’ Roden](mailto:bombe@pterodactylus.net)
26 class WeatherTrigger : Trigger {
28 private val dateFormatter = DateFormat.getDateInstance(DateFormat.LONG, Locale.ENGLISH)
29 private lateinit var state: WeatherState
30 private var changed = false
32 override fun mergeStates(previousState: State, currentState: State): State {
33 changed = previousState != currentState
34 state = currentState as WeatherState
38 override fun triggers(): Boolean {
42 override fun output(reaction: Reaction): Output {
43 val output = DefaultOutput("The Weather (according to ${state.service}) on ${dateFormatter.format(state.dateTime.toInstant().toEpochMilli())}")
44 output.addText("text/html", generateHtmlOutput())
48 private fun generateHtmlOutput(): String {
49 return createHTML().html {
53 +".weather-states { display: table; } "
54 +".hour-state, .header { display: table-row; } "
55 +".hour-state > div { display: table-cell; padding: 0em 0.5em; text-align: center; } "
56 +".header > div { display: table-cell; padding: 0em 0.5em; font-weight: bold; text-align: center; } "
61 val startTime = state.dateTime.toInstant()
62 h1 { +"The Weather (according to wetter.de) on %s".format(dateFormatter.format(startTime.toEpochMilli())) }
63 val showFeltTemperature = state.any { it.feltTemperature != null }
64 val showGustSpeed = state.any { it.gustSpeed != null }
65 val showHumidity = state.any { it.humidity != null }
66 div("weather-states") {
69 div { +"Temperature" }
73 div { +"Chance of Rain" }
83 div { +"Description" }
88 div("time") { +"%tH:%<tM".format(startTime.plus(it.hourIndex.toLong(), ChronoUnit.HOURS).toEpochMilli()) }
89 div("temperature") { +"${it.temperature} °C" }
90 if (showFeltTemperature) {
91 div("felt-temperature") { +if (it.feltTemperature != null) "(${it.feltTemperature} °C)" else "" }
93 div("rain-probability") { +"${it.rainProbability.times(100).toInt()}%" }
94 div("rain-amount") { +"${it.rainAmount.minDigits()} l/m²" }
95 div("wind-direction") { +it.windDirection.arrow }
96 div("wind-speed") { +"${it.windSpeed} km/h" }
98 div("gust-speed") { +if (it.gustSpeed != null) "(up to ${it.gustSpeed} km/h)" else "" }
101 div("humidity") { +if (it.humidity != null) "${it.humidity.times(100).toInt()}%" else "" }
103 div("description") { +it.description }
104 div("image") { img(src = it.image) }
112 private fun Double.minDigits(): String {
113 return this.toString().replace(Regex("\\.0*$"), "")