Friday, June 1, 2018

Using the Kano Motion Sensor from Powershell

Kano has created a nice motion sensor package for $20-30.

https://www.amazon.com/Kano-Motion-Sensor-Kit-movement/dp/B072JGWCM8/

They say you're required to use the Kano app, but I didn't want to. Luckily, once the drivers are installed the sensor simply acts as a serial device. I looked in device manager and found it on COM4 on my machine:


So I opened powershell and tried to connect to COM4:

PS C:\Users\user> $kano = new-object system.io.ports.serialport COM4,115200,none,8,one
PS C:\Users\user> $kano.open()
Exception calling "Open" with "0" argument(s): "Access to the port 'COM4' is denied."
At line:1 char:1
+ $kano.open()
+ ~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : UnauthorizedAccessException

Oops! I still had the Kano app open - it was hogging the port! So I closed the app and tried again:

PS C:\Users\user> $kano.open()
PS C:\Users\user> while (1) { $kano.ReadLine() }
{"type": "event", "name": "proximity-data", "detail": {"proximity": 0}}
{"type": "event", "name": "proximity-data", "detail": {"proximity": 0}}
{"type": "event", "name": "proximity-data", "detail": {"proximity": 82}}
{"type": "event", "name": "proximity-data", "detail": {"proximity": 94}}
{"type": "event", "name": "proximity-data", "detail": {"proximity": 229}}
{"type": "event", "name": "proximity-data", "detail": {"proximity": 255}}
{"type": "event", "name": "proximity-data", "detail": {"proximity": 0}}
{"type": "event", "name": "proximity-data", "detail": {"proximity": 251}}
{"type": "event", "name": "proximity-data", "detail": {"proximity": 255}}
{"type": "event", "name": "proximity-data", "detail": {"proximity": 159}}
{"type": "event", "name": "proximity-data", "detail": {"proximity": 1}}
{"type": "event", "name": "proximity-data", "detail": {"proximity": 113}}
{"type": "event", "name": "proximity-data", "detail": {"proximity": 21}}
{"type": "event", "name": "proximity-data", "detail": {"proximity": 1}}
{"type": "event", "name": "proximity-data", "detail": {"proximity": 0}}
{"type": "event", "name": "proximity-data", "detail": {"proximity": 0}}

Success! From here I can parse the JSON output to get motion events and proximity. 

But don't forget to clean up:

PS C:\Users\mccartrb> $kano.close()

Tuesday, August 9, 2016

Using HTML in a Web.config file

I wanted some pop up contextual help in an app I was writing. I got to the point where my requirements were:

  1. Use the bootstrap "popover" so I could include HTML and style the pop-up
  2. Include the help text in the config so it could be easily changed in the future 
Here's how I did it. 


Step 1: add a new section to web.config – this will prevent the text from needing to be escaped or treated with any special handlers

<section name="contextHelp" type="System.Configuration.IgnoreSectionHandler" allowLocation="false" />



Step 2: add the values for the section – I’m using CDATA to hide the markup and that should be sufficient for hyperlinks or images too

<contextHelp>
    <groupTitle><![CDATA[A group title with specific identifying information will be easy to search for later.<br /><br />Use specific hierarchy to help.
]]>

    </groupTitle>
</contextHelp>

Step 3: add code to read the config in the code-behind

protected string HelpTextTitle { get; set; }

protected new void Page_Init(object sender, EventArgs e)
{
  // Help Sections
  XmlDocument doc = new XmlDocument();
  doc.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
  HelpTextTitle = doc.SelectSingleNode("/configuration/contextHelp/groupTitle").FirstChild.Value;
}

Step 4: add values into markup

<a href="#" rel="group_title"><i class="fa fa-question-circle"></i></a>
<script>
$(document).ready(function () {
       $("[rel=group_title]").popover({
             title : 'Group Title', 
             html: 'true', 
             trigger: 'focus', 
             content: '<%=HelpTextTitle %>'
       });
});
</script>


Friday, July 15, 2016

Authenticating with Web Services written in a different language

Ah, the great promise of web services! We'll all speak the same language, XML/SOAP/JSON! Yeah, not so much. My latest interaction with this was a service that wanted a WS-Security authentication header but didn't want all the fields that Microsoft uses by default; without laying blame my understanding is some Java-based services don't accept a timestamp and this was my experience. Here's how I solved the problem:

WCF wasn’t really an option; because one of the functions had a callback it wanted to do duplex communication which among other things doesn’t support SSL. So here’s what I did:
  1.  Added WSE3 to the project (Microsoft.Web.Services3.dll)

  2. Create an old-school Web Reference in .NET (advanced option in “Add Service Reference”)

  3. Changed the resulting Referencs.cs class from System.Web.Services.Protocols.SoapHttpClientProtocol to Microsoft.Web.Services3.WebServicesClientProtocol

  4. Created a policy to allow me to filter the SOAP

  5. public class ClientUserNamePolicyAssertion : SecurityPolicyAssertion
        {
            public string UserName { get; set; }
            public string Password { get; set; }
           
            public ClientUserNamePolicyAssertion(string userName, string password)
            {
                this.UserName = userName;
                this.Password = password;
            }

            // and let the assertion know you have an output filter for outgoing SOAP packets
            public override SoapFilter CreateClientOutputFilter(FilterCreationContext context)
            {
                return new ClientSendSecurityFilter(this);
            }

            public override SoapFilter CreateServiceInputFilter(FilterCreationContext context)
            {
                throw new NotImplementedException();
            }

            public override SoapFilter CreateServiceOutputFilter(FilterCreationContext context)
            {
                throw new NotImplementedException();
            }

            public override SoapFilter CreateClientInputFilter(FilterCreationContext context)
            {
                return null;
            }
        } 

  6. Created the filter – here is where I remove the timestamp header, right before the packet goes out

  7. public class ClientSendSecurityFilter : SendSecurityFilter
        {
            private ClientUserNamePolicyAssertion clientUserNamePolicyAssertion = null;

            public ClientSendSecurityFilter(ClientUserNamePolicyAssertion userNamePolicyAssertion)
                : base(userNamePolicyAssertion.ServiceActor, true)
            {
                this.clientUserNamePolicyAssertion = userNamePolicyAssertion;
            }

            public override void SecureMessage(SoapEnvelope soapEnvelope, Security security)
            {
                UsernameToken usernameToken = new UsernameToken(clientUserNamePolicyAssertion.UserName, clientUserNamePolicyAssertion.Password, PasswordOption.SendPlainText);
                security.Tokens.Add(usernameToken);  
            }

            public override SoapFilterResult ProcessMessage(SoapEnvelope envelope)
            {
                // go ahead and run the base so the headers and security are added
                SoapFilterResult res = base.ProcessMessage(envelope);

                // remove the timestamp from the security header
                foreach (XmlNode n in envelope.Header.ChildNodes) {
                    if (n.Name == "wsse:Security")
                    {
                        foreach (XmlNode p in n)
                        {
                            if (p.Name == "wsu:Timestamp") n.RemoveChild(p);
                        }
                    }
                }

                //Debug.Write(envelope.OuterXml);

                return res;
            }
        }


  8. Then in the main I can create the service, apply my policy and make calls

TheirJavaService svc = new TheirJavaService();
ClientUserNamePolicyAssertion assert = new ClientUserNamePolicyAssertion("myusername", "mypassword");
Policy policy = new Policy();
policy.Assertions.Add(assert);
svc.SetPolicy(policy);
TheirServiceRequest req = new TheirServiceRequest();
TheirServiceResponse res = svc.GetResponseFromTheirService(req);

Friday, November 1, 2013

Generating Excel and formatting numbers

I often write HTML tables and pass them off as Excel documents. This is an easy way to automate creation of an Excel sheet and gives you formatting options. The one I can never remember is how to force numbers to be formatted as text cells - to keep long numbers like 143417623220 from showing in scientific notation like 1.43+E11.

So here's the trick. Set a stylesheet and use this to force your numeric cells to format properly.

.text{
  mso-number-format:"\@"; /* force text format for numbers */
}


Thursday, October 18, 2012

Use CAML when searching SharePoint

CAML is worth the effort. I went from this (powershell):

$listitem = $list.Items | Where { ($_.Title -eq $row["Title"]) -and ($_.Name -eq $row["Name"]) }

To this:
$query = New-Object Microsoft.SharePoint.SPQuery 
$caml = '<where><and><eq><fieldref name="Title"><value type="Text">' + $row["Title"] + '</value></fieldref></eq><eq><fieldref name="Name"><value type="Text">' + $row["Name"] + '</value></fieldref></eq></and></where>'
$query.Query = $caml 
$listitem = $list.GetItems($query)[0]
And the code in question is now about 60 times faster. I’m guessing that the first line is scanning every item until it finds the one it wants, while the CAML query somehow avoids that. Big improvement. 

Friday, August 24, 2012

Powershell tip 1: get-member


Get-Member will tell you all the properties and methods of an object.

So,

“this is a string” | Get-Member will return all the string options like split, join, whatever.

Get-SPContentDatabase | Get-Member on a sharepoint server will show you lots of methods you can run on the db, like “DiskSizeRequired” and “Server” to get information about the db.  

So if you have something in powershell and don’t know what to do with it, try piping it to get-member. 

Saturday, August 20, 2011

ASP.NET without Visual Studio

Sometimes you just don't need a project. You're writing a report or something really low rent and you just need to get a page out there quickly. Well you're in luck, inline declarations can be used to obviate the need for a project, references, etc.

You can have these standalone in any IIS web directory or you can make them into an app by putting them in a folder, adding a web.config, declaring an app pool in IIS admin, etc.

You can mix HTML and ASP tags as needed, or generate elements in code (as I've done below).

Your first line needs to declare a page and language used:
<%@ Page Language="VB" Debug="true" %>

You can import libraries from the GAC by using Import
<%@ Import Namespace="System.Text" %>

You can import libraries from outside the GAC using Register Assembly and then Import
<%@ Register Assembly="System.DirectoryServices, Version=2.0.50727.3053, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" Namespace="System.DirectoryServices" TagPrefix="SD" %>
<%@ Import Namespace="System.DirectoryServices" %>

VB.NET uses a Page_Init function to initialize your web app.
<script runat="server">

Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs)
 
    ' POST takes the usr and pwd fields and authenticates
    If Request.Form.Count > 0 Then
      AuthEntry(Request.Form.Item("usr"), Request.Form.Item("pwd"))
    End If
 
    ' no parameters will generate a login form
    If Request.QueryString.Count = 0 And Request.Form.Count = 0 Then
      GenerateForm
    End If
End Sub

' you can generate form elements using code or do more traditional ASP layout below the script tag
Protected Sub GenerateForm()
  Dim Form1 As System.Web.UI.HtmlControls.HtmlForm
  Dim Label1 As System.Web.UI.WebControls.Label
  Dim usr As System.Web.UI.WebControls.TextBox
  Dim pwd As System.Web.UI.WebControls.TextBox
  Dim btn1 As System.Web.UI.WebControls.Button

  Form1 = New HtmlForm()
  Form1.ID = "myForm"

  usr = New TextBox()
  usr.ID = "usr"
  Form1.Controls.Add(usr)

  pwd = New TextBox()
  pwd.ID = "pwd"
  pwd.TextMode = TextBoxMode.Password
  Form1.Controls.Add(pwd)

  btn1 = new Button()
  btn1.Text = "Login"
  Form1.Controls.Add(btn1)

  Page.Controls.Add(Form1)

End Sub

' Here is a function that will validate a user against Active Directory
' You are essentially performing an authenticated search for your own account.
Protected Sub AuthEntry(usr as String, pwd as String)

  Dim path as String = "LDAP://your.domain.com/CN=Users,DC=your,DC=domain,DC=com"
  Dim entry as DirectoryEntry = new DirectoryEntry(path,usr,pwd,AuthenticationTypes.Secure)

  Try
    Dim obj as Object = entry.NativeObject
    Dim search as DirectorySearcher = new DirectorySearcher(entry)
    search.Filter = "(SAMAccountName=" + usr + ")"
    search.PropertiesToLoad.Add("samaccountname")
    Dim result As SearchResultCollection = search.FindAll()
    If result.Count > 0 Then
      ' authenticated
      Response.Write("Success!")
      Exit Sub
    End If
  Catch ex As Exception
    Response.Write(ex.Message)
    Exit Sub
  End Try

End Sub

</script>