OmniLinkBridge/OmniLinkBridge/Settings.cs
2019-12-24 22:32:42 -05:00

300 lines
11 KiB
C#

using log4net;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net.Mail;
using System.Reflection;
namespace OmniLinkBridge
{
public static class Settings
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public static void LoadSettings()
{
NameValueCollection settings = LoadCollection(Global.config_file);
// HAI / Leviton Omni Controller
Global.controller_address = settings.CheckEnv("controller_address");
Global.controller_port = ValidatePort(settings, "controller_port");
Global.controller_key1 = settings.CheckEnv("controller_key1");
Global.controller_key2 = settings.CheckEnv("controller_key2");
Global.controller_name = settings.CheckEnv("controller_name") ?? "OmniLinkBridge";
// Controller Time Sync
Global.time_sync = ValidateYesNo(settings, "time_sync");
Global.time_interval = ValidateInt(settings, "time_interval");
Global.time_drift = ValidateInt(settings, "time_drift");
// Verbose Console
Global.verbose_unhandled = ValidateYesNo(settings, "verbose_unhandled");
Global.verbose_event = ValidateYesNo(settings, "verbose_event");
Global.verbose_area = ValidateYesNo(settings, "verbose_area");
Global.verbose_zone = ValidateYesNo(settings, "verbose_zone");
Global.verbose_thermostat_timer = ValidateYesNo(settings, "verbose_thermostat_timer");
Global.verbose_thermostat = ValidateYesNo(settings, "verbose_thermostat");
Global.verbose_unit = ValidateYesNo(settings, "verbose_unit");
Global.verbose_message = ValidateYesNo(settings, "verbose_message");
// mySQL Logging
Global.mysql_logging = ValidateYesNo(settings, "mysql_logging");
Global.mysql_connection = settings.CheckEnv("mysql_connection");
// Web Service
Global.webapi_enabled = ValidateYesNo(settings, "webapi_enabled");
Global.webapi_port = ValidatePort(settings, "webapi_port");
Global.webapi_override_zone = LoadOverrideZone<WebAPI.OverrideZone>(settings, "webapi_override_zone");
// MQTT
Global.mqtt_enabled = ValidateYesNo(settings, "mqtt_enabled");
Global.mqtt_server = settings.CheckEnv("mqtt_server");
Global.mqtt_port = ValidatePort(settings, "mqtt_port");
Global.mqtt_username = settings.CheckEnv("mqtt_username");
Global.mqtt_password = settings.CheckEnv("mqtt_password");
Global.mqtt_prefix = settings.CheckEnv("mqtt_prefix") ?? "omnilink";
Global.mqtt_discovery_prefix = settings.CheckEnv("mqtt_discovery_prefix") ?? "homeassistant";
Global.mqtt_discovery_name_prefix = settings.CheckEnv("mqtt_discovery_name_prefix") ?? string.Empty;
if (!string.IsNullOrEmpty(Global.mqtt_discovery_name_prefix))
Global.mqtt_discovery_name_prefix += " ";
Global.mqtt_discovery_ignore_zones = ValidateRange(settings, "mqtt_discovery_ignore_zones");
Global.mqtt_discovery_ignore_units = ValidateRange(settings, "mqtt_discovery_ignore_units");
Global.mqtt_discovery_override_zone = LoadOverrideZone<MQTT.OverrideZone>(settings, "mqtt_discovery_override_zone");
// Notifications
Global.notify_area = ValidateYesNo(settings, "notify_area");
Global.notify_message = ValidateYesNo(settings, "notify_message");
// Email Notifications
Global.mail_server = settings.CheckEnv("mail_server");
Global.mail_tls = ValidateYesNo(settings, "mail_tls");
Global.mail_port = ValidatePort(settings, "mail_port");
Global.mail_username = settings.CheckEnv("mail_username");
Global.mail_password = settings.CheckEnv("mail_password");
Global.mail_from = ValidateMailFrom(settings, "mail_from");
Global.mail_to = ValidateMailTo(settings, "mail_to");
// Prowl Notifications
Global.prowl_key = ValidateMultipleStrings(settings, "prowl_key");
// Pushover Notifications
Global.pushover_token = settings.CheckEnv("pushover_token");
Global.pushover_user = ValidateMultipleStrings(settings, "pushover_user");
}
private static string CheckEnv(this NameValueCollection settings, string name)
{
string env = Environment.GetEnvironmentVariable(name.ToUpper());
return !string.IsNullOrEmpty(env) ? env : settings[name];
}
private static ConcurrentDictionary<int, T> LoadOverrideZone<T>(NameValueCollection settings, string section) where T : new()
{
try
{
ConcurrentDictionary<int, T> ret = new ConcurrentDictionary<int, T>();
if (settings.CheckEnv(section) == null)
return ret;
string[] ids = settings.CheckEnv(section).Split(',');
for (int i = 0; i < ids.Length; i++)
{
Dictionary<string, string> attributes = ids[i].TrimEnd(new char[] { ';' }).Split(';')
.Select(s => s.Split('='))
.ToDictionary(a => a[0].Trim(), a => a[1].Trim(), StringComparer.InvariantCultureIgnoreCase);
if (!attributes.ContainsKey("id") || !int.TryParse(attributes["id"], out int attrib_id))
throw new Exception("Missing or invalid id attribute");
T override_zone = new T();
if (((object)override_zone) is WebAPI.OverrideZone webapi_zone)
{
if (!attributes.ContainsKey("device_type") || !Enum.TryParse(attributes["device_type"], out WebAPI.DeviceType attrib_device_type))
throw new Exception("Missing or invalid device_type attribute");
webapi_zone.device_type = attrib_device_type;
}
else if (((object)override_zone) is MQTT.OverrideZone mqtt_zone)
{
if (!attributes.ContainsKey("device_class") || !Enum.TryParse(attributes["device_class"], out MQTT.BinarySensor.DeviceClass attrib_device_class))
throw new Exception("Missing or invalid device_class attribute");
mqtt_zone.device_class = attrib_device_class;
}
ret.TryAdd(attrib_id, override_zone);
}
return ret;
}
catch (Exception ex)
{
log.Error("Invalid override zone specified for " + section, ex);
throw;
}
}
private static int ValidateInt(NameValueCollection settings, string section)
{
try
{
return int.Parse(settings.CheckEnv(section));
}
catch
{
log.Error("Invalid integer specified for " + section);
throw;
}
}
private static HashSet<int> ValidateRange(NameValueCollection settings, string section)
{
try
{
return new HashSet<int>(settings.CheckEnv(section).ParseRanges());
}
catch
{
log.Error("Invalid range specified for " + section);
throw;
}
}
private static int ValidatePort(NameValueCollection settings, string section)
{
try
{
int port = int.Parse(settings.CheckEnv(section));
if (port < 1 || port > 65534)
throw new Exception();
return port;
}
catch
{
log.Error("Invalid port specified for " + section);
throw;
}
}
private static MailAddress ValidateMailFrom(NameValueCollection settings, string section)
{
try
{
return new MailAddress(settings.CheckEnv(section));
}
catch
{
log.Error("Invalid email specified for " + section);
throw;
}
}
private static MailAddress[] ValidateMailTo(NameValueCollection settings, string section)
{
try
{
if(settings.CheckEnv(section) == null)
return new MailAddress[] {};
string[] emails = settings.CheckEnv(section).Split(',');
MailAddress[] addresses = new MailAddress[emails.Length];
for(int i=0; i < emails.Length; i++)
addresses[i] = new MailAddress(emails[i]);
return addresses;
}
catch
{
log.Error("Invalid email specified for " + section);
throw;
}
}
private static string[] ValidateMultipleStrings(NameValueCollection settings, string section)
{
try
{
if (settings.CheckEnv(section) == null)
return new string[] { };
return settings.CheckEnv(section).Split(',');
}
catch
{
log.Error("Invalid string specified for " + section);
throw;
}
}
private static bool ValidateYesNo (NameValueCollection settings, string section)
{
string value = settings.CheckEnv(section);
if (value == null)
return false;
if (string.Compare(value, "yes", true) == 0)
return true;
else if (string.Compare(value, "no", true) == 0)
return false;
else
{
log.Error("Invalid yes/no specified for " + section);
throw new Exception();
}
}
private static NameValueCollection LoadCollection(string sFile)
{
NameValueCollection settings = new NameValueCollection();
try
{
FileStream fs = new FileStream(sFile, FileMode.Open, FileAccess.Read);
StreamReader sr = new StreamReader(fs);
while (true)
{
string line = sr.ReadLine();
if (line == null)
break;
if (line.StartsWith("#"))
continue;
int pos = line.IndexOf('=', 0);
if (pos == -1)
continue;
string key = line.Substring(0, pos).Trim();
string value = line.Substring(pos + 1).Trim();
settings.Add(key, value);
}
sr.Close();
fs.Close();
}
catch (FileNotFoundException ex)
{
log.Error("Unable to parse settings file " + sFile, ex);
throw;
}
return settings;
}
}
}