Example #1
0
 public ListElement(string name, string unit, string tag, int index, double value, CustomAdapter adapter, int packetId)
 {
     this.adapter  = adapter;
     this.packetId = packetId;
     this.name     = name;
     this.value    = value;
     this.unit     = unit;
     this.tag      = tag;
     this.index    = index;
     min           = max = value;
     UpdateLimits(value);
     changed = true;
 }
Example #2
0
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            try {
                starting = true;

                //verifyStoragePermissions();

                Thread.CurrentThread.CurrentCulture =
                    System.Globalization.CultureInfo.InvariantCulture;

                //RequestWindowFeature(WindowFeatures.ActionBar);

                //Window.AddFlags(WindowManagerFlags.Fullscreen);
                //ActionBar.SetDisplayShowHomeEnabled(true);

                ActionBar.SetDisplayShowTitleEnabled(false);
                ActionBar.SetDisplayShowHomeEnabled(false);
                ActionBar.NavigationMode = ActionBarNavigationMode.Tabs;

                /*var setHasEmbeddedTabsMethod = ActionBar.Class
                 *        .GetDeclaredMethod("setHasEmbeddedTabs", Java.Lang.Boolean.Type);
                 * setHasEmbeddedTabsMethod.Accessible=true;
                 * setHasEmbeddedTabsMethod.Invoke(ActionBar, true);*/

                //ActionBar.MenuVisibility = true;
                //ActionBar.DisplayOptions=ActionBarDisplayOptions
                SetContentView(Resource.Layout.Main);

                gridView1         = FindViewById <GridView>(Resource.Id.gridView1);
                ladapter          = new CustomAdapter(this, gridView1);
                gridView1.Adapter = ladapter;

                parser = new Parser(this, ladapter);

                statusText = FindViewById <TextView>(Resource.Id.StatusText);

                /*statusTimer = new System.Timers.Timer(10 * 1000);
                 * statusTimer.Elapsed += timer_Elapsed;
                 * statusTimer.AutoReset = false;*/

                statusText.Visibility = Android.Views.ViewStates.Invisible;
                statusText.Text       = "";

                // initalize menu
                menuIcon = FindViewById <ImageView>(Resource.Id.menuIcon);
                PopupMenu menu = new PopupMenu(this, menuIcon);
                menu.Inflate(Resource.Menu.OptionsMenu);
                //menu.Menu.FindItem(Resource.Id.selectionMode).SetCheckable(true);

                menuIcon.Click += (s, arg) => {
                    menu.Show();
                };
                menuIcon.Clickable  = true;
                menu.MenuItemClick += Menu_MenuItemClick;

                prefIcon = FindViewById <ImageView>(Resource.Id.prefIcon);
                PopupMenu prefMenu = new PopupMenu(this, prefIcon);
                prefMenu.Inflate(Resource.Menu.Wrench);
                //menu.Menu.FindItem(Resource.Id.selectionMode).SetCheckable(true);

                prefIcon.Click += (s, arg) => {
                    prefMenu.Show();
                };
                prefIcon.Clickable      = true;
                prefMenu.MenuItemClick += Menu_MenuItemClick;

                // context menu
                RegisterForContextMenu(gridView1);

                bluetoothHandler = new BluetoothHandler(this, parser);

                tabs = new List <Tab>();

                prefs = PreferenceManager.GetDefaultSharedPreferences(this);

                if (prefs.Contains("tabs"))
                {
                    // read saved stuff
                    LoadTabs();
                }
                else
                {
                    CreateDefaultTabs();
                }

                convertToImperial = prefs.GetBoolean("convertToImperial", false);
                prefMenu.Menu.FindItem(Resource.Id.metric)
                .SetChecked(!convertToImperial);

                gridView1.Clickable  = true;
                gridView1.ItemClick += (object sender, AdapterView.ItemClickEventArgs e) => {
                    int packetId = ladapter[e.Position].packetId;
                    if (!ladapter[e.Position].selected)
                    {
                        sel = 1;
                    }
                    else
                    {
                        sel++;
                    }
                    if (sel > 3)
                    {
                        sel = 0;
                    }

                    if (sel == 0 || sel == 1)
                    {
                        ladapter[e.Position].selected = sel == 1;
                        ladapter.Touch(ladapter[e.Position]);
                    }
                    else
                    {
                        foreach (var item in ladapter.items.Where(x => x.packetId == packetId))
                        {
                            item.selected = sel == 2;
                            ladapter.Touch(item);
                        }
                    }
                    //ladapter.NotifyDataSetChanged();
                };


                var VersionName = PackageManager.GetPackageInfo(PackageName, 0).VersionName;
                //Toast.MakeText(this, "Version " + VersionName, ToastLength.Long).Show();
                prefMenu.Menu.Add("Version " + VersionName);
                var StoredVersion = prefs.GetString("version", "");
                if (StoredVersion != VersionName)
                {
                    AlertDialog.Builder alert = new AlertDialog.Builder(this);
                    alert.SetTitle("Welcome to Scan My Tesla version " + VersionName + "!");

                    if (StoredVersion == "" && prefs.All.Keys.Any()) // if there are any prefs in here, must mean we are upgrading
                    {
                        alert.SetMessage(                            // but version recording started in 1.3.0, so upgrading from earlier than that
                            "This version has some new and redesigned tabs.\n" +
                            "Use 'Factory reset all tabs' from the left menu if you want the new layout.\n" +
                            "Or, you can select 'New tab' from the right menu to add a single tab from the templates.");
                    }

                    alert.SetCancelable(true);
                    alert.SetPositiveButton("Changelog", (senderAlert, args) => {
                        var uri    = Android.Net.Uri.Parse("https://sites.google.com/view/scanmytesla/changelog");
                        var intent = new Intent(Intent.ActionView, uri);
                        StartActivity(intent);
                    });

                    alert.SetNegativeButton("Close", (senderAlert, args) => {
                    });

                    Dialog dialog = alert.Create();
                    dialog.Show();

                    PutPref("version", VersionName);
                }


#if !disablebluetooth
                var storedDevice = prefs.GetString("device", "");
                if (storedDevice == "")
                {
                    StartActivityForResult(typeof(DeviceListActivity), 1);
                }
                else
                {
                    Intent intent = new Intent(this, typeof(MainActivity));
                    intent.PutExtra("device_address", storedDevice);
                    OnActivityResult(1, Result.Ok, intent);
                }

                var t = tabs.Where(x => x.name == prefs.GetString("currentTab", "")).FirstOrDefault();
                if (t != null)
                {
                    ActionBar.SelectTab(t.ActionBarTab);
                }

                //Finish();
#else
                var t = tabs.Where(x => x.name == prefs.GetString("currentTab", "")).FirstOrDefault();
                if (t != null)
                {
                    ActionBar.SelectTab(t.ActionBarTab);
                }

                bluetoothHandler.Initialize(null);

                Window.SetFlags(WindowManagerFlags.KeepScreenOn, WindowManagerFlags.KeepScreenOn);

                //bluetoothHandler.Start();
#endif
                starting = false;

                verifyStoragePermissions();

                /*if (LogTimer == null)
                 * LogTimer = new Timer(LogTimerCallback, null, 2000, 200);*/

                //SupportFunctions.Version = context.PackageManager.GetPackageInfo(context.PackageName, 0).VersionName;
                //prefs.GetInt("")
            }
            catch (Exception e) {
                //set alert for executing the task
                AlertDialog.Builder alert = new AlertDialog.Builder(this);
                alert.SetTitle("Error");
                alert.SetMessage(e.ToString());
                alert.SetCancelable(false);
                alert.SetPositiveButton("OK", (senderAlert, args) => {
                    System.Environment.Exit(1);
                });

                Dialog dialog = alert.Create();
                dialog.Show();
            }
        }
Example #3
0
        public Parser(MainActivity mainActivity, CustomAdapter adapter)
        {
            this.adapter      = adapter;
            this.mainActivity = mainActivity;
            items             = new ConcurrentDictionary <string, ListElement>();
            packets           = new SortedList <int, Packet>();
            time = SystemClock.ElapsedRealtime() + 1000;

            /* tags:
             *  p: performance
             *  t: trip
             *  b: battery
             *  c: temperature
             *  f: front drive unit
             *  s: startup (app will wait until these packets are found before starting 'normal' mode)
             *  i: imperial
             *  m: metric
             *  i: ignore (in trip tabs, with slow/ELM adapters)
             *  z: BMS
             *  x: Cells
             *  e: efficiency
             */

            Packet p;


#if USEDCB
            //String dbcPath = "Model3CAN.dbc";
            //string path = Directory.GetCurrentDirectory();
            //var pathFile = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads);
            ////IEnumerable<string> files2 = Directory.EnumerateFiles((String)pathFile, "*.dbc");
            //dbcPath = (string)pathFile + "/" + dbcPath;
            //inputStream = mainActivity.Assets.Open("CAN_BUS_LOG_Feb_10.txt");

            //LINES 185 to 239 are taken from CANBUS-Analyzer https://github.com/amund7/CANBUS-Analyzer
            //if (dbcPath != null)
            if (true)
            {
                Reader reader = new DBCLib.Reader();

                reader.AllowErrors = true;

                //List<object> entries = reader.Read(dbcPath);
                //AssetManager assets = this.Assets;
                List <KeyValuePair <uint, string> > errors   = null;
                List <KeyValuePair <uint, string> > warnings = null;
                using (StreamReader streamReader = new StreamReader(mainActivity.Assets.Open("Model3CAN.dbc"), Encoding.Default, false))
                {
                    List <object> entries = reader.Read(streamReader, "Model3CAN.dbc", errors, warnings);

                    //List<object> entries = reader.Read(mainActivity.Assets.Open("Model3CAN.dbc"));

                    foreach (object entry in entries)
                    {
                        if (entry is DBCLib.Message)
                        {
                            DBCLib.Message message = (DBCLib.Message)entry;

                            packets.Add((int)message.Id, p = new Packet((int)message.Id, this));
                            foreach (DBCLib.Message.Signal signal in message.Signals)
                            {
                                var valueLookup = (DBCLib.Value)
                                                  entries.Where(x => x is DBCLib.Value && ((DBCLib.Value)x).ContextSignalName == signal.Name).FirstOrDefault();
                                p.AddValue(
                                    signal.Name.Replace("_", " ").Remove(signal.Name.Length - 3), //.Replace("_", " "),
                                    signal.Unit,
                                    NameToTag(message.Id),                                        //NameToTag(signal.Name),
                                    (bytes) =>
                                {
                                    double result;
                                    if (signal.StartBit + signal.BitSize > bytes.Length * 8)   // check data length
                                    {
                                        return(0);
                                    }
                                    if (signal.Multiplexer)   // if this is our multiplex / page selector
                                    {
                                        return
                                        (p.currentMultiplexer =                                         // store it
                                                                ExtractSignalFromBytes(bytes, signal)); // and return it
                                    }
                                    else if (signal.MultiplexerIdentifier != null)
                                    {                                                             // else if this is a sub-item
                                        if (signal.MultiplexerIdentifier == p.currentMultiplexer) // check if we're on the same page
                                        {
                                            result = ExtractSignalFromBytes(bytes, signal);       // then return it
                                        }
                                        else
                                        {
                                            return(0);
                                        }
                                    }
                                    else
                                    {
                                        result = ExtractSignalFromBytes(bytes, signal);
                                    }
                                    if (valueLookup != null)
                                    {
                                        string s =
                                            valueLookup.Mapping.Where(x => x.Key == result).FirstOrDefault().Value; //TryGetValue((long)result, out s);
                                        if (s != null)
                                        {
                                            return(0);
                                        }
                                        //return s;
                                    }
                                    return(result);
                                },
                                    null
                                    );
                            }
                        }
                    }
                }
            }
#else
            /*packets.Add(0x256, p = new Packet(0x256, this));
             * p.AddValue("Metric", "bool", "s", (bytes) => {
             * metric = Convert.ToBoolean(bytes[3] & 0x80);
             * if (metric) {
             *  foreach (var packet in packets)
             *    foreach (var v in packet.Value.values)
             *      if (v.tag.Contains("i"))
             *        packet.Value.values.Remove(v);
             * } else {
             *  foreach (var packet in packets)
             *    foreach (var v in packet.Value.values)
             *      if (v.tag.Contains("m"))
             *        packet.Value.values.Remove(v);
             * }
             * return metric ? 1 : 0;
             * });*/
            packets.Add(0x528, p = new Packet(0x528, this));
            p.AddValue("UNIX Time", "Sec", "br", (bytes) => (bytes[0] << 8 * 3) + (bytes[1] << 8 * 2) + (bytes[2] << 8) + bytes[3]);

            packets.Add(0x266, p = new Packet(0x266, this));
            p.AddValue("Rr inverter 12V", "V12", "", (bytes) => bytes[0] / 10.0);
            p.AddValue("Rr power", " kW", "e", (bytes) => mechPower =
                           ((bytes[2] + ((bytes[3] & 0x7) << 8)) - (512 * (bytes[3] & 0x4))) / 2.0);
            //p.AddValue("Rr mech power HP", "HP", "pf", (bytes) => mechPower * kw_to_hp);
            p.AddValue("Rr dissipation", " kW", "", (bytes) => {
                rDissipation = bytes[1] * 125.0 / 1000.0;

                /*dissipationUpdated = true;
                 * dissipationTimeStamp = DateTime.Now.Millisecond;*/
                return(rDissipation);
            });
            p.AddValue("Rr stator current", "A", "", (bytes) => bytes[4] + ((bytes[5] & 0x7) << 8));
            p.AddValue("Rr regen power max", "KW", "b", (bytes) => (bytes[7] * 4) - 200);
            p.AddValue("Rr drive power max", "KW", "b", (bytes) => drivePowerMax =
                           (((bytes[6] & 0x3F) << 5) + ((bytes[5] & 0xF0) >> 3)) + 1);


            packets.Add(0x132, p = new Packet(0x132, this));
            p.AddValue("Battery voltage", " V", "bpr", (bytes) => volt =
                           (bytes[0] + (bytes[1] << 8)) / 100.0);
            p.AddValue("Battery current", " A", "b", (bytes) => amp =
                           1000 - ((Int16)((((bytes[3]) << 8) + bytes[2]))) / 10.0);
            p.AddValue("Battery power", " kW", "bpe", (bytes) => power = amp * volt / 1000.0);

            packets.Add(0x3B6, p = new Packet(0x3B6, this));
            p.AddValue("Odometer", "Km", "b",
                       (bytes) => odometer = (bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24)) / 1000.0);

            packets.Add(0x154, p = new Packet(0x154, this));
            p.AddValue("Rr torque measured", "Nm", "p", (bytes) => torque =
                           (bytes[5] + ((bytes[6] & 0x1F) << 8) - (512 * (bytes[6] & 0x10))) * 0.25);

            packets.Add(0x108, p = new Packet(0x108, this));
            p.AddValue("Rr motor RPM", "RPM", "",
                       (bytes) => rrpm = (bytes[5] + (bytes[6] << 8)) - (512 * (bytes[6] & 0x80)));

            packets.Add(0x376, p = new Packet(0x376, this));
            p.AddValue("Inverter temp 1", " C", "e",
                       (bytes) => (bytes[0] - 40));
            p.AddValue("Inverter temp 2", " C", "e",
                       (bytes) => (bytes[1] - 40));
            p.AddValue("Inverter temp 3", " C", "e",
                       (bytes) => (bytes[2] - 40));
            p.AddValue("Inverter temp 4", " C", "e",
                       (bytes) => (bytes[4] - 40));


            packets.Add(0x292, p = new Packet(0x292, this));
            p.AddValue("SOC UI", "%", "br", (bytes) => (bytes[0] + ((bytes[1] & 0x3) << 8)) / 10.0);
            p.AddValue("SOC Min", "%", "br", (bytes) => ((bytes[1] >> 2) + ((bytes[2] & 0xF) << 6)) / 10.0);
            p.AddValue("SOC Max", "%", "br", (bytes) => ((bytes[2] >> 4) + ((bytes[3] & 0x3F) << 4)) / 10.0);
            p.AddValue("SOC Avg", "%", "br", (bytes) => ((bytes[3] >> 6) + ((bytes[4]) << 2)) / 10.0);



            packets.Add(0x252, p = new Packet(0x252, this));
            p.AddValue("Max discharge power", "kW", "b", (bytes) => (bytes[2] + (bytes[3] << 8)) / 100.0);
            p.AddValue("Max regen power", "kW", "b", (bytes) => (bytes[0] + (bytes[1] << 8)) / 100.0);

            packets.Add(0x2A4, p = new Packet(0x2A4, this));
            p.AddValue("7 bit 0", "b", "br",
                       (bytes) => (bytes[0] & 0x7F));
            p.AddValue("5 bit 1", "b", "br",
                       (bytes) => (bytes[1] & 0xF8) >> 3);
            p.AddValue("5 bit 2", "b", "br",
                       (bytes) => ((bytes[1] & 0x7) << 2) + ((bytes[2] & 0xC0) >> 6));
            p.AddValue("7 bit 3", "b", "br",
                       (bytes) => (bytes[3] & 0x7F));
            p.AddValue("7 bit 4", "b", "br",
                       (bytes) => (bytes[4] & 0xFE) >> 1);

            /*p.AddValue("33A 12 bit 3", "b", "br",
             * (bytes) => (bytes[3] + ((bytes[4] & 0x0F) << 8)));
             * p.AddValue("33A 12 bit 4", "b", "br",
             * (bytes) => (((bytes[4] & 0xF0) >> 4) + ((bytes[5]) << 4)));
             * p.AddValue("33A 12 bit 5", "b", "br",
             * (bytes) => (bytes[6] + ((bytes[7] & 0x0F) << 8)));*/


            packets.Add(0x352, p = new Packet(0x352, this));
            p.AddValue("Nominal full pack", "kWh", "br", (bytes) => nominalFullPackEnergy = (bytes[0] + ((bytes[1] & 0x03) << 8)) * 0.1);
            p.AddValue("Nominal remaining", "kWh", "br", (bytes) => nominalRemaining      = ((bytes[1] >> 2) + ((bytes[2] & 0x0F) * 64)) * 0.1);
            p.AddValue("Expected remaining", "kWh", "r", (bytes) => ((bytes[2] >> 4) + ((bytes[3] & 0x3F) * 16)) * 0.1);
            p.AddValue("Ideal remaining", "kWh", "r", (bytes) => ((bytes[3] >> 6) + ((bytes[4] & 0xFF) * 4)) * 0.1);
            p.AddValue("To charge complete", "kWh", "", (bytes) => (bytes[5] + ((bytes[6] & 0x03) << 8)) * 0.1);
            p.AddValue("Energy buffer", "kWh", "br", (bytes) => buffer = ((bytes[6] >> 2) + ((bytes[7] & 0x03) * 64)) * 0.1);

            /*p.AddValue("SOC", "%", "br", (bytes) => soc = (nominalRemaining - buffer) / (nominalFullPackEnergy - buffer) * 100.0);
             * This one seems to be confirmed to be far off the UI displayed SOC
             */

            packets.Add(0x212, p = new Packet(0x212, this));
            p.AddValue("Battery temp", "C", "i",
                       (bytes) => ((bytes[7]) / 2.0) - 40.0);

            packets.Add(0x321, p = new Packet(0x321, this));
            p.AddValue("CoolantTempBatteryInlet", "C", "e",
                       (bytes) => ((bytes[0] + ((bytes[1] & 0x3) << 8)) * 0.125) - 40);
            p.AddValue("CoolantTempPowertrainInlet", "C", "e",
                       (bytes) => (((((bytes[2] & 0xF) << 8) + bytes[1]) >> 2) * 0.125) - 40);
            p.AddValue("Ambient Temp raw", "C", "e",
                       (bytes) => ((bytes[3] * 0.5) - 40));
            p.AddValue("Ambient Temp filtered", "C", "e",
                       (bytes) => ((bytes[5] * 0.5) - 40));



            packets.Add(0x3D2, p = new Packet(0x3D2, this));
            p.AddValue("Charge total", "kWH", "bs",
                       (bytes) => {
                chargeTotal =
                    (bytes[0] +
                     (bytes[1] << 8) +
                     (bytes[2] << 16) +
                     (bytes[3] << 24)) / 1000.0;

                /*if (mainActivity.currentTab.trip.chargeStart == 0)
                 * mainActivity.currentTab.trip.chargeStart = chargeTotal;
                 * charge = chargeTotal - mainActivity.currentTab.trip.chargeStart;*/
                return(chargeTotal);
            });

            p.AddValue("Discharge total", "kWH", "b",
                       (bytes) => {
                dischargeTotal =
                    (bytes[4] +
                     (bytes[5] << 8) +
                     (bytes[6] << 16) +
                     (bytes[7] << 24)) / 1000.0;

                /*if (mainActivity.currentTab.trip.dischargeStart == 0)
                 * mainActivity.currentTab.trip.dischargeStart = dischargeTotal;
                 * discharge = dischargeTotal - mainActivity.currentTab.trip.dischargeStart;*/
                return(dischargeTotal);
            });



            /*packets.Add(0x712, p = new Packet(0x712, this));
             * p.AddValue("Last cell block updated", "xb", "", (bytes) => {
             * int cell = 0;
             * double voltage = 0.0;
             * for (int i = 0; i < 3; i++) {
             *  voltage = (((bytes[i * 2 + 3] << 8) + bytes[i * 2 + 2]) /100.0);
             *  if (voltage > 0)
             *    UpdateItem("Cell " + (cell = ((bytes[0]) * 3 + i + 1)).ToString().PadLeft(2) + " temp"
             *      , "zVC"
             *      , "z"
             *      , bytes[0]
             *      , voltage
             *      , 0x712);
             * }
             * return bytes[0];
             * });
             */

            // these are placeholders for the filters to be generated correctly.
            p.AddValue("Cell temp min", "C", "b", null);
            p.AddValue("Cell temp avg", "C", "bcp", null);
            p.AddValue("Cell temp max", "C", "b", null);
            p.AddValue("Cell temp diff", "Cd", "bc", null);
            p.AddValue("Cell min", "Vc", "b", null);
            p.AddValue("Cell avg", "Vc", "bpzr", null);
            p.AddValue("Cell max", "Vc", "b", null);
            p.AddValue("Cell diff", "Vcd", "bz", null);
            for (int i = 1; i <= 96; i++)
            {
                p.AddValue("Cell " + i.ToString().PadLeft(2) + " voltage"
                           , "zVC"
                           , "z", null);
            }
            for (int i = 1; i <= 32; i++)
            {
                p.AddValue("Cell " + i.ToString().PadLeft(2) + " temp"
                           , "zCC"
                           , "c"
                           , null);
            }
#endif
        }
Example #4
0
        public Parser(MainActivity mainActivity, CustomAdapter adapter)
        {
            this.adapter      = adapter;
            this.mainActivity = mainActivity;
            items             = new ConcurrentDictionary <string, ListElement>();
            packets           = new SortedList <int, Packet>();
            time = SystemClock.ElapsedRealtime() + 1000;

            /* tags:
             *  p: performance
             *  t: trip
             *  b: battery
             *  c: temperature
             *  f: front drive unit
             *  s: startup (app will wait until these packets are found before starting 'normal' mode)
             *  i: imperial
             *  m: metric
             *  i: ignore (in trip tabs, with slow/ELM adapters)
             *  z: BMS
             *  x: Cells
             *  e: efficiency
             */

            Packet p;

            /*packets.Add(0x256, p = new Packet(0x256, this));
             * p.AddValue("Metric", "bool", "s", (bytes) => {
             * metric = Convert.ToBoolean(bytes[3] & 0x80);
             * if (metric) {
             *  foreach (var packet in packets)
             *    foreach (var v in packet.Value.values)
             *      if (v.tag.Contains("i"))
             *        packet.Value.values.Remove(v);
             * } else {
             *  foreach (var packet in packets)
             *    foreach (var v in packet.Value.values)
             *      if (v.tag.Contains("m"))
             *        packet.Value.values.Remove(v);
             * }
             * return metric ? 1 : 0;
             * });*/

            packets.Add(0x102, p = new Packet(0x102, this));
            p.AddValue("Battery voltage", " V", "bpr", (bytes) => volt =
                           bytes.Count() >= 6 ? (bytes[0] + (bytes[1] << 8)) / 100.0 : bytes[100]); // deliberately throws outofrangeexception if bytes.Count()<=4
            p.AddValue("Battery current", " A", "b", (bytes) => amp =
                           bytes.Count() < 9 ? 1000 - ((Int16)((((bytes[3] & 0x7F) << 8) + bytes[2]) << 1)) / 20.0 : bytes[100]);
            p.AddValue("Battery power", " kW", "bpe", (bytes) => power = amp * volt / 1000.0);
            //p.AddValue("cell average", "Vc", "bp", (bytes) => numCells > 70 ? volt / numCells : bytes[100]);
            //p.AddValue("negative terminal", "C", (bytes) => ((bytes[6] + ((bytes[7] & 0x07) << 8))) * 0.1 - 10);

            packets.Add(0x210, p = new Packet(0x210, this));
            p.AddValue("DC-DC current", "A12", "b", (bytes) => bytes[4]);
            p.AddValue("DC-DC voltage", "V12", "b", (bytes) => bytes[5] / 10.0);
            p.AddValue("DC-DC coolant inlet", "C", "c", (bytes) => ((bytes[2] - (2 * (bytes[2] & 0x80))) * 0.5) + 40);
            p.AddValue("DC-DC input power", "W", "b", (bytes) => dcIn   = (bytes[3] * 16));
            p.AddValue("12v systems", "W", "e", (bytes) => dcIn         = (bytes[3] * 16));
            p.AddValue("DC-DC output power", "W", "b", (bytes) => dcOut = (bytes[4] * bytes[5] / 10.0));
            p.AddValue("DC-DC efficiency", "%", "e", (bytes) => dcOut / dcIn * 100.0);
            p.AddValue("400V systems", " kW", "e", (bytes) => power - dcIn / 1000.0);
            p.AddValue("Heating/cooling", "kW", "eh", (bytes) => {
                if (dissipationUpdated ||
                    SystemClock.ElapsedRealtime() > dissipationTimeStamp + 2000)
                {
                    hvacPower          = (power - (rInput + fInput) - (dcIn / 1000.0));
                    dissipationUpdated = false;
                    return(hvacPower);
                }
                else
                {
                    return(bytes[100]);
                }
            }, new int[] { 0x102, 0x266, 0x2E5 });

            packets.Add(0x1D4, p = new Packet(0x1D4, this));
            p.AddValue("Fr torque measured", "Nm", "pf", (bytes) => frTorque =
                           (bytes[5] + ((bytes[6] & 0x1F) << 8) - (512 * (bytes[6] & 0x10))) * 0.25);
            p.AddValue("Rr/Fr torque bias", "%", "pf",
                       (bytes) => Math.Abs(frTorque) > Math.Abs(torque) ? 0 : Math.Abs(torque) / (Math.Abs(frTorque) + Math.Abs(torque)) * 100);

            packets.Add(0x154, p = new Packet(0x154, this));
            p.AddValue("Rr torque measured", "Nm", "p", (bytes) => torque =
                           (bytes[5] + ((bytes[6] & 0x1F) << 8) - (512 * (bytes[6] & 0x10))) * 0.25);
            //p.AddValue("Pedal position A", "%", "",  (bytes) => bytes[2] * 0.4);
            p.AddValue("Watt pedal", "%", "i", (bytes) => bytes[3] * 0.4);

            /*p.AddValue("HP 'measured'", "HP", "p",
             *  (bytes) => (torque * rpm / 9549 * kw_to_hp));*/

            packets.Add(0x2E5, p = new Packet(0x2E5, this));
            p.AddValue("Fr mech power", " kW", "f", (bytes) => fMechPower =
                           ((bytes[2] + ((bytes[3] & 0x7) << 8)) - (512 * (bytes[3] & 0x4))) / 2.0);
            p.AddValue("Fr dissipation", " kW", "f", (bytes) => fDissipation = bytes[1] * 125.0 / 1000.0 - 0.5);
            p.AddValue("Fr input power", " kW", "", (bytes) => fInput        = fMechPower + fDissipation);
            p.AddValue("Fr mech power HP", "HP", "pf", (bytes) => fMechPower * kw_to_hp);
            p.AddValue("Fr stator current", "A", "f", (bytes) => bytes[4] + ((bytes[5] & 0x7) << 8));
            p.AddValue("Fr drive power max", " kW", "b", (bytes) => drivePowerMax =
                           (((bytes[6] & 0x3F) << 5) + ((bytes[5] & 0xF0) >> 3)) + 1);
            p.AddValue("Mech power combined", " kW", "f", (bytes) => combinedMechPower = mechPower + fMechPower,
                       new int[] { 0x266 });
            p.AddValue("HP combined", "HP", "pf", (bytes) => (mechPower + fMechPower) * kw_to_hp,
                       new int[] { 0x266 });
            p.AddValue("Fr efficiency", "%", "e", (bytes) => fDissipation > 0.0 ? Math.Abs(fMechPower) / (Math.Abs(fMechPower) + fDissipation + 0.5) * 100.0 : bytes[100]);
            //p.AddValue("Fr+Rr efficiency", "%", "e", (bytes) => Math.Abs(mechPower+fMechPower) / (Math.Abs(mechPower+fMechPower) + fDissipation + rDissipation) * 100.0);

            packets.Add(0x266, p = new Packet(0x266, this));
            p.AddValue("Rr inverter 12V", "V12", "", (bytes) => bytes[0] / 10.0);
            p.AddValue("Rr mech power", " kW", "", (bytes) => mechPower =
                           ((bytes[2] + ((bytes[3] & 0x7) << 8)) - (512 * (bytes[3] & 0x4))) / 2.0);
            p.AddValue("Rr dissipation", " kW", "", (bytes) => {
                rDissipation         = bytes[1] * 125.0 / 1000.0 - 0.5;
                dissipationUpdated   = true;
                dissipationTimeStamp = Android.OS.SystemClock.ElapsedRealtime();
                return(rDissipation);
            });
            p.AddValue("Rr input power", " kW", "", (bytes) => rInput = mechPower + rDissipation);
            p.AddValue("Propulsion", " kW", "e", (bytes) => rInput + fInput);
            p.AddValue("Rr mech power HP", "HP", "p", (bytes) => mechPower * kw_to_hp);
            p.AddValue("Rr stator current", "A", "", (bytes) => bytes[4] + ((bytes[5] & 0x7) << 8));
            p.AddValue("Rr regen power max", "KW", "b", (bytes) => (bytes[7] * 4) - 200);
            p.AddValue("Rr drive power max", "KW", "b", (bytes) => drivePowerMax =
                           (((bytes[6] & 0x3F) << 5) + ((bytes[5] & 0xF0) >> 3)) + 1);
            p.AddValue("Rr efficiency", "%", "e", (bytes) => rDissipation > 0.0 ? Math.Abs(mechPower) / (Math.Abs(mechPower) + rDissipation + 0.5) * 100.0 : bytes[100]);
            //p.AddValue("Non-propulsive", "kW", "e", (bytes) => power - (rInput+fInput));

            /*p.AddValue("Car efficiency", "%", "e", (bytes) => {
             * //if (Math.Abs(mechPower + fMechPower) > Math.Abs(power))
             *  //return 100.0;
             * //if Math.Abs(mechPower + fMechPower) < Math.Abs(power)
             *  //return 0.0;*/
            /*return Math.Abs(mechPower + fMechPower) / Math.Abs(power) * 100.0;
             * });*/

            packets.Add(0x145, p = new Packet(0x145, this));
            p.AddValue("Fr torque estimate", "Nm", "f",
                       (bytes) => ((bytes[0] + ((bytes[1] & 0xF) << 8)) - (512 * (bytes[1] & 0x8))) / 2);

            packets.Add(0x116, p = new Packet(0x116, this));
            p.AddValue("Rr torque estimate", "Nm", "",
                       (bytes) => ((bytes[0] + ((bytes[1] & 0xF) << 8)) - (512 * (bytes[1] & 0x8))) / 2);
            p.AddValue("Speed", "km|h", "e", (bytes) => speed = ((bytes[2] + ((bytes[3] & 0xF) << 8)) - 500) / 20.0 * miles_to_km);
            p.AddValue("Consumption", "wh|km", "p",
                       (bytes) => power / speed * 1000,
                       new int[] { 0x102 });

            packets.Add(0x306, p = new Packet(0x306, this));
            p.AddValue("Rr coolant inlet", "C", "c", (bytes) => bytes[5] == 0 ? bytes[100] : bytes[5] - 40);
            p.AddValue("Rr inverter PCB", "C", "", (bytes) => bytes[0] - 40);
            p.AddValue("Rr stator", "C", "cp", (bytes) => bytes[2] - 40);
            p.AddValue("Rr DC capacitor", "C", "", (bytes) => bytes[3] - 40);
            p.AddValue("Rr heat sink", "C", "c", (bytes) => bytes[4] - 40);
            p.AddValue("Rr inverter", "C", "c", (bytes) => bytes[1] - 40);

            packets.Add(0x382, p = new Packet(0x382, this));
            p.AddValue("Nominal full pack", "kWh", "br", (bytes) => nominalFullPackEnergy = (bytes[0] + ((bytes[1] & 0x03) << 8)) * 0.1);
            p.AddValue("Nominal remaining", "kWh", "br", (bytes) => nominalRemaining      = ((bytes[1] >> 2) + ((bytes[2] & 0x0F) * 64)) * 0.1);
            p.AddValue("Expected remaining", "kWh", "r", (bytes) => ((bytes[2] >> 4) + ((bytes[3] & 0x3F) * 16)) * 0.1);
            p.AddValue("Ideal remaining", "kWh", "r", (bytes) => ((bytes[3] >> 6) + ((bytes[4] & 0xFF) * 4)) * 0.1);
            p.AddValue("To charge complete", "kWh", "", (bytes) => (bytes[5] + ((bytes[6] & 0x03) << 8)) * 0.1);
            p.AddValue("Energy buffer", "kWh", "br", (bytes) => buffer = ((bytes[6] >> 2) + ((bytes[7] & 0x03) * 64)) * 0.1);
            //p.AddValue("SOC nominal", "%", "br", (bytes) => nominalRemaining / nominalFullPackEnergy * 100.0);
            p.AddValue("SOC", "%", "br", (bytes) => soc = (nominalRemaining - buffer) / (nominalFullPackEnergy - buffer) * 100.0);
            p.AddValue("Usable full pack", "kWh", "br", (bytes) => (nominalFullPackEnergy - buffer));
            p.AddValue("Usable remaining", "kWh", "br", (bytes) => (nominalRemaining - buffer));

            packets.Add(0x302, p = new Packet(0x302, this));
            //p.AddValue("SOC Min", "%", "br", (bytes) => (bytes[0] + ((bytes[1] & 0x3) << 8)) / 10.0);
            //p.AddValue("SOC UI", "%", "br", (bytes) => ((bytes[1] >> 2) + ((bytes[2] & 0xF) << 6)) / 10.0);

            p.AddValue("DC Charge total", "kWH", "bs",
                       (bytes) => {
                if (bytes[2] >> 4 == 0)
                {
                    dcChargeTotal =
                        (bytes[4] +
                         (bytes[5] << 8) +
                         (bytes[6] << 16) +
                         (bytes[7] << 24)) / 1000.0;
                    if (mainActivity.currentTab.trip.dcChargeStart == 0)
                    {
                        mainActivity.currentTab.trip.dcChargeStart = dcChargeTotal;
                    }
                    dcCharge = dcChargeTotal - mainActivity.currentTab.trip.dcChargeStart;
                    return(dcChargeTotal);
                }
                else
                {
                    return(bytes[100]);
                }
            });

            p.AddValue("AC Charge total", "kWH", "bs",
                       (bytes) => {
                if (bytes[2] >> 4 == 1)
                {
                    acChargeTotal =
                        (bytes[4] +
                         (bytes[5] << 8) +
                         (bytes[6] << 16) +
                         (bytes[7] << 24)) / 1000.0;
                    if (mainActivity.currentTab.trip.acChargeStart == 0)
                    {
                        mainActivity.currentTab.trip.acChargeStart = acChargeTotal;
                    }
                    acCharge = acChargeTotal - mainActivity.currentTab.trip.acChargeStart;
                    return(acChargeTotal);
                }
                else
                {
                    return(bytes[100]);
                }
            });
            p.AddValue("DC Charge", "kWh", "ti",
                       (bytes) => dcChargeTotal - mainActivity.currentTab.trip.dcChargeStart);
            p.AddValue("AC Charge", "kWh", "ti",
                       (bytes) => acChargeTotal - mainActivity.currentTab.trip.acChargeStart);

            packets.Add(0x3D2, p = new Packet(0x3D2, this));
            p.AddValue("Charge total", "kWH", "bs",
                       (bytes) => {
                chargeTotal =
                    (bytes[0] +
                     (bytes[1] << 8) +
                     (bytes[2] << 16) +
                     (bytes[3] << 24)) / 1000.0;
                if (mainActivity.currentTab.trip.chargeStart == 0)
                {
                    mainActivity.currentTab.trip.chargeStart = chargeTotal;
                }
                charge = chargeTotal - mainActivity.currentTab.trip.chargeStart;
                return(chargeTotal);
            });

            p.AddValue("Discharge total", "kWH", "b",
                       (bytes) => {
                dischargeTotal =
                    (bytes[4] +
                     (bytes[5] << 8) +
                     (bytes[6] << 16) +
                     (bytes[7] << 24)) / 1000.0;
                if (mainActivity.currentTab.trip.dischargeStart == 0)
                {
                    mainActivity.currentTab.trip.dischargeStart = dischargeTotal;
                }
                discharge = dischargeTotal - mainActivity.currentTab.trip.dischargeStart;
                return(dischargeTotal);
            });
            p.AddValue("Regenerated", "kWh", "tr",
                       (bytes) => regen = charge - acCharge - dcCharge);
            p.AddValue("Energy", "kWh", "tr",
                       (bytes) => energy = discharge - regen);
            p.AddValue("Discharge", "kWh", "r",
                       (bytes) => discharge);
            p.AddValue("Charge", "kWh", "r",
                       (bytes) => charge);
            p.AddValue("Regen total", "kWH", "b",
                       (bytes) => regenTotal = chargeTotal - acChargeTotal - dcChargeTotal,
                       new int[] { 0x302 });
            p.AddValue("Regen %", "% ", "tr",
                       (bytes) => energy > 0 ? regen / discharge * 100 : bytes[100]);//,
            //new int[] { 0x302 });

            p.AddValue("Discharge cycles", "x", "b",
                       (bytes) => nominalFullPackEnergy > 0 ? dischargeTotal / nominalFullPackEnergy : bytes[100],
                       new int[] { 0x382 });
            p.AddValue("Charge cycles", "x", "b",
                       (bytes) => nominalFullPackEnergy > 0 ? chargeTotal / nominalFullPackEnergy : bytes[100],
                       new int[] { 0x382 });

            packets.Add(0x562, p = new Packet(0x562, this));
            p.AddValue("Odometer", "Km", "b",
                       (bytes) => odometer = (bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24)) / 1000.0 * miles_to_km);
            p.AddValue("Distance", "km", "tsr",
                       (bytes) => {
                if (mainActivity.currentTab.trip.odometerStart == 0)
                {
                    mainActivity.currentTab.trip.odometerStart = odometer;
                }
                return(tripDistance = odometer - mainActivity.currentTab.trip.odometerStart);
            });
            p.AddValue("Trip consumption", "wh|km", "tr",
                       (bytes) => tripDistance > 0 ? energy / tripDistance * 1000 : bytes[100],
                       new int[] { 0x3D2 });

            /*p.AddValue("Lifetime consumption", "wh/km", "bt",
             *  (bytes) => odometer > 0 ? dischargeTotal / odometer * 1000 : bytes[100]);*/

            packets.Add(0x115, p = new Packet(0x115, this));
            p.AddValue("Fr motor RPM", "RPM", "",
                       (bytes) => frpm = (bytes[4] + (bytes[5] << 8)) - (512 * (bytes[5] & 0x80)));
            // 0x115 --- DIS_motorRPM = (data[4] + (data[5]<<8)) - (512 * (data[5]&0x80));

            packets.Add(0x106, p = new Packet(0x106, this));
            p.AddValue("Rr motor RPM", "RPM", "",
                       (bytes) => rrpm = (bytes[4] + (bytes[5] << 8)) - (512 * (bytes[5] & 0x80)));

            packets.Add(0x232, p = new Packet(0x232, this));
            p.AddValue("BMS max discharge", "KW", "b", (bytes) => (bytes[2] + (bytes[3] << 8)) / 100.0);
            p.AddValue("BMS max charge", "KW", "b", (bytes) => (bytes[0] + (bytes[1] << 8)) / 100.0);

            packets.Add(0x168, p = new Packet(0x168, this));
            p.AddValue("Brake pedal", "%", "",
                       (bytes) => (bytes[0] + (bytes[1] << 8)) - 3239);

            packets.Add(0x00E, p = new Packet(0x00E, this));
            p.AddValue("Steering angle", "deg", "",
                       (bytes) => (((bytes[0] << 8) + bytes[1] - 8200.0) / 10.0));

            packets.Add(0x338, p = new Packet(0x338, this));
            p.AddValue("Rated range", "km", "br",
                       (bytes) => (bytes[0] + (bytes[1] << 8)) * miles_to_km);
            p.AddValue("Typical range", "km", "br",
                       (bytes) => (bytes[2] + (bytes[3] << 8)) * miles_to_km);
            p.AddValue("Full rated range", "km", "br",
                       (bytes) => (bytes[0] + (bytes[1] << 8)) * miles_to_km / (soc == 0.0 ? 100.0 : soc) * 100.0);
            p.AddValue("Full typical range", "km", "br",
                       (bytes) => (bytes[2] + (bytes[3] << 8)) * miles_to_km / (soc == 0.0 ? 100.0 : soc) * 100.0);



            packets.Add(0x6F2, p = new Packet(0x6F2, this));
            p.AddValue("Last cell block updated", "xb", "", (bytes) => {
                Int64 data = BitConverter.ToInt64(bytes, 0);
                if (bytes[0] < 24)
                {
                    int cell = 0;
                    for (int i = 0; i < 4; i++)
                    {
                        UpdateItem("Cell " + (cell = ((bytes[0]) * 4 + i + 1)).ToString().PadLeft(2) + " voltage"
                                   , "zVC"
                                   , "z"
                                   , (bytes[0]) * 4 + i + 2000
                                   , ((data >> ((14 * i) + 8)) & 0x3FFF) * 0.000305
                                   , 0x6F2);
                    }
                    if (cell > numCells)
                    {
                        numCells = cell;
                    }
                }
                else
                {
                    for (int i = 0; i < 4; i++)
                    {
                        UpdateItem("Cell " + ((bytes[0] - 24) * 4 + i + 1).ToString().PadLeft(2) + " temp"
                                   , "zCC"
                                   , "c"
                                   , ((bytes[0] - 24) * 4 + i) * 4 + 3 + 2000
                                   , ((Int16)(((data >> ((14 * i) + 6)) & 0xFFFC)) * 0.0122 / 4.0)
                                   , 0x6F2);
                    }
                }

                return(bytes[0]);
            });

            // these are a bit stupid, but they are placeholders for the filters to be generated correctly.
            p.AddValue("Cell temp min", "C", "bz", null, null, 1001);
            p.AddValue("Cell temp avg", "C", "bcpz", null, null, 1002);
            p.AddValue("Cell temp max", "C", "bz", null, null, 1003);
            p.AddValue("Cell temp diff", "Cd", "bz", null, null, 1004);
            p.AddValue("Cell min", "Vc", "bz", null, null, 1005);
            p.AddValue("Cell avg", "Vc", "bprzx", null, null, 1006);
            p.AddValue("Cell max", "Vc", "bz", null, null, 1007);
            p.AddValue("Cell diff", "Vcd", "bzx", null, null, 1008);
            for (int i = 1; i <= 96; i++)
            {
                p.AddValue("Cell " + i.ToString().PadLeft(2) + " voltage"
                           , "zVC"
                           , "zx"
                           , null
                           , null
                           , i + 2000);
            }
            for (int i = 1; i <= 32; i++)
            {
                p.AddValue("Cell " + i.ToString().PadLeft(2) + " temp"
                           , "zCC"
                           , "zc"
                           , null
                           , null
                           , i * 3 + 2000);
            }


            /*packets.Add(0x222, p = new Packet(0x222, this));
             * p.AddValue("Charge rate", "??", "e",
             * (bytes) => (bytes[0] + (bytes[1] << 8)) / 100.0);
             * p.AddValue("Charger volt", "V", "e",
             * (bytes) => (bytes[2] + (bytes[3] << 8)) / 100.0);*/


            packets.Add(0x2A8, p = new Packet(0x2A8, this));
            p.AddValue("Front left", "WRPM", "",
                       (bytes) => fl = (bytes[4] + (bytes[3] << 8)) * 0.7371875 / 9.73);
            p.AddValue("Front right", "WRPM", "",
                       (bytes) => fr = (bytes[6] + (bytes[5] << 8)) * 0.7371875 / 9.73);
            p.AddValue("Front drive ratio", ":1", "",
                       (bytes) => frpm > 1000 ? frpm / ((fl + fr) / 2) : bytes[100],
                       new int[] { 0x115 });


            packets.Add(0x288, p = new Packet(0x288, this));
            p.AddValue("Rear left", "WRPM", "",
                       (bytes) => rl = (bytes[4] + (bytes[3] << 8)) * 0.7371875 / 9.73);
            p.AddValue("Rear right", "WRPM", "",
                       (bytes) => rr = (bytes[7] + (bytes[6] << 8)) * 0.7371875 / 9.73);
            p.AddValue("Rear drive ratio", ":1", "",
                       (bytes) => rrpm > 1000 ? rrpm / ((rl + rr) / 2) : bytes[100],
                       new int[] { 0x106 });

            packets.Add(0x318, p = new Packet(0x318, this));
            p.AddValue("Outside temp", " C", "h",
                       (bytes) => (bytes[0] / 2.0 - 40));
            p.AddValue("Outside temp filtered", " C", "",
                       (bytes) => (bytes[1] / 2.0 - 40));
            p.AddValue("Inside temp", " C", "h",
                       (bytes) => (bytes[2] / 2.0 - 40));
            p.AddValue("A/C air temp", " C", "h",
                       (bytes) => (bytes[4] / 2.0 - 40));
            //318 - temperaturer. 0, 1, 2, 4:  / 2 - 40 = C



            packets.Add(0x3F8, p = new Packet(0x3F8, this));
            p.AddValue("Floor vent L", " C", "h",
                       (bytes) => ((bytes[4] + (bytes[5] << 8)) / 10.0) - 40);
            p.AddValue("Floor vent R", " C", "h",
                       (bytes) => ((bytes[6] + (bytes[7] << 8)) / 10.0) - 40);
            p.AddValue("Mid vent L", " C", "h",
                       (bytes) => ((bytes[0] + (bytes[1] << 8)) / 10.0) - 40);
            p.AddValue("Mid vent R", " C", "h",
                       (bytes) => ((bytes[2] + (bytes[3] << 8)) / 10.0) - 40);
            //3F8 - as int. tror dette er 4 tempavlesninger evt innblÄstemperatur, F / 10->C

            /*packets.Add(0x388, p = new Packet(0x388, this));
             * p.AddValue("Floor L", " C", "h",
             * (bytes) => (bytes[1] / 2.0 - 20));
             * p.AddValue("Floor R", " C", "h",
             * (bytes) => (bytes[0] / 2.0 - 20));
             * p.AddValue("Temp 1", " C", "h",
             * (bytes) => (bytes[2] / 2.0 - 20));
             * p.AddValue("Temp 2", " C", "h",
             * (bytes) => (bytes[3] / 2.0 - 20));
             * p.AddValue("Temp 3", " C", "h",
             * (bytes) => (bytes[4] / 2.0 - 20));
             * p.AddValue("Temp 4", " C", "h",
             * (bytes) => (bytes[5] / 2.0 - 20));*/
            //388 - temperaturer!0 - 1: / 4 = C, 2,3,4,5: / 2 - 40 = C

            packets.Add(0x308, p = new Packet(0x308, this));
            p.AddValue("Louver 1", "b", "h",
                       (bytes) => bytes[0] > 0 ? bytes[0] : bytes[100]);
            p.AddValue("Louver 2", "b", "h",
                       (bytes) => bytes[1] > 0 ? bytes[1] : bytes[100]);
            p.AddValue("Louver 3", "b", "h",
                       (bytes) => bytes[2] > 0 ? bytes[2] : bytes[100]);
            p.AddValue("Louver 4", "b", "h",
                       (bytes) => bytes[3] > 0 ? bytes[3] : bytes[100]);
            p.AddValue("Louver 5", "b", "h",
                       (bytes) => bytes[4] > 0 ? bytes[4] : bytes[100]);
            p.AddValue("Louver 6", "b", "h",
                       (bytes) => bytes[5] > 0 ? bytes[5] : bytes[100]);
            p.AddValue("Louver 7", "b", "h",
                       (bytes) => bytes[6] > 0 ? bytes[6] : bytes[100]);
            p.AddValue("Louver 8", "b", "h",
                       (bytes) => bytes[7] > 0 ? bytes[7] : bytes[100]);
            //388 - temperaturer!0 - 1: / 4 = C, 2,3,4,5: / 2 - 40 = C

            packets.Add(0x2AA, p = new Packet(0x2AA, this));
            p.AddValue("HVAC floor", "0", "h",
                       (bytes) => {
                var set1 = bytes[2] & 0x07;
                feet     = false;
                seat     = false;
                win      = false;
                switch (set1)
                {
                case 1:
                    seat = true;
                    break;

                case 2:
                    feet = true;
                    seat = true;
                    break;

                case 3:
                    feet = true;
                    break;

                case 4:
                    feet = true;
                    win  = true;
                    break;

                case 5:
                    win = true;
                    break;

                case 6:
                    feet = true;
                    seat = true;
                    win  = true;
                    break;

                case 7:
                    seat = true;
                    win  = true;
                    break;
                }
                return(feet ? 1 : 0);
            });
            p.AddValue("HVAC mid", "0", "h",
                       (bytes) => seat ? 1 : 0);
            p.AddValue("HVAC window", "0", "h",
                       (bytes) => win ? 1 : 0);

            /*p.AddValue("HVAC recycle", "0", "eh",
             *  (bytes) => {
             *    return (bytes[3] & 0x10) >> 4;
             *  });
             * p.AddValue("HVAC recycle2", "0", "eh",
             *  (bytes) => {
             *    return (bytes[3] & 0x8) >> 3;
             *  });*/
            p.AddValue("HVAC A/C", "0", "h",
                       (bytes) => {
                var set3 = bytes[4] & 0x01;
                return(set3);
            });
            p.AddValue("HVAC on/off", "0", "h",
                       (bytes) =>
                       (bytes[3] & 0x10) >> 4 == 0 ? 1 : 0);

            p.AddValue("HVAC fan speed", "X", "h",
                       (bytes) => (bytes[2] & 0xf0) >> 4);

            p.AddValue("HVAC temp left", " C", "h",
                       (bytes) => bytes[0] / 2.0);
            p.AddValue("HVAC temp right", " C", "h",
                       (bytes) => bytes[1] / 2.0);
        }