Giter Club home page Giter Club logo

the-notifier-awakens's Introduction

NotiStar

It has been a dark time for Notifier, but there is still hope. A new era has arrived. The previous Notifier major version was controlled by Dart jQuery and Vanilla JavaScript. In an attempt to rescue Notifier, a few of dotkom's people went together and created a React App. This app was fused with Notifier, giving it strength and power one could not even imagine was possible in 2014...

Table of contents

Infoscreen setup

More information can be found in the infoscreen folder.

3 steps:

  1. Equipment: Get a screen, Raspberry PI (with NOOBS or Raspian), HDMI cable, keyboard and mouse.
  2. Internet: Connect Raspberry PI to a WiFi or ethernet cable.
  3. Run bash <(curl -Ls http://gg.gg/notiwall-cli) install {infoscreen-id} {secret} in a terminal (Ctrl+Alt+T) and follow the instructions.

The Notiwall API (Notiwatch)

The Notiwall API keeps track of infoscreens and sensor data. This is what you can get from the API:

GET

The part in brackets: {...}, is encoded in base64 (such that secrets does not mess with the url).

  • /api/v1 => List all infoscreen names.
  • /api/v1/{infoscreen_name:secret} => Get infoscreen info.
    • This includes IP and sensor data if any of them are present.
  • /api/v1/{infoscreen_name:secret}/ip => Get IP only (if it exists).
  • /api/v1/{infoscreen_name}/sensors => Get sensor data (if it exists).
  • /api/v1/{infoscreen_name}/screenshot.png => Get last screenshot of infoscreen (if it exists).
    Example:
    Infoscreen for online-kontoret

POST

The part in brackets: {...}, is encoded in base64 (such that secrets does not mess with the url).

  • /api/v1/sensors/{infoscreen_name:secret} => Update a sensor (this also triggers websockets to all clients listening for this sensor).
  • /api/v1/ip/{infoscreen_name:secret} => Update IP of infoscreen.
  • /api/v1/screenshot/{infoscreen_name:secret} => Upload and update screenshot to infoscreen.

WebSockets (socket.io)

We currently use socket.io in both client and server. It is probably easiest if you use that too if you want to listen for sensor data (like the office door or coffee machine). This is how it can be used:

import io from 'socket.io-client';

// Url format: wss://notiwall.online.ntnu.no/{machine}/{sensor_name}
socket = io('wss://notiwall.online.ntnu.no/online-door/office-door');
socket.on('status', sensor => {
  console.log(`Sensor ${sensor.name} is updated to: ${sensor.value}`);
});

Development

yarn # Install dependencies
yarn start # Start development

# Setup own CORS server allowing requests from anywhere
yarn cors-server

How it works

1. Fetch data

image

First the app starts a fetch schedule described by ./src/defaults/apis.js. A fetch sequence can look like this (more details in ./src/defaults/apis.js):

  ...
  github: {
    interval: 60, // Fetch every 60th second
    print: true, // (Optional) Print results into the browser console
    url: `https://api.github.com/users/{{users.*}}/repos`, // Create multiple URLs from users. Currently 'dotkom' is the only user
    users: {
      dotkom: 'dotkom',
    },
  },
  ...

The code above will generate a URL that can be accessed using github.users.dotkom. The fetch call is not called unless a component asks for github.users.dotkom. More on how components can use this in the next section.

Complete example
  ...
  linjeforening: {
    interval: 1000, // Fetch every 1000th second
    delay: 10, // Start fetch after 10 seconds
    url: 'https://events.com/api/v1/events?start=[[now.datetime]]', // Any URL, can use datestamps from current time

    // Random objects can be used for permutations in requests/urls. See the body below or the url from previous example
    somedata: {
      business: '210',
      social: '121',
      open: '111',
    }

    // Tranform API data using STJS (read more about transforms in the examples below)
    transform: {
      events: {
        '{{#each results}}': {
          startDate: '{{event_start}}',
          endDate: '{{event_end}}',
          title: '{{title}}',
          image: 'https://events.com[[{{link}}#HTML:#eventImage > img@src]]', // [[What to scrape:selector@attribute]]
        },
      },
    }
    scrape: ['events.*.image'], // Tell app to scrape images from the transform above
    cache: true, // You probably want to cache the images (src attribute values) above

    method: 'POST', // All request methods are allowed
    request: { body: '{"type":"{{somedata.*}}"' }, // Modify the request with headers and stuff. Can use permuations on body
    body: { type: '{{somedata.*}}' }, // Same as above, just simpler if only body is needed
    cors: true, // Some sites does not allow CORS, but enabling this will allow everything

    print: true, // You probably want to output requests when debugging
    printTransform: true, // Output after transform of data is also useful
  },

The full example can be used through linjeforening.somedata.business, linjeforening.somedata.social and linjeforening.somedata.open.

More examples:

Plain REST example
  ...
  githubDotKom: {
    interval: 60,
    url: `https://api.github.com/users/dotkom`,
  },
  ...

Available through: githubDotkom

Transform the API output before it is passed to components

We use STJS to transform the input from the API data. This is for example useful when using multiple event APIs and you want a spesific structure on the data passed to the components.

You can read about STJS transforms here: https://selecttransform.github.io/site/transform.html

  ...
  githubDotKom: {
    interval: 60,
    url: `https://api.github.com/users/dotkom`,
+   transform: {
+     image: '{{avatar_url}}',
+     description: '{{bio}}',
+     url: '{{html_url}}',
+   }
  },
  ...

Output:

{
  image: 'https://avatars0.githubusercontent.com/u/693951?v=4',
  description: 'Drifts- og utviklingskomiteen i Online, linjeforeningen for Informatikk ved NTNU.',
  url: 'https://github.com/dotkom',
}
Dealing with RSS feeds

When dealing with other formats than JSON, you can specify this by appending these to the URL:

  • {URL}#GET (JSON => JSON)
  • {URL}#POST[#body] (JSON => JSON)
  • {URL}#RSS (XML => JSON)
  • {URL}#HTML[:query-selector[(at)attribute]] (HTML => JSON)
  • {URL}#HTML2JSON[:query-selector[(at)attribute]] (HTML => JSON)
  • {URL}#HTML2HTML[:query-selector[(at)attribute]] (HTML => outerHTML)
  • {URL}#HTML2TEXT[:query-selector[(at)attribute]] (HTML => innerText)
  • {URL}#TEXT (Plain text => Plain text)
  • More info on this in ./src/defaults/apis.js.
  ...
  redditArticles: {
    interval: 86400,
    url: `https://www.reddit.com/.rss#RSS`, // <-- Appended #RSS
    transform: {
      articles: {
        '{{#each feed.entry}}': {
          title: '{{title[0]}}',
          date: '{{updated[0]}}',
          link: '{{link[0].$.href}}',
          author: '{{author[0].name}}',
          image: 'https://www.redditstatic.com/new-icon.png',
        },
      },
    },
  },
  ...

Output:

{
  articles: [
    {
      title: 'Witcher III (My last comic)',
      date: '1970-01-01T23:41:07+00:00',
      link: 'https://www.reddit.com/r/gaming/comments/ajdml1/witcher_iii_my_last_comic/',
      author: '/u/SrGrafo',
      image: 'https://www.redditstatic.com/new-icon.png',
    },
    ...
  ]
}
Dealing with RSS feeds (And scraping images from each feed element)
  ...
  vgArticles: {
    interval: 86400,
    url: `https://www.vg.no/rss/feed/?categories=1068&limit=10#RSS`,
+   cache: true,
+   scrape: ['articles.*.author'],
    transform: {
      articles: {
        '{{#each rss.channel[0].item}}': {
          title: '{{title[0]}}',
          date: '{{pubDate[0]}}',
          link: '{{link[0]}}',
+         author: '[[{{link[0]}}#HTML:article > div > ul > li]]',
          image: '{{image[0]}}',
        },
      },
    },
  },
  ...

Output:

{
  articles: [
    {
      title: 'Tittel på artikkel',
      date: 'Thu, 01 Jan 1970 23:01:00 +0100',
      link: 'http://www.vg.no/nyheter/innenriks/...',
      author: 'Ola Normann',
      image: 'https://imbo.vgc.no/users/vgno/images/451f60dc338...',
    },
    ...
  ]
}
Scrape HTML content from any website

A lot of websites does not have a JSON API and it is therefore handy to be able to fetch spesific data from an element in a HTML document.

The syntax for retrieving HTML is like this:

  • {URL}#HTML[:query-selector[(at)attribute]] (HTML => HTML)
  ...
  komplett: {
    interval: 60,
+    url: `https://www.komplett.no/product/823822/tv-lyd-bilde/hodetelefoner/hodetelefoner/bose-qc-25-hodetelefon-apple#HTML:.product-main-info-stockstatus > div > div > span`,
+    cors: true,
  },
  ...

Output:

{
  state: '20+ stk. på lager.',
}

2. Manage components

image

To use the data from the API's you need a component to pass the data into. Components for each affiliation (linjeforening) is described in ./src/defaults/affiliations.js. Each affiliation can have a set of components:

  ...
  dotkom: { // Select affiliation in `./src/defaults/settings.js`
    name: 'DotKom',
    components: [

      // Shorthand existing template
+     'Clock',

      // Extended existing template
+     {
+       template: 'GitHub',
+       apis: {
+         repos: 'github.users.dotkom',
+       },
+     },

      // Shorthand self made template (with pipe transformation and loop)
+     '<h1>{{affiliation|upper|back  er kult}}!{{#each [1,3,3,7]}}{{this}}{{#end}}</h1>',
      // Output: <h1>DOTKOM er kult!1337</h1>

      // Extended self made template
+     {
+       template: '<h1>Time: {{seconds|time HH:mm:ss}}</h1>',
+       apis: {
+         seconds: 'seconds', // Listen to changes in seconds
+       },
+     },
      // Output: <h1>Time: 16:42:00</h1>

    ],
  },
  ...

Components can have two different formats:

Using existing template (preferred)

This option is most preferred as you can choose between a large variety of templates written in React. This exposes the true power of the web with animations and data handling.

Example of `Events` template usage
[
  ...
  {
    template: 'Events',
    apis: {
      events: '{{affiliation}}Events:events', // {{affiliation}} is injected from settings
    },
  },
  ...
]
Example of `Bus` template usage
[
  ...
  {
    template: 'Bus',
    name: '{{bus:glos}}', // The bus name displayed on the screen. If none have been chosen, then bus name will be set to 'glos' as default
    count: '{{busCount}}', // Control amount of departures from settings
    apis: {
      fromCity: 'tarbus.stops.{{bus:glos}}.fromCity:departures',
      toCity: 'tarbus.stops.{{bus:glos}}.toCity:departures',
    },
  },
  ...
]

Self made template

You can also create your own components with data from any API.

Example
[
  ...
  {
    template: '<h1>Klokke: {{variable|time HH:mm:ss}}</h1>', // Using pipe syntax to format the time from milliseconds to HH:mm:ss
    apis: {
      variable: 'time', // Makes it possible to listen for time changes
    },
  },
  ...
]

Shorthand (Will not update regularly as it does not listen to a time API):

[
  ...
  '<h1>Klokke: {{clock|time HH:mm}}</h1>',
  ...
]
Example with template loops
[
  ...
  {
    template: `<h1>Bus table for {{bus:glos|translate}}</h1>
{{#each departures}}
<div>{{number}} - {{name}} ({{registeredTime|time HH:mm}})</div>
{{#end}}`,
    apis: {
      departures: 'tarbus.stops.{{bus:glos}}.fromCity:departures',
    },
  },
  ...
]

3. Manage layout

image

Most times you want to specify a layout. This can either be fixed using plain CSS or the inbuilt app grid systemᵗᵐ. Here is an example from both:

The Notifier Grid Systemᵗᵐ (preferred)
  ...
  dotkom: {
    name: 'DotKom',
+   layouts: {
+     0: ['Clock', 'GitHub'], // From 0 to 511px
+     512: ['Clock GitHub'], // From 512px and out
+   },
    components: [
      'Clock',
      {
        template: 'GitHub',
        ...
      },
    ],
  },
  ...
Equivalent example with affiliation CSS
  ...
  dotkom: {
    name: 'DotKom',
+   layouts: {}, // Deactivate layout generator
+   css: `
+   .Components {
+     grid-template: "Clock" "GitHub" / 1fr;
+   }
+   @media (min-width: 512px) {
+     .Components {
+       grid-template: "Clock GitHub" / 1fr 1fr;
+     }
+   }`,
    components: [
      'Clock',
      {
        template: 'GitHub',
        ...
      },
    ],
  },
  ...

If no layouts are specified, the layout will be autogenerated based on the component order and screen width.

More advanced layouts

image

You can achieve everything with plain CSS, but The Notifier Grid Systemᵗᵐ is probably good enough for most layouts.

The Notifier Grid Systemᵗᵐ advanced
  ...
  dotkom: {
    name: 'DotKom',
    layouts: {

      0: [
        'Clock',
+       'Clock-2', // Identified through the dash (-)
+       'ThirdClock', // Identified through "id" property
+       'GitHub \ 1' // Make GitHub component use 1fr of the space (aka. rest of the screen, if any)
      ],

      512: [

        // Justify colums using "/" at the start of a layout
+       '/ 1fr 400px', // 1 fraction for first column and 400px for second column

        'Clock Clock-2',
+       'ThirdClock GitHub / 1fr', // Using 1fr, auto or minmax(...) is allowed
+       'GitHub / 2', // Use 2fr
      ],

      // You can also just use plain CSS Grid template syntax
+     1024: `
+       "Clock Clock-2 ThirdClock"
+       "GitHub GitHub GitHub" 2fr
+       / 1fr 1fr 1fr
+     `,

      2048: [
+       // Use "|" to fill area between two columns with 1fr
+       '/ 400px 400px | 400px',

        // Adding a value to the filled area is also possible: '/ 400px 400px |100px 400px'

        'Clock Clock-2 ThirdClock', // Adds a "." (space) with size of 1fr between Clock-2 and ThirdClock
        'GitHub GitHub GitHub', // Stretch GitHub over 1fr, not adding a blank space

        // Actually generated: 'Clock Clock-2 . ThirdClock'
        //                     'GitHub GitHub GitHub GitHub'
        //                     '/ 400px 400px 1fr 400px'
      ]

    },
    components: [
      'Clock',
+     'Clock-2', // Use dash (-) to use multiple components of the same type
      {
        template: 'Clock',
+       id: 'ThirdClock', // Use "id" to set layout name
      },
      {
        template: 'GitHub',
        ...
      },
    ],
  },
  ...

Real layout example

Example of the Online affiliation using four different layouts at specified widths:

notifier-gif

Example of the Online affiliation
  ...
  online: {
    name: 'Online',
+    layouts: {
+      0: ['Logo', 'Clock', 'Office', 'Bus', 'Bus2', 'Events'],
+      720: [
+        '/ 200px . . 200px',
+        'Logo Logo Logo',
+        'Office Office Clock Clock',
+        '. . . .',
+        'Bus Bus Bus2 Bus2',
+        '. . . .',
+        'Events Events Events .',
+      ],
+      1400: [
+        'Logo Logo Office Clock',
+        '. . . .',
+        '. . Events Events / 1',
+        'Bus Bus Events Events',
+        'Bus2 Bus2 Events Events',
+        '. . Events Events / 1',
+        '. . . .',
+      ],
+      2000: [
+        'Logo Logo Office Clock',
+        '. . .',
+        'Bus Bus2 Events Events',
+        '. . .',
+      ],
+    },
    components: [...],
  },
  ...

the-notifier-awakens's People

Contributors

annemoss avatar chr1stian avatar dependabot[bot] avatar monikahm avatar niklasmh avatar plusk avatar xtrah avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

the-notifier-awakens's Issues

Create a cache system

Such that data from APIs that has consistent data can be saved to a cache for faster access and fewer requests. Cache items should also have a time to last option.

Make Bus component

Make it look like this:
image

And this if there is two tables:
image

  • Support 2 buses
  • Create timetable

Fix grid-layout

  • Make it possible to change grid using affiliation settings.

Make Events component

  • Make multiple templates that can be changed through the settings.
  • Find an API that can be used to get events from OW4.

Some suggestions on designs are:
image

image

image

image

Deploy production build to AWS S3

Production build is currently deployed built to a local folder on Nansen.
Since this is just static files, it could easily be deployed to an S3 bucket on AWS to reduce strain on Nansen.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.