This commit is contained in:
BlubbFish 2019-12-04 21:55:26 +01:00
parent 6dbc675a8b
commit fdb5abc125
15 changed files with 643 additions and 90 deletions

10
Snips/Events/TextEvent.cs Normal file
View File

@ -0,0 +1,10 @@
using System;
namespace BlubbFish.Iot.Snips.Events {
public class TextEvent : EventArgs { }
public class StringEvent : TextEvent {
public String Text { get; private set; }
public StringEvent(String text) => this.Text = text;
}
}

View File

@ -2,17 +2,17 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using BlubbFish.Iot.Snips.DataInputs; using BlubbFish.Iot.Snips.DataInputs;
using BlubbFish.Utils.IoT.Connector;
using LitJson; using LitJson;
namespace BlubbFish.Iot.Snips.Intents { namespace BlubbFish.Iot.Snips.Intents {
abstract public class AEspIntent : AIntent { abstract public class AEspIntent : AIntent {
protected readonly Dictionary<String, EnvEsp8266> esps; protected Dictionary<String, EnvEsp8266> esps;
protected String espid = null; protected String espid = null;
protected AEspIntent(JsonData data, ABackend snips, Dictionary<String, EnvEsp8266> esps) : base(data, snips) => this.esps = esps; protected AEspIntent(JsonData data) : base(data) { }
public override void Parse() { public void ParseESP() {
Console.WriteLine("AEspIntent.ParseESP(): Parse the ESP data.");
String ort = this.slots["ort"]; String ort = this.slots["ort"];
String mort = "ESP8266-" + ort; String mort = "ESP8266-" + ort;
Dictionary<String, Double> goal = new Dictionary<String, Double>(); Dictionary<String, Double> goal = new Dictionary<String, Double>();
@ -30,6 +30,8 @@ namespace BlubbFish.Iot.Snips.Intents {
} }
} }
internal void SetEsps(Dictionary<String, EnvEsp8266> esps) => this.esps = esps;
protected String GetSpeakName(String name) { protected String GetSpeakName(String name) {
String ort = name.Substring(8); String ort = name.Substring(8);
switch(ort) { switch(ort) {

View File

@ -1,55 +1,55 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using BlubbFish.Utils.IoT.Connector; using BlubbFish.Iot.Snips.Events;
using BlubbFish.Utils;
using LitJson; using LitJson;
namespace BlubbFish.Iot.Snips.Intents { namespace BlubbFish.Iot.Snips.Intents {
public abstract class AIntent { public abstract class AIntent {
protected readonly JsonData data;
private readonly ABackend snips;
protected readonly Dictionary<String, String> slots = new Dictionary<String, String>(); protected readonly Dictionary<String, String> slots = new Dictionary<String, String>();
protected AIntent(JsonData data, ABackend snips) { public delegate void EventText(Object sender, TextEvent e);
this.data = data; public delegate void EventShutdown(Object sender, EventArgs e);
this.snips = snips;
this.ParseSlots();
}
private void ParseSlots() { public event EventText SendText;
foreach(JsonData item in this.data["slots"]) { public event EventShutdown EndSession;
protected AIntent(JsonData intentdata) => this.ParseIntent(intentdata);
private void ParseIntent(JsonData intentdata) {
Console.WriteLine("AIntent.ParseIntent(): Get intent and parse data.");
foreach(JsonData item in intentdata["slots"]) {
this.slots.Add(item["slotName"].ToString(), item["value"]["value"].ToString()); this.slots.Add(item["slotName"].ToString(), item["value"]["value"].ToString());
} }
} }
protected void SnipsSay(String text) { public static AIntent GetInstance(JsonData data) {
((ADataBackend)this.snips).Send("hermes/tts/say", String intent = "BlubbFish.Iot.Snips.Intents." + GetIntentName(data).ToUpperLower();
JsonMapper.ToJson( Type t = null;
new Dictionary<String, Object> { try {
{ "id",this.data["id"].ToString() }, t = Type.GetType(intent, true);
{ "lang","de" }, } catch(System.IO.FileNotFoundException) {
{ "sessionId", this.data["sessionId"].ToString() }, Console.Error.WriteLine("Intent " + intent + " could not load!");
{ "siteId","default" }, return null;
{ "text",text }
} }
) return (AIntent)t.GetConstructor(new Type[] { typeof(JsonData) }).Invoke(new Object[] { data });
);
Console.WriteLine("TTS: " + text);
} }
protected void SnipsEndSpeak() { public static String GetIntentName(JsonData data) {
((ADataBackend)this.snips).Send("hermes/tts/sayFinished", String i = data["intent"]["intentName"].ToString();
JsonMapper.ToJson( return i.Substring(i.IndexOf(':')+1);
new Dictionary<String, Object> {
{ "id", this.data["id"].ToString() },
{ "sessionId", this.data["sessionId"].ToString() }
}
)
);
Console.WriteLine("say Finished");
} }
protected void NotifyChangedAnswerText(String text) => this.SendText?.Invoke(this, new StringEvent(text));
protected void NotifyEndSession() => this.EndSession?.Invoke(this, new EventArgs());
public abstract void Parse(); public abstract void Parse();
public abstract void Answer(); public virtual void Dispose() {
Console.WriteLine("AIntent.Dispose(): Destroy the intent.");
this.slots.Clear();
}
} }
} }

View File

@ -1,21 +1,19 @@
using System; using System;
using System.Collections.Generic;
using BlubbFish.Iot.Snips.DataInputs;
using BlubbFish.Utils.IoT.Connector;
using LitJson; using LitJson;
namespace BlubbFish.Iot.Snips.Intents { namespace BlubbFish.Iot.Snips.Intents {
public class GetHum : AEspIntent { public class Gethum : AEspIntent {
public GetHum(JsonData data, ABackend snips, Dictionary<String, EnvEsp8266> esps) : base(data, snips, esps) { public Gethum(JsonData data) : base(data) { }
}
public override void Answer() { public override void Parse() {
Console.WriteLine("Gethum.Parse(): Tell the humidity.");
this.ParseESP();
if(this.espid != null) { if(this.espid != null) {
this.SnipsSay("Die Luftfeuchtigkeit beträgt " + Math.Round(this.esps[this.espid].Humidity, 1) + " Prozent relative Luftfeuchtigkeit " + this.GetSpeakName(this.espid) + "!"); this.NotifyChangedAnswerText("Die Luftfeuchtigkeit beträgt " + Math.Round(this.esps[this.espid].Humidity, 1) + " Prozent relative Luftfeuchtigkeit " + this.GetSpeakName(this.espid) + "!");
} else { } else {
this.SnipsSay("Ich habe bisher keine Sensordaten erhalten!"); this.NotifyChangedAnswerText("Ich habe bisher keine Sensordaten erhalten!");
} }
this.SnipsEndSpeak(); this.NotifyEndSession();
} }
} }
} }

View File

@ -1,21 +1,19 @@
using System; using System;
using System.Collections.Generic;
using BlubbFish.Iot.Snips.DataInputs;
using BlubbFish.Utils.IoT.Connector;
using LitJson; using LitJson;
namespace BlubbFish.Iot.Snips.Intents { namespace BlubbFish.Iot.Snips.Intents {
class GetTemp : AEspIntent { class Gettemp : AEspIntent {
public GetTemp(JsonData data, ABackend snips, Dictionary<String, EnvEsp8266> esps) : base(data, snips, esps) { public Gettemp(JsonData data) : base(data) { }
}
public override void Answer() { public override void Parse() {
Console.WriteLine("Gettemp.Parse(): Tell the temperatur.");
this.ParseESP();
if(this.espid != null) { if(this.espid != null) {
this.SnipsSay(this.GetSpeakName(this.espid) + " ist es " + Math.Round(this.esps[this.espid].Temperature,1) + " Grad "+this.slots["feel"]+"!"); this.NotifyChangedAnswerText(this.GetSpeakName(this.espid) + " ist es " + Math.Round(this.esps[this.espid].Temperature, 1) + " Grad " + this.slots["feel"] + "!");
} else { } else {
this.SnipsSay("Ich habe bisher keine Sensordaten erhalten!"); this.NotifyChangedAnswerText("Ich habe bisher keine Sensordaten erhalten!");
} }
this.SnipsEndSpeak(); this.NotifyEndSession();
} }
} }
} }

View File

@ -8,17 +8,10 @@ using LitJson;
namespace BlubbFish.Iot.Snips.Intents { namespace BlubbFish.Iot.Snips.Intents {
public class Wetter : AIntent { public class Wetter : AIntent {
private String wetterText; public Wetter(JsonData data) : base(data) { }
public Wetter(JsonData data, ABackend snips) : base(data, snips) {
}
public override void Answer() {
this.SnipsSay("Die Wettervorhersage für NRW Lautet:");
this.SnipsSay(this.wetterText);
this.SnipsEndSpeak();
}
public override void Parse() { public override void Parse() {
Console.WriteLine("Wetter.Parse(): Catch the latest weather data and send that.");
String html = null; String html = null;
String url = "https://opendata.dwd.de/weather/text_forecasts/txt/"; String url = "https://opendata.dwd.de/weather/text_forecasts/txt/";
@ -56,9 +49,10 @@ namespace BlubbFish.Iot.Snips.Intents {
} }
wetter = Encoding.UTF8.GetString(Encoding.Convert(Encoding.GetEncoding(1252), Encoding.UTF8, buffer)); wetter = Encoding.UTF8.GetString(Encoding.Convert(Encoding.GetEncoding(1252), Encoding.UTF8, buffer));
wetter = wetter.Replace("\r\r\n", "\n"); wetter = wetter.Replace("\r\r\n", "\n");
Int32 start = wetter.IndexOf("\n\n")+2; Int32 start = wetter.IndexOf("Deutscher Wetterdienst");
Int32 end = wetter.IndexOf("Detaillierter Wetterablauf") - start; Int32 end = wetter.IndexOf("Detaillierter Wetterablauf") - start;
this.wetterText = wetter.Substring(start, end); this.NotifyChangedAnswerText(wetter.Substring(start, end));
this.NotifyEndSession();
} }
} }
} }

236
Snips/Notifier/Wetter.cs Normal file
View File

@ -0,0 +1,236 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Threading;
using System.Xml;
using System.Xml.Linq;
using BlubbFish.Iot.Snips.SessionHandling;
using BlubbFish.Utils.IoT.Connector;
namespace BlubbFish.Iot.Snips.Notifier {
public class Wetter : PersistSession {
public Wetter(ADataBackend snipsbackend) : base(snipsbackend) => this.StartBackgroundTask();
public Int32[] places = { 805382056 /*Sankt Augustin*/, 805382060 /*Siegburg*/, 105314000 /*Bonn*/ };
private readonly Dictionary<String, Dictionary<Int32, List<Weatheralert>>> alerts = new Dictionary<String, Dictionary<Int32, List<Weatheralert>>>() {
{"warnings", new Dictionary<Int32, List<Weatheralert>>() },
{"vorabInformation", new Dictionary<Int32, List<Weatheralert>>()}
};
protected override void BackgroundRunner() {
//this.Startup();
/*this.PlayAudio(File.ReadAllBytes("C:\\Windows\\media\\Alarm05.wav"));
while(!this.AudioIsPlaying) { Thread.Sleep(50); }
while(this.AudioIsPlaying) { Thread.Sleep(50); }
this.CreateSingleSession("Wetter", "Wetterwarnung");*/
while(true) {
//this.DownloadAndUnzipUpdate();
DateTime n = DateTime.Now;
this.ParseFiles();
Console.WriteLine((DateTime.Now - n).TotalSeconds);
Thread.Sleep(60000);
}
}
private void ParseFiles() {
try {
foreach(FileInfo file in new DirectoryInfo("wettertmp/unzip").EnumerateFiles()) {
XmlDocument xml = new XmlDocument();
xml.Load(file.FullName);
foreach(XmlNode area in xml.SelectNodes("//*[local-name()='info']/*[local-name()='area']")) {
Weatheralert alert = new Weatheralert {
Start = DateTime.Parse(xml.SelectSingleNode("//*[local-name()='info']/*[local-name()='onset']").InnerText),
End = DateTime.Parse(xml.SelectSingleNode("//*[local-name()='info']/*[local-name()='expires']").InnerText),
Regionname = area.SelectSingleNode(".//*[local-name()='areaDesc']").InnerText,
Level = this.GetLevel(xml.SelectSingleNode("//*[local-name()='info']")),
Type = this.GetEventCode("GROUP", xml.SelectSingleNode("//*[local-name()='info']")),
Urgency = xml.SelectSingleNode("//*[local-name()='info']/*[local-name()='urgency']").InnerText,
AltitudeStart = this.GetAltitudeStartFromAltitude(Double.Parse(area.SelectSingleNode(".//*[local-name()='altitude']").InnerText, CultureInfo.InvariantCulture)),
Event = xml.SelectSingleNode("//*[local-name()='info']/*[local-name()='event']").InnerText,
Headline = xml.SelectSingleNode("//*[local-name()='info']/*[local-name()='headline']").InnerText,
Description = xml.SelectSingleNode("//*[local-name()='info']/*[local-name()='description']").InnerText,
AltitudeEnd = this.GetAltitudeEndFromCeiling(Double.Parse(area.SelectSingleNode(".//*[local-name()='ceiling']").InnerText, CultureInfo.InvariantCulture)),
StateShort = this.GetGeocode("STATE", area),
Instruction = xml.SelectSingleNode("//*[local-name()='info']/*[local-name()='instruction']").InnerText,
State = this.GetState(area),
Ii = Int32.Parse(this.GetEventCode("II", xml.SelectSingleNode("//*[local-name()='info']"))),
Published = DateTime.Parse(xml.SelectSingleNode("//*[local-name()='info']/*[local-name()='effective']").InnerText),
WarnCellId = Int32.Parse(this.GetGeocode("WARNCELLID", area) == "" ? "0" : this.GetGeocode("WARNCELLID", area))
};
if(alert.Regionname != "polygonal event area") {
if(alert.Urgency == "Immediate") {
if(!this.alerts["warnings"].ContainsKey(alert.WarnCellId)) {
this.alerts["warnings"].Add(alert.WarnCellId, new List<Weatheralert>());
}
this.alerts["warnings"][alert.WarnCellId].Add(alert);
} else {
if(!this.alerts["vorabInformation"].ContainsKey(alert.WarnCellId)) {
this.alerts["vorabInformation"].Add(alert.WarnCellId, new List<Weatheralert>());
}
this.alerts["vorabInformation"][alert.WarnCellId].Add(alert);
}
}
}
}
} catch(Exception e) {
Utils.Helper.WriteError(e.Message);
}
}
private String GetState(XmlNode area) {
String stateShort = this.GetGeocode("STATE", area);
switch(stateShort) {
case "NRW":
return "Nordrhein-Westfalen";
case "RP":
return "Rheinland-Pfalz";
case "BY":
return "Bayern";
case "BW":
return "Baden-Württemberg";
case "HE":
return "Hessen";
case "SN":
return "Sachsen";
case "TH":
return "Thüringen";
case "NS":
return "Niedersachsen";
case "HH":
return "Hamburg";
case "HB":
return "Bremen";
case "SH":
return "Schleswig-Holstein";
case "SL":
return "Saarland";
case "SA":
return "Sachsen-Anhalt";
case "BB":
return "Brandenburg";
case "BL":
return "Berlin";
case "MV":
return "Mecklenburg-Vorpomern";
}
return "";
}
private String GetGeocode(String geocode, XmlNode area) {
foreach(XmlNode code in area.SelectNodes(".//*[local-name()='geocode']")) {
if(code.SelectSingleNode(".//*[local-name()='valueName']").InnerText == geocode) {
return code.SelectSingleNode(".//*[local-name()='value']").InnerText;
}
}
return "";
}
private Double? GetAltitudeEndFromCeiling(Double ceiling) {
Double result = Math.Round(ceiling * 0.3048);
if(result == 0) {
return null;
} else {
return result;
}
}
private Double? GetAltitudeStartFromAltitude(Double altitude) {
Double result = Math.Round(altitude * 0.3048);
if(result == 0) {
return null;
} else {
return result;
}
}
private String GetEventCode(String eventCode, XmlNode info) {
foreach(XmlNode code in info.SelectNodes(".//*[local-name()='eventCode']")) {
if(code.SelectSingleNode(".//*[local-name()='valueName']").InnerText == eventCode) {
return code.SelectSingleNode(".//*[local-name()='value']").InnerText;
}
}
return "";
}
private Int32 GetLevel(XmlNode info) {
if(info.SelectSingleNode(".//*[local-name()='urgency']").InnerText == "Future") {
return 1;
} else {
switch(info.SelectSingleNode(".//*[local-name()='severity']").InnerText) {
case "Minor": return 2;
case "Moderate": return 3;
case "Severe": return 4;
case "Extreme": return 5;
}
return 0;
}
}
private void DownloadAndUnzipUpdate() {
try {
HttpWebRequest requestw = (HttpWebRequest)WebRequest.Create("https://opendata.dwd.de/weather/alerts/cap/COMMUNEUNION_DWD_STAT/Z_CAP_C_EDZW_LATEST_PVW_STATUS_PREMIUMDWD_COMMUNEUNION_DE.zip");
using(HttpWebResponse response = (HttpWebResponse)requestw.GetResponse()) {
String date = DateTime.Parse(response.Headers.Get("Last-Modified")).ToFileTime().ToString();
if(!File.Exists("wettertmp/zip/" + date + ".zip")) {
this.ClearFiles();
using(Stream stream = response.GetResponseStream())
using(FileStream filestream = File.OpenWrite("wettertmp/zip/" + date + ".zip")) {
stream.CopyTo(filestream);
filestream.Flush();
filestream.Close();
}
ZipFile.ExtractToDirectory("wettertmp/zip/" + date + ".zip", "wettertmp/unzip");
}
}
} catch(Exception e) {
Utils.Helper.WriteError(e.Message);
}
}
private void ClearFiles() {
foreach(FileInfo file in new DirectoryInfo("wettertmp/zip").EnumerateFiles()) {
file.Delete();
}
foreach(FileInfo file in new DirectoryInfo("wettertmp/unzip").EnumerateFiles()) {
file.Delete();
}
}
private void Startup() {
if(!Directory.Exists("wettertmp")) {
Directory.CreateDirectory("wettertmp");
}
if(!Directory.Exists("wettertmp/zip")) {
Directory.CreateDirectory("wettertmp/zip");
}
if(!Directory.Exists("wettertmp/unzip")) {
Directory.CreateDirectory("wettertmp/unzip");
}
this.ClearFiles();
}
private struct Weatheralert {
public DateTime Start { get; set; }
public DateTime End { get; set; }
public String Regionname { get; set; }
public Int32 Level { get; set; }
public String Type { get; set; }
public Double? AltitudeStart { get; set; }
public String Event { get; set; }
public String Headline { get; set; }
public String Description { get; set; }
public Double? AltitudeEnd { get; set; }
public String StateShort { get; set; }
public String Instruction { get; set; }
public String State { get; set; }
public Int32 Ii { get; set; }
public DateTime Published { get; set; }
public Int32 WarnCellId { get; set; }
public String Urgency { get; set; }
public override String ToString() => this.Regionname+" ("+this.WarnCellId+"): "+this.Headline;
}
}
}

View File

@ -20,7 +20,11 @@ namespace BlubbFish.Iot.Snips {
InIReader backends = InIReader.GetInstance("backends"); InIReader backends = InIReader.GetInstance("backends");
Dictionary<String, ABackend> sources = new Dictionary<String, ABackend>(); Dictionary<String, ABackend> sources = new Dictionary<String, ABackend>();
foreach(String item in backends.GetSections(false)) { foreach(String item in backends.GetSections(false)) {
sources.Add(item, ABackend.GetInstance(backends.GetSection(item), ABackend.BackendType.Data)); Dictionary<String, String> sourcesettings = backends.GetSection(item);
if(item == "snips") {
sourcesettings.Add("topic", "hermes/dialogueManager/sessionStarted;hermes/intent/#;hermes/dialogueManager/sessionEnded;hermes/audioServer/default/playBytes/+;hermes/audioServer/default/playFinished;hermes/tts/say;hermes/tts/sayFinished");
}
sources.Add(item, ABackend.GetInstance(sourcesettings, ABackend.BackendType.Data));
} }
SnipsBot s = new SnipsBot(sources, InIReader.GetInstance("settings").GetSection("general")); SnipsBot s = new SnipsBot(sources, InIReader.GetInstance("settings").GetSection("general"));
s.Dispose(); s.Dispose();

View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using BlubbFish.Utils.IoT.Connector;
using LitJson;
namespace BlubbFish.Iot.Snips.SessionHandling {
abstract public class PersistSession : Session {
private Thread bgthread;
public PersistSession(ADataBackend snipsmqtt) : base("", "default", snipsmqtt) {
Console.WriteLine("PersistSession.PersistSession(): Start a Persist Session.");
this.CustomId = "PersistSession."+this.GetType().Name;
}
protected void CreateSingleSession(String customData, String text) {
Console.WriteLine("PersistSession.CreateSingleSession(): Send request to end the session.");
this.SnipsBackend.Send("hermes/dialogueManager/startSession", JsonMapper.ToJson(
new Dictionary<String, Object> {
{ "siteId", "default" },
{ "customData", this.CustomId },
{ "init", new Dictionary<String, Object> {
{ "type", "notification" },
{ "text", text }
}}
}
));
}
protected void StartBackgroundTask() {
this.bgthread = new Thread(this.BackgroundRunner);
this.bgthread.Start();
}
protected abstract void BackgroundRunner();
public static List<String> GetNotfierList() {
List<String> list = new List<String>();
foreach(Type t in Assembly.GetExecutingAssembly().GetTypes()) {
if(t.Namespace == "BlubbFish.Iot.Snips.Notifier" && t.Name != "ANotifier" && !t.IsNested) {
list.Add(t.Name);
}
}
return list;
}
public static PersistSession GetInstance(String @class, ADataBackend snipsmqtt) {
String intent = "BlubbFish.Iot.Snips.Notifier." + @class;
Type t = null;
try {
t = Type.GetType(intent, true);
} catch(System.IO.FileNotFoundException) {
Console.Error.WriteLine("Notifier " + intent + " could not load!");
return null;
}
return (PersistSession)t.GetConstructor(new Type[] { typeof(ADataBackend) }).Invoke(new Object[] { snipsmqtt });
}
public void SetSessionId(String sessionid) => this.SessionId = sessionid;
public void SetSideId(String siteid) => this.SiteId = siteid;
public override void Dispose() {
if(this.bgthread != null) {
this.bgthread.Abort();
}
base.Dispose();
}
}
}

View File

@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using BlubbFish.Iot.Snips.Events;
using BlubbFish.Iot.Snips.Intents;
using BlubbFish.Utils;
using BlubbFish.Utils.IoT.Connector;
using BlubbFish.Utils.IoT.Connector.Data;
using LitJson;
namespace BlubbFish.Iot.Snips.SessionHandling {
public class Session {
protected readonly ADataBackend SnipsBackend;
public String CustomId { get; protected set; }
public String SessionId { get; protected set; }
public String SiteId { get; protected set; }
public AIntent Intent { get; private set; }
public Boolean AudioIsPlaying { get; private set; }
public Boolean TtsIsPlaying { get; private set; }
public Session(String sessionId, String siteId, ADataBackend snipsmqtt) {
Console.WriteLine("Session.Session(): Start a new session.");
this.SessionId = sessionId;
this.SiteId = siteId;
this.CustomId = "";
this.SnipsBackend = snipsmqtt;
this.AudioIsPlaying = false;
this.TtsIsPlaying = false;
}
public void AddIntent(JsonData data, Dictionary<String, DataInputs.EnvEsp8266> esps) {
Console.WriteLine("Session.AddIntent(): Create intent " + data["intent"]["intentName"].ToString()+".");
this.Intent = AIntent.GetInstance(data);
if(this.Intent.GetType().HasAbstract(typeof(AEspIntent))) {
((AEspIntent)this.Intent).SetEsps(esps);
}
this.Intent.SendText += this.ContinueSession;
this.Intent.EndSession += this.EndSession;
this.Intent.Parse();
}
private void EndSession(Object sender, EventArgs e) {
Console.WriteLine("Session.EndSession(): Send request to end the session.");
this.SnipsBackend.Send("hermes/dialogueManager/endSession",
JsonMapper.ToJson(
new Dictionary<String, Object> {
{ "sessionId", this.SessionId }
}
)
);
}
private void ContinueSession(Object sender, TextEvent e) {
Console.WriteLine("Session.ContinueSession(): Continue session with text: (" + ((StringEvent)e).Text.Replace("\n", "") + ").");
this.SnipsBackend.Send("hermes/dialogueManager/continueSession",
JsonMapper.ToJson(
new Dictionary<String, Object> {
{ "sessionId", this.SessionId },
{ "text", ((StringEvent)e).Text }
}
)
);
}
protected void PlayAudio(Byte[] wav) {
Console.WriteLine("Session.PlayAudio(): Play an Audio Stream.");
((Mqtt)this.SnipsBackend).Send("hermes/audioServer/default/playBytes/" + this.CustomId, wav);
}
public virtual void Dispose() {
Console.WriteLine("Session.Dispose(): Destroy the current session.");
if(this.Intent != null) {
this.Intent.SendText -= this.ContinueSession;
this.Intent.Dispose();
}
}
public void AudioIsBusy() {
Console.WriteLine("Session.AudioIsBusy(): Notify Session that the Audiodevice is busy.");
this.AudioIsPlaying = true;
}
public void AudioIsFree() {
Console.WriteLine("Session.AudioIsFree(): Notify Session that the Audiodevice is free.");
this.AudioIsPlaying = false;
}
internal void TtsIsFree() {
Console.WriteLine("Session.TtsIsFree(): Notify Session that the TTS-Engine is free.");
this.TtsIsPlaying = false;
}
internal void TtsIsBusy() {
Console.WriteLine("Session.TtsIsBusy(): Notify Session that the TTS-Engine is busy.");
this.TtsIsPlaying = true;
}
}
}

View File

@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using BlubbFish.Utils;
using BlubbFish.Utils.IoT.Connector;
using LitJson;
namespace BlubbFish.Iot.Snips.SessionHandling {
public class SessionManager {
private readonly ADataBackend snipsmqtt;
private readonly Dictionary<String, Session> sessions = new Dictionary<String, Session>();
public SessionManager(ABackend snipsbackend) {
this.snipsmqtt = (ADataBackend)snipsbackend;
this.StartNotifier();
}
private void StartNotifier() {
foreach(String item in PersistSession.GetNotfierList()) {
this.sessions.Add("PersistSession."+item, PersistSession.GetInstance(item, this.snipsmqtt));
}
}
public void SessionStarted(JsonData data) {
Console.WriteLine("SessionManager.SessionStarted(): Start a new session with sessionId: " + data["sessionId"].ToString() + ".");
String sessionid = data["sessionId"].ToString();
if(data["customData"].IsString && this.sessions.ContainsKey(data["customData"].ToString()) && this.sessions[data["customData"].ToString()].GetType().HasAbstract(typeof(PersistSession))) {
((PersistSession)this.sessions[data["customData"].ToString()]).SetSessionId(sessionid);
((PersistSession)this.sessions[data["customData"].ToString()]).SetSideId(data["siteId"].ToString());
} else {
if(this.sessions.ContainsKey(sessionid)) {
throw new Exception("BlubbFish.Iot.Snips.SessionHandling.SessionManager.SessionStarted(): Session is already there!");
} else {
this.sessions.Add(sessionid, new Session(sessionid, data["siteId"].ToString(), this.snipsmqtt));
}
}
}
public void Intent(JsonData data, Dictionary<String, DataInputs.EnvEsp8266> esps) {
Console.WriteLine("SessionManager.Intent(): Attach an intent to the session sessionId: " + data["sessionId"].ToString() + ".");
String sessionid = data["sessionId"].ToString();
if(data["customData"].IsString && this.sessions.ContainsKey(data["customData"].ToString()) && this.sessions[data["customData"].ToString()].GetType().HasAbstract(typeof(PersistSession)) && this.sessions[data["customData"].ToString()].SessionId == sessionid) {
sessionid = data["customData"].ToString();
}
if(!this.sessions.ContainsKey(sessionid)) {
throw new Exception("BlubbFish.Iot.Snips.SessionHandling.SessionManager.Intent(): Sessionid not found!");
} else {
this.sessions[sessionid].AddIntent(data, esps);
}
}
internal void NotifyTtsIsSpeaking() {
Console.WriteLine("SessionManager.NotifyTtsIsSpeaking(): Notify all Sessions and Notifier that TTS is busy.");
foreach(KeyValuePair<String, Session> item in this.sessions) {
item.Value.TtsIsBusy();
}
}
internal void NotifyTtsIsEnded() {
Console.WriteLine("SessionManager.NotifyTtsIsEnded(): Notify all Sessions and Notifier that TTs is free.");
foreach(KeyValuePair<String, Session> item in this.sessions) {
item.Value.TtsIsFree();
}
}
internal void NotifyAudioIsEnded() {
Console.WriteLine("SessionManager.NotifyAudioIsEnded(): Notify all Sessions and Notifier that Audio is free.");
foreach(KeyValuePair<String, Session> item in this.sessions) {
item.Value.AudioIsFree();
}
}
internal void NotifyAudioIsPlaying() {
Console.WriteLine("SessionManager.NotifyAudioIsPlaying(): Notify all Sessions and Notifier that Audio is busy.");
foreach(KeyValuePair<String, Session> item in this.sessions) {
item.Value.AudioIsBusy();
}
}
public void SessionEnded(JsonData data) {
Console.WriteLine("SessionManager.SessionEnded(): Destroy the session with sessionId: " + data["sessionId"].ToString() + ".");
String sessionid = data["sessionId"].ToString();
if(data["customData"].IsString && this.sessions.ContainsKey(data["customData"].ToString()) && this.sessions[data["customData"].ToString()].GetType().HasAbstract(typeof(PersistSession)) && this.sessions[data["customData"].ToString()].SessionId == sessionid) {
((PersistSession)this.sessions[data["customData"].ToString()]).SetSessionId("");
((PersistSession)this.sessions[data["customData"].ToString()]).SetSideId("default");
} else {
if(!this.sessions.ContainsKey(sessionid)) {
throw new Exception("BlubbFish.Iot.Snips.SessionHandling.SessionManager.SessionEnded(): Sessionid not found!");
} else {
this.sessions[sessionid].Dispose();
this.sessions.Remove(sessionid);
}
}
}
public void Dispose() {
foreach(KeyValuePair<String, Session> item in this.sessions) {
item.Value.Dispose();
}
}
}
}

View File

@ -35,6 +35,7 @@
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
@ -44,14 +45,19 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="DataInputs\EnvEsp8266.cs" /> <Compile Include="DataInputs\EnvEsp8266.cs" />
<Compile Include="Events\TextEvent.cs" />
<Compile Include="Helper\Texthelper.cs" /> <Compile Include="Helper\Texthelper.cs" />
<Compile Include="Intents\AEspIntent.cs" /> <Compile Include="Intents\AEspIntent.cs" />
<Compile Include="Intents\AIntent.cs" /> <Compile Include="Intents\AIntent.cs" />
<Compile Include="Intents\GetHum.cs" /> <Compile Include="Intents\GetHum.cs" />
<Compile Include="Intents\GetTemp.cs" /> <Compile Include="Intents\GetTemp.cs" />
<Compile Include="Intents\Wetter.cs" /> <Compile Include="Intents\Wetter.cs" />
<Compile Include="Notifier\Wetter.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SessionHandling\PersistSession.cs" />
<Compile Include="SessionHandling\Session.cs" />
<Compile Include="SessionHandling\SessionManager.cs" />
<Compile Include="SnipsBot.cs" /> <Compile Include="SnipsBot.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -92,6 +98,7 @@
<None Include="dpkg\postinst" /> <None Include="dpkg\postinst" />
<None Include="dpkg\preinst" /> <None Include="dpkg\preinst" />
<None Include="dpkg\prerm" /> <None Include="dpkg\prerm" />
<None Include="json1.json" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<ItemGroup> <ItemGroup>

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using BlubbFish.Iot.Snips.DataInputs; using BlubbFish.Iot.Snips.DataInputs;
using BlubbFish.Iot.Snips.Intents; using BlubbFish.Iot.Snips.SessionHandling;
using BlubbFish.Utils.IoT.Bots; using BlubbFish.Utils.IoT.Bots;
using BlubbFish.Utils.IoT.Connector; using BlubbFish.Utils.IoT.Connector;
using BlubbFish.Utils.IoT.Events; using BlubbFish.Utils.IoT.Events;
@ -9,34 +9,39 @@ using LitJson;
namespace BlubbFish.Iot.Snips { namespace BlubbFish.Iot.Snips {
class SnipsBot : MultiSourceBot { class SnipsBot : MultiSourceBot {
private readonly Boolean debug = true; private readonly Boolean debug = false;
private readonly Dictionary<String, EnvEsp8266> esps = new Dictionary<String, EnvEsp8266>(); private readonly Dictionary<String, EnvEsp8266> esps = new Dictionary<String, EnvEsp8266>();
private readonly SessionManager sessions;
public SnipsBot(Dictionary<String, ABackend> sources, Dictionary<String, String> settings) : base(sources, settings) { public SnipsBot(Dictionary<String, ABackend> sources, Dictionary<String, String> settings) : base(sources, settings) {
this.logger.SetPath(settings["loggingpath"]); this.logger.SetPath(settings["loggingpath"]);
this.sources["snips"].MessageIncomming += this.IntentWorker; this.sources["snips"].MessageIncomming += this.IntentWorker;
this.sources["iot"].MessageIncomming += this.GetEspData; this.sources["iot"].MessageIncomming += this.GetEspData;
this.sessions = new SessionManager(sources["snips"]);
this.WaitForShutdown(); this.WaitForShutdown();
} }
private void IntentWorker(Object sender, BackendEvent e) { private void IntentWorker(Object sender, BackendEvent e) {
if(e.From.ToString().StartsWith("hermes/audioServer/default/playBytes/")) {
this.sessions.NotifyAudioIsPlaying();
} else {
try { try {
JsonData data = JsonMapper.ToObject(e.Message); JsonData data = JsonMapper.ToObject(e.Message);
AIntent intent = null; if(e.From.ToString() == "hermes/dialogueManager/sessionStarted") {
if(data["intent"]["intentName"].ToString() == "blubbfish:GetTemp") { this.sessions.SessionStarted(data);
intent = new GetTemp(data, this.sources["snips"], this.esps); } else if(e.From.ToString().StartsWith("hermes/intent/")) {
} else if(data["intent"]["intentName"].ToString() == "blubbfish:GetHum") { this.sessions.Intent(data, this.esps);
intent = new GetHum(data, this.sources["snips"], this.esps); } else if(e.From.ToString() == "hermes/dialogueManager/sessionEnded") {
} else if(data["intent"]["intentName"].ToString() == "blubbfish:Wetter") { this.sessions.SessionEnded(data);
intent = new Wetter(data, this.sources["snips"]); } else if(e.From.ToString() == "hermes/audioServer/default/playFinished") {
this.sessions.NotifyAudioIsEnded();
} else if(e.From.ToString() == "hermes/tts/say") {
this.sessions.NotifyTtsIsSpeaking();
} else if(e.From.ToString() == "hermes/tts/sayFinished") {
this.sessions.NotifyTtsIsEnded();
} }
if(intent != null) { } catch(Exception ex) { Utils.Helper.WriteError(ex.Message); }
Console.WriteLine("Intent Start " + data["intent"]["intentName"].ToString());
intent.Parse();
intent.Answer();
Console.WriteLine("Intent Finished!");
} }
} catch { }
} }
private void GetEspData(Object sender, BackendEvent e) { private void GetEspData(Object sender, BackendEvent e) {
@ -55,6 +60,7 @@ namespace BlubbFish.Iot.Snips {
} }
public override void Dispose() { public override void Dispose() {
this.sessions.Dispose();
foreach(KeyValuePair<String, ABackend> item in this.sources) { foreach(KeyValuePair<String, ABackend> item in this.sources) {
item.Value.Dispose(); item.Value.Dispose();
} }

View File

@ -1,4 +1,4 @@
[snips] [snips]
type=mqtt type=mqtt
server=localhost server=localhost
topic=hermes/nlu/intentParsed topic=hermes/intent/#

25
Snips/json1.json Normal file
View File

@ -0,0 +1,25 @@
{
"time": "1559769902000",
"warnings": { "805382056": null },
"vorabInformation": {
"805382056": [
{
"start": 1559761200000,
"end": 1559793600000,
"regionName": "Stadt Sankt Augustin",
"level": 1,
"type": "THUNDERSTORM",
"altitudeStart": null,
"event": "VORABINFORMATION SCHWERES GEWITTER",
"headline": "VORABINFORMATION UNWETTER vor SCHWEREM GEWITTER",
"description": "Ab Mittwochabend k\u00f6nnen aus S\u00fcden teils schwere Gewitter aufziehen. Dabei k\u00f6nnen \u00f6rtlich heftiger Starkregen (30 bis 40 l\/qm in kurzer Zeit), mittelgro\u00dfer Hagel und orkanartige B\u00f6en (um 110 km\/h) auftreten. Auch einzelne Tornados sind nicht ausgeschlossen. Die Gewitterneigung bleibt bis in die Morgenstunden des Donnerstags bestehen.\n\n\nDies ist eine Vorabinformation. Sie wird herausgegeben, wenn ein Unwetterereignis wahrscheinlich ist, die Unsicherheiten betreffend Zeit, Gebiet und Intensit\u00e4t aber noch gro\u00df sind. Unwetterwarnungen werden bei Bedarf zeitnah ausgegeben.\n\nF\u00fcr diese Vorabinformation wird es voraussichtlich keine Aktualisierung geben.",
"altitudeEnd": null,
"stateShort": "",
"instruction": "Dies ist ein Hinweis auf eine Wetterlage mit hohem Unwetterpotential. Er soll die rechtzeitige Vorbereitung von Schutzma\u00dfnahmen erm\u00f6glichen. \n\nGewitter mit den genannten Begleiterscheinungen treten typischerweise sehr lokal auf und treffen mit voller Intensit\u00e4t meist nur wenige Orte. Genauere Angaben zu Ort, Gebiet und Zeitpunkt des Ereignisses k\u00f6nnen erst mit der Ausgabe der amtlichen Warnungen erfolgen. Bitte verfolgen Sie die weiteren Wettervorhersagen mit besonderer Aufmerksamkeit.",
"state": null,
"ii": 40,
"published": 1559718420000
}
]
}
}