• Register
    • Login
    • Search
    • Recent
    • Tags
    • Popular
    1. Home
    2. MattFox

    Please Note This forum exists for community support for the Mango product family and the Radix IoT Platform. Although Radix IoT employees participate in this forum from time to time, there is no guarantee of a response to anything posted here, nor can Radix IoT, LLC guarantee the accuracy of any information expressed or conveyed. Specific project questions from customers with active support contracts are asked to send requests to support@radixiot.com.

    Radix IoT Website Mango 3 Documentation Website Mango 4 Documentation Website
    • Profile
    • Following 1
    • Followers 11
    • Topics 58
    • Posts 1149
    • Best 77
    • Controversial 0
    • Groups 0

    MattFox

    @MattFox

    Electronics Technologist, Automation Engineer, Programmer, tinkerer, and in some cases miracle worker, as in it's a miracle that it works (haha)

    Need a mango consultant? I do contract dashboard work.

    84
    Reputation
    2213
    Profile views
    1149
    Posts
    11
    Followers
    1
    Following
    Joined Last Online
    Website foxes.link Location New Zealand

    MattFox Unfollow Follow

    Best posts made by MattFox

    • RE: Publisher don't listen

      @Jérôme-Girard if you're using a non root user you'll need to configure linux to allow non root users to use sub 1024 ports.
      namely
      sudo sysctl net.ipv4.ip_unprivileged_port_start=502
      and
      sudo nano /etc/sysctl.conf
      then set
      net.ipv4.ip_forward=1

      That should enable you to fire the publisher up on 502

      Fox

      posted in Mango General
      MattFox
      MattFox
    • RE: Excel Reports Sent at Wrong Time

      This will be a handy post for those who are running cloud hosted solutions as there isn't a mangoES settings section for custom installs.

      posted in User help
      MattFox
      MattFox
    • RE: Charts for past 12 hours

      Correct, look under API Docs -> Components -> maSerialChart
      They have a complete guideline of what attribute tags to use to apply values. You will want the series-x-values where the x is the chart number.
      The Chart update rate is affected by the maPointValues components which you can also read about. The realtime="true" attribute will allow datapoints to update into the chart at the rate they change or are updated.
      If you don't want to average, make sure you set the rollup type to NONE. Alternatively, use the rollup-interval to set the interval duration between datapoints in the chart, note that changing from the live updating rate may mean you;ll need to average.

      posted in Dashboard Designer & Custom AngularJS Pages
      MattFox
      MattFox
    • RE: Pop up window (face plate)

      OK!
      As a starting point to work from. I'll work with you so you learn how it all works as you go...
      (All code is placed in the /opt/mango/overrides/web... directories)
      /modules/mangoUI/web/dev/components/settingsModal.js

      define(['angular', 'require'], function(angular, require) {
      'use strict';
      
      /* Date: 1/9/18
      	   Author: Matt "Fox" Fox
      	   Desc: component written to launch a modal dialog with either a built in template or just spit out a list of points to save one by one.
      	*/
      	settingsModalController.$inject = ['$mdDialog','maJsonStore'];
      		function settingsModalController ($mdDialog,maJsonStore)
      		{
      			var ctrl = this;
      			ctrl.mdDialog = $mdDialog;
      			ctrl.parsedOptions=[];
      			
      			ctrl.values={};
      			
      			this.$onInit = function()
      			{
      				if(this.points && !angular.isArray(this.points))
      				{
      					console.log('Error: points field not an array of point objects');
      				}
      				if(this.pointOptions && !angular.isArray(this.pointOptions) )
      				{
      					console.log('Error: points options field not an array');
      				}
      				ctrl.clickOffClose = ctrl.clickOffClose===true?true:false;
      				
      				
      				
      				
      				
      			};
      			
      			this.$onChanges = function(e)
      			{
      				console.log(e);
      				checkPoints();
      			}
      			
      			function checkPoints()
      			{
      				ctrl.parsedOptions=[];
      				if(ctrl.points && angular.isArray(ctrl.points))
      				{
      					if(ctrl.pointOptions && angular.isArray(ctrl.pointOptions) )
      					{
      						var ptName,ptDesc;
      						for(var i=0; i<ctrl.points.length; i++)
      						{
      							ptName=ctrl.points[ i ].name;
      							ptDesc='';
      							if(ctrl.pointOptions[ i ] && ctrl.pointOptions[ i ].hasOwnProperty('name') )
      							{
      								ptName = ctrl.pointOptions[ i ].name;
      							}
      							if(ctrl.pointOptions[ i ] && ctrl.pointOptions[ i ].hasOwnProperty('desc') )
      							{
      								ptDesc = ctrl.pointOptions[ i ].desc;
      							}
      							ctrl.parsedOptions.push( {name:ptName,desc:ptDesc} );
      						}
      					}
      					else
      					{
      						for(var i=0; i<ctrl.points.length; i++)
      						{
      							ptName=ctrl.points[ i ].name;
      							ptDesc='';
      						
      							ctrl.parsedOptions.push( {name:ptName,desc:ptDesc} );
      						}
      					}
      				}
      				else if(ctrl.pointOptions && angular.isArray(ctrl.pointOptions) && !ctrl.points )
      				{	
      					var ptId, ptName, ptDesc;
      					for(var i=0; i<ctrl.pointOptions.length; i++)
      					{
      						if(ctrl.pointOptions[ i ] && !ctrl.pointOptions[ i ].hasOwnProperty('name') )
      						{
      							// ctrl.pointOptions=[];
      							ctrl.parsedOptions=[];
      							return;
      						}
      						ptId=i;
      						ptDesc='';
      						ptName=ctrl.pointOptions[ i ].name;
      						if(ctrl.pointOptions[ i ] && ctrl.pointOptions[ i ].hasOwnProperty('id') )
      						{
      							ptId = ctrl.pointOptions[ i ].id;
      						}
      						if(ctrl.pointOptions[ i ] && ctrl.pointOptions[ i ].hasOwnProperty('desc') )
      						{
      							ptDesc = ctrl.pointOptions[ i ].desc;
      						}						
      						ctrl.parsedOptions.push( {id:ptId,name:ptName,desc:ptDesc} );
      					}
      				}
      			}
      			
      			/* DIALOG CODE */
      			ctrl.showDialogue = function(ev) {
      			   // ctrl.updateModals();
      			   if( ctrl.altTemplateUrl!==undefined  )
      			   {
      				   try
      				   {
      					   
      					ctrl.mdDialog.show({
      					  templateUrl:ctrl.altTemplateUrl, 
      					  parent: angular.element(document.body),
      					  targetEvent: ev,
      					  clickOutsideToClose: ctrl.clickOffClose
      					}).then(
      						function()
      						{
      							console.log('function if closed/hidden manually'); 
      						},
      						function()
      						{
      							console.log('function to fire if closed from clicking outside/cancelled');
      						}
      					);
      				   }catch(e){console.warn(e);}
      			   }
      			   else
      			   {
      				   try{
      					   
      					ctrl.mdDialog.show({
      					  contentElement: '#settingsModal',
      					  parent: angular.element(document.body),
      					  targetEvent: ev,
      					  clickOutsideToClose: ctrl.clickOffClose
      					}).then(
      						function()
      						{
      							console.log('function if closed manually'); 
      						},
      						function()
      						{
      							console.log('function to fire if closed from clicking outside/cancelled');
      						}
      					);
      				   }catch(e){console.warn(e);}
      				}
      			};
      			
      			ctrl.hideDialogue = function() {
      				ctrl.mdDialog.hide();
      			};
      
      			ctrl.cancel = function() {	  
      				ctrl.mdDialog.cancel();
      			};
      			/* DIALOG CODE END*/
      			
      			/* JSON Store Stuff: */
      			ctrl.saveToStore = function(data=null)
      			{
      				
      				var item = maJsonStore.newItem( ctrl.jsonStore.toLowerCase() );
      				item.jsonData = data===null ? ctrl.values : data;
      				item.readPermission= "user";
      				item.editPermission= "user"; //can change this at your leisure or we can break it out into a setting...
      				
      				var r = item.$save();
      				
      				if(r.status)
      				{ 
      				}
      				else
      				{ 
      				}
      			};
      			
      			ctrl.loadFromStore = function()
      			{
      				maJsonStore.get({xid: ctrl.jsonStore.toLowerCase() }).$promise.then(function(item) {
      				ctrl.values = item.jsonData;
      			});
      			/* JSON Store Stuff END: */
      			};
      			
      			/* Virtual Point Save stuff */
      			ctrl.saveToPoint = function()
      			{
      				if(ctrl.savePoint===undefined)
      				{
      					alert('Point to save to not set as attribute!');
      					return;
      				}
      				ctrl.savePoint.setValue( JSON.stringify(ctrl.values) );
      			};	
      			
      			/* Virtual Point Save stuff END */
      	}
      		
      		
      		
      	
      	var settingsModal={
      		bindings:{
      			jsonStore: '<?', 		//Store name to save settings to
      			savePoint: '<?', 		//alternatively, save my JSON to a virtual pt, MUST BE TYPE DATAPOINT...
      			altTemplateUrl:'=?', 	//If we want to use the popup to show something else: use this template...
      			modalTitle:'=?', 		//seemed like a good idea at the time... will only work if altTemplateUrl is empty
      			clickOffClose:'=?', 	//set to true to click outside modal to shut it
      			pointOptions:'<?', 	//Alternate names for points,  (object array) [{id:'',name:'',desc:''}], id used for key name if using json/virtual point save
      			points: '<?',			 //Array of points to set individually
      		},
      		controller: settingsModalController,
      		templateUrl:'/modules/mangoUI/web/dev/views/modalDemo.html'
      	};
      	
      	return settingsModal;
      });
      

      and the template markup to add:
      /modules/mangoUI/web/dev/views/modalDemo.html

       <ma-button  hue="hue-2" palette="primary" label="Open Dialogue" ng-click="$ctrl.showDialogue()" raised="true"></ma-button>
       <div ng-if="!$ctrl.altTemplateUrl" style="visibility: hidden">
          <div class="md-dialog-container" id="settingsModal">
            <md-dialog layout-padding>
      			<md-dialog-actions><md-button ng-if="!$ctrl.clickOffClose" ng-click="$ctrl.cancel()" class="md-primary">x</md-button></md-dialog-actions>
       <div ng-if="!$ctrl.points && !$ctrl.parsedOptions">
      		Custom markup needed!
      		</div>
      		 
             <h1 ng-if="$ctrl.modalTitle!==undefined">{{$ctrl.modalTitle}}</h1> <small ng-if="$ctrl.clickOffClose">Click outside dialogue to close</small>
      		
      		<div ng-if="$ctrl.points">
      		<md-input-container ng-repeat="(index,point) in $ctrl.points" style="display:block;width:100%;">
      	   {{$ctrl.parsedOptions[index].name}}
      			<br/>	
      	   {{$ctrl.parsedOptions[index].desc}}
      			<br/>	
      			
      		<span style="display:inline-block;">Current Value: <ma-point-value style="display:inline-block;" point="point" ></ma-point-value></span>
      		 <ma-set-point-value  point="point" show-button="true"></ma-set-point-value> 
      		</md-input-container>
      		</div>
      		<div ng-if="!$ctrl.points && $ctrl.parsedOptions.length>0">
      			<md-input-container ng-repeat="input in $ctrl.parsedOptions">
      			{{input.name}}<br/>{{input.desc}}
      			<input ng-model="$ctrl.values[ input.id ]"/>
      			</md-input-container>
      		</div>
      		
            </md-dialog>
          </div>
        </div>
      

      More than happy to help you work through this (it will be a bit of a crash course)
      I've not added the buttons on the template for saving using a virtual point or JSON store, but that can be added quite easily.
      Basically you can generate a popup which will populate with inputs that can be named, given an ID and a description if necessary. This can also be used to override point names to more 'user friendly' equivalents for your users if they're filling out items in the dialogue box.

      inside the userModule.js directory (assuming that's the name of the file you've followed from Jared's tutorial) you'll want this structure:

      /userModule.js
      /components/settingsModal.js
      /views/modalDemo.html

      and inside userModule.js you'll want this:

      define(['angular', 'require','./components/settingsModal.js'], function(angular, require,settingsModal) {
          'use strict';
       var userModule = angular.module('userModule', ['maUiApp']);
      try {	
      	userModule.component('settingsModal', settingsModal);
      }
      catch(e){console.log(e);}	
          return userModule;
      });
      

      Markup in the dashboard page builder will be:

      <settings-modal point-options="[{id:'test',name:'input 1',desc:'input test 1'},{id:'test2',name:'input 2',desc:'input test 2'}]" points="vp" click-off-close="true"></settings-modal>
      

      See if you can get the popup showing for you first. I'm using 3.4.1 so I'm not completely sure if this will be affected in your version of mangoUI...

      Fox

      EDIT: sorry I'll be sure to add some more code comments later...
      New Edit: added data to save to store as an optional argument

      posted in Wishlist
      MattFox
      MattFox
    • RE: Advice on moving components / controllers / services out of userModule

      Fixed, just mixed up the full path of the file with the userModule URL after closer inspection.

      posted in User help
      MattFox
      MattFox
    • RE: Removing last edited page from the default load

      @phillip-weeks
      are your referring to this post you made last year?
      https://forum.infiniteautomation.com/topic/3077/is-it-possible-to-roll-back-a-latest-ui-update-to-the-previous-version/5

      Looks like Jared mentions something about wiping your cache....

      0_1557969475663_cb114c78-dc94-48e4-8372-81248930ef06-image.png

      posted in Dashboard Designer & Custom AngularJS Pages
      MattFox
      MattFox
    • RE: MQTT Publish

      @Rav-Panchalingam from what I've seen. Mango can only subscribe. Not publish. They've never implemented a publisher despite requests dating back 3+ years

      posted in User help
      MattFox
      MattFox
    • RE: How to change date format on chart?
      /ui/docs/ng-mango/ma-serial-chart
      

      Read the docs under API Docs -> components -> maSerialChart

      there's an attribute you can set to alter the format:

      time-format
      (optional)
      string	
      The moment.js time format to be used in displaying timestamps on the X axis.
      

      Put your moment.js format in there and that will let you set the format to 'DD mm' etc

      posted in Mango Automation general Discussion
      MattFox
      MattFox
    • RE: Big News! Infinite Automation, Mango and BitBox USA have merged to create Radix IoT

      Congrats for making it through the other side. Would be good to have a chat considering currently our system doesn't scale well with mango. If the merger can help with this that'd be grand. It's the bottleneck in our system and I'm struggling to streamline what we have.

      Fox

      posted in Announcements
      MattFox
      MattFox
    • RE: How I use chart from AmCharts

      Seeing how amcharts have kindly given the source code on how to do this; you would need to get your required data points. Use a get point values query for the respective data points required. Use a "1 YEARS" AVERAGE/SUM rollup if you have multiple values for a given year depending on what you are calculating.

      Next write an angular component/directive which creates a time-line pie instance, and format the incoming point values with their points into the
      "[ 'year' : [ { 'value' : 'datapointValue1', 'label': 'point 1 name'}, { 'value' : 'datapointValue2, 'label': 'point 2 name'} ]" format.

      The supplied source code on amcharts will enable you to create animate effects etc.
      To get yourself started, I suggest you look at Jared's pie chart directive then build on top of that in a new file, just be sure to change the name from pieChart to timePieChart so you don't cause any problems with the native UI components.

      Hope that helps!

      Fox

      posted in Dashboard Designer & Custom AngularJS Pages
      MattFox
      MattFox

    Latest posts made by MattFox

    • RE: Data point with non-linear output?

      @tomatopi a lot of people have asked the same thing, nobody knows... I'm of the opinion they will stick with angular. I'm more of a Vue3 person myself....

      Thanks for the logic, can show you something basic to get you started

      Fox

      posted in Mango General
      MattFox
      MattFox
    • RE: Data point with non-linear output?

      @tomatopi stick to raw, especially if you find you need to amend your values again later.

      You'll need to understand angularJS if you want to extend the dashboard for your own benefit.
      Alternatively if you're in the position where you can share your conversion factors, learn about the mango userModule and read about angularJS directives and we can work together to enable you to create your first custom component.

      -Fox

      posted in Mango General
      MattFox
      MattFox
    • RE: Legacy Tank Level Indicators and Status LEDS in new Mango?

      @dan Those tank level indicators look like VU meters...
      Seems it can definitely be coded (frontend, not from the java code generated in the back).
      Hit me up if you wish for further assistance

      Fox

      posted in Dashboard Designer & Custom AngularJS Pages
      MattFox
      MattFox
    • RE: Data point with non-linear output?

      @tomatopi are you trying to display the modified values or do you need to store them??
      If you only want to display, I'd advise writing an angularJS directive to manipulate the data for you then passing that into your chart.

      The alternative is storing the data in a separate database then using mango to parse the DB and use the data that has been modified in this other database...

      Find a way to take this data and store it in a csv file, then write a java class (there are examples here) to parse and modify the raw data into the format required.

      Means you'd natively fix your data before pushing it into mango..

      Fox

      posted in Mango General
      MattFox
      MattFox
    • RE: Mango V4.4.2 Serial Data Source Escaping CR LF Characters Wrong?

      @Turbo if you're using linux, you can use socat to do a serial to tcp connection instead.

      Fox

      posted in User help
      MattFox
      MattFox
    • RE: Help needed (newbie) Accessing an API with a Scripting Datasource Mango 4.4.2

      After working with Ray, I threw together a repository with a script that allows either CLI or mango to fire data into mango 4 by stating the xid, user, pass/bearer, host, and port.

      https://github.com/riagominota/degreedays-mangoclient
      This repo however, can be rejigged to work with other third party api sources you like.

      After that, Ray has kindly sent back his config here for future people to use as a guideline for their own work:

      MS Windows environment NodeJS Mango Client Steps for DegreeDays.net NodeJS API:
      On your PC where Mango is installed:
      Install Node.js 18.12.1 (x64) which includes npm:
      https://nodejs.org/dist/v18.12.1/node-v18.12.1-x64.msi


      The Following are Optional for NodeJS programming development environment purposes:

      	Install Visual Studio Code (optional):
       			https://code.visualstudio.com/sha/download?build=stable&os=win32-x64-user
       
      	Install WSL Subsystem for Linux (optional):
       		https://learn.microsoft.com/en-us/windows/wsl/
       		From Windows PowerShell: wsl --install
       
      	Install WSL Visual Studio code Extension (optional):
       			https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl
      
      • Create a new Empty Project folder, then from a command prompt CD into that folder,
        (Important!) Run: "npm init" without quotes to initialize a new nodeJS package.json,
        and then run the following npm installs from the command line in that folder:
         npm install mango-client
         
         npm install luxon
         
         npm install form-data
         
         npm install node-fetch@2
      
      • In the same project folder, create a new file called "ddMango.js", and place the following code in the file, then save:
      //Begin Code *********************************************************
      /**
       * Date: 2/1/23 - DD/MM/YYYY
       * Author: Matt 'Fox' Fox
       * Description: Logs into mango, and fires data into system for a given datapoint.
       * Call the file by running `node ddMango.js --xid=... --user=... --pass=...` - ideal for evented calls
       * Optionally, host and port can be added if not localhost or 8080 etc.
       */
      const process = require('process');
      const  MangoClient  = require('mango-client');
      const { DateTime } = require("luxon");
      
      const REQ_ARG_LIST = ["xid","user","pass"];
      const ARG_LIST = ["bearer","host","port"]; // A bearer token can be used instead if desired
      
      let ARG_VALS = {xid:"",user:"",pass:"",bearer:"",host:"localhost",port:8080};
      
      // Args are provided in the form of --xid=... --user=... --pass=...
      //This means we can call this from mango itself
      const hasArgVal = key => {
          // Return true if the key exists and a value is defined
          for( let str of process.argv)
              if ( str.includes( `--${ key }` ) ) return true;
      
          return false;
      }
      
      const getArgVal = arg => {
      
          let val = null;
          if( hasArgVal(arg) ){
            val = process.argv.find( element => element.startsWith( `--${arg}=` ) );
            val = val.replace( `--${arg}=` , '' );
          }
      
          // Return null if the key does not exist and a value is not defined
          return val;
      }
      
       // Ensure all args are properly populated before firing the script...
      const VerifyArgs = () =>{
          for(let k in ARG_LIST)
          {
              if( hasArgVal(ARG_LIST[k]) )
              {
                  ARG_VALS[ ARG_LIST[k] ] = getArgVal(ARG_LIST[k]);
              }
          }
      
          for(let k in REQ_ARG_LIST)
          {
              if( hasArgVal(REQ_ARG_LIST[k]) )
              {
                  ARG_VALS[ REQ_ARG_LIST[k] ] = getArgVal(REQ_ARG_LIST[k]);
              }
              else
              {
                  if(REQ_ARG_LIST[k]=="pass" && ARG_VALS.bearer!==""){continue;}
      
                  console.log(`ERROR: argument --${REQ_ARG_LIST[k]} has not been passed as an argument to this script. Exiting.`);
                  process.exit(1);
              }
          }
      }
      
      VerifyArgs();
      
      // Prepare new Mango Client Instance to receive ARG_VALS
      const MangoClientInstance = new MangoClient({...ARG_VALS});
      if(ARG_VALS.bearer!=="")
          MangoClientInstance.setBearerAuthentication(ARG_VALS.user,ARG_VALS.bearer);
      else
          MangoClientInstance.setBasicAuthentication(ARG_VALS.user,ARG_VALS.pass);
      
          // Save point values Array of:
          //  {xid,value,dataType,timestamp,annotation}
          // to Mango Client instance.
      const insertValuesV4 = (xid,values) => {
          return MangoClientInstance.restRequest({
              path: `/rest/latest/point-values/${xid}`,
              method: 'PUT',
              data: values
          }).then(response => {
              return response.data;
          });
      }
      
      // Get degree days data here... then let's send it to mango.
      // You will need to parse the date into a timestamp using luxon
      const crypto = require('crypto'), // built-in module, no need to npm install
        fetch = require('node-fetch'); // install with npm install node-fetch@2
      
      //Test DegreeDays API account info for Cape Cod MA, will need your own DegreeDays.net Account
      const accountKey = 'test-test-test';
      const securityKey = 'test-test-test-test-test-test-test-test-test-test-test-test-test';
      const endpoint = 'http://apiv1.degreedays.net/json';
      
      const location = {
        type: 'PostalCodeLocation',
       //Cape Cod MA Test API zip code
        postalCode: '02532',
        countryCode: 'US'
      };
      
      const breakdown = {
        type: 'DailyBreakdown',
        period: {
          type: 'LatestValuesPeriod',
          numberOfValues: 1 //This number (days) can be increased for a larger set of historic data.
        }
      };
      const locationDataRequest = {
        type: 'LocationDataRequest',
        location: location,
        dataSpecs: {
          myHDD: {
            type: 'DatedDataSpec',
            calculation: {
              type: 'HeatingDegreeDaysCalculation',
              baseTemperature: {
                unit: 'F',
                value: 65
              }
            },
            breakdown: breakdown
          }
        }
      };
      const fullRequest = {
        securityInfo: {
          endpoint: endpoint,
          accountKey: accountKey,
          timestamp: new Date().toISOString(),
          random: Buffer.from(crypto.randomBytes(12)).toString('hex')
        },
        request: locationDataRequest
      };
      const fullRequestJson = JSON.stringify(fullRequest);
      
      const signatureBytes = 
        crypto.createHmac('sha256', securityKey).update(fullRequestJson).digest();
      function base64urlEncode(unencoded) {
        return Buffer.from(unencoded).toString('base64')
          .replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
      }
      
      fetch(endpoint, {
          method: 'POST',
          body: 'request_encoding=base64url' +
            '&signature_method=HmacSHA256' +
            '&signature_encoding=base64url' +
            '&encoded_request=' + base64urlEncode(fullRequestJson) +
            '&encoded_signature=' + base64urlEncode(signatureBytes),
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
          }
        })
        .then(response => {
          if (!response.ok) {
            throw new Error(response.status + ': ' + response.statusText);
          }
          return response.json();
        })
        .catch(err => console.error('Problem connecting to API:', err))
        .then(parsedObject => handleResponse(parsedObject))
        .catch(err => console.error('Problem handling API response:', err));
      
      function handleResponse(fullResponse) {
        const response = fullResponse.response;
        if (response.type === 'Failure') {
          console.log('Request failure:', response);
        } else {
       //   console.log('Station ID: ', response.stationId);
          const hddData = response.dataSets.myHDD;
          if (hddData.type === 'Failure') {
            console.log('Failure for HDD DataSet:', hddData);
          } else {
             // console.log('HDD:');
            for (var v of hddData.values) {
             // let degreeDaysValue = console.log(v.d + ': ' + v.v);
            let degreeDaysValue = v.v;
            let degreeDaysDate = DateTime.fromISO(v.d).toUnixInteger()*1000
             // console.log(degreeDaysValue);
             // console.log(degreeDaysDate);
      
      // FIRE!! (Insert Values into Mango XID)
             insertValuesV4(ARG_VALS.xid,
              {
                  value:degreeDaysValue,
                  dataType:"NUMERIC",
                  timestamp:degreeDaysDate
              }
      );
            }
          }
        }
      }
      // End Code ************************************************
      
      • Now log into Mango as admin, and create a new user - being sure to set the role to match read and set point permissions of the datapoint you want to write to. In my case it was 'DegreeDaysUser'

      Create a new Scripting Data Source ie. "DegreeDays.net API", and Paste the following script in the data source:

      souce: https://forum.mango-os.com/topic/3818/invoke-shell-commands-from-a-scripting-environment

      //Begin Code
      com.serotonin.m2m2.rt.maint.work.ProcessWorkItem.queueProcess("C:/Program Files/nodejs/node C:/NodeJSApps/degreedaysAPI/ddMango.js --xid=DP_DegreeDaysHDD --user=dduser --pass=ddpassword --host=localhost --port=8080", 10);
      //End Code
      

      ***PLEASE NOTE: the full paths to the node binary (C:/Program Files/nodejs/node.exe) as well as the full path to the ddMango.js script e.g. C:/NodeJSApps/degreedaysAPI/ddMango.js Make sure these match your specific folder path environment. Also change the ddMango.js script --xid,--user,--pass,--host,--port argument values to match your environment.

      • On this same Scripting data source, select "Polling" and then set the polling to use the desired schedule (In this case CRON Pattern: 0 30 0 ? * * * was used)
        The CRON pattern above represents running this script everyday at 12:30AM. Validate script, then save.

      • (Important!) Now re-open this same Scripting Data Source you just created, and then create a new DataPoint with the following XID:
        DP_DegreeDaysHDD (or whatever suits your naming conventions...)

      • Change the new datapoint to Data Type "Numeric", and Logging Type: "All Data", also fill out the point name, and Variable name field with names of your choosing, then assign the "DegreeDaysUser" role to have Read, edit, set permissions, then save the point.


      To Test to see if the script works:

      • Click "Validate" for the script again on this scripting data source, you should see the newly added DP_DegreeDaysHDD datapoint update with values.
      • Also check the next day after 12:30AM to see if CRON fired the script to update the DP_DegreeDaysHDD value as well.

      Thanks for your contribution Ray!

      -Fox

      posted in Scripting general Discussion
      MattFox
      MattFox
    • RE: Uploading an Excel for Reports

      @Curtis my only suggestion is looking at ma.log to see if there's any more details than a generic message.
      You can find it in [mango_home]/logs/ma.log

      Fox

      posted in How-To
      MattFox
      MattFox
    • RE: Mango V4.4.2 Serial Data Source Escaping CR LF Characters Wrong?

      @Turbo I found a bug with the serial data source combined with the virtual serial socket meant that after a reboot, you had to save the serial data source in order for it to restart and properly fire up the virtual serial socket.
      I wonder if you're encountering a similar issue here....

      Fox

      posted in User help
      MattFox
      MattFox
    • RE: Mango V4.4.2 Serial Data Source Escaping CR LF Characters Wrong?

      @Turbo let's see if web.archive.org can help us here. If it can, I might be able to publish any relevant info here in the forum.

      Fox

      posted in User help
      MattFox
      MattFox
    • RE: Help needed (newbie) Accessing an API with a Scripting Datasource Mango 4.4.2

      @raylatbasix Jammy, I'll be here

      posted in Scripting general Discussion
      MattFox
      MattFox