agent_status_bulb 0.1.0 → 0.2.0
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 +4 -4
- data/.rubocop.yml +6 -0
- data/CHANGELOG.md +8 -0
- data/exe/asb +7 -1
- data/lib/agent_status_bulb/bulb.rb +43 -0
- data/lib/agent_status_bulb/cli.rb +34 -56
- data/lib/agent_status_bulb/configure.rb +37 -22
- data/lib/agent_status_bulb/version.rb +1 -1
- metadata +17 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1687e4b5c95d5ab157aa65b015cc8ea7ca7f57790271819d4ff279ca3a550e3e
|
|
4
|
+
data.tar.gz: 4d0125c57a0f6689f297bb7ba460040afe0455e6ead33d33628d34f4ca03e8f7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 894c08da441f4af27c49630952bd7219c771edd363a797a836738d45d7a2f01ea69edb2d21e49407db0248493a7703156a4f36c90457d22a43e612cabd49b4c5
|
|
7
|
+
data.tar.gz: 82a12d8f0f460f8295fe2adb3611eb09167eb061948a4acd5af377b6d5429ab5ee298930c4d8ae767ff0d53e9ae5916ba6d6f90f33f548c1894faa245d57e7e0
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.0] - 2025-11-19
|
|
4
|
+
|
|
5
|
+
- Introduced `AgentStatusBulb::Bulb` to wrap SwitchBot integration and provide run/wait/idle/off color APIs with automatic power control
|
|
6
|
+
- Rebuilt the CLI on top of Thor and routed every command through `handle_error` to normalize error handling
|
|
7
|
+
- Simplified configuration input/save/load flows by driving them from a single field definition list
|
|
8
|
+
- Added RSpec coverage for CLI, Configure, and Bulb to document expected behaviour
|
|
9
|
+
- Added RuboCop configuration to keep coding style consistent
|
|
10
|
+
|
|
3
11
|
## [0.1.0] - 2025-11-18
|
|
4
12
|
|
|
5
13
|
- Initial release
|
data/exe/asb
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'switchbot'
|
|
4
|
+
|
|
5
|
+
module AgentStatusBulb
|
|
6
|
+
class Bulb
|
|
7
|
+
def self.from_config(config)
|
|
8
|
+
client = Switchbot::Client.new(config.fetch(:token), config.fetch(:secret))
|
|
9
|
+
new(client.color_bulb(config.fetch(:device_id)))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(device)
|
|
13
|
+
@device = device
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def blue
|
|
17
|
+
apply_color('0:0:255')
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def orange
|
|
21
|
+
apply_color('255:125:0')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def green
|
|
25
|
+
apply_color('0:255:0')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def off
|
|
29
|
+
@device.off
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def apply_color(value)
|
|
35
|
+
turn_on_if_needed
|
|
36
|
+
@device.color(value)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def turn_on_if_needed
|
|
40
|
+
@device.on if @device.off?
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -1,78 +1,56 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'thor'
|
|
3
4
|
require 'agent_status_bulb'
|
|
4
|
-
require 'switchbot'
|
|
5
5
|
require 'agent_status_bulb/configure'
|
|
6
|
+
require 'agent_status_bulb/bulb'
|
|
6
7
|
|
|
7
8
|
module AgentStatusBulb
|
|
8
|
-
class Cli
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def initialize(argv, io: $stdin, out: $stdout, err: $stderr)
|
|
14
|
-
@argv = argv.dup
|
|
15
|
-
@io = io
|
|
16
|
-
@out = out
|
|
17
|
-
@err = err
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def run
|
|
21
|
-
command = @argv.shift
|
|
22
|
-
handle_command(command)
|
|
23
|
-
rescue StandardError => e
|
|
24
|
-
@err.puts(e.message)
|
|
25
|
-
1
|
|
26
|
-
end
|
|
9
|
+
class Cli < Thor
|
|
10
|
+
map 'run' => :run_command
|
|
11
|
+
map 'wait' => :wait_command
|
|
12
|
+
map 'idle' => :idle_command
|
|
13
|
+
map 'off' => :off_command
|
|
27
14
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return configure! if command == 'configure'
|
|
32
|
-
return apply!(command) if status_colors.key?(command) || command == 'off'
|
|
33
|
-
|
|
34
|
-
@err.puts usage
|
|
35
|
-
1
|
|
15
|
+
desc 'configure', 'Save Token/Secret/Device ID'
|
|
16
|
+
def configure
|
|
17
|
+
handle_error { configurator.configure! }
|
|
36
18
|
end
|
|
37
19
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
20
|
+
desc 'run', 'Set color to running (blue)'
|
|
21
|
+
def run_command
|
|
22
|
+
handle_error { bulb.blue }
|
|
41
23
|
end
|
|
42
24
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
0
|
|
25
|
+
desc 'wait', 'Set color to waiting (orange)'
|
|
26
|
+
def wait_command
|
|
27
|
+
handle_error { bulb.orange }
|
|
47
28
|
end
|
|
48
29
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
bulb.on if bulb.off?
|
|
53
|
-
bulb
|
|
30
|
+
desc 'idle', 'Set color to idle (green)'
|
|
31
|
+
def idle_command
|
|
32
|
+
handle_error { bulb.green }
|
|
54
33
|
end
|
|
55
34
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
bulb.color(status_colors.fetch(command))
|
|
35
|
+
desc 'off', 'Turn off the bulb'
|
|
36
|
+
def off_command
|
|
37
|
+
handle_error { bulb.off }
|
|
60
38
|
end
|
|
61
39
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
40
|
+
no_commands do
|
|
41
|
+
def handle_error
|
|
42
|
+
yield
|
|
43
|
+
rescue StandardError => e
|
|
44
|
+
raise Thor::Error, e.message
|
|
45
|
+
end
|
|
65
46
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
47
|
+
def bulb
|
|
48
|
+
@bulb ||= AgentStatusBulb::Bulb.from_config(configurator.load!)
|
|
49
|
+
end
|
|
69
50
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
'run' => '0:0:255',
|
|
74
|
-
'wait' => '255:125:0'
|
|
75
|
-
}
|
|
51
|
+
def configurator
|
|
52
|
+
@configurator ||= AgentStatusBulb::Configure.new
|
|
53
|
+
end
|
|
76
54
|
end
|
|
77
55
|
end
|
|
78
56
|
end
|
|
@@ -7,44 +7,59 @@ require 'io/console'
|
|
|
7
7
|
module AgentStatusBulb
|
|
8
8
|
class Configure
|
|
9
9
|
DEFAULT_PATH = File.expand_path('~/.config/agent_status_bulb.yml').freeze
|
|
10
|
+
CREDENTIAL_FIELDS = [
|
|
11
|
+
{ key: :token, label: 'Token', conceal: true },
|
|
12
|
+
{ key: :secret, label: 'Secret', conceal: true },
|
|
13
|
+
{ key: :device_id, label: 'Device ID', conceal: false }
|
|
14
|
+
].freeze
|
|
10
15
|
|
|
11
|
-
def initialize(path
|
|
16
|
+
def initialize(path = DEFAULT_PATH)
|
|
12
17
|
@path = path
|
|
13
|
-
@io = io
|
|
14
|
-
@out = out
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
def configure!
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
FileUtils.mkdir_p(File.dirname(@path))
|
|
23
|
-
File.write(@path, { 'token' => token, 'secret' => secret, 'device_id' => device_id }.to_yaml)
|
|
24
|
-
@out.puts "Saved config to: #{@path}"
|
|
21
|
+
credentials = gather_credentials
|
|
22
|
+
write_config(credentials)
|
|
23
|
+
$stdout.puts "Saved config to: #{@path}"
|
|
25
24
|
end
|
|
26
25
|
|
|
27
26
|
def load!
|
|
28
|
-
|
|
27
|
+
data = read_config
|
|
28
|
+
CREDENTIAL_FIELDS.each_with_object({}) do |field, result|
|
|
29
|
+
value = data.fetch(field[:key].to_s, '').to_s.strip
|
|
30
|
+
raise missing_config_error if value.empty?
|
|
31
|
+
|
|
32
|
+
result[field[:key]] = value
|
|
33
|
+
end
|
|
34
|
+
end
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
token = data['token'].to_s
|
|
32
|
-
secret = data['secret'].to_s
|
|
33
|
-
device_id = data['device_id'].to_s
|
|
36
|
+
private
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
def gather_credentials
|
|
39
|
+
CREDENTIAL_FIELDS.each_with_object({}) do |field, result|
|
|
40
|
+
result[field[:key]] = prompt(field[:label], conceal: field[:conceal])
|
|
37
41
|
end
|
|
42
|
+
end
|
|
38
43
|
|
|
39
|
-
|
|
44
|
+
def write_config(credentials)
|
|
45
|
+
FileUtils.mkdir_p(File.dirname(@path))
|
|
46
|
+
File.write(@path, credentials.transform_keys(&:to_s).to_yaml)
|
|
40
47
|
end
|
|
41
48
|
|
|
42
|
-
|
|
49
|
+
def read_config
|
|
50
|
+
raise "Config file not found: #{@path}" unless File.file?(@path)
|
|
51
|
+
|
|
52
|
+
YAML.safe_load_file(@path, permitted_classes: [], aliases: false) || {}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def missing_config_error
|
|
56
|
+
"Config must contain token, secret, and device_id: #{@path}"
|
|
57
|
+
end
|
|
43
58
|
|
|
44
59
|
def prompt(label, conceal: false)
|
|
45
|
-
|
|
46
|
-
input = conceal ?
|
|
47
|
-
|
|
60
|
+
$stdout.print "#{label}: "
|
|
61
|
+
input = conceal ? $stdin.noecho(&:gets) : $stdin.gets
|
|
62
|
+
$stdout.puts if conceal
|
|
48
63
|
input.to_s.strip
|
|
49
64
|
end
|
|
50
65
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: agent_status_bulb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yoshiki Takagi
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-11-
|
|
11
|
+
date: 2025-11-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: switchbot
|
|
@@ -24,6 +24,20 @@ dependencies:
|
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: thor
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
27
41
|
description: Provides simple commands (asb run, asb wait, asb idle) to visualize the
|
|
28
42
|
state of any agent or task through color changes.
|
|
29
43
|
email:
|
|
@@ -42,6 +56,7 @@ files:
|
|
|
42
56
|
- Rakefile
|
|
43
57
|
- exe/asb
|
|
44
58
|
- lib/agent_status_bulb.rb
|
|
59
|
+
- lib/agent_status_bulb/bulb.rb
|
|
45
60
|
- lib/agent_status_bulb/cli.rb
|
|
46
61
|
- lib/agent_status_bulb/configure.rb
|
|
47
62
|
- lib/agent_status_bulb/version.rb
|