Hallo!
Zuerstmal muss ich etwas klugsch**en: Es heisst natürlich Windows Phone 8.
Um Daten von IPS ins Telefon zu bekommen nutze ich Push-Raw-Nachrichten mit einem Skript aus dem Internet:
/**
*Windows Phone 7 Push Notification in php by Rudy HUYN
**/
final class WindowsPhonePushDelay
{
const Immediate=0;
const In450Sec=10;
const In900Sec=20;
private function __construct(){}
}
class WindowsPhonePushNotification
{
private $notif_url = '';
function WindowsPhonePushNotification($notif_url)
{
$this->notif_url = $notif_url;
}
/**
* Toast notifications are system-wide notifications that do not disrupt the user workflow or require intervention to resolve. They are displayed at the top of the screen for ten seconds before disappearing. If the toast notification is tapped, the application that sent the toast notification will launch. A toast notification can be dismissed with a flick.
* Two text elements of a toast notification can be updated:
* Title. A bolded string that displays immediately after the application icon.
* Sub-title. A non-bolded string that displays immediately after the Title.
*/
public function push_toast($title, $subtitle, $param, $delay = WindowsPhonePushDelay::Immediate, $message_id=NULL)
{
$msg = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" .
"<wp:Notification xmlns:wp=\"WPNotification\">" .
"<wp:Toast>" .
"<wp:Text1>".htmlspecialchars($title)."</wp:Text1>" .
"<wp:Text2>".htmlspecialchars($subtitle)."</wp:Text2>" .
"<wp:Param>$param</wp:Param>" .
"</wp:Toast>" .
"</wp:Notification>";
return $this->push('toast',$delay+2,$message_id, $msg);
}
/**
*A Tile displays in the Start screen if the end user has pinned it. Three elements of the Tile can be updated:
*@background_url : You can use a local resource or remote resource for the background image of a Tile.
*@title : The Title must fit a single line of text and should not be wider than the actual Tile. If this value is not set, the already-existing Title will display in the Tile.
*@count. an integer value from 1 to 99. If not set in the push notification or set to any other integer value, the current Count value will continue to display.
*/
public function push_tile($widebackcontent, $widebackbackground_url, $widebackground_url, $smallbackground_url, $background_url, $title, $count, $backbackground_url, $backtitle, $backcontent, $delay = WindowsPhonePushDelay::Immediate,$message_id=NULL)
{
$msg = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" .
"<wp:Notification xmlns:wp=\"WPNotification\" Version=\"2.0\">" .
"<wp:Tile Template=\"FlipTile\">" .
"<wp:SmallBackgroundImage Action=\"Update\">".htmlspecialchars($smallbackground_url)."</wp:SmallBackgroundImage>" .
"<wp:WideBackgroundImage Action=\"Update\">".htmlspecialchars($widebackground_url)."</wp:WideBackgroundImage>" .
"<wp:WideBackBackgroundImage Action=\"Update\">".htmlspecialchars($widebackbackground_url)."</wp:WideBackBackgroundImage>" .
"<wp:WideBackContent Action=\"Update\">$widebackcontent</wp:WideBackContent>" .
"<wp:BackgroundImage Action=\"Update\">".htmlspecialchars($background_url)."</wp:BackgroundImage>" .
"<wp:Count Action=\"Update\">$count</wp:Count>" .
"<wp:Title Action=\"Update\">".htmlspecialchars($title)."</wp:Title>" .
"<wp:BackBackgroundImage Action=\"Update\">".htmlspecialchars($backbackground_url)."</wp:BackBackgroundImage>" .
"<wp:BackTitle Action=\"Update\">".htmlspecialchars($backtitle)."</wp:BackTitle>" .
"<wp:BackContent Action=\"Update\">".htmlspecialchars($backcontent)."</wp:BackContent>" .
"</wp:Tile>" .
"</wp:Notification>";
return $this->push('token',$delay+1, $message_id,$msg);
}
public function clear_tile($widebackcontent, $widebackbackground_url, $widebackground_url, $smallbackground_url, $background_url, $title, $count, $backbackground_url, $backtitle, $backcontent, $delay = WindowsPhonePushDelay::Immediate,$message_id=NULL)
{
$msg = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" .
"<wp:Notification xmlns:wp=\"WPNotification\" Version=\"2.0\">" .
"<wp:Tile Template=\"FlipTile\">" .
"<wp:SmallBackgroundImage Action=\"Update\">".htmlspecialchars($smallbackground_url)."</wp:SmallBackgroundImage>" .
"<wp:WideBackgroundImage Action=\"Update\">".htmlspecialchars($widebackground_url)."</wp:WideBackgroundImage>" .
"<wp:WideBackBackgroundImage Action=\"Clear\"></wp:WideBackBackgroundImage>" .
"<wp:WideBackContent Action=\"Clear\"></wp:WideBackContent>" .
"<wp:BackgroundImage Action=\"Update\">".htmlspecialchars($background_url)."</wp:BackgroundImage>" .
"<wp:Count Action=\"Update\">$count</wp:Count>" .
"<wp:Title Action=\"Update\">".htmlspecialchars($title)."</wp:Title>" .
"<wp:BackBackgroundImage Action=\"Update\">".htmlspecialchars($backbackground_url)."</wp:BackBackgroundImage>" .
"<wp:BackTitle Action=\"Clear\"></wp:BackTitle>" .
"<wp:BackContent Action=\"Clear\"></wp:BackContent>" .
"</wp:Tile>" .
"</wp:Notification>";
return $this->push('token',$delay+1, $message_id,$msg);
}
/**
* If you do not wish to update the Tile or send a toast notification, you can instead send raw information to your application using a raw notification. If your application is not currently running, the raw notification is discarded on the Microsoft Push Notification Service and is not delivered to the device. The payload of a raw notification has a maximum size of 1 KB.
*/
public function push_raw($data, $delay = WindowsPhonePushDelay::Immediate,$message_id=NULL)
{
return $this->push(NULL,$delay+3,$message_id, $data);
}
/**
*@target : type of notification
*@delay : immediate, in 450sec or in 900sec
*@message_id : The optional custom header X-MessageID uniquely identifies a notification message. If it is present, the same value is returned in the notification response. It must be a string that contains a UUID
*/
private function push($target,$delay,$message_id,$msg)
{
$sendedheaders= array(
'Content-Type: text/xml',
'Accept: application/*',
"X-NotificationClass: $delay"
);
if($message_id!=NULL)
$sendedheaders[]="X-MessageID: $message_id";
if($target!=NULL)
$sendedheaders[]="X-WindowsPhone-Target:$target";
$req = curl_init();
curl_setopt($req, CURLOPT_HEADER, true);
curl_setopt($req, CURLOPT_HTTPHEADER,$sendedheaders);
curl_setopt($req, CURLOPT_POST, true);
curl_setopt($req, CURLOPT_POSTFIELDS, $msg);
curl_setopt($req, CURLOPT_URL, $this->notif_url);
curl_setopt($req, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($req);
curl_close($req);
$result=array();
foreach(explode("
",$response) as $line)
{
$tab=explode(":",$line,2);
if(count($tab)==2)
$result[$tab[0]]=trim($tab[1]);
}
print_r($result);
}
}
function umlaute_ersetzen($text){
$such_array = array ('ä', 'ö', 'ü', 'ß');
$ersetzen_array = array ('ae', 'oe', 'ue', 'ss');
$neuer_text = str_replace($such_array, $ersetzen_array, $text);
return $neuer_text;
}
Der Aufruf der Funktion erfolgt dann z.B. über:
$notif=new WindowsPhonePushNotification($uri);
$notif->push_raw($RawData);
Der URI wird im Telefon automatisch registriert und muss zu IP-Symcon übertragen werden, die Variable $RawData enthält dann den zu sendenden String der in der Telefon App nach dem Empfang aufbereitet werden kann. Bei mir wird der String z.B. so zusammen gesetzt:
$RawData = "IPSDaten=$Zeit;
IPSEGLicht=$IPSEGLicht;
.
.
.
";
In der Windows Phone App muss dann eben der PushChannel eingerichtet werden:
// Holds the push channel that is created or found.
HttpNotificationChannel pushChannel;
// The name of our push channel.
string channelName = "ToastChannel";
this.Unloaded += new RoutedEventHandler(MainPage_Unloaded);
pushChannel = new HttpNotificationChannel(channelName);
// Register for all the events before attempting to open the channel.
pushChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(PushChannel_ChannelUriUpdated);
pushChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(PushChannel_ErrorOccurred);
pushChannel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(PushChannel_HttpNotificationReceived);
// Register for this notification only if you need to receive the notifications while your application is running.
pushChannel.ShellToastNotificationReceived += new EventHandler<NotificationEventArgs>(PushChannel_ShellToastNotificationReceived);
pushChannel.Open();
try
{
// Bind this new channel for toast events.
pushChannel.BindToShellTile();
// MessageBox.Show(String.Format("pushChannel.BindToShellTile() erfolgreich"));
}
catch { }
try
{
pushChannel.BindToShellToast();
// MessageBox.Show(String.Format("pushChannel.BindToShellToast() erfolgreich"));
}
catch { }
Damit man die URI im IP-Symcon hat muss diese übertragen werden:
void PushChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
{
Dispatcher.BeginInvoke(() =>
{
// Display the new URI for testing purposes. Normally, the URI would be passed back to your web service at this point.
System.Diagnostics.Debug.WriteLine(e.ChannelUri.ToString());
URI = e.ChannelUri.ToString();
settings["Uri"] = e.ChannelUri.ToString();
settings.Save();
postData1 = "Uri";
postData2 = e.ChannelUri.ToString();
SendPost();
// MessageBox.Show(String.Format("Channel Uri upgedated {0}", e.ChannelUri.ToString()));
});
}
Dazu kommt noch der Abschnitt in dem die empfangenen Push-Raw-Daten verarbeitet werden sollen:
//Aktionen beim Empfang von Push Raw Nachricht
void PushChannel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e)
{
using (System.IO.StreamReader reader = new System.IO.StreamReader(e.Notification.Body))
{
message = reader.ReadToEnd();
}
// Dispatcher.BeginInvoke(() => MessageBox.Show(String.Format("Update erhalten um {0}: {1}", DateTime.Now.ToShortTimeString(),message)));
try
{
if (message.StartsWith("IPSDaten"))
{
string[] stringSeparators = new string[] { ";" };
string[] Temp = message.Split(stringSeparators, StringSplitOptions.None);
foreach (string n in Temp)
{
string[] stringSeparators2 = new string[] { "=" };
string[] Temp2 = n.Split(stringSeparators2, StringSplitOptions.None);
if (Temp2[0].StartsWith("
"))
{
Temp2[0] = Temp2[0].Replace("
", "");
}
if (Temp2[1] =="")
{
//settings[Temp2[0]] = 0;
Temp2[1] = Temp2[1].Replace("", "0");
//Dispatcher.BeginInvoke(() => MessageBox.Show(String.Format("n0:n1 in Temp2: {0} = false", Temp2[0])));
}
else
{
settings[Temp2[0]] = Temp2[1];
//Dispatcher.BeginInvoke(() => MessageBox.Show(String.Format("n0:n1 in Temp2: {0} = {1}", Temp2[0], Temp2[1])));
}
settings.Save();
}
}
}
catch { }
Die Empfangenen Daten speichere ich im IsolatedStorage, also noch
using System.IO.IsolatedStorage;
und
IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings;
hinzufügen.
Die Daten vom Telefon werden bei mir über einen HttpWebRequest vom Telefon an IP-Symcon gesendet:
//Daten an IP-Symcon übergeben
void SendPost()
{
var url = "http://***.php";
// Create the web request object
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
// Start the request
webRequest.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), webRequest);
}
void GetRequestStreamCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
// End the stream request operation
Stream postStream = webRequest.EndGetRequestStream(asynchronousResult);
// Create the post data
string postData = "param1=" + postData1 + "&" + "param2=" + postData2 + "&" + "param3=" + postData3 + "&" + "param4=" + postData4 + "&" + "param6=" + postData6;
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Add the post data to the web request
postStream.Write(byteArray, 0, byteArray.Length);
postStream.Close();
// Start the web request
webRequest.BeginGetResponse(new AsyncCallback(GetResponseCallback), webRequest);
}
void GetResponseCallback(IAsyncResult asynchronousResult)
{
try
{
HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
HttpWebResponse response;
// End the get response operation
response = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamReader = new StreamReader(streamResponse);
var Response = streamReader.ReadToEnd();
streamResponse.Close();
streamReader.Close();
response.Close();
}
catch (WebException e)
{
// Error treatment
// ...
}
}
Damit der Webrequest die Daten an IP-Symcon übergeben kann habe ich unter Webfront eine PHP-Datei mit folgendem Inhalt erstellt:
<title>WPPushdienst</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7"/>
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link rel="stylesheet" type="text/css" href="../webfront.css" />
<?php
SetValueString(12345 ,$_REQUEST['param1']);
SetValueString(12345 ,$_REQUEST['param2']);
SetValueString(12345 ,$_REQUEST['param3']);
SetValueString(12345 ,$_REQUEST['param4']);
SetValueString(12345 ,$_REQUEST['param5']);
SetValueString(12345 ,$_REQUEST['param6']);
?>
<HEAD></HEAD>
Im Großen und Ganzen wäre dass schon alles.
Da ich mit den Codeschnipseln noch ein bisschen mehr mache könnte man das Projekt je nach Bedarf noch etwas einschrumpfen.
Die Übertragung der Daten könnte man natürlich auch mittels JSON umsetzen, habe mich aber vorerst für den normalen Webrequest mit Parameterübergabe entschieden.
Da WP8 im Moment nur sehr eingeschränkt VPN unterstützt funktioniert die Lösung natürlich nur im lokalen Netzwerk oder wenn man die IP-Symcon-Webfront-Ports auf dem Router weiterleitet. Da das ein Sicherheitsrisiko ist möchte ich dies hier nicht empfehlen. Wenn es für WP8 bald auch Plugins für VPN über SSL/SSTP gibt und auto-triggered VPN wird das ganze wieder interessanter.
Ich hoffe ich konnte einen Anstoss zur Umsetzung geben, da ich selbst kein sehr erfahrener Programmierer bin freue ich mich auch über Verbesserungsvorschläge.
Gruß
Strichcode