Ha 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/bin/pry ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'pry' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("pry", "pry")
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rake' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("rake", "rake")
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rdebug-ide' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("ruby-debug-ide", "rdebug-ide")
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'thor' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("thor", "thor")
data/exe/ha ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/ha'
4
+ Ha::CLI.start(ARGV)
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require "ha/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "Ha"
9
+ spec.version = Ha::VERSION
10
+ spec.authors = ["Pito Salas"]
11
+ spec.email = ["pitosalas@gmail.com"]
12
+
13
+ spec.summary = %q{Cli to control Home Automation}
14
+ spec.description = %q{A well behaved CLI for controlling Philips Hue and other home automation devices.}
15
+ spec.homepage = "http://www.salas.com"
16
+ spec.license = "MIT"
17
+
18
+ spec.files = `git ls-files -z`.split("\x0")
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "pry-byebug"
24
+ spec.add_development_dependency "rake", '~> 0'
25
+ spec.add_development_dependency "minitest"
26
+ spec.add_development_dependency 'ruby-debug-ide', '~> 0'
27
+
28
+ spec.add_runtime_dependency "thor", '~> 0'
29
+ spec.add_runtime_dependency "faraday", '~> 0'
30
+ end
@@ -0,0 +1,22 @@
1
+ require "thor"
2
+ require 'pry-byebug'
3
+ require_relative "ha/version"
4
+ require_relative "ha/hue_command"
5
+ require_relative "ha/context"
6
+
7
+ module Ha
8
+ class CLI < Thor
9
+ desc "hello NAME", "say hello to NAME"
10
+
11
+ def hello(name)
12
+ puts "Hello #{name}"
13
+ end
14
+
15
+ desc "hue SUBCOMMAND", "commands for working with Philips Hue"
16
+ subcommand "hue", HueCommand
17
+
18
+ def self.exit_on_failure?
19
+ true
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,49 @@
1
+ class CliTable
2
+ attr_accessor :headers, :rows, :column_widths
3
+
4
+ def add(headers, rows)
5
+ @headers = headers
6
+ @rows = rows
7
+ end
8
+
9
+ def rows_count
10
+ @rows.length
11
+ end
12
+
13
+
14
+ def render
15
+ result = header_render + "\n"
16
+ @rows.each do
17
+ |row|
18
+ result << row_render(row) + "\n"
19
+ end
20
+ result
21
+ end
22
+
23
+ def header_render
24
+ result = ""
25
+ @headers.each_index { |i| result << (" %#{column_widths[i]}s" % headers[i]) }
26
+ result
27
+ end
28
+
29
+ def row_render row
30
+ result = ""
31
+ @headers.each_index { |i| result << (" %#{column_widths[i]}s" % format_value(row[i])) }
32
+ result
33
+ end
34
+
35
+ def reset
36
+ @rows = []
37
+ end
38
+
39
+ private
40
+
41
+ def format_value(val)
42
+ if [true, false].include? val
43
+ val ? "Yes" : "No"
44
+ elsif val.nil?
45
+ val = "nil"
46
+ end
47
+ val
48
+ end
49
+ end
@@ -0,0 +1,20 @@
1
+ require "yaml"
2
+
3
+ class Context
4
+ FILE_SPEC = "#{Dir.home}/.ha"
5
+ attr_accessor :context
6
+
7
+ def initialize
8
+ if File.exist? FILE_SPEC
9
+ myfile = File.open(FILE_SPEC, "r")
10
+ @context = YAML.load(myfile.read)
11
+ end
12
+ @context ||= {}
13
+ end
14
+
15
+ def save
16
+ puts "saving context"
17
+ myfile = File.open(FILE_SPEC, "w")
18
+ myfile.write(@context.to_yaml)
19
+ end
20
+ end
@@ -0,0 +1,12 @@
1
+ class Super
2
+ def initialize(number)
3
+ puts "Super #{number}"
4
+ end
5
+ end
6
+
7
+ class Sub < Super
8
+ def initialize(number)
9
+ super
10
+ puts "Sub #{number}"
11
+ end
12
+ end
@@ -0,0 +1,19 @@
1
+ require_relative "hue_resource"
2
+ class Group < HueResource
3
+ attr_reader :detail, :on, :name, :lights
4
+ def initialize(key, hashvalue)
5
+ super
6
+ @lights = hashvalue["lights"]
7
+ @lights_s = hashvalue["lights"].join(",")
8
+ gen_reskey("g")
9
+ @state.merge! ({"on" =>@lights_s, "name" => @name, "detail" => @detail})
10
+ end
11
+
12
+ def array(selectors)
13
+ selectors.map { |key| @state[key] }
14
+ end
15
+
16
+ def self.owning(number, grouparray)
17
+ grouparray.select { |group| group.lights.include? number}
18
+ end
19
+ end
@@ -0,0 +1,78 @@
1
+ require 'json'
2
+ require 'faraday'
3
+ require_relative 'sensor'
4
+ require_relative 'light'
5
+ require_relative 'group'
6
+ require_relative 'rule'
7
+
8
+ BRIDGE_IP = "10.0.0.89"
9
+ USERNAME = "78UEGUotX3otmWxbhiucELCLiiKmaD9E2O5YW-d1"
10
+
11
+
12
+ class Hue
13
+
14
+ def initialize(context, bridge_state)
15
+ @context = context
16
+ @bridge_state = bridge_state
17
+ @groups = groups
18
+ @sensors = sensors
19
+ @lights = lights
20
+ end
21
+
22
+ def self.bridge_state
23
+ unless defined? @bridge_state
24
+ conn = Faraday.new(url: "http://#{BRIDGE_IP}")
25
+ get = conn.get("/api/#{USERNAME}/")
26
+ @bridge_state = JSON.parse(get.body)
27
+ end
28
+ @bridge_state
29
+ end
30
+
31
+ def pair
32
+ @context[:useraccount] = "12345"
33
+ @context.save
34
+ end
35
+
36
+ def sensors
37
+ parsed = @bridge_state["sensors"]
38
+ parsed.to_a.map { |sensor_pair| Sensor.new(*sensor_pair)}
39
+ end
40
+
41
+ def lights
42
+ parsed = @bridge_state["lights"]
43
+ parsed.to_a.map { |light_pair| Light.new(*light_pair, @groups)}
44
+ end
45
+
46
+ def groups
47
+ parsed = @bridge_state["groups"]
48
+ parsed.to_a.map { |group_pair| Group.new(*group_pair)}
49
+ end
50
+
51
+ def rules
52
+ parsed = @bridge_state["rules"]
53
+ parsed.to_a.map { |rule_pair| Rule.new(*rule_pair)}
54
+ end
55
+
56
+ def all_a selector
57
+ items = sensors_a selector
58
+ items += lights_a selector
59
+ items += groups_a selector
60
+ items += rules_a selector
61
+ end
62
+
63
+ def sensors_a selector
64
+ sensors.map { |sensor| sensor.array(selector)}
65
+ end
66
+
67
+ def lights_a selector
68
+ lights.map { |light| light.array(selector)}
69
+ end
70
+
71
+ def groups_a selector
72
+ groups.map { |group| group.array(selector)}
73
+ end
74
+
75
+ def rules_a selector
76
+ rules.map { |rule| rule.array(selector)}
77
+ end
78
+ end
@@ -0,0 +1,34 @@
1
+ require_relative 'cli_table'
2
+ require_relative 'hue'
3
+
4
+ # The 'hue' commmands
5
+ class HueCommand < Thor
6
+ desc "list", "list Hue items"
7
+ def list
8
+ hue = Hue.new(context, Hue.bridge_state)
9
+ table = CliTable.new
10
+ table.headers = list_headers
11
+ table.rows = hue.all_a list_headers
12
+ table.column_widths = column_widths
13
+ puts table.render
14
+ end
15
+
16
+ desc "pair", "run pairing process to allow you to access the hue bridge"
17
+ def pair
18
+ Hue.new(context).pair
19
+ end
20
+
21
+ private
22
+
23
+ def context
24
+ Context.new
25
+ end
26
+
27
+ def list_headers
28
+ ["id", "name", "on", "detail"]
29
+ end
30
+
31
+ def column_widths
32
+ ["5", "22", "12", "-30"]
33
+ end
34
+ end
@@ -0,0 +1,13 @@
1
+ class HueResource
2
+ def initialize(key, hash_value)
3
+ @hash_value = hash_value
4
+ @lastupdated = @hash_value.dig("state", "lastupdated")
5
+ @detail = hash_value["type"]
6
+ @name = hash_value["name"]
7
+ @state = { "key" => key, "lastupdated" => @lastupdated, "detail" => @detail, "name" => @name}
8
+ end
9
+
10
+ def gen_reskey(detail)
11
+ @state.merge!({ "id" => detail + @state["key"]})
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ require_relative "hue_resource"
2
+ class Light < HueResource
3
+
4
+ def initialize(key, hashvalue, grouparray)
5
+ super(key, hashvalue)
6
+ @grouparray = grouparray
7
+ @onstate = hashvalue.dig("state", "on")
8
+ @brightstate = hashvalue.dig("state", "bri")
9
+ @combinedsate = "#{@onstate} (#{@brightstate})"
10
+ @number = key
11
+ @group = Group.owning(key, grouparray)
12
+ gen_reskey("l")
13
+ build_name
14
+ @state.merge! ({"on" => @combinedsate})
15
+ end
16
+
17
+ def build_name
18
+ @state["detail"] += " in " + Group.owning(@number, @grouparray).first.name
19
+ end
20
+
21
+ def array(selectors)
22
+ selectors.map { |key| @state[key] }
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ require_relative "hue_resource"
2
+ class Rule < HueResource
3
+
4
+ attr_reader :detail, :on, :name, :lights
5
+ def initialize(key, hashvalue)
6
+ super(key, hashvalue)
7
+ @detail = "#{hashvalue["conditions"].length} conds => #{hashvalue["actions"].length} acts"
8
+ @state.merge! ({"on" => @hash_value["status"], "name" => @name, "detail" => @detail})
9
+ gen_reskey("r")
10
+ end
11
+
12
+ def array(selectors)
13
+ selectors.map { |key| @state[key] }
14
+ end
15
+
16
+ def self.owning(number, grouparray)
17
+ grouparray.select { |group| group.lights.include? number}
18
+ end
19
+ end