using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Reflection; using System.Text; using System.Text.RegularExpressions; namespace RobvanderWoude { internal class Youless { static readonly string progver = "1.00"; static string address = "youless"; static string ipaddress = string.Empty; static int Main( string[] args ) { #region Initialize Variables CultureInfo culture = CultureInfo.GetCultureInfo( "en-US" ); CultureInfo.CurrentCulture = culture; // SETTING CurrentCulture requires .NET Framework 4.6 string exepath = new Uri( Assembly.GetExecutingAssembly( ).CodeBase ).LocalPath; string configfile = Path.Combine( Directory.GetParent( exepath ).FullName, Path.GetFileNameWithoutExtension( exepath ) + ".ip" ); #endregion Initialize Variables #region Get Youless Address if ( args.Contains( "/?" ) ) { return ShowHelp( ); } else if ( args.Length == 0 ) { // Get the IP address for the default hostname "youless" ValidateAddress( address ); } else if ( args.Length == 1 ) { if ( ValidateAddress( args[0] ) ) { address = args[0]; } } else if ( args.Length > 1 ) { return ShowHelp( ); } else if ( File.Exists( configfile ) ) { address = File.ReadAllText( configfile ).Trim( ); if ( !ValidateAddress( address ) ) { File.Delete( configfile ); address = string.Empty; } } if ( string.IsNullOrWhiteSpace( ipaddress ) ) { while ( string.IsNullOrWhiteSpace( ipaddress ) ) { address = PromptForIP( ); } } if ( !File.Exists( configfile ) ) { File.WriteAllText( configfile, ipaddress ); } #endregion Get Youless Address #region Device Data Console.WriteLine( ); string url = string.Format( "http://{0}/d", ipaddress ); YoulessClass.fw = string.Empty; if ( !YoulessReadData( url ) ) { Console.Error.WriteLine( "\nUitlezen Youless {0} niet gelukt", ipaddress ); return -1; } Console.WriteLine( "Youless model :\t{0}", YoulessClass.model ); if ( !string.IsNullOrWhiteSpace( YoulessClass.fw ) ) { Console.WriteLine( "Youless firmware :\t{0}", YoulessClass.fw ); } Console.WriteLine( "Youless MAC adres :\t{0}", YoulessClass.mac ); Console.WriteLine( "Youless IP adres :\t{0}", ipaddress ); #endregion Device Data #region Power Consumption Data Console.WriteLine( ); url = string.Format( "http://{0}/e", ipaddress ); // URL for power consumption log if ( YoulessReadData( url ) ) { Console.WriteLine( "Tijdstip :\t{0}", YoulessClass.gts.ToString( "yyyy-MM-dd HH:mm" ) ); Console.WriteLine( ); Console.WriteLine( "Vermogen :\t{0} W", YoulessClass.pwr ); Console.WriteLine( "P1 afname hoog tarief :\t{0} kWh", YoulessClass.p1 ); Console.WriteLine( "P1 afname laag tarief :\t{0} kWh", YoulessClass.p2 ); Console.WriteLine( "P1 teruglevering hoog tarief :\t{0} kWh", YoulessClass.n1 ); Console.WriteLine( "P1 teruglevering laag tarief :\t{0} kWh", YoulessClass.n2 ); Console.WriteLine( "Netto totaal verbruik :\t{0} kWh", YoulessClass.net ); Console.WriteLine( ); Console.WriteLine( "Gas verbruik :\t{0} m\x00B3", YoulessClass.gas ); } else { Console.Error.WriteLine( "\nUitlezen Youless {0} niet gelukt", ipaddress ); return -1; } #endregion Power Consumption Data return 0; } static PingReply Ping( string address ) { Ping pingSender = new Ping( ); PingOptions options = new PingOptions( ); options.DontFragment = true; // Create a buffer of 32 bytes of data to be transmitted. string data = new string( 'a', 32 ); byte[] buffer = Encoding.ASCII.GetBytes( data ); int timeout = 120; return pingSender.Send( address, timeout, buffer, options ); } static string PromptForIP( ) { Console.Write( "Vul het IP-adres van de Youless in: " ); string ip = Console.ReadLine( ).Trim( ); if ( ValidateAddress( ip, true ) ) { return ip; } else { return string.Empty; } } public static int ShowHelp( params string[] errmsg ) { #region Error Message if ( errmsg.Length > 0 ) { List errargs = new List( errmsg ); errargs.RemoveAt( 0 ); Console.Error.WriteLine( ); Console.ForegroundColor = ConsoleColor.Red; Console.Error.Write( "ERROR:\t" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( errmsg[0], errargs.ToArray( ) ); Console.ResetColor( ); } #endregion Error Message #region Help Text /* Youless.exe, Version 1.00 Read data from a Youless LS120 energy monitor device Usage: Youless.exe [ ip ] Where: ip is the IP address of the Youless device Notes: If no IP address is specified, the program will try to resolve the IP address for hostname "youless". If unsuccessful, you will be prompted to enter it, and the address will be saved in a file named Youless.cfg in the program's parent directory. This program requires .NET Framework version 4.6. Return code (\"ErrorLevel\") equals -1 in case of (command line) errors, otherwise 0. Written by Rob van der Woude https://www.robvanderwoude.com */ #endregion Help Text #region Display Help Text Console.Error.WriteLine( ); Console.Error.WriteLine( "Youless.exe, Version {0}", progver ); Console.Error.WriteLine( "Read data from a Youless LS120 energy monitor device" ); Console.Error.WriteLine( ); Console.Error.Write( "Usage: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "Youless.exe [ ip ]" ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.Write( "Where: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "ip" ); Console.ResetColor( ); Console.Error.WriteLine( " is the IP address of the Youless device" ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Notes: If no IP address is specified, the program will try to resolve the" ); Console.Error.WriteLine( " IP address for hostname \"youless\". If unsuccessful, you will be" ); Console.Error.WriteLine( " prompted to enter it, and the address will be saved in a file" ); Console.Error.WriteLine( " named Youless.ip in the program's parent directory." ); Console.Error.WriteLine( " This program requires .NET Framework version 4.6." ); Console.Error.WriteLine( " Return code (\"ErrorLevel\") equals -1 in case of (command line)" ); Console.Error.WriteLine( " errors, otherwise 0." ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Written by Rob van der Woude" ); Console.Error.WriteLine( "https://www.robvanderwoude.com" ); #endregion Display Help Text return -1; } static bool ValidateAddress( string address, bool iponly = false ) { bool valid = true; if ( iponly ) { Regex regex = new Regex( @"^(\d+(\.\d+){3}|[:a-f\d]{3,})$", RegexOptions.IgnoreCase ); valid = regex.IsMatch( address ); } if ( valid ) { PingReply reply = Ping( address ); valid = ( reply.Status == IPStatus.Success ); if ( valid ) { ipaddress = reply.Address.ToString( ); } else { Console.Error.WriteLine( "Unable to reach {0}", address ); } } return valid; } static bool YoulessReadData( string url ) { try { using ( WebClient wc = new WebClient( ) ) { string[] json = wc.DownloadString( url ).Split( "{}[],".ToCharArray( ), StringSplitOptions.RemoveEmptyEntries ); foreach ( string keyval in json ) { string key = keyval.Split( ":".ToCharArray( ) )[0].Trim( "\"\t ".ToCharArray( ) ); string val = keyval.Split( ":".ToCharArray( ), 2 )[1].Trim( "\"\t ".ToCharArray( ) ); switch ( key ) { case "cs0": YoulessClass.cs0 = double.Parse( val ); break; case "fw": YoulessClass.fw = val; break; case "gas": YoulessClass.gas = double.Parse( val ); break; case "gts": string timestamp = string.Format( "20{0}-{1}-{2} {3}:{4}", val.Substring( 0, 2 ), val.Substring( 2, 2 ), val.Substring( 4, 2 ), val.Substring( 6, 2 ), val.Substring( 8, 2 ) ); YoulessClass.gts = DateTime.Parse( timestamp ); break; case "mac": YoulessClass.mac = val.Replace( ':', '-' ).ToUpper( ); break; case "model": YoulessClass.model = val; break; case "n1": YoulessClass.n1 = double.Parse( val ); break; case "n2": YoulessClass.n2 = double.Parse( val ); break; case "net": YoulessClass.net = double.Parse( val ); break; case "p1": YoulessClass.p1 = double.Parse( val ); break; case "p2": YoulessClass.p2 = double.Parse( val ); break; case "ps0": YoulessClass.ps0 = int.Parse( val ); break; case "pwr": YoulessClass.pwr = int.Parse( val ); break; case "tm": YoulessClass.tm = new DateTime( long.Parse( val ) ); break; case "ts0": YoulessClass.ts0 = new DateTime( long.Parse( val ) ); break; default: // invalid/unknown key return false; } } } return true; } catch ( Exception e ) { Console.Error.WriteLine( e.Message ); return false; } } } public static class YoulessClass { public static double cs0 { get; set; } public static string fw { get; set; } public static double gas { get; set; } public static DateTime gts { get; set; } public static string mac { get; set; } public static string model { get; set; } public static double n1 { get; set; } public static double n2 { get; set; } public static double net { get; set; } public static double p1 { get; set; } public static double p2 { get; set; } public static int ps0 { get; set; } public static int pwr { get; set; } public static DateTime tm { get; set; } public static DateTime ts0 { get; set; } } }