Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
inherit_from:
- .rubocop_todo.yml

AllCops:
TargetRubyVersion: 2.7
NewCops: enable
Expand Down
5 changes: 5 additions & 0 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Metrics/MethodLength:
Max: 20

Metrics/ClassLength:
Max: 200
40 changes: 34 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
# NovaCloud Client (WIP)

Sprint 01 delivered the core HTTP client for the NovaCloud Open Platform. The gem now:
Sprint 01 delivered the core HTTP client for the NovaCloud Open Platform. The gem now:

- Manages configuration once (`app_key`, `app_secret`, `service_domain`).
- Handles authentication headers automatically via Faraday middleware.
- Maps HTTP errors to a typed exception hierarchy.
- Normalizes GET/POST payloads and parses JSON responses.

Sprint 02 expands on this foundation with dedicated resource helpers (`client.players`, `client.control`) and typed response objects (e.g., `NovacloudClient::Objects::Player`).

## Resource Overview

- **Players**: `list`, `statuses`, `running_status`
- **Control**: `brightness`, `volume`, `video_source`, `screen_power`, `screen_status`, `screenshot`, `reboot`, `request_result`
- **Screens** (VNNOXCare): `list`, `monitor`, `detail`
- **Logs**: `control_history`

## Quick Start

```ruby
Expand All @@ -18,11 +27,22 @@ client = NovacloudClient::Client.new(
service_domain: "open-us.vnnox.com"
)

response = client.request(
http_method: :get,
endpoint: "/v2/player/list",
params: { start: 0, count: 20 }
players = client.players.list(count: 20)
first_player = players.first

statuses = client.players.statuses(player_ids: players.map(&:player_id))

request = client.control.brightness(
player_ids: players.map(&:player_id),
brightness: 80,
notice_url: "https://example.com/callback"
)

result = client.control.request_result(request_id: request.request_id)
puts result.all_successful?

screens = client.screens.list(status: 1)
puts screens.first.name
```

### Development
Expand All @@ -33,4 +53,12 @@ bundle exec rspec
bundle exec rubocop
```

Sprint 02 will add resource helpers (e.g., `client.players.list`) and response objects built on these foundations.
### Documentation

Run YARD to generate HTML API documentation for the gem:

```bash
bundle exec yard doc
```

Then browse the docs via the generated `doc/index.html` or launch a local server with `bundle exec yard server --reload`.
20 changes: 20 additions & 0 deletions lib/novacloud_client/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
require_relative "configuration"
require_relative "middleware/authentication"
require_relative "middleware/error_handler"
require_relative "resources/players"
require_relative "resources/control"
require_relative "resources/screens"
require_relative "resources/logs"

module NovacloudClient
# Central entry point for interacting with the NovaCloud API.
Expand Down Expand Up @@ -37,6 +41,22 @@ def request(http_method:, endpoint:, params: {})
parse_body(response)
end

def players
@players ||= Resources::Players.new(self)
end

def control
@control ||= Resources::Control.new(self)
end

def screens
@screens ||= Resources::Screens.new(self)
end

def logs
@logs ||= Resources::Logs.new(self)
end

private

def build_connection
Expand Down
49 changes: 49 additions & 0 deletions lib/novacloud_client/objects/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# frozen_string_literal: true

require "time"

module NovacloudClient
module Objects
# Base class providing attribute assignment and coercion helpers.
class Base
def initialize(attributes = {})
assign_attributes(attributes)
end

private

def assign_attributes(attributes)
attributes.each do |key, value|
writer = "#{key}="
if respond_to?(writer)
public_send(writer, value)
next
end

normalized_writer = normalize_writer(key)
next if normalized_writer == writer

public_send(normalized_writer, value) if respond_to?(normalized_writer)
end
end

def parse_timestamp(value)
return nil if value.nil?
return value if value.is_a?(Time)

Time.parse(value.to_s)
rescue ArgumentError
value
end

def normalize_writer(key)
normalized = key.to_s
normalized = normalized.gsub(/([A-Z\d]+)([A-Z][a-z])/, "\\1_\\2")
.gsub(/([a-z\d])([A-Z])/, "\\1_\\2")
.tr("-", "_")
.downcase
"#{normalized}="
end
end
end
end
21 changes: 21 additions & 0 deletions lib/novacloud_client/objects/control_log_entry.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

require_relative "base"

module NovacloudClient
module Objects
# Represents an execution record for a remote control command.
class ControlLogEntry < Base
attr_accessor :status, :type
attr_reader :execute_time

def execute_time=(value)
@execute_time = parse_timestamp(value)
end

def success?
status.to_i.zero? ? false : status.to_i == 1
end
end
end
end
55 changes: 55 additions & 0 deletions lib/novacloud_client/objects/control_result.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

require_relative "base"

module NovacloudClient
module Objects
# Represents the result of a control or queued request returning success/fail lists.
class ControlResult < Base
attr_reader :successes, :failures

def initialize(attributes = {})
@successes = []
@failures = []
super
end

def success=(value)
@successes = Array(value)
end

def fail=(value)
@failures = Array(value)
end

def all_successful?
failures.empty?
end

def partial_success?
successes.any? && failures.any?
end

def all_failed?
successes.empty?
end

def success_count
successes.size
end

def failure_count
failures.size
end

# Maintain API compatibility with camelCase keys.
def success
successes
end

def fail
failures
end
end
end
end
37 changes: 37 additions & 0 deletions lib/novacloud_client/objects/player.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

require_relative "base"

module NovacloudClient
module Objects
# Represents a player returned from NovaCloud player APIs.
class Player < Base
attr_accessor :player_id, :player_type, :name, :sn, :version, :ip
attr_reader :last_online_time, :online_status

def last_online_time=(value)
@last_online_time = parse_timestamp(value)
end

def online_status=(value)
@online_status = value.to_i
end

def online?
online_status == 1
end

def offline?
!online?
end

def synchronous?
player_type.to_i == 1
end

def asynchronous?
player_type.to_i == 2
end
end
end
end
29 changes: 29 additions & 0 deletions lib/novacloud_client/objects/player_status.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

require_relative "base"

module NovacloudClient
module Objects
# Represents basic status information for a player.
class PlayerStatus < Base
attr_accessor :player_id, :sn
attr_reader :online_status, :last_online_time

def online_status=(value)
@online_status = value.to_i
end

def last_online_time=(value)
@last_online_time = parse_timestamp(value)
end

def online?
online_status == 1
end

def offline?
!online?
end
end
end
end
17 changes: 17 additions & 0 deletions lib/novacloud_client/objects/queued_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

require_relative "control_result"

module NovacloudClient
module Objects
# Represents the enqueue result for asynchronous player commands.
class QueuedRequest < ControlResult
attr_reader :request_id

# Queue responses include a request ID used to poll for results later.
def request_id=(value)
@request_id = value&.to_s
end
end
end
end
21 changes: 21 additions & 0 deletions lib/novacloud_client/objects/screen.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

require_relative "base"

module NovacloudClient
module Objects
# Represents a screen/device entry from VNNOXCare APIs.
class Screen < Base
attr_accessor :sid, :name, :mac, :sn, :address, :longitude, :latitude,
:status, :camera, :brightness, :env_brightness

def online?
status.to_i == 1
end

def camera_enabled?
camera.to_i == 1
end
end
end
end
29 changes: 29 additions & 0 deletions lib/novacloud_client/objects/screen_detail.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

require_relative "base"

module NovacloudClient
module Objects
# Represents detailed telemetry for a screen, including nested hardware info.
class ScreenDetail < Base
attr_accessor :identifier, :input_source, :mac, :master_control, :module,
:monitor_card, :receiving_card, :screen, :sid, :smart_module, :sn

NESTED_HASH_FIELDS = %i[input_source master_control module monitor_card receiving_card screen smart_module].freeze

def initialize(attributes = {})
super
ensure_nested_hashes!
end

private

def ensure_nested_hashes!
NESTED_HASH_FIELDS.each do |field|
value = public_send(field)
public_send("#{field}=", value || {})
end
end
end
end
end
12 changes: 12 additions & 0 deletions lib/novacloud_client/objects/screen_monitor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

require_relative "base"

module NovacloudClient
module Objects
# Represents high-level monitoring metrics for a screen device.
class ScreenMonitor < Base
attr_accessor :display_device, :brightness, :env_brightness, :height, :width, :sn
end
end
end
Loading