syncsign 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +38 -0
- data/Rakefile +38 -0
- data/lib/syncsign.rb +9 -0
- data/lib/syncsign/hub.rb +25 -0
- data/lib/syncsign/node-display.rb +18 -0
- data/lib/syncsign/node.rb +73 -0
- data/lib/syncsign/service.rb +89 -0
- data/lib/syncsign/template.rb +50 -0
- data/lib/syncsign/version.rb +4 -0
- data/lib/syncsign/widgets.rb +323 -0
- metadata +53 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2ec8ce97ef1f4882420617de3310d74e2d4a3783a4f1922847c706c3eb04b13c
|
4
|
+
data.tar.gz: 0a867fa0debe259b37d347aff38800f9ae589a4e2afde253d69fbe2f2cab4bbe
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fc9cc2483763d6a35cee7d3c9370b2abe6774b5870321ec219ed02c26b61a0d60378c8a38149507bcef9e4cecbb6b88f9a4888e888732d7d47516d6597ab4d3b
|
7
|
+
data.tar.gz: 9bba6acf10f9b33d4a9d11065e12ae6c3d4270bc95e991e03651aace8dd0c52655fe07ea628f110572a0b58b6fce7ca51601dcf48d43cd30308b574b5b165de9
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# SyncSign - Ruby interface to the SyncSign e-paper display system
|
2
|
+
|
3
|
+
## Description
|
4
|
+
|
5
|
+
Ruby class/gem to access the [SyncSign](http://sync-sign.com) cloud service to display information on e-paper displays.
|
6
|
+
|
7
|
+
## Features
|
8
|
+
* Display circles, lines, rectangles, QR codes, and text on the displays.
|
9
|
+
* Obtain information about associated hubs and displays.
|
10
|
+
|
11
|
+
## TODO
|
12
|
+
* Support non-display nodes that connect to the SyncSign hub (like temperature or occupancy sensors)
|
13
|
+
* Support display-resident images (item type IMAGE)
|
14
|
+
* Support bitmap images from a URI (item type BITMAP\_URI)
|
15
|
+
* Support icons in text boxes
|
16
|
+
|
17
|
+
## Use
|
18
|
+
|
19
|
+
Create a SyncSign Service instance using your API key (found in the SyncSign portal settings):
|
20
|
+
|
21
|
+
require 'syncsign'
|
22
|
+
|
23
|
+
signsvc = SyncSign::Service.new('<api key>')
|
24
|
+
node = signsvc.node('<node id>')
|
25
|
+
items = [
|
26
|
+
SyncSign::Widget::Rectangle.new(x: 8, y: 8, width: 208, height: 60, pen_width: 2),
|
27
|
+
SyncSign::Widget::Textbox.new(x: 16, y: 16, width: 192, height: 44, font: :roboto_slab, size: 24, align: :center, text: "Hello, World!")
|
28
|
+
]
|
29
|
+
template = SyncSign::Template.new(items: items)
|
30
|
+
node.render(template: template)
|
31
|
+
|
32
|
+
## Examples
|
33
|
+
|
34
|
+
Check out the examples/ folder for:
|
35
|
+
* account\_info.rb - Show information about the account associated with the provided API key.
|
36
|
+
* nodes.rb - List all nodes on the system and information about them.
|
37
|
+
* render.rb - Render a sample screen to the given node.
|
38
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.setup
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
rescue LoadError
|
8
|
+
end
|
9
|
+
|
10
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
11
|
+
require "syncsign/version"
|
12
|
+
require "yard"
|
13
|
+
|
14
|
+
YARD::Rake::YardocTask.new do |t|
|
15
|
+
t.files = ['lib/**/*.rb']
|
16
|
+
t.options = %w(--markup-provider=redcarpet --markup=markdown --main=README.md)
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "Builds the gem"
|
20
|
+
task :gem => :build
|
21
|
+
task :build => :yard do
|
22
|
+
system "gem build syncsign.gemspec"
|
23
|
+
Dir.mkdir("pkg") unless Dir.exists?("pkg")
|
24
|
+
system "mv syncsign-#{SyncSign::VERSION}.gem pkg/"
|
25
|
+
end
|
26
|
+
|
27
|
+
task :install => :build do
|
28
|
+
system "sudo gem install pkg/syncsign-#{SyncSign::VERSION}.gem"
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Release the gem - Gemcutter"
|
32
|
+
task :release => :build do
|
33
|
+
system "git tag -a v#{SyncSign::VERSION} -m 'Tagging #{SyncSign::VERSION}'"
|
34
|
+
system "git push --tags"
|
35
|
+
system "gem push pkg/syncsign-#{SyncSign::VERSION}.gem"
|
36
|
+
end
|
37
|
+
|
38
|
+
task :default => [:spec]
|
data/lib/syncsign.rb
ADDED
data/lib/syncsign/hub.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module SyncSign
|
2
|
+
##
|
3
|
+
# Object that represents a SyncSign hub.
|
4
|
+
class Hub
|
5
|
+
# @return [String] the serial number of the hub.
|
6
|
+
attr_reader :sn
|
7
|
+
# @return [String] the friendly name of the hub.
|
8
|
+
attr_reader :name
|
9
|
+
|
10
|
+
##
|
11
|
+
# Initialize a new hub object (normally only called from Service#hubs)
|
12
|
+
def initialize(sn: null, name: null, service: null)
|
13
|
+
@sn = sn
|
14
|
+
@name = name
|
15
|
+
@service = service
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Retrieve a collection of all +Node+ objects serviced by this hub.
|
20
|
+
def nodes
|
21
|
+
SyncSign::Node::parse_collection(service: @service, nodeinfo: @service.api_call(path: "/devices/#{@sn}/nodes"))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module SyncSign
|
2
|
+
##
|
3
|
+
# Object that represents a SyncSign display node.
|
4
|
+
class Display < Node
|
5
|
+
##
|
6
|
+
# Render a template to this display.
|
7
|
+
# @param template [Template] Template to render to this display.
|
8
|
+
def render(template: nil)
|
9
|
+
@service.api_call(type: :post, path: "/nodes/#{@id}/renders", data: template.to_s)
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Return true if the node can display a colour other than black and white.
|
14
|
+
def has_colour?
|
15
|
+
['D29R', 'D75'].include? @model
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module SyncSign
|
2
|
+
##
|
3
|
+
# Object that represents a SyncSign node.
|
4
|
+
# TODO: Split this up and add support for non-display nodes.
|
5
|
+
class Node
|
6
|
+
# @return [String] the Node ID of this node.
|
7
|
+
attr_reader :id
|
8
|
+
# @return [String] the friendly name of this node.
|
9
|
+
attr_reader :name
|
10
|
+
# @return [Integer] the current battery level of this node (0-100)
|
11
|
+
attr_reader :battery
|
12
|
+
# @return [Integer] the current signal level of this node (0-100)
|
13
|
+
attr_reader :signal
|
14
|
+
# @return [String] model of thid node.
|
15
|
+
attr_reader :model
|
16
|
+
|
17
|
+
##
|
18
|
+
# Initialize a new Node object (normally only called from Hub#nodes or Service#nodes).
|
19
|
+
def initialize(service: nil, id: nil, name: nil, online: nil, battery: nil, signal: nil, model: nil)
|
20
|
+
@service = service
|
21
|
+
@id = id
|
22
|
+
@name = name
|
23
|
+
@online = online
|
24
|
+
@battery = battery
|
25
|
+
@signal = signal
|
26
|
+
@model = model
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Return true if the node was online during the last information gathering.
|
31
|
+
def is_online?
|
32
|
+
@online
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Parse a JSON description of a single node into a +Node+ object.
|
37
|
+
# Normally only called from Hub#nodes or Service#nodes.
|
38
|
+
# @api private
|
39
|
+
def self.parse(service: nil, nodeinfo: nil)
|
40
|
+
node_class = Node
|
41
|
+
case nodeinfo['type']
|
42
|
+
when 'DISPLAY'
|
43
|
+
node_class = Display
|
44
|
+
# TODO: support sensors and any other node types
|
45
|
+
end
|
46
|
+
node_class.new(
|
47
|
+
service: service,
|
48
|
+
id: nodeinfo['nodeId'],
|
49
|
+
name: nodeinfo['name'],
|
50
|
+
online: nodeinfo['onlined'],
|
51
|
+
battery: nodeinfo['batteryLevel'],
|
52
|
+
signal: nodeinfo['signalLevel'],
|
53
|
+
model: nodeinfo['model']
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Parse JSON array of nodes into a collection of Node objects.
|
59
|
+
# @param service [SyncSign::Service] Instance of the SyncSign Service class.
|
60
|
+
# @param nodeinfo [String] Information about a collection of nodes,
|
61
|
+
# in JSON format.
|
62
|
+
# @return [Array] Array of Node objects.
|
63
|
+
# @api private
|
64
|
+
def self.parse_collection(service: nil, nodeinfo: nil)
|
65
|
+
nodes = []
|
66
|
+
nodeinfo.each do |node|
|
67
|
+
nodes.push Node::parse(service: service, nodeinfo: node)
|
68
|
+
end
|
69
|
+
p nodes
|
70
|
+
nodes
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
##
|
5
|
+
# The SyncSign module contains everything needed to interface with the
|
6
|
+
# SyncSign cloud server and display information on their e-paper displays.
|
7
|
+
module SyncSign
|
8
|
+
##
|
9
|
+
# Raised if an error occurs communicating with the SyncSign cloud service.
|
10
|
+
class APICallException < StandardError
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# Object that communicates with the SyncSign cloud service.
|
15
|
+
class Service
|
16
|
+
##
|
17
|
+
# Set up a new connection to the SyncSign cloud service.
|
18
|
+
# @param apikey [String] the API key provided through the SyncSign portal.
|
19
|
+
def initialize(apikey: null)
|
20
|
+
@apikey = apikey
|
21
|
+
@baseurl = "https://api.sync-sign.com/v2/key/#{apikey}"
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Retrieve an array of information about the SyncSign cloud service account
|
26
|
+
# associated with this object.
|
27
|
+
def account_info()
|
28
|
+
api_call()
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Retrieve a collection of all +Hub+ objects registered under the account.
|
33
|
+
def hubs
|
34
|
+
hubs = []
|
35
|
+
hub_data = api_call(path: "/devices")
|
36
|
+
hub_data.each do |hub|
|
37
|
+
hubs.push Hub.new(
|
38
|
+
service: self,
|
39
|
+
sn: hub['sn'],
|
40
|
+
name: hub['info']['friendlyName']
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
hubs
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Retrieve a collection of all +Node+ objects registered under the account.
|
49
|
+
def nodes()
|
50
|
+
Node::parse_collection(service: self, nodeinfo: api_call(path: "/nodes"))
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Retrieve a single +Node+ object with the given id.
|
55
|
+
# @param id [String] Node ID to retrieve the object for.
|
56
|
+
def node(id)
|
57
|
+
Node::parse(service: self, nodeinfo: api_call(path: "/nodes/#{id}"))
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Make a call to the SyncSign cloud service.
|
62
|
+
# @param type [Symbol] The HTTP method to use, either :get or :put.
|
63
|
+
# @param path [String] The path to make the API call to, minus the base path.
|
64
|
+
# @param data [String] The POST data to send with the API call.
|
65
|
+
# @api private
|
66
|
+
def api_call(type: :get, path: "", data: "")
|
67
|
+
apiurl = URI.parse("#{@baseurl}#{path}")
|
68
|
+
http_obj = Net::HTTP.new(apiurl.host, apiurl.port)
|
69
|
+
http_obj.use_ssl = true
|
70
|
+
response = nil
|
71
|
+
http_obj.start() do |http|
|
72
|
+
req = nil
|
73
|
+
if(type == :get) then
|
74
|
+
req = Net::HTTP::Get.new(apiurl.request_uri)
|
75
|
+
else
|
76
|
+
req = Net::HTTP::Post.new(apiurl.request_uri)
|
77
|
+
req.body = data
|
78
|
+
req['Content-Type'] = 'application/json'
|
79
|
+
end
|
80
|
+
response = http.request(req)
|
81
|
+
end
|
82
|
+
if(response.code.to_i != 200) then
|
83
|
+
raise APICallException, response.read_body
|
84
|
+
end
|
85
|
+
JSON.parse(response.read_body)['data']
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module SyncSign
|
4
|
+
##
|
5
|
+
# Object that represents a template that can be rendered onto a display.
|
6
|
+
# Contains +Widget+s which define the UI elements on the +Template+.
|
7
|
+
class Template
|
8
|
+
attr_accessor :bgcolour, :pollrate
|
9
|
+
attr_reader :items
|
10
|
+
|
11
|
+
##
|
12
|
+
# Initialize a new template and optionally add items to it.
|
13
|
+
# @param bgcolour [Symbol] Background colour, one of :black, :white, or :red.
|
14
|
+
# @param items [Array] List of Widgets to add to the template.
|
15
|
+
# @param pollrate [Integer] How often (in ms) the node should poll the hub
|
16
|
+
# for new information.
|
17
|
+
def initialize(bgcolour: :white, items: [], pollrate: 10000)
|
18
|
+
@bgcolour = bgcolour
|
19
|
+
@pollrate = pollrate
|
20
|
+
|
21
|
+
@items = []
|
22
|
+
items.each do |item|
|
23
|
+
self.+(item)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Add a new widget to the template.
|
29
|
+
# @param to_add [Widget] Widget to add to the template.
|
30
|
+
def +(to_add)
|
31
|
+
# TODO: make sure we only add widgets
|
32
|
+
@items.push to_add
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Output this template as JSON in the format that the SyncSign service understands.
|
37
|
+
def to_s
|
38
|
+
background = {bgColor: @bgcolour.to_s.upcase}
|
39
|
+
items = @items.collect { |item| item.to_a }
|
40
|
+
options = {'pollRate': @pollrate}
|
41
|
+
{
|
42
|
+
layout: {
|
43
|
+
background: background,
|
44
|
+
items: items,
|
45
|
+
options: options
|
46
|
+
}
|
47
|
+
}.to_json
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,323 @@
|
|
1
|
+
module SyncSign
|
2
|
+
##
|
3
|
+
# Raised when UI elements are not properly aligned.
|
4
|
+
# Several UI elements must be aligned to a multiple of 8 or corruption will
|
5
|
+
# occur on the display.
|
6
|
+
class AlignmentException < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
##
|
10
|
+
# Widgets are UI elements that are placed onto a +Template+ for rendering.
|
11
|
+
class Widget
|
12
|
+
##
|
13
|
+
# An item that contains only x/y coordinates. This can't be used on its own, only its
|
14
|
+
# superclasses can be added to templates.
|
15
|
+
class Item
|
16
|
+
# @return [Integer] horizontal position of the item.
|
17
|
+
attr_accessor :x
|
18
|
+
# @return [Integer] vertical position of the item.
|
19
|
+
attr_accessor :y
|
20
|
+
|
21
|
+
##
|
22
|
+
# Initialize a new Item widget.
|
23
|
+
# @param x [Integer] The horizontal position of the item.
|
24
|
+
# @param y [Integer] The vertical position of the item.
|
25
|
+
def initialize(x: nil, y: nil)
|
26
|
+
@x = x
|
27
|
+
@y = y
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# A box that contains x/y coordinates, width and height, and colour information.
|
33
|
+
# This can't be used on its own, only its superclasses can be added to templates.
|
34
|
+
# You may be looking for +Rectangle+ if you want to draw rectangles.
|
35
|
+
class Box < Item
|
36
|
+
# @return [Integer] the width of the box.
|
37
|
+
attr_accessor :width
|
38
|
+
# @return [Integer] the height of the box.
|
39
|
+
attr_accessor :height
|
40
|
+
# @return [Symbol] the stroke or foreground colour of the box.
|
41
|
+
attr_accessor :colour
|
42
|
+
# @return [Symbol] the background colour of the box.
|
43
|
+
attr_accessor :bgcolour
|
44
|
+
|
45
|
+
def initialize(x: nil, y: nil, width: nil, height: nil, colour: :black, bgcolour: :white)
|
46
|
+
Widget::check_colours [colour, bgcolour]
|
47
|
+
@colour = colour
|
48
|
+
@bgcolour = bgcolour
|
49
|
+
@width = width
|
50
|
+
@height = height
|
51
|
+
super(x: x, y: y)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# A widget that draws a rectangle.
|
57
|
+
class Rectangle < Box
|
58
|
+
attr_accessor :pen_width
|
59
|
+
|
60
|
+
##
|
61
|
+
# Initialize a new rectangle widget.
|
62
|
+
# @param x [Integer] horizontal position of the left side of the rectangle.
|
63
|
+
# @param y [Integer] vertical position of the top of the rectangle.
|
64
|
+
# @param width [Integer] how wide the rectangle should be.
|
65
|
+
# @param height [Integer] how tall the rectangle should be.
|
66
|
+
# @param colour [Symbol] The stroke colour used for the rectangle
|
67
|
+
# (either black, white, or red).
|
68
|
+
# @param bgcolour [Symbol] The fill colour used for the rectangle
|
69
|
+
# (either black, white, or red).
|
70
|
+
# @param pen_width [Integer] The width in pixels of the stroke.
|
71
|
+
def initialize(x: nil, y: nil, width: nil, height: nil, colour: :black, bgcolour: :white, pen_width: 1, fillpattern: :none, strokepattern: :solid)
|
72
|
+
Widget::check_patterns [fillpattern, strokepattern]
|
73
|
+
raise(AlignmentException, "Rect: x and width must both be a multiple of 8") if x % 8 != 0 or width % 8 != 0
|
74
|
+
@pen_width = pen_width
|
75
|
+
@fillpattern = fillpattern
|
76
|
+
@strokepattern = strokepattern
|
77
|
+
super(x: x, y: y, width: width, height: height, colour: colour, bgcolour: bgcolour)
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Convert the widget into an array for sending to the SyncSign service.
|
82
|
+
def to_a
|
83
|
+
{
|
84
|
+
'type': 'RECTANGLE',
|
85
|
+
'data': {
|
86
|
+
'block': {x: @x, y: @y, w: @width, h: @height},
|
87
|
+
'fillColor': @bgcolour.to_s.upcase,
|
88
|
+
'fillPattern': @fillpattern.to_s.upcase,
|
89
|
+
'strokeColor': @colour.to_s.upcase,
|
90
|
+
'strokePattern': @strokepattern.to_s.upcase,
|
91
|
+
'strokeThickness': @pen_width
|
92
|
+
}
|
93
|
+
}
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# A widget that draws a line.
|
99
|
+
class Line
|
100
|
+
attr_accessor :x0, :y0, :x1, :y1, :bgcolour, :colour, :pattern
|
101
|
+
|
102
|
+
##
|
103
|
+
# Initialize a new line widget.
|
104
|
+
# @param x0 [Integer] horizontal position of the starting point of the line.
|
105
|
+
# @param y0 [Integer] vertical position of the starting point of the line.
|
106
|
+
# @param x1 [Integer] horizontal position of the ending point of the line.
|
107
|
+
# @param y1 [Integer] vertical position of the ending point of the line.
|
108
|
+
# @param colour [Symbol] The stroke colour used for the rectangle
|
109
|
+
# (either black, white, or red).
|
110
|
+
# @param bgcolour [Symbol] The fill colour used for the rectangle
|
111
|
+
# (either black, white, or red).
|
112
|
+
# @param pattern [Symbol] The linestyle to use when drawing the line, one of solid, interleave, dash_tiny, dash_mid, or dash_wide.
|
113
|
+
def initialize(x0: nil, y0: nil, x1: nil, y1: nil, bgcolour: :white, colour: :black, pattern: :solid)
|
114
|
+
Widget::check_colours [colour, bgcolour]
|
115
|
+
Widget::check_patterns [pattern]
|
116
|
+
@x0 = x0
|
117
|
+
@y0 = y0
|
118
|
+
@x1 = x1
|
119
|
+
@y1 = y1
|
120
|
+
@colour = colour
|
121
|
+
@bgcolour = bgcolour
|
122
|
+
@pattern = pattern
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Convert the widget into an array for sending to the SyncSign service.
|
127
|
+
def to_a
|
128
|
+
{
|
129
|
+
'type': 'LINE',
|
130
|
+
'data': {
|
131
|
+
'block': {x0: @x0, y0: @y0, x1: @x1, y1: @y1},
|
132
|
+
'backgroundColor': @bgcolour.to_s.upcase,
|
133
|
+
'lineColor': @colour.to_s.upcase,
|
134
|
+
'linePattern': @pattern.to_s.upcase
|
135
|
+
}
|
136
|
+
}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
##
|
141
|
+
# A widget that draws a circle.
|
142
|
+
class Circle < Item
|
143
|
+
attr_accessor :radius, :bgcolour, :colour, :fillpattern, :strokepattern
|
144
|
+
|
145
|
+
##
|
146
|
+
# Initialize a new circle widget.
|
147
|
+
# @param x [Integer] horizontal position of the centre of the circle.
|
148
|
+
# @param y [Integer] vertical position of the centre of the circle
|
149
|
+
# @param radius [Integer] The radius of the circle in pixels.
|
150
|
+
# @param colour [Symbol] The stroke colour used for the circle
|
151
|
+
# (either black, white, or red).
|
152
|
+
# @param bgcolour [Symbol] The fill colour used for the circle
|
153
|
+
# (either black, white, or red).
|
154
|
+
# @param fillpattern [Symbol] The fill pattern to use when filling the circle.
|
155
|
+
# @param strokepattern [Symbol] The stroke pattern to use when drawing the circle.
|
156
|
+
# @param pen_width [Integer] The thickness in pixels of the stroke.
|
157
|
+
def initialize(x: nil, y: nil, radius: nil, bgcolour: :white, colour: :black, fillpattern: :none, strokepattern: :solid, pen_width: 1)
|
158
|
+
Widget::check_colours [colour, bgcolour]
|
159
|
+
Widget::check_patterns [fillpattern, strokepattern]
|
160
|
+
@radius = radius
|
161
|
+
@colour = colour
|
162
|
+
@bgcolour = bgcolour
|
163
|
+
@fillpattern = fillpattern
|
164
|
+
@strokepattern = strokepattern
|
165
|
+
@pen_width = pen_width
|
166
|
+
super(x: x, y:y)
|
167
|
+
end
|
168
|
+
|
169
|
+
##
|
170
|
+
# Convert the widget into an array for sending to the SyncSign service.
|
171
|
+
def to_a
|
172
|
+
{
|
173
|
+
'type': 'CIRCLE',
|
174
|
+
'data': {
|
175
|
+
'center': {x: @x, y: @y},
|
176
|
+
'fillColor': @bgcolour.to_s.upcase,
|
177
|
+
'fillPattern': @fillpattern.to_s.upcase,
|
178
|
+
'strokeColor': @colour.to_s.upcase,
|
179
|
+
'strokePattern': @strokepattern.to_s.upcase,
|
180
|
+
'strokeThickness': @pen_width
|
181
|
+
}
|
182
|
+
}
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
##
|
187
|
+
# A widget that draws a text box.
|
188
|
+
class Textbox < Box
|
189
|
+
attr_accessor :font, :size, :id, :align, :text
|
190
|
+
|
191
|
+
##
|
192
|
+
# Initialize a new text box widget.
|
193
|
+
# @param x [Integer] horizontal position of the left side of the text box.
|
194
|
+
# @param y [Integer] vertical position of the top of the text box.
|
195
|
+
# @param width [Integer] how wide the text box should be.
|
196
|
+
# @param height [Integer] how tall the text box should be.
|
197
|
+
# @param colour [Symbol] The text colour used for the text.
|
198
|
+
# @param bgcolour [Symbol] The background colour used for the text box.
|
199
|
+
# @param font [Symbol] The font to use when drawing the text.
|
200
|
+
# @param size [Integer] The point size to use when drawing the text.
|
201
|
+
# @param align [Symbol] Whether to align the text left, center, or right.
|
202
|
+
# @param text [String] The text to draw in the box.
|
203
|
+
# @param id [String] An ID value to attach to the text box.
|
204
|
+
def initialize(x: nil, y: nil, width: nil, height: nil, colour: :black, bgcolour: :white, font: nil, size: nil, id: nil, align: :left, text: nil)
|
205
|
+
check_font(font: font, size: size)
|
206
|
+
raise(AlignmentException, "Textbox: either y or height must be a multiple of 8") if y % 8 != 0 and height % 8 != 0
|
207
|
+
raise(AlignmentException, "Textbox: width must be a multiple of 8") if width % 8 != 0
|
208
|
+
@font = font.upcase
|
209
|
+
@size = size
|
210
|
+
@id = id
|
211
|
+
@align = align
|
212
|
+
@text = text
|
213
|
+
super(x: x, y: y, width: width, height: height, colour: colour, bgcolour: bgcolour)
|
214
|
+
end
|
215
|
+
|
216
|
+
##
|
217
|
+
# Convert the widget into an array for sending to the SyncSign service.
|
218
|
+
def to_a
|
219
|
+
{
|
220
|
+
'type': 'TEXT',
|
221
|
+
'data': {
|
222
|
+
'block': {x: @x, y: @y, w: @width, h: @height},
|
223
|
+
'textColor': @colour.to_s.upcase,
|
224
|
+
'textAlign': @align.to_s.upcase,
|
225
|
+
'font': "#{@font}_#{@size.to_s}",
|
226
|
+
'text': @text
|
227
|
+
}
|
228
|
+
}
|
229
|
+
end
|
230
|
+
|
231
|
+
def check_font(font: nil, size: nil)
|
232
|
+
available_sizes = {
|
233
|
+
:ddin => [16, 24, 32, 48, 64, 128],
|
234
|
+
:ddin_condensed => [16, 24, 32, 48, 64],
|
235
|
+
:charriot => [10],
|
236
|
+
:aprilsans => [10, 16, 24],
|
237
|
+
:roboto_condensed => [24, 48],
|
238
|
+
:roboto_slab => [24, 48],
|
239
|
+
:yanone_kaffeesatz => [24, 44],
|
240
|
+
:kaushan_script => [20, 32],
|
241
|
+
:sriracha => [24],
|
242
|
+
:dorsa => [32],
|
243
|
+
:londrina_outline => [36],
|
244
|
+
:bungee_shade => [36],
|
245
|
+
:noto_serif => [16],
|
246
|
+
:noto_sans => [24, 40]
|
247
|
+
}
|
248
|
+
|
249
|
+
if(!available_sizes.keys.include? font) then
|
250
|
+
raise ArgumentError, "#{font} is not a valid font. Available fonts: #{available_sizes.keys}"
|
251
|
+
end
|
252
|
+
if(!available_sizes[font].include? size) then
|
253
|
+
raise ArgumentError, "#{font} is not available in size #{size}. Available sizes for this font: #{available_sizes[font].join(", ")}"
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
##
|
259
|
+
# A widget that draws a QR code.
|
260
|
+
class QRCode < Item
|
261
|
+
# @return [Integer] scale of the QR code (how many pixels to use to
|
262
|
+
# represent each base pixel).
|
263
|
+
attr_accessor :scale
|
264
|
+
# @return [Integer] the version (which is actually size, in QR-speak)
|
265
|
+
# of this QR code.
|
266
|
+
attr_accessor :version
|
267
|
+
# @return [Symbol] the level of error checking code for this QR code,
|
268
|
+
# either :low, :medium, :quartile, or :high.
|
269
|
+
attr_accessor :ecclevel
|
270
|
+
# @return [String] the text to encode in this QR code.
|
271
|
+
attr_accessor :text
|
272
|
+
|
273
|
+
##
|
274
|
+
# Initialize a new QR code widget.
|
275
|
+
# @param x [Integer] horizontal position of the left side of the QR code.
|
276
|
+
# @param y [Integer] vertical position of the top of the QR code.
|
277
|
+
# @param scale [Integer] scale of the QR code (how many pixels to use to
|
278
|
+
# represent each base pixel).
|
279
|
+
# @param version [Integer] the version (which is actually size, in QR-speak)
|
280
|
+
# of this QR code.
|
281
|
+
# @param ecclevel [Symbol] the level of error checking code for this QR code,
|
282
|
+
# either :low, :medium, :quartile, or :high.
|
283
|
+
# @param text [String] the text to encode in this QR code.
|
284
|
+
def initialize(x: nil, y: nil, scale: 4, version: 2, ecclevel: :medium, text: nil)
|
285
|
+
@scale = scale
|
286
|
+
@version = version
|
287
|
+
@ecclevel = ecclevel
|
288
|
+
@text = text
|
289
|
+
|
290
|
+
super(x: x, y: y)
|
291
|
+
end
|
292
|
+
|
293
|
+
##
|
294
|
+
# Convert the widget into an array for sending to the SyncSign service.
|
295
|
+
def to_a
|
296
|
+
{
|
297
|
+
'type': 'QRCODE',
|
298
|
+
'data': {
|
299
|
+
'scale': @scale,
|
300
|
+
'eccLevel': @ecclevel.to_s.upcase,
|
301
|
+
'version': @version,
|
302
|
+
'position': {x: @x, y: @y},
|
303
|
+
'text': @text
|
304
|
+
}
|
305
|
+
}
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def self.check_colours(colours)
|
310
|
+
colours.each do |colour|
|
311
|
+
next if [:white, :black, :red].include? colour
|
312
|
+
raise ArgumentError, "Colour must be :white, :black, or :red."
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
def self.check_patterns(patterns)
|
317
|
+
patterns.each do |pattern|
|
318
|
+
next if [:solid, :interleave, :dash_tiny, :dash_medium, :dash_wide, :none].include? pattern
|
319
|
+
raise ArgumentError, "Pattern must be :solid, :interleave, :dash_tiny, :dash_medium, :dash_wide, or :none."
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: syncsign
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- sarahemm
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-08-16 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Interface library for SyncSign e-paper displays
|
14
|
+
email: github@sen.cx
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- README.md
|
20
|
+
- Rakefile
|
21
|
+
- lib/syncsign.rb
|
22
|
+
- lib/syncsign/hub.rb
|
23
|
+
- lib/syncsign/node-display.rb
|
24
|
+
- lib/syncsign/node.rb
|
25
|
+
- lib/syncsign/service.rb
|
26
|
+
- lib/syncsign/template.rb
|
27
|
+
- lib/syncsign/version.rb
|
28
|
+
- lib/syncsign/widgets.rb
|
29
|
+
homepage: http://github.com/sarahemm/ruby-syncsign
|
30
|
+
licenses:
|
31
|
+
- MIT
|
32
|
+
metadata: {}
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 2.7.9
|
50
|
+
signing_key:
|
51
|
+
specification_version: 4
|
52
|
+
summary: SyncSign interface library
|
53
|
+
test_files: []
|