5374d4cb6a33b8dc6ee6ee4a29622e819b4d912e
[rhynodge.git] / src / main / kotlin / net / pterodactylus / rhynodge / webpages / weather / WeatherTrigger.kt
1 package net.pterodactylus.rhynodge.webpages.weather
2
3 import kotlinx.html.body
4 import kotlinx.html.div
5 import kotlinx.html.h1
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
20
21 /**
22  * Detects changes in the weather and creates email texts.
23  *
24  * @author [David ‘Bombe’ Roden](mailto:bombe@pterodactylus.net)
25  */
26 class WeatherTrigger : Trigger {
27
28     private val dateFormatter = DateFormat.getDateInstance(DateFormat.LONG, Locale.ENGLISH)
29     private lateinit var state: WeatherState
30     private var changed = false
31
32     override fun mergeStates(previousState: State, currentState: State): State {
33         changed = previousState != currentState
34         state = currentState as WeatherState
35         return currentState
36     }
37
38     override fun triggers(): Boolean {
39         return changed
40     }
41
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())
45         return output
46     }
47
48     private fun generateHtmlOutput(): String {
49         return createHTML().html {
50             head {
51                 style("text/css") {
52                     unsafe {
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; } "
57                     }
58                 }
59             }
60             body {
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") {
67                     div("header") {
68                         div { +"Time" }
69                         div { +"Temperature" }
70                         if (showHumidity) {
71                             div { +"feels like" }
72                         }
73                         div { +"Chance of Rain" }
74                         div { +"Amount" }
75                         div { +"Wind from" }
76                         div { +"Speed" }
77                         if (showGustSpeed) {
78                             div { +"Gusts" }
79                         }
80                         if (showHumidity) {
81                             div { +"Humidity" }
82                         }
83                         div { +"Description" }
84                         div { +"Image" }
85                     }
86                     state.forEach {
87                         div("hour-state") {
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 "" }
92                             }
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" }
97                             if (showGustSpeed) {
98                                 div("gust-speed") { +if (it.gustSpeed != null) "(up to ${it.gustSpeed} km/h)" else "" }
99                             }
100                             if (showHumidity) {
101                                 div("humidity") { +if (it.humidity != null) "${it.humidity.times(100).toInt()}%" else "" }
102                             }
103                             div("description") { +it.description }
104                             div("image") { img(src = it.image) }
105                         }
106                     }
107                 }
108             }
109         }.toString()
110     }
111
112     private fun Double.minDigits(): String {
113         return this.toString().replace(Regex("\\.0*$"), "")
114     }
115
116 }