Use a single hour state for all weather services
[rhynodge.git] / src / main / kotlin / net / pterodactylus / rhynodge / webpages / weather / wetterde / WetterDeTrigger.kt
1 package net.pterodactylus.rhynodge.webpages.weather.wetterde
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  * TODO
23  *
24  * @author [David ‘Bombe’ Roden](mailto:bombe@pterodactylus.net)
25  */
26 class WetterDeTrigger : Trigger {
27
28     private val dateFormatter = DateFormat.getDateInstance(DateFormat.LONG, Locale.ENGLISH)
29     private lateinit var state: WetterDeState
30     private var changed = false
31
32     override fun mergeStates(previousState: State, currentState: State): State {
33         changed = previousState != currentState
34         state = currentState as WetterDeState
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 wetter.de) on %s".format(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                         +".hour-state { display: table-row; } "
54                         +".hour-state > div { display: table-cell; padding-right: 1em; } "
55                     }
56                 }
57             }
58             body {
59                 val startTime = state.dateTime.toInstant()
60                 h1 { +"The Weather (according to wetter.de) on %s".format(dateFormatter.format(startTime.toEpochMilli())) }
61                 state.forEach {
62                     div("hour-state") {
63                         div("time") { +"%tH:%<tM".format(startTime.plus(it.hourIndex.toLong(), ChronoUnit.HOURS).toEpochMilli()) }
64                         div("temperature") { +"%d °C".format(it.temperature) }
65                         it.feltTemperature?.let {
66                             div("felt-temperature") { +"(%d °C)".format(it) }
67                         }
68                         div("rain-probability") { +"%d%%".format((it.rainProbability * 100).toInt()) }
69                         div("rain-amount") { +"%s l/m²".format(it.rainAmount.minDigits()) }
70                         div("wind-direction") { +it.windDirection.arrow }
71                         div("wind-speed") { +"%d km/h".format(it.windSpeed) }
72                         it.gustSpeed?.let {
73                             div("gust-speed") { +"(up to %d km/h)".format(it) }
74                         }
75                         it.humidity?.let {
76                             div("humidity") { +"%d%%".format((it * 100).toInt()) }
77                         }
78                         div("description") { +it.description }
79                         div("image") { img(src = it.image) }
80                     }
81                 }
82             }
83         }.toString()
84     }
85
86     private fun Double.minDigits(): String {
87         return this.toString().replace(Regex("\\.0*$"), "")
88     }
89
90 }