ni-c / heimcontrol.js

Home-Automation with node.js and Raspberry PI
MIT License
1.41k stars 297 forks source link

How to create plugins? #16

Closed sebastienvg closed 10 years ago

sebastienvg commented 10 years ago

After hours of patience I finally got this working, yes there's plenty of thing to be added or enhanced, however heimcontrol.js is the most advanced home automation raspberryPI project I have been playing with, congrats for the hard work and for sharing with us.

I wish I can help to extend features, such as lockitron (they provide an API), mochad (x10) and maybe NFC or RFID authentication (yes I am one of those implanted geek, both hands). Can anyone help me understand how adding plugins works? Say I already have a some nodes apps how can I add it? I couldn't really find any hint even after trying to duplicate a plugin and looking at it. Is there any documentation?

Thanks for the great project!

ConnorRoberts commented 10 years ago

AFAIK there is no documentation, but I can briefly explain the different aspects (I wrote the RGB Light control plugin :))

There is no special declaration of plugins, just pop it into the plugin folder and some magic somewhere will include it without anything else.

Files:

socket.emit('(pluginname)', data);

* views/
     - item.jade 
          - This is the HTML content (though in Jade format) of each item on the main page, it should be similarly formatted to this, but replace the colourwheel div with whatever you want :)

each item in items div.plugin-container.rgblights p.description

     - settings.jade
        - This is the HTML content (again, Jade) for the settings page, for the format - just copy and modify from another plugin, too big to put here really :)
* index.js 
     - This file contains the actual code to be executed when the button or whatever is clicked (see https://github.com/ni-c/heimcontrol.js/blob/master/plugins/rgblights/index.js) In this file the data from the view file (public/js/rgblights/index.js) is received through sockets in this snippet:

app.get('sockets').on('connection', function(socket) { socket.on('(pluginname)', function(data) { that.picker(data); // This then runs the picker function within this file passing the data from the view with it }); });

The picker function then actually does the work, such as turning on the light in this example (and should be kept here for the sake of the API)

This ended up much messier than intended, markdown bullets are so horrible to use! ;) I hope this helps a little, if you have any more specific questions then reply or email me (should be on my profile *I think*)

If I've missed anything important or made any mistakes feel free to correct me :P @ni-c 

(Off-topic, hows the implant? What do you use it for at the moment? :P Seems kinda cool to have :D)
sebastienvg commented 10 years ago

Thank you so much for the detailed explanations, I should be able to get going with that, and I am sure I'll have more questions and I'll try to be as precise as possible.

Implants are great, I use them to open my car's doors, trunk or hood (there's 3 RFID readers, one in the front of the car, one in the driver's mirror and one in the tail light), I also have reader of front of the house and the garage door, the goal is get as far as possible from keys. I use the NFC implant to unlock my phone or computers when I am not home (the phone locks itself whenever the screen goes off and I am not around my home wireless network or connect to my car computer).

Hopefully I'll be able to take advantage of heimcontrol and implement what I've done so far.

Thanks again for your help!

ConnorRoberts commented 10 years ago

No problem, using it myself and would like to see more plugins ;)

The implant seems incredibly cool, did you implant it yourself? I would totally get one but I just couldn't do it myself and oddly enough, no-where seems to do it ;)

sebastienvg commented 10 years ago

I bought mine of dangerousthings.com and to implant them I had to call many tattoo places, they will implant anything, horns, screws and bolts but no implants, until I found http://waycooloakville.com/ they did a great job, real surgeons!

I am sure you'll find a cool piercer that knows what he is going.

ConnorRoberts commented 10 years ago

I bet I'd have even less luck getting someone to do it being 16 :(

It would be really cool, but I think I'll be waiting a few years first!

thebadger412 commented 10 years ago

@ConnorRoberts I was looking through your RGB plugin and I can't figure out how item.jade and settings.jade works.

For example in settings.jade you have a plugin-container div which seems to have all of the data needed to render the plugin.. However at the bottom you have a plugin-container #template.. could you please go through how this template interacts and what it is for?

I am close to getting something working I think but just need someone to sort of document it all a little bit better.. thanks for starting the documentation off.. ps (I cannot believe yo are 16.. you totally own me at programming.. pps Wahey Manchester!

ConnorRoberts commented 10 years ago

I'll write up some proper documentation some time if no-one else is, but for now, i'll just try to explain better ;) (I apologise if some parts are stating the obvious, but it'll make it easier for people looking at this in the future :))

Firstly, item.jade - this is the content of the main page where the controls are

each item in items 

Loops through the items in the plugin, for example if you have 2 RGB light setups, then this will loop for each item

  div.plugin-container.rgblights
    p.description

General HTML crap

      - if (item.description)
        span.muted RGB Light:  
        strong #{item.description} 
      - else 
        span.muted RGB Light: 
        strong PINS #{item.pins.R}, #{item.pins.G}, #{item.pins.B} 

If there's a description set for the item, output that. Otherwise just fill the space with what pins it should be / is connected to

    div(class="colourwheel", id="wheel-#{item._id}" data-value="#FFFFFF", data-id="#{item._id}", data-socket="rgblights")    

Just the colour wheel element HTML with a unique name set (just uses the items id. data-id stores the ID which will be read from elsewhere

And onto the settings.jade file, this is probably where its getting confusing (It took me way too long to figure this out myself ;))

div.container
  h1 Settings
  h2 RGB Lights
  p Setup: 
    a(href="http://ni-c.github.com/heimcontrol.js/plugins/rgblights.html") http://ni-c.github.com/heimcontrol.js/plugins/rgblights.html

More HTML crap

  - if (typeof(success) !== 'undefined')
    div.alert.alert-success.fade.in
      button.close(type="button", data-dismiss="alert") ×
      =success

Shows a nice little box if things work

  div.row16
    form(action="/settings/rgblights", method="post")
      div#rgblights
        each item, i in items
          div.plugin-container.rgblights.settings(id="#{item._id}")
            button.btn.btn-danger.pull-right.delete(type="button",data-delete="#{item._id}")
              i.icon-trash.icon-large
              |  Delete
            label(for="description") Description (optional):
            input(type="text", name="data[#{i}][description]", value="#{item.description}")
            label(for="pin") Arduino PINs:
            input.rgb.uppercase(type="text", name="data[#{i}][pins][R]", placeholder="R", maxlength="2", required="1", value="#{item.pins.R}", pattern="^A[0-9]|[0-9]{1,2}$")
            input.rgb.uppercase(type="text", name="data[#{i}][pins][G]", placeholder="G", maxlength="2", required="1", value="#{item.pins.G}", pattern="^A[0-9]|[0-9]{1,2}$")
            input.rgb.uppercase(type="text", name="data[#{i}][pins][B]", placeholder="B", maxlength="2", required="1", value="#{item.pins.B}", pattern="^A[0-9]|[0-9]{1,2}$")

            input(type="hidden", name="data[#{i}][_id]", value="#{item._id}")
            input(type="hidden", name="data[#{i}][status]", value="#{item.status}")

This chunk is outputted for every item that is saved into the database, this form is to make changes to pre-existing ones


      p.rgblights.buttons
        button.btn.btn-info.add(data-target="rgblights", type="button") 
          i.icon-plus.icon-large
          |  Add

        button.btn.btn-success.save(type="submit") 
          i.icon-save.icon-large
          |  Save

input(type="hidden", name="iterator", id="iterator", value=(items.length))

a couple of buttons

the add button causes a chunk of main.js (outside of the plugin) to be fired which basically duplicates the html below every time you press it

the save button submits the actual

element


div.plugin-container.rgblights.settings#template(style="display: none;")
  button.btn.btn-danger.pull-right.delete(type="button")
    i.icon-trash.icon-large
    |  Delete
  label(for="description") Description (optional):
  input(type="text", name="data[%i%][description]", placeholder="Living room")
  label(for="pin") Arduino PIN:
  input.rgb.uppercase(type="text", name="data[%i%][pins][R]", placeholder="R", maxlength="2", required="1", pattern="^A[0-9]|[0-9]{1,2}$")
  input.rgb.uppercase(type="text", name="data[%i%][pins][G]", placeholder="G", maxlength="2", required="1", pattern="^A[0-9]|[0-9]{1,2}$")
  input.rgb.uppercase(type="text", name="data[%i%][pins][B]", placeholder="B", maxlength="2", required="1", pattern="^A[0-9]|[0-9]{1,2}$")

  input(type="hidden", name="data[%i%][status]", value="0")

This bit of HTML is almost the same as the piece above, this is the form which is shown when you press the "add" button, the main differences is the #{i} becomes %i% which took me way too long to notice when I tried being clever any copying and pasting!

TL;DR - The plugin-container #template element is practically the same as plugin-container div and is duplicated every time you press the add button :)

And woo Manchester ;D