thomas-ghysbrecht-thermostat-exercise 0.1.2.pre.a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/thermostat +64 -0
- data/lib/httpsvalue.rb +10 -0
- data/lib/logger.rb +25 -0
- data/lib/mqtttemp.rb +53 -0
- data/lib/parser.rb +68 -0
- data/lib/status.rb +25 -0
- data/lib/thermos.rb +90 -0
- metadata +51 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bd2a9d561838ec347e9472ce9d99afe78a35eb1e
|
4
|
+
data.tar.gz: e0700576bdd5ac5d5517bf80da662684f01b669a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 57a6b7915f5f8622ae6fa79b82f78675daff5c0fac8b3a80344cd1e382b90d4087437affc966cbd6f42cf10c621a7709e62cd49427bc095d268954715a50c8e9
|
7
|
+
data.tar.gz: daacfb7593ebb0a02906a0d1a4c8ef86108be863fee0beb57756272b89d27d726b2591f7920f6110d8e19f4a6998c987bc0679d517b46d08d868d3c4de130f26
|
data/bin/thermostat
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative "../lib/thermos.rb"
|
4
|
+
require_relative "../lib/httpsvalue.rb"
|
5
|
+
require_relative "../lib/logger.rb"
|
6
|
+
require_relative "../lib/status.rb"
|
7
|
+
require_relative "../lib/mqtttemp.rb"
|
8
|
+
require_relative "../lib/parser.rb"
|
9
|
+
require "OpenSSL"
|
10
|
+
require 'json'
|
11
|
+
require 'rubygems'
|
12
|
+
|
13
|
+
parser_var = Parser.new
|
14
|
+
options = parser_var.parse(ARGV)
|
15
|
+
|
16
|
+
logger = ThermoLogger.new()
|
17
|
+
status = Status.new()
|
18
|
+
|
19
|
+
if(options.mqtt)
|
20
|
+
mqtttemp = Mqtttemp.new(options.mqtt)
|
21
|
+
end
|
22
|
+
|
23
|
+
nest = Thermos.new(options.target, options.range)
|
24
|
+
|
25
|
+
#Get a termperature from the console arguments
|
26
|
+
if(options.kelvin || options.celcius || options.fahrenheit)
|
27
|
+
nest.set_kelvin(options.kelvin) if options.kelvin
|
28
|
+
nest.set_celcius(options.celcius) if options.celcius
|
29
|
+
nest.set_fahrenheit(options.fahrenheit) if options.fahrenheit
|
30
|
+
status.get_status(nest)
|
31
|
+
logger.log_event(nest)
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
#Get a temperature from the internet
|
36
|
+
if(options.httplink)
|
37
|
+
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE #Bypass the SSL certificate problem, but gives a warning :(
|
38
|
+
httpsvalue = Httpsvalue.new
|
39
|
+
nest.set_celcius(httpsvalue.get_value(options.httplink).to_f)
|
40
|
+
status.get_status(nest)
|
41
|
+
logger.log_event(nest)
|
42
|
+
end
|
43
|
+
|
44
|
+
#Get a temperature from a Json file
|
45
|
+
if(options.json)
|
46
|
+
nest.set_celcius(JSON.parse(File.read(options.json))["temperature"].to_f)
|
47
|
+
status.get_status(nest)
|
48
|
+
logger.log_event(nest)
|
49
|
+
end
|
50
|
+
|
51
|
+
#Get the temperature from MQTT
|
52
|
+
if(options.subscribe)
|
53
|
+
mqtttemp.on_change do |temp|
|
54
|
+
nest.set_celcius(temp);
|
55
|
+
status.get_status(nest);
|
56
|
+
logger.log_event(nest);
|
57
|
+
mqtttemp.send_led_hex(nest.get_hex_leds, options.publish) if options.publish
|
58
|
+
end
|
59
|
+
|
60
|
+
mqtttemp.enable_thread(options.subscribe);
|
61
|
+
|
62
|
+
while(true)
|
63
|
+
end
|
64
|
+
end
|
data/lib/httpsvalue.rb
ADDED
data/lib/logger.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
# The ThermoLoger class logs the thermos object's status in a file.
|
4
|
+
#
|
5
|
+
# @author Thomas Ghysbrecht <ghysbrecht@hotmail.com>
|
6
|
+
class ThermoLogger
|
7
|
+
|
8
|
+
# Logs a message to a file
|
9
|
+
def log_message(message)
|
10
|
+
open('log.txt', 'a') do |f|
|
11
|
+
f.puts message
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Logs the thermos class to a file
|
16
|
+
def log_event(thermos)
|
17
|
+
current_time = DateTime.now
|
18
|
+
log_message("-------------------------------------------------------")
|
19
|
+
log_message(current_time.strftime "%d/%m/%Y %H:%M:%S")
|
20
|
+
log_message("Received temperature value: " + thermos.temperature.to_s)
|
21
|
+
log_message("Led value: " + thermos.get_hex_leds)
|
22
|
+
log_message("Heater is " + (thermos.relaisHeater ? "ON" : "OFF"))
|
23
|
+
log_message("Cooler is " + (thermos.relaisCooler ? "ON" : "OFF"))
|
24
|
+
end
|
25
|
+
end
|
data/lib/mqtttemp.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'mqtt'
|
2
|
+
|
3
|
+
# Mqtt class that makes it possible to receive and send values via mqtt.
|
4
|
+
#
|
5
|
+
# You can attach a block of code to run when a value is received. Also you can
|
6
|
+
# send the led hex value via mqtt to a specified topic.
|
7
|
+
# @author Thomas Ghysbrecht <ghysbrecht@hotmail.com>
|
8
|
+
class Mqtttemp
|
9
|
+
|
10
|
+
# Set up the class with the brokere server address
|
11
|
+
def initialize(server)
|
12
|
+
@server = server;
|
13
|
+
end
|
14
|
+
|
15
|
+
# Attachach a codeblock that will be executed when a value is received
|
16
|
+
def on_change &block
|
17
|
+
@on_change_block = block
|
18
|
+
end
|
19
|
+
|
20
|
+
# Enable a thread that executes the on_change block when something is
|
21
|
+
# received on the topic given via the argument.
|
22
|
+
def enable_thread(mqtt_topic)
|
23
|
+
Thread.new do
|
24
|
+
MQTT::Client.connect(@server) do |c|
|
25
|
+
c.get(mqtt_topic) do |topic,message|
|
26
|
+
if(isValidJson(message))
|
27
|
+
@on_change_block.call(JSON.parse(message)["temperature"]) unless @on_change_block.nil?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def isValidJson(json)
|
36
|
+
JSON.parse(json)
|
37
|
+
return true
|
38
|
+
rescue JSON::ParserError => e
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
|
42
|
+
# Send a hex value via MQTT to a topic.
|
43
|
+
# Argumens:
|
44
|
+
# hex (string) : The hex value that needs to be sent in format "FFAA33".
|
45
|
+
# topic (string) : The topic where to send the hex value to.
|
46
|
+
def send_led_hex(hex, topic)
|
47
|
+
MQTT::Client.connect(@server) do |c|
|
48
|
+
data = '{"color":"'+ hex +'"}'
|
49
|
+
c.publish(topic, data)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
data/lib/parser.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
Options = Struct.new(:kelvin, :celcius, :target, :fahrenheit, :range, :httplink, :mqtt, :json, :subscribe, :publish )
|
4
|
+
|
5
|
+
# Parser class using optparse, this makes it possible to easly use this
|
6
|
+
# app as an CLI app.
|
7
|
+
# @author Thomas Ghysbrecht <ghysbrecht@hotmail.com>
|
8
|
+
class Parser
|
9
|
+
def parse(options)
|
10
|
+
args = Options.new()
|
11
|
+
|
12
|
+
opt_parser = OptionParser.new do |opts|
|
13
|
+
opts.banner = "Usage: app.rb [options]"
|
14
|
+
|
15
|
+
opts.on("-K", "--Kelvin=TEMP",Float, "Temperature in Kelvin") do |arg|
|
16
|
+
args.kelvin = arg
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.on("-C", "--Celcius=TEMP",Float, "Temperature in Celcius") do |arg|
|
20
|
+
args.celcius = arg
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on("-F", "--Fahrenheit=TEMP",Float, "Temperature in Fahrenheit") do |arg|
|
24
|
+
args.fahrenheit = arg
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on("-t", "--target=TEMP",Float, "Target temperature") do |arg|
|
28
|
+
args.target = arg
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on("-r", "--range=TEMP",Float, "Range for temperature") do |arg|
|
32
|
+
args.range = arg
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("-H", "--httpget=LINK", "Get the temperature from the link") do |arg|
|
36
|
+
args.httplink = arg
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("-m", "--mqtt=LINK", "Set the mqtt broker link") do |arg|
|
40
|
+
args.mqtt = arg
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on("-s", "--subscribe=NAME", "Subscribe to MQTT channel (broker link mandatory)") do |arg|
|
44
|
+
args.subscribe = arg
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on("-p", "--publish=NAME", "Publish to MQTT channel (broker link mandatory)") do |arg|
|
48
|
+
args.publish = arg
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on("-j", "--json=FILENAME", "Use a JSON file for the temperature") do |arg|
|
52
|
+
args.json = arg
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on("-h", "--help", "Prints this help") do
|
56
|
+
puts opts
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
opt_parser.parse!(options)
|
62
|
+
|
63
|
+
raise OptionParser::MissingArgument if args.range.nil?
|
64
|
+
raise OptionParser::MissingArgument if args.target.nil?
|
65
|
+
|
66
|
+
return args
|
67
|
+
end
|
68
|
+
end
|
data/lib/status.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Status class that prints out the themos object's status
|
2
|
+
#
|
3
|
+
# @author Thomas Ghysbrecht <ghysbrecht@hotmail.com>
|
4
|
+
class Status
|
5
|
+
# Prints out the current status in the console
|
6
|
+
def get_status(nest)
|
7
|
+
puts "----------------------------------------------------------------------------------------"
|
8
|
+
puts "+-------------------------+"
|
9
|
+
puts "| Led Status |"
|
10
|
+
puts "+-------------------------+"
|
11
|
+
puts "Red led: #{nest.red.round(0)}"
|
12
|
+
puts "Green led: #{nest.green.round(0)}"
|
13
|
+
puts "Blue led: #{nest.blue.round(0)}"
|
14
|
+
puts "Hex value: " + nest.get_hex_leds
|
15
|
+
puts "+-------------------------+"
|
16
|
+
puts "| Relais Status |"
|
17
|
+
puts "+-------------------------+"
|
18
|
+
puts "Cooler: " + (nest.relaisCooler ? "ON" : "OFF")
|
19
|
+
puts "Heater: " + (nest.relaisHeater ? "ON" : "OFF")
|
20
|
+
puts "+-------------------------+"
|
21
|
+
puts "| Temperature Status |"
|
22
|
+
puts "+-------------------------+"
|
23
|
+
puts "Temperature: #{nest.temperature.round(1)}"
|
24
|
+
end
|
25
|
+
end
|
data/lib/thermos.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# Temperature class with multiple temperature compatibility
|
2
|
+
#
|
3
|
+
# The Temperature class stores a temperature. And decides the led color and relais.
|
4
|
+
# @author Thomas Ghysbrecht <ghysbrecht@hotmail.com>
|
5
|
+
class Thermos
|
6
|
+
|
7
|
+
attr_accessor :target
|
8
|
+
attr_accessor :range
|
9
|
+
|
10
|
+
attr_reader :red
|
11
|
+
attr_reader :green
|
12
|
+
attr_reader :blue
|
13
|
+
attr_reader :relaisCooler
|
14
|
+
attr_reader :relaisHeater
|
15
|
+
attr_reader :temperature
|
16
|
+
|
17
|
+
# Initializes the class with a target and range
|
18
|
+
# Arguments:
|
19
|
+
# - target (float) : The target temperature
|
20
|
+
# - range (float) : What temperatures are acceptable around the target
|
21
|
+
def initialize(target, range)
|
22
|
+
@target = target
|
23
|
+
@range = range
|
24
|
+
reset_leds
|
25
|
+
@relaisCooler = false;
|
26
|
+
@relaisHeater = false;
|
27
|
+
end
|
28
|
+
|
29
|
+
# Sets the temperature with a Celcius value.
|
30
|
+
# This method also reset and sets the leds.
|
31
|
+
def set_celcius (temperature)
|
32
|
+
@temperature = temperature
|
33
|
+
reset_leds
|
34
|
+
set_leds
|
35
|
+
end
|
36
|
+
|
37
|
+
# Sets the temperature with a Kelvin value
|
38
|
+
# This method also reset and sets the leds.
|
39
|
+
def set_kelvin (temperature)
|
40
|
+
@temperature = temperature - 273.15
|
41
|
+
reset_leds
|
42
|
+
set_leds
|
43
|
+
end
|
44
|
+
|
45
|
+
# Sets the temperature with a Fahrenheit value
|
46
|
+
# This method also reset and sets the leds.
|
47
|
+
def set_fahrenheit (temperature)
|
48
|
+
@temperature = (temperature - 32)/1.8
|
49
|
+
reset_leds
|
50
|
+
set_leds
|
51
|
+
end
|
52
|
+
|
53
|
+
def reset_leds
|
54
|
+
@red = 0.0
|
55
|
+
@green = 0.0
|
56
|
+
@blue = 0.0
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def set_leds
|
61
|
+
if(@temperature < (@target - @range))
|
62
|
+
multiplier = ( (@target - @temperature) / (5 * @range)) #5 times the range from the target is very blue
|
63
|
+
multiplier > 1 ? multiplier = 1 : multiplier = multiplier
|
64
|
+
@blue = 255*multiplier
|
65
|
+
|
66
|
+
@relaisHeater = true;
|
67
|
+
@relaisCooler = false;
|
68
|
+
|
69
|
+
elsif(@temperature > (@target + @range))
|
70
|
+
multiplier = ( (@temperature - @target) / (5 * @range)) #5 times the range from the target is very red
|
71
|
+
multiplier > 1 ? multiplier = 1 : multiplier = multiplier
|
72
|
+
@red = 255*multiplier
|
73
|
+
|
74
|
+
@relaisHeater = false;
|
75
|
+
@relaisCooler = true;
|
76
|
+
else
|
77
|
+
multiplier = ((@temperature - @target).abs / @range ) #0 time the range from the target is very green
|
78
|
+
@green = 255*(1-multiplier)
|
79
|
+
|
80
|
+
@relaisHeater = false;
|
81
|
+
@relaisCooler = false;
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Return the led status in hex format -> FF88AA
|
86
|
+
def get_hex_leds
|
87
|
+
"%02x%02x%02x" % [@red,@green,@blue]
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
metadata
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: thomas-ghysbrecht-thermostat-exercise
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2.pre.a
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Thomas Ghysbrecht
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-05-09 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A thermostat simulator in ruby
|
14
|
+
email: ghysbrecht@hotmail.com
|
15
|
+
executables:
|
16
|
+
- thermostat
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- bin/thermostat
|
21
|
+
- lib/httpsvalue.rb
|
22
|
+
- lib/logger.rb
|
23
|
+
- lib/mqtttemp.rb
|
24
|
+
- lib/parser.rb
|
25
|
+
- lib/status.rb
|
26
|
+
- lib/thermos.rb
|
27
|
+
homepage: https://github.com/thomieboy/ruby-thermos
|
28
|
+
licenses:
|
29
|
+
- MIT
|
30
|
+
metadata: {}
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">"
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 1.3.1
|
45
|
+
requirements: []
|
46
|
+
rubyforge_project:
|
47
|
+
rubygems_version: 2.5.2
|
48
|
+
signing_key:
|
49
|
+
specification_version: 4
|
50
|
+
summary: Ruby thermostat
|
51
|
+
test_files: []
|