summaryrefslogtreecommitdiff
path: root/config.go
diff options
context:
space:
mode:
Diffstat (limited to 'config.go')
-rw-r--r--config.go93
1 files changed, 92 insertions, 1 deletions
diff --git a/config.go b/config.go
index edd6f4b..7d3cab8 100644
--- a/config.go
+++ b/config.go
@@ -3,9 +3,33 @@ package main
import (
_ "embed"
"encoding/json"
+ "fmt"
"log"
+ "net"
+ "net/url"
+ "regexp"
+ "strconv"
+ "time"
)
+const (
+ MQTT_BROKER_REGEX = `^tcp://127\.0\.0\.1:\d+$`
+ MQTT_CLIENT_ID_REGEX = `^[0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]{1,23}$`
+ MQTT_TOPIC_PREFIX_REGEX = `^[a-zA-Z0-9]{1,20}$`
+)
+
+var (
+ mqttBrokerRegexp *regexp.Regexp
+ mqttClientIDRegexp *regexp.Regexp
+ mqttTopicPrefixRegexp *regexp.Regexp
+)
+
+func init() {
+ mqttBrokerRegexp = regexp.MustCompile(MQTT_BROKER_REGEX)
+ mqttClientIDRegexp = regexp.MustCompile(MQTT_CLIENT_ID_REGEX)
+ mqttTopicPrefixRegexp = regexp.MustCompile(MQTT_TOPIC_PREFIX_REGEX)
+}
+
//go:embed configs/default.json
var defaultConfig []byte
@@ -25,8 +49,75 @@ type StartupConfig struct {
Homematic HomematicConfig `json:"homematic"`
}
+func (sc StartupConfig) Validate() error {
+ if !mqttBrokerRegexp.MatchString(sc.MQTT.Broker) {
+ return fmt.Errorf(
+ "mqtt/broker configuration '%s' does not match regular expression '%s'.",
+ sc.MQTT.Broker,
+ MQTT_BROKER_REGEX,
+ )
+ }
+
+ if !mqttClientIDRegexp.MatchString(sc.MQTT.ClientID) {
+ return fmt.Errorf(
+ "mqtt/client-id configuration '%s' does not match regular expression '%s'.",
+ sc.MQTT.ClientID,
+ MQTT_CLIENT_ID_REGEX,
+ )
+ }
+
+ if !mqttTopicPrefixRegexp.MatchString(sc.MQTT.TopicPrefix) {
+ return fmt.Errorf(
+ "mqtt/topic-prefix configuration '%s' does not match regular expression '%s'.",
+ sc.MQTT.TopicPrefix,
+ MQTT_TOPIC_PREFIX_REGEX,
+ )
+ }
+
+ cu, err := url.Parse(sc.Homematic.CCU)
+ if err != nil {
+ return fmt.Errorf("homematic/ccu configuration '%s' cannot be parsed as URL: %v", sc.Homematic.CCU, err)
+ }
+
+ if cu.Scheme != `http` {
+ return fmt.Errorf("homematic/ccu configuration '%s' must use scheme 'http'.", sc.Homematic.CCU)
+ }
+
+ host, portstring, err := net.SplitHostPort(cu.Host)
+ if err != nil {
+ return fmt.Errorf("homematic/ccu configuration '%s' can't be split into hostname and port: %v", sc.Homematic.CCU, err)
+ }
+
+ ip := net.ParseIP(host)
+ if ip == nil {
+ return fmt.Errorf("homematic/ccu configuration '%s' must use a plain IP address as host.", sc.Homematic.CCU)
+ }
+
+ _, err = strconv.Atoi(portstring)
+ if err != nil {
+ return fmt.Errorf("homematic/ccu configuration '%s' must use a numeric port: %v", sc.Homematic.CCU, err)
+ }
+
+ _, err = time.ParseDuration(sc.Homematic.PollingPeriod)
+ if err != nil {
+ return fmt.Errorf("homematic/polling-period configuration '%s' could not be parsed to duration: %v", sc.Homematic.PollingPeriod, err)
+ }
+
+ return nil
+}
+
func (sc *StartupConfig) FromJSON(data []byte) error {
- return json.Unmarshal(data, sc)
+ err := json.Unmarshal(data, sc)
+ if err != nil {
+ return fmt.Errorf("Failed to unmarshal configuration: %w", err)
+ }
+
+ err = sc.Validate()
+ if err != nil {
+ return fmt.Errorf("Could not validate configuration: %w", err)
+ }
+
+ return nil
}
func GetStartupConfig() StartupConfig {