fritzbox-smarthome 0.5.0 → 0.7.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/.ruby-version +1 -1
- data/CHANGELOG.md +11 -0
- data/README.md +6 -0
- data/fritzbox-smarthome.gemspec +1 -1
- data/lib/fritzbox/smarthome/actor.rb +45 -18
- data/lib/fritzbox/smarthome/heater.rb +12 -12
- data/lib/fritzbox/smarthome/resource.rb +45 -11
- data/lib/fritzbox/smarthome/session.rb +22 -0
- data/lib/fritzbox/smarthome/smoke_detector.rb +8 -8
- data/lib/fritzbox/smarthome/switch.rb +15 -15
- data/lib/fritzbox/smarthome/version.rb +1 -1
- data/lib/fritzbox/smarthome.rb +1 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4ed289626e1d8c8cacccb9f89eed39178c5b11c781c99a649b3872521a3d0d4
|
4
|
+
data.tar.gz: 02b35061da61c6f1084b87192eefe55cc83113526addf7caff584240256997f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2840a781e1717f8e9b11fa1a18929b316c930e57ee3b5e57a24574ad2f065c1b44fa3837e85dc0e88367ff1f402826d5210d8bbf2b9a9079b48dbe655a9d1e8
|
7
|
+
data.tar.gz: 02f972de29e194f5c42f7671c3caddfdfe046951e0fd91095f4614412c5afb76a1861ed71211f89d0e44b52ff909dd79df4f7e93d558d50e9903d217ab0e8be4
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.
|
1
|
+
3.2.0
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v0.7.0
|
4
|
+
|
5
|
+
* Measure HTTP request duration
|
6
|
+
* Print the request time in debug log-level
|
7
|
+
* Cache authentication between requests
|
8
|
+
* Keep the returned SessionID for 60 minutes in memory
|
9
|
+
|
10
|
+
## v0.6.0
|
11
|
+
|
12
|
+
* Add support for `Actor.find_by!(ain:)` and `Actor#reload`
|
13
|
+
|
3
14
|
## v0.5.0
|
4
15
|
|
5
16
|
* Unify on/off interface
|
data/README.md
CHANGED
@@ -41,6 +41,12 @@ actors = Fritzbox::Smarthome::Actor.all
|
|
41
41
|
# Get all actors of type Heater
|
42
42
|
heaters = Fritzbox::Smarthome::Heater.all
|
43
43
|
heaters.last.update_hkr_temp_set(BigDecimal('21.5'))
|
44
|
+
|
45
|
+
# Get a specific actor via it's AIN
|
46
|
+
actor = Fritzbox::Smarthome::Actor.find_by!(ain: '0815 4711')
|
47
|
+
|
48
|
+
# Reload the data of an already fetched actor
|
49
|
+
actor.reload
|
44
50
|
```
|
45
51
|
|
46
52
|
## Development
|
data/fritzbox-smarthome.gemspec
CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
|
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
-
f.match(%r{^(test|spec|features)/})
|
17
|
+
f.match(%r{^(test|spec|features|examples)/})
|
18
18
|
end
|
19
19
|
spec.bindir = 'exe'
|
20
20
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
@@ -4,36 +4,63 @@ module Fritzbox
|
|
4
4
|
include ActiveModel::Model
|
5
5
|
|
6
6
|
attr_accessor \
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
:id,
|
8
|
+
:type,
|
9
|
+
:ain,
|
10
|
+
:present,
|
11
|
+
:name,
|
12
|
+
:manufacturer,
|
13
|
+
:group_members
|
14
|
+
|
15
|
+
ResourceNotFound = Class.new(RuntimeError)
|
14
16
|
|
15
17
|
class << self
|
16
18
|
def all(types: ['group', 'device'])
|
17
|
-
|
18
|
-
|
19
|
+
xml = parse(get(command: 'getdevicelistinfos'))
|
20
|
+
|
19
21
|
Array.wrap(types.map { |type| xml.dig('devicelist', type) }.flatten).compact.map do |data|
|
20
22
|
klass = Actor.descendants.find { |k| k.match?(data) } || Actor
|
21
23
|
self.in?([klass, Actor]) ? klass.new_from_api(data) : nil
|
22
24
|
end.compact
|
23
25
|
end
|
24
26
|
|
27
|
+
def find_by!(ain: nil)
|
28
|
+
data = parse(get(command: 'getdeviceinfos', ain: ain)).fetch('device')
|
29
|
+
klass = Actor.descendants.find { |k| k.match?(data) } || Actor
|
30
|
+
|
31
|
+
instance = klass.new(ain: ain)
|
32
|
+
instance.assign_from_api(data)
|
33
|
+
instance
|
34
|
+
rescue KeyError
|
35
|
+
raise ResourceNotFound, "Unable to find actor with ain='#{ain}'"
|
36
|
+
end
|
37
|
+
|
25
38
|
def new_from_api(data)
|
26
|
-
new
|
27
|
-
|
28
|
-
|
29
|
-
ain: data.dig('@identifier').to_s,
|
30
|
-
present: data.dig('present') == '1',
|
31
|
-
name: (data.dig('name') || data.dig('@productname')).to_s,
|
32
|
-
manufacturer: (data.dig('manufacturer') || data.dig('@manufacturer')).to_s,
|
33
|
-
group_members: data.dig('groupinfo', 'members').to_s.split(',').presence
|
34
|
-
)
|
39
|
+
instance = new
|
40
|
+
instance.assign_from_api(data)
|
41
|
+
instance
|
35
42
|
end
|
36
43
|
end
|
44
|
+
|
45
|
+
def assign_from_api(data)
|
46
|
+
assign_attributes(
|
47
|
+
id: data.dig('@id').to_s,
|
48
|
+
type: data.dig('groupinfo').present? ? :group : :device,
|
49
|
+
ain: data.dig('@identifier').to_s,
|
50
|
+
present: data.dig('present') == '1',
|
51
|
+
name: (data.dig('name') || data.dig('@productname')).to_s,
|
52
|
+
manufacturer: (data.dig('manufacturer') || data.dig('@manufacturer')).to_s,
|
53
|
+
group_members: data.dig('groupinfo', 'members').to_s.split(',').presence
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def reload
|
58
|
+
xml = parse(get(command: 'getdeviceinfos', ain: ain))
|
59
|
+
assign_from_api(xml.fetch('device'))
|
60
|
+
self
|
61
|
+
rescue KeyError
|
62
|
+
raise ResourceNotFound, "Unable to reload actor with ain='#{ain}'"
|
63
|
+
end
|
37
64
|
end
|
38
65
|
end
|
39
66
|
end
|
@@ -14,19 +14,19 @@ module Fritzbox
|
|
14
14
|
def match?(data)
|
15
15
|
data.key?('hkr')
|
16
16
|
end
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
)
|
28
|
-
|
29
|
-
end
|
19
|
+
def assign_from_api(data)
|
20
|
+
super(data)
|
21
|
+
|
22
|
+
assign_attributes(
|
23
|
+
battery: data.dig('battery').to_i,
|
24
|
+
batterylow: data.dig('batterylow').to_i,
|
25
|
+
hkr_temp_is: data.dig('hkr', 'tist').to_i * 0.5,
|
26
|
+
hkr_temp_set: data.dig('hkr', 'tsoll').to_i * 0.5,
|
27
|
+
hkr_next_change_period: Time.at(data.dig('hkr', 'nextchange', 'endperiod').to_i),
|
28
|
+
hkr_next_change_temp: data.dig('hkr', 'nextchange', 'tchange').to_i * 0.5
|
29
|
+
)
|
30
30
|
end
|
31
31
|
|
32
32
|
def update_hkr_temp_set(value)
|
@@ -1,6 +1,10 @@
|
|
1
1
|
module Fritzbox
|
2
2
|
module Smarthome
|
3
3
|
class Resource
|
4
|
+
cattr_accessor :session
|
5
|
+
|
6
|
+
AuthenticationError = Class.new(RuntimeError)
|
7
|
+
|
4
8
|
class << self
|
5
9
|
# @param params [Hash] key/value pairs that will be appended to the switchcmd query string
|
6
10
|
def get(command:, ain: nil, param: nil, **params)
|
@@ -12,9 +16,20 @@ module Fritzbox
|
|
12
16
|
url = "#{url}&#{key}=#{value}"
|
13
17
|
end
|
14
18
|
|
15
|
-
|
19
|
+
response = measure(url) { HTTParty.get(url, **httparty_options) }
|
20
|
+
|
21
|
+
raise AuthenticationError if response.code == 403
|
22
|
+
|
23
|
+
response
|
24
|
+
rescue AuthenticationError
|
25
|
+
raise if session.nil?
|
16
26
|
|
17
|
-
|
27
|
+
self.session = nil
|
28
|
+
retry
|
29
|
+
end
|
30
|
+
|
31
|
+
def parse(response)
|
32
|
+
nori.parse(response.body)
|
18
33
|
end
|
19
34
|
|
20
35
|
private
|
@@ -22,19 +37,28 @@ module Fritzbox
|
|
22
37
|
delegate :config, to: Smarthome
|
23
38
|
|
24
39
|
def authenticate
|
25
|
-
|
26
|
-
|
27
|
-
|
40
|
+
return session.id if session.present? && session.valid?
|
41
|
+
|
42
|
+
session_id = measure("authentication") do
|
43
|
+
response = HTTParty.get(login_endpoint, **httparty_options)
|
44
|
+
xml = nori.parse(response.body)
|
45
|
+
challenge = xml.dig('SessionInfo', 'Challenge')
|
28
46
|
|
29
|
-
|
47
|
+
md5 = Digest::MD5.hexdigest("#{challenge}-#{config.password}".encode('UTF-16LE'))
|
30
48
|
|
31
|
-
|
32
|
-
|
49
|
+
url = "#{login_endpoint}?response=#{challenge}-#{md5}"
|
50
|
+
url = "#{url}&username=#{config.username}" if config.username.present?
|
33
51
|
|
34
|
-
|
52
|
+
response = HTTParty.get(url, **httparty_options)
|
35
53
|
|
36
|
-
|
37
|
-
|
54
|
+
xml = nori.parse(response.body)
|
55
|
+
|
56
|
+
xml.dig('SessionInfo', 'SID')
|
57
|
+
end
|
58
|
+
|
59
|
+
self.session = Session.new(session_id)
|
60
|
+
|
61
|
+
session_id
|
38
62
|
end
|
39
63
|
|
40
64
|
def login_endpoint
|
@@ -51,7 +75,17 @@ module Fritzbox
|
|
51
75
|
def nori
|
52
76
|
@nori ||= Nori.new
|
53
77
|
end
|
78
|
+
|
79
|
+
def measure(identifier, &block)
|
80
|
+
time_start = Time.now
|
81
|
+
result = block.call
|
82
|
+
time_elapsed = (Time.now - time_start).to_f.round(3)
|
83
|
+
config.logger.debug("Request `#{identifier}` took #{time_elapsed} seconds")
|
84
|
+
result
|
85
|
+
end
|
54
86
|
end
|
87
|
+
|
88
|
+
delegate :get, :parse, to: :class
|
55
89
|
end
|
56
90
|
end
|
57
91
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Fritzbox
|
2
|
+
module Smarthome
|
3
|
+
class Session
|
4
|
+
TIMEOUT_MINUTES = 60
|
5
|
+
|
6
|
+
def initialize(id)
|
7
|
+
self.id = id
|
8
|
+
self.valid_until = Time.now + TIMEOUT_MINUTES.minutes
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid?
|
12
|
+
self.valid_until > Time.now
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :id, :valid_until
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_writer :id, :valid_until
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -10,15 +10,15 @@ module Fritzbox
|
|
10
10
|
def match?(data)
|
11
11
|
data.key?('alert')
|
12
12
|
end
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
)
|
20
|
-
|
21
|
-
end
|
15
|
+
def assign_from_api(data)
|
16
|
+
super(data)
|
17
|
+
|
18
|
+
assign_attributes(
|
19
|
+
alert_state: data.dig('alert', 'state').to_i,
|
20
|
+
last_alert: Time.at(data.dig('alert', 'lastalertchgtimestamp').to_i)
|
21
|
+
)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -19,22 +19,22 @@ module Fritzbox
|
|
19
19
|
def match?(data)
|
20
20
|
data.key?('switch')
|
21
21
|
end
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
)
|
36
|
-
|
37
|
-
end
|
24
|
+
def assign_from_api(data)
|
25
|
+
super(data)
|
26
|
+
|
27
|
+
assign_attributes(
|
28
|
+
switch_state: data.dig('switch', 'state').to_i,
|
29
|
+
switch_mode: data.dig('switch', 'mode').to_s,
|
30
|
+
switch_lock: data.dig('switch', 'lock').to_i,
|
31
|
+
switch_devicelock: data.dig('switch', 'devicelock').to_i,
|
32
|
+
powermeter_voltage: data.dig('powermeter', 'voltage').to_i,
|
33
|
+
powermeter_power: data.dig('powermeter', 'power').to_i,
|
34
|
+
powermeter_energy: data.dig('powermeter', 'energy').to_i,
|
35
|
+
temperature_celsius: data.dig('temperature', 'celsius').to_i,
|
36
|
+
temperature_offset: data.dig('temperature', 'offset').to_i
|
37
|
+
)
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
data/lib/fritzbox/smarthome.rb
CHANGED
@@ -6,6 +6,7 @@ require 'nori'
|
|
6
6
|
require 'fritzbox/smarthome/version'
|
7
7
|
require 'fritzbox/smarthome/null_logger'
|
8
8
|
require 'fritzbox/smarthome/properties'
|
9
|
+
require 'fritzbox/smarthome/session'
|
9
10
|
require 'fritzbox/smarthome/resource'
|
10
11
|
require 'fritzbox/smarthome/actor'
|
11
12
|
require 'fritzbox/smarthome/heater'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fritzbox-smarthome
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Klaus Meyer
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-12-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -176,6 +176,7 @@ files:
|
|
176
176
|
- lib/fritzbox/smarthome/properties.rb
|
177
177
|
- lib/fritzbox/smarthome/properties/simple_on_off.rb
|
178
178
|
- lib/fritzbox/smarthome/resource.rb
|
179
|
+
- lib/fritzbox/smarthome/session.rb
|
179
180
|
- lib/fritzbox/smarthome/smoke_detector.rb
|
180
181
|
- lib/fritzbox/smarthome/switch.rb
|
181
182
|
- lib/fritzbox/smarthome/version.rb
|
@@ -198,7 +199,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
198
199
|
- !ruby/object:Gem::Version
|
199
200
|
version: '0'
|
200
201
|
requirements: []
|
201
|
-
rubygems_version: 3.
|
202
|
+
rubygems_version: 3.4.1
|
202
203
|
signing_key:
|
203
204
|
specification_version: 4
|
204
205
|
summary: Client library to interface with Smarthome features of your FritzBox
|