• Categories
    • Recent
    • Tags
    • Popular
    • Register
    • Login

    custom widget: send command to node

    Scheduled Pinned Locked Moved Dashboard
    21 Posts 4 Posters 2.8k Views 2 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • E Offline
      elgarfo
      last edited by

      hi,

      i have read the "widget - sensor custom buttons", "rgb controller", "custom widget" and "custom widget - dynamic images on dashboard" topics but did only get a hint on what to do.

      what i have:
      a custom widget with a nice, touch enabled color picker, which is working. which means:

      • can be displayed on the dashboard
      • and is usable
      • and javascript events are fired (currently logs a rgb hex value to browser console)
      • using: iro.js

      what i want:

      • send the selected rgb hex value to a node

      i saw a mention to create a custom angular controller and plug that into Utilities > HTML additional headers > AngularJS custom controllers.
      can anyone provide an example of a angularjs controller to put there?

      i tried to use Utilities > Scripts like in the example "Custom widget" but the variables defined there do not seem to be accessible in the Templates. at least i was unable to use it to send a value to the node.

      so... i am currently stuck.
      is there any documentation (other than plowing through mycontrollers code) which describes how to add a custom widget which can send a value to a node generated in js? i know its possible to send a value using the Utilities > Scripts but this is rather static, as the script only runs during widget creation. which means it only runs on opening the dashboard and if i change the color-picker by touching/clicking it, i can not push the value to a node.

      jkandasaJ 1 Reply Last reply Reply Quote 0
      • jkandasaJ Offline
        jkandasa @elgarfo
        last edited by

        @elgarfo Yes, It is possible to add your custom controller in Utilities > HTML additional headers > AngularJS custom controllers. can you share your current work, I can extend from there?

        1 Reply Last reply Reply Quote 0
        • E Offline
          elgarfo
          last edited by

          @jkandasa, thanks for helping me out.

          currently i have this as template:

          <style type="text/css">
          .wrap {
            color: #fff;
            background: #171F30;
            margin: 0 auto;
            display: flex;
            flex-direction: row;
            align-items: center;
            justify-content: center;
          }
          </style>
          <div class="wrap">
            <div class="colorPicker"></div>
          </div>
          <script>
          var colorPicker = new iro.ColorPicker(".colorPicker", {
            width: 320,
            height: 320,
            color: {r: 255, g: 0, b: 0},
            anticlockwise: true,
            borderWidth: 1,
            borderColor: "#fff",
          });
          
          colorPicker.on("color:change", function(color) {
            console.log("hex: " + color.hexString);
          });
          </script>
          

          and this for the script:

          var colorSensor = mcApi.uidTag().getByUid("hi-wz-rgb-1").getResource();
          var uuid = mcApi.utils().getUUID();
          

          i tried to copy the "Sensors" controller from github modify it to make for a "Sensors2" controller an pasted that into the "AngularJS custom controllers":

          'use strict';
          
          angular.module('adf.widget.myc-sen-vars2', [])
            .config(function(dashboardProvider){
              dashboardProvider
                .widget('mycSenVars2', {
                  title: 'Sensors2',
                  description: 'Monitor and change sensors state',
                  templateUrl: 'controllers/adf-widgets/adf-myc-sen-vars/view.html?mcv=${mc.gui.version}',
                  controller: 'mycSenVarsController2',
                  controllerAs: 'mycSenVars2',
                  config: {
                    variableIds:[],
                    showIcon: true,
                    itemsPerRow:"2",
                    refreshTime:30,
                  },
                  edit: {
                    templateUrl: 'controllers/adf-widgets/adf-myc-sen-vars/edit.html?mcv=${mc.gui.version}',
                    controller: 'mycSenVarsEditController2',
                    controllerAs: 'mycSenVarsEdit2',
                  }
                });
            })
            .controller('mycSenVarsController2', function($scope, $interval, config, mchelper, $filter, SensorsFactory, TypesFactory, CommonServices){
              var mycSenVars2 = this;
          
              mycSenVars2.showLoading = true;
              mycSenVars2.isSyncing = true;
              mycSenVars2.variables = {};
              $scope.tooltipEnabled = false;
              $scope.hideVariableName= !config.showIcon;
              $scope.cs = CommonServices;
          
              //HVAC heater options - HVAC flow state
              $scope.hvacOptionsFlowState = TypesFactory.getHvacOptionsFlowState();
              //HVAC heater options - HVAC flow mode
              $scope.hvacOptionsFlowMode = TypesFactory.getHvacOptionsFlowMode();
              //HVAC heater options - HVAC fan speed
              $scope.hvacOptionsFanSpeed = TypesFactory.getHvacOptionsFanSpeed();
          
              //Defined variable types list
              $scope.definedVariableTypes = CommonServices.getSensorVariablesKnownList();
          
          
          
            //update rgba color
            $scope.updateRgba = function(variable){
              variable.value = CommonServices.rgba2hex(variable.rgba);
              $scope.updateVariable(variable);
            };
          
              function loadVariables(){
                mycSenVars2.isSyncing = true;
                SensorsFactory.getVariables({'ids':config.variableIds}, function(response){
                    mycSenVars2.variables = response;
                    mycSenVars2.isSyncing = false;
                    if(mycSenVars2.showLoading){
                      mycSenVars2.showLoading = false;
                    }
                });
              };
          
              function updateVariables(){
                if(mycSenVars2.isSyncing){
                  return;
                }else if(config.variableIds.length > 0){
                  loadVariables();
                }
              }
          
              //load variables initially
              loadVariables();
              //updateVariables();
          
              //Update Variable / Send Payload
              $scope.updateVariable = function(variable){
                SensorsFactory.updateVariable(variable, function(){
                  //update Success
                },function(error){
                  displayRestError.display(error);
                });
              };
          
              // refresh every second
              var promise = $interval(updateVariables, config.refreshTime*1000);
          
              // cancel interval on scope destroy
              $scope.$on('$destroy', function(){
                $interval.cancel(promise);
              });
            }).controller('mycSenVarsEditController2', function($scope, $interval, config, mchelper, $filter, TypesFactory, CommonServices){
              var mycSenVarsEdit2 = this;
              mycSenVarsEdit2.cs = CommonServices;
              mycSenVarsEdit2.variables = TypesFactory.getSensorVariables();
            });
          

          but this does not show up at all in the dashboard.

          that's why i am asking for a simple example for a custom angular controller as i am not too familiar with angular yet.

          jkandasaJ 1 Reply Last reply Reply Quote 0
          • jkandasaJ Offline
            jkandasa @elgarfo
            last edited by

            @elgarfo Thanks for the script. Looks like iro.js do not have angular support 😞
            For two way binding, we need angularJS support.

            1 Reply Last reply Reply Quote 0
            • E Offline
              elgarfo
              last edited by

              @jkandasa ok thats a little sad. but this will not stop me from trying 🙂

              lets say i wanted to write a custom widget (lets also assume something simple like an html5 slider/range input)

              template:

              <input id="slider1" type="range" min="0" max="100" />
              <script type="text/javascript">
                $('#slider1').on('change', function(e) {
                  console.log('slider1 value: '  + $(this).val());
                });
              </script>
              

              script:

              var stepperMotor = mcApi.uidTag().getByUid("hi-wz-blinds-1").getResource();
              var uuid = mcApi.utils().getUUID();
              

              as far as i can tell, this is a pretty basic example which does not rely on incompatible external libs. what do i need to add to which section to make this a widget with two-way binding?

              all i am looking for is an example for a custom 2 way widget.

              jkandasaJ 1 Reply Last reply Reply Quote 0
              • jkandasaJ Offline
                jkandasa @elgarfo
                last edited by jkandasa

                @elgarfo I do not know how to do with HTML 5. however I made a two-way widget with the angular slider.

                Steps:

                • create HTML template with sensor variable id
                • create dummy javascript to supply buildings
                • add custom css and js files.
                • add custom controller
                • add dependency module in angular (workaround, I do not find a way to include new modules into the controller creation)

                HTML template:

                <div ng-controller="myCustomWidgetController" ng-init="sVariable.id=${sId};loadVariable();">
                  <rzslider rz-slider-model="sVariable.value" rz-slider-options="slider.options"></rzslider>
                </div>
                

                Dummy JavaScript:

                // dummy script
                

                9de831a0-816c-4f94-8285-6e427c01b480-image.png

                CSS and JS file:

                https://cdnjs.cloudflare.com/ajax/libs/angularjs-slider/6.6.0/rzslider.min.css
                https://cdnjs.cloudflare.com/ajax/libs/angularjs-slider/6.6.0/rzslider.min.js
                

                52843ff4-392c-4de7-8132-887af7ae6b13-image.png

                Custom controller:

                myControllerModule.controller('myCustomWidgetController', function($scope, SensorsFactory){
                    $scope.sVariable = {
                      id: null,
                      value:null
                    };
                  
                    $scope.loadVariable = function(){
                      SensorsFactory.getVariable({id:$scope.sVariable.id}, function(response){
                        if(response.value != undefined){
                          $scope.sVariable.value = parseInt(response.value);
                        }
                      });
                    }
                
                    $scope.slider = {
                      options: {
                        floor: 0,
                        ceil: 100,
                        step: 1,
                        minLimit: 0,
                        maxLimit: 90,
                        onEnd: function() {
                            $scope.sendPayload($scope.sVariable);
                        },
                      }
                  };
                
                    // send payload
                    $scope.sendPayload = function(sVariable){
                      SensorsFactory.updateVariable(sVariable, function(){
                        //update Success
                      },function(error){
                        displayRestError.display(error);
                      });
                    };
                  });
                

                Workaround, add a module on the app level

                File: mycontroller/www/app.js

                rzModule
                

                f53f954f-e081-49d1-a8de-f127ec382950-image.png

                Widget in dashboard

                d933f59c-b34b-4b55-9800-2bfabfbe07a6-image.png

                d67fa588-62c3-415b-ad8b-b3984d9b882b-image.png

                1 Reply Last reply Reply Quote 1
                • E Offline
                  elgarfo
                  last edited by elgarfo

                  @jkandasa your support is outstanding 👍
                  your example works good and i can use it to build a dashboard to my needs. thank you very very much.

                  the reason it took me so long to respond is, that i needed to trace a startup error on my end:
                  [$injector:nomod] Module 'rzModule' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.

                  symptoms:

                  • dashboard not loading correctly ({{ variable }} everywhere, nothing works)
                  • angular throwing error about undefined module
                  • testing with dev-console on firefox angular.module('rzModule'); yielded no error.
                  • rzslider.js was actually loaded

                  cause:

                  • firefox does not support <link rel="import">
                  • the polyfill i pushed to github fb3d0d96 is working but:
                  • too late.

                  so the polyfill for the import-feature is working, as i am able to load the rzModule after the dashboard html loaded completely and angular tried to load.
                  but it is not working fast enough.

                  i am currently trying to find a way to either delay angular startup until all imports are handled or to find a better way to include the /_configurations/additional-headers.html

                  current workaround: put <script src="https://cdnjs.cloudflare.com/ajax/libs/angularjs-slider/6.6.1/rzslider.js"></script> directly into index.html for firefox 😕

                  EDIT:
                  html imports are also deprecated in chrome since v70 and will be removed with v73 which is only two versions from now (v71 as of this writing). see: chrome platform status

                  jkandasaJ 2 Replies Last reply Reply Quote 1
                  • jkandasaJ Offline
                    jkandasa @elgarfo
                    last edited by

                    @elgarfo If we change anything in our current implementation, can fix the issue in both firefox and chrome? Any idea?

                    1 Reply Last reply Reply Quote 0
                    • jkandasaJ Offline
                      jkandasa @elgarfo
                      last edited by

                      @elgarfo Thank you for the PR https://github.com/mycontroller-org/mycontroller/pull/486

                      1 Reply Last reply Reply Quote 0
                      • W Offline
                        wanvo
                        last edited by

                        How can I download the latest SNAPSHOT with this PR?
                        Is this it? Https: //travis-ci.org/mycontroller-org/mycontroller/builds/472109919

                        jkandasaJ 1 Reply Last reply Reply Quote 0
                        • jkandasaJ Offline
                          jkandasa @wanvo
                          last edited by

                          @wanvo Compiled and uploaded in SNAPSHOT location.

                          1 Reply Last reply Reply Quote 1
                          • W Offline
                            wanvo
                            last edited by wanvo

                            @jkandasa Thanx!
                            I checked this widget in Chrome and Firefox, it works great.

                            1 Reply Last reply Reply Quote 1
                            • R Offline
                              RMF69
                              last edited by RMF69

                              I'm just testing out various examples to understand how things work, I'm getting

                              Failed at: ${sId}  [in template "unknown" at line 1, column 69]
                              
                              

                              which I assume is from the first line of the html template, its not finding the angularjs custom controller ? I did modify app.js.

                              I'm pretty sure I've done all of the above to get the slider to appear on dashboard, but I must of missed something. I pasted the angularjs custom controller into the text field in HTML additional headers.

                              Any help 🙂 thanks

                              R 1 Reply Last reply Reply Quote 0
                              • R Offline
                                RMF69 @RMF69
                                last edited by RMF69

                                @RMF69 To myself... the script binding id mistyped {sId:2}... got the camel case wrong. Slider is now visible but I get 400 error... as I don't have the variable set up correctly which will get updated ? I've created a global variable "sVariable" but I don't think this is right. How do I get the value out of the slider when its value changes ?

                                R 1 Reply Last reply Reply Quote 0
                                • R Offline
                                  RMF69 @RMF69
                                  last edited by

                                  @RMF69 Ok looking through code, I understand that its updating a sensor.variabel and that this id is passed in via the script binding {sId:31}... 31 is the id of a sensor S_LIGHT_LEVEL![alt text](Screenshot from 2019-12-28 13-05-08.png
                                  I still get 400 bad requests... and

                                  ERROR [Acme.Utils.ThreadPool(0)-PooledThread: Acme.Serve.Serve$ServeConnection@1415568e] [org.mycontroller.standalone.api.jaxrs.exception.mappers.ApplicationExceptionMapper:42] ApplicationException,
                                  org.jboss.resteasy.spi.ApplicationException: java.lang.NullPointerException
                                  

                                  I'd love to understand what I've done wrong:)

                                  jkandasaJ 1 Reply Last reply Reply Quote 0
                                  • jkandasaJ Offline
                                    jkandasa @RMF69
                                    last edited by

                                    @RMF69 Can you report your MyController version? and if you modified the script please report your script.

                                    R 1 Reply Last reply Reply Quote 0
                                    • R Offline
                                      RMF69 @jkandasa
                                      last edited by

                                      @jkandasa

                                      Thanks, I've got the latest, Version 1.5.0. And I've not changed anything, just the script binding {sId:31} where 31 is the id of a light dimmer virtual sensor... see picture. I took the ID from the url of that page in the picture below.Screenshot_2020-01-02_18-31-18.png

                                      jkandasaJ 1 Reply Last reply Reply Quote 0
                                      • jkandasaJ Offline
                                        jkandasa @RMF69
                                        last edited by jkandasa

                                        @RMF69 Click on the Edit icon on the Level variable and use the id from the URL.

                                        in version 1.5, you no need to hardcode rzModule on mycontroller/www/app.js. We have an option on UI to update angular modules.
                                        3482e3a0-d2f2-4189-9f3f-2f4ae7750d72-image.png

                                        R 1 Reply Last reply Reply Quote 0
                                        • R Offline
                                          RMF69 @jkandasa
                                          last edited by

                                          @jkandasa Thanks for confirming that in 1.5, the hardcoding of rzModule in mycontroller/www/app.js isn't required. However if I remove rzModule then the slider widget isn't displayed.

                                          Still I can't get the example to work...I've got the id from the place you mention, mine is still 31... I've also noticed that the intial value of the sensor->variable isn't populated. I created simple script to update the value

                                          var myImports = new JavaImporter(java.lang, java.util, org.mycontroller.standalone.utils.McUtils);
                                          
                                          with(myImports) {
                                              dimSensor = mcApi.uidTag().getByUid("dimSensor").getResource();
                                              var v = dimSensor.value; //value2/3 etc
                                              dimSensor.setValue(parseInt(v) + 1);
                                              mcApi.sensor().sendPayload(dimSensor);
                                          }
                                          

                                          I insert the following code into "AngularJs custom controllers"

                                          myControllerModule.controller('myCustomWidgetController', function($scope, SensorsFactory){
                                              $scope.sVariable = {
                                                id: null,
                                                value:null
                                              };
                                            
                                              $scope.loadVariable = function(){
                                                SensorsFactory.getVariable({id:$scope.sVariable.id}, function(response){
                                                  if(response.value != undefined){
                                                    $scope.sVariable.value = parseInt(response.value);
                                                  } 
                                                });
                                              }
                                          
                                              $scope.slider = {
                                                options: {
                                                  floor: 0,
                                                  ceil: 100,
                                                  step: 1,
                                                  minLimit: 0,
                                                  maxLimit: 90,
                                                  onEnd: function() {
                                                      $scope.sendPayload($scope.sVariable);
                                                  },
                                                }
                                            };
                                          
                                              // send payload
                                              $scope.sendPayload = function(sVariable){
                                                SensorsFactory.updateVariable(sVariable, function(){
                                                  //update Success
                                                },function(error){
                                                  displayRestError.display(error);
                                                });
                                              };
                                            });
                                          

                                          Which is just the original example above, no changes... it seems to me that the pointer/reference to the variable I want to have connected to the slider is correct or working, both when the value is initialized on the loadVariable and when updated. But I just can't see what I've done wrong.

                                          jkandasaJ 1 Reply Last reply Reply Quote 0
                                          • jkandasaJ Offline
                                            jkandasa @RMF69
                                            last edited by

                                            @RMF69 Do you see any error on the browser console and/or in MyController log file?

                                            R 1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post

                                            0

                                            Online

                                            587

                                            Users

                                            529

                                            Topics

                                            3.4k

                                            Posts
                                            Copyright © 2015-2025 MyController.org | Contributors | Localization