fritzbox-smarthome 0.1.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 782cdee20c938c01c4ee6f6560e05f7a7636d8b9315e6c63f577959e3df85f45
4
- data.tar.gz: a33ea016b04d0a710d13e0e1089850d74f1d16f14e9346683fac961640be2851
3
+ metadata.gz: 430136a0736fded1a9e2cf308cdeb00129e94514259915306146fc65be4fb04a
4
+ data.tar.gz: 8755e3f921c2c815b1e6d38d3372bcfff068e6a8aa7a47785d07213585e67101
5
5
  SHA512:
6
- metadata.gz: af9c5ed89753c194f8eefaf29dcf4893e9f68c99e7595cb95196b778b9bdbce2a7a6e2efa0b650ddf52f5fccab2e19f0bd065be68a4d805f1f94b96990a6fd61
7
- data.tar.gz: 4aabacf20397befaf87d069307e2bc525bfaee04ecae6563d63ee4b1c6487f62ede5f75d850257e63fecf87ade33c21b7ef85877a00de4fde861b763b0fb7137
6
+ metadata.gz: 94ac858e01f25649f9171df8cf685658b4a4e17f5c230a1394569c37d3052444ed1b155d8eb9a58d61fc056eb471347ee5c0c163b0a777651a8abceac4a3c76a
7
+ data.tar.gz: 198756b6e8a1d7d2cfcf886e26f5e2efd3d75eb655d2533e94074c936b42da531d58147ceba13e3a54b5e621f66fe99f56fe5652cbdd6c2b64294a095fa5e22e
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.0.0
1
+ 3.1.2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.4.0
4
+
5
+ * Add support for Fritz!DECT lightbulbs
6
+
7
+ ## v0.3.0
8
+
9
+ * Use generic Actor class for unrecognised device
10
+ * Allow usage in rails up to 7.1.x
11
+ * Use ruby 3.1.x in development
12
+ * Update a other dependencies
13
+
14
+ ## v0.2.0
15
+
16
+ **Attention**: This release contains breaking changes! Check [README](README.md) for new interface.
17
+
18
+ * Add support for more actors types: switches and smoke detector
19
+ * Extract heater specific attributes and logic to own class
20
+
3
21
  ## v0.1.3
4
22
 
5
23
  * avoid nil error in actors:all smart devices request
data/README.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  Ruby client library to interface with Smarthome features of your FritzBox.
4
4
 
5
+ Currently implemented actor types:
6
+
7
+ * Heater
8
+ * SmokeDetector
9
+ * Switch
10
+
5
11
  ## Installation
6
12
 
7
13
  Add this line to your application's Gemfile:
@@ -28,10 +34,12 @@ Fritzbox::Smarthome.configure do |config|
28
34
  config.verify_ssl = false
29
35
  end
30
36
 
37
+ # Get all actors of any type
31
38
  actors = Fritzbox::Smarthome::Actor.all
32
39
 
33
- actor = actors.last
34
- actor.update_hkr_temp_set(BigDecimal.new('21.5'))
40
+ # Get all actors of type Heater
41
+ heaters = Fritzbox::Smarthome::Heater.all
42
+ heaters.last.update_hkr_temp_set(BigDecimal('21.5'))
35
43
  ```
36
44
 
37
45
  ## Development
@@ -20,14 +20,14 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ['lib']
22
22
 
23
- spec.add_dependency 'activesupport', '>= 5.1', '< 6.2'
24
- spec.add_dependency 'activemodel', '>= 5.1', '< 6.2'
25
- spec.add_dependency 'httparty', '~> 0.16'
23
+ spec.add_dependency 'activesupport', '>= 5.1', '<= 7.1'
24
+ spec.add_dependency 'activemodel', '>= 5.1', '<= 7.1'
25
+ spec.add_dependency 'httparty', '~> 0.20'
26
26
  spec.add_dependency 'nori', '~> 2.6'
27
- spec.add_dependency 'nokogiri', '~> 1.8'
27
+ spec.add_dependency 'nokogiri', '~> 1.13'
28
28
 
29
29
  spec.add_development_dependency 'rake', '~> 13.0'
30
- spec.add_development_dependency 'rspec', '~> 3.0'
31
- spec.add_development_dependency 'webmock', '~> 3.3'
32
- spec.add_development_dependency 'byebug', '~> 11.0'
30
+ spec.add_development_dependency 'rspec', '~> 3.11'
31
+ spec.add_development_dependency 'webmock', '~> 3.14'
32
+ spec.add_development_dependency 'byebug', '~> 11.1'
33
33
  end
@@ -9,49 +9,31 @@ module Fritzbox
9
9
  :ain,
10
10
  :present,
11
11
  :name,
12
- :hkr_temp_is,
13
- :hkr_temp_set,
14
- :hkr_next_change_period,
15
- :hkr_next_change_temp,
12
+ :manufacturer,
16
13
  :group_members
17
14
 
18
15
  class << self
19
16
  def all(types: ['group', 'device'])
20
17
  response = get(command: 'getdevicelistinfos')
21
18
  xml = nori.parse(response.body)
22
-
23
19
  Array.wrap(types.map { |type| xml.dig('devicelist', type) }.flatten).compact.map do |data|
24
- new_from_api(data)
25
- end
26
- end
27
-
28
- def only_heaters
29
- all.select { |record| record.hkr_temp_is.present? }
20
+ klass = Actor.descendants.find { |k| k.match?(data) } || Actor
21
+ self.in?([klass, Actor]) ? klass.new_from_api(data) : nil
22
+ end.compact
30
23
  end
31
24
 
32
25
  def new_from_api(data)
33
26
  new(
34
- id: data.dig('@id').to_s,
35
- type: data.dig('groupinfo').present? ? :group : :device,
36
- ain: data.dig('@identifier').to_s,
37
- present: data.dig('present') == '1',
38
- name: data.dig('name').to_s,
39
- hkr_temp_is: data.dig('hkr', 'tist').to_i * 0.5,
40
- hkr_temp_set: data.dig('hkr', 'tsoll').to_i * 0.5,
41
- hkr_next_change_period: Time.at(data.dig('hkr', 'nextchange', 'endperiod').to_i),
42
- hkr_next_change_temp: data.dig('hkr', 'nextchange', 'tchange').to_i * 0.5,
43
- group_members: data.dig('groupinfo', 'members').to_s.split(',').presence
27
+ id: data.dig('@id').to_s,
28
+ type: data.dig('groupinfo').present? ? :group : :device,
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
44
34
  )
45
35
  end
46
36
  end
47
-
48
- def update_hkr_temp_set(value)
49
- raise ArgumentError unless value.is_a? BigDecimal
50
- value = (value / 0.5).to_i
51
- response = self.class.get(command: 'sethkrtsoll', ain: ain, param: value)
52
- raise 'Could not set temperature' unless response.body == "#{value}\n"
53
- true
54
- end
55
37
  end
56
38
  end
57
39
  end
@@ -0,0 +1,41 @@
1
+ module Fritzbox
2
+ module Smarthome
3
+ class Heater < Actor
4
+
5
+ attr_accessor \
6
+ :battery,
7
+ :batterylow,
8
+ :hkr_temp_is,
9
+ :hkr_temp_set,
10
+ :hkr_next_change_period,
11
+ :hkr_next_change_temp
12
+
13
+ class << self
14
+ def match?(data)
15
+ data.key?('hkr')
16
+ end
17
+
18
+ def new_from_api(data)
19
+ instance = super
20
+ instance.assign_attributes(
21
+ battery: data.dig('battery').to_i,
22
+ batterylow: data.dig('batterylow').to_i,
23
+ hkr_temp_is: data.dig('hkr', 'tist').to_i * 0.5,
24
+ hkr_temp_set: data.dig('hkr', 'tsoll').to_i * 0.5,
25
+ hkr_next_change_period: Time.at(data.dig('hkr', 'nextchange', 'endperiod').to_i),
26
+ hkr_next_change_temp: data.dig('hkr', 'nextchange', 'tchange').to_i * 0.5
27
+ )
28
+ instance
29
+ end
30
+ end
31
+
32
+ def update_hkr_temp_set(value)
33
+ raise ArgumentError unless value.is_a? BigDecimal
34
+ value = (value / 0.5).to_i
35
+ response = self.class.get(command: 'sethkrtsoll', ain: ain, param: value)
36
+ raise 'Could not set temperature' unless response.body == "#{value}\n"
37
+ true
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fritzbox
4
+ module Smarthome
5
+ class Lightbulb < Actor
6
+
7
+ attr_accessor \
8
+ :simpleonoff_state
9
+
10
+ class << self
11
+ def match?(data)
12
+ data.fetch('@productname', '') =~ /FRITZ!DECT 5\d{2}/i
13
+ end
14
+
15
+ def new_from_api(data)
16
+ instance = super
17
+ instance.assign_attributes(
18
+ simpleonoff_state: data.dig('simpleonoff', 'state').to_i,
19
+ )
20
+ instance
21
+ end
22
+ end
23
+
24
+ def active?
25
+ simpleonoff_state == 1
26
+ end
27
+
28
+ def toggle!
29
+ value = active? ? 0 : 1
30
+ response = self.class.get(command: 'setsimpleonoff', ain: ain, onoff: value)
31
+
32
+ response.ok? && @simpleonoff_state = value
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fritzbox
4
+ module Smarthome
5
+ class NullLogger < Logger
6
+ def initialize(*_args)
7
+ super(File::NULL)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -2,11 +2,18 @@ module Fritzbox
2
2
  module Smarthome
3
3
  class Resource
4
4
  class << self
5
- def get(command:, ain: nil, param: nil)
5
+ # @param params [Hash] key/value pairs that will be appended to the switchcmd query string
6
+ def get(command:, ain: nil, param: nil, **params)
6
7
  url = "#{config.endpoint}/webservices/homeautoswitch.lua?switchcmd=#{command}&sid=#{authenticate}"
7
8
  url = "#{url}&ain=#{ain}" if ain.present?
8
9
  url = "#{url}&param=#{param}" if param.present?
9
10
 
11
+ params.each_with_object(url) do |(key, value)|
12
+ url = "#{url}&#{key}=#{value}"
13
+ end
14
+
15
+ config.logger.debug(url)
16
+
10
17
  HTTParty.get(url, **httparty_options)
11
18
  end
12
19
 
@@ -0,0 +1,25 @@
1
+ module Fritzbox
2
+ module Smarthome
3
+ class SmokeDetector < Actor
4
+
5
+ attr_accessor \
6
+ :alert_state,
7
+ :last_alert
8
+
9
+ class << self
10
+ def match?(data)
11
+ data.key?('alert')
12
+ end
13
+
14
+ def new_from_api(data)
15
+ instance = super
16
+ instance.assign_attributes(
17
+ alert_state: data.dig('alert', 'state').to_i,
18
+ last_alert: Time.at(data.dig('alert', 'lastalertchgtimestamp').to_i)
19
+ )
20
+ instance
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,41 @@
1
+ module Fritzbox
2
+ module Smarthome
3
+ class Switch < Actor
4
+
5
+ attr_accessor \
6
+ :switch_state,
7
+ :switch_mode,
8
+ :switch_lock,
9
+ :switch_devicelock,
10
+ :simpleonoff_state,
11
+ :powermeter_voltage,
12
+ :powermeter_power,
13
+ :powermeter_energy,
14
+ :temperature_celsius,
15
+ :temperature_offset
16
+
17
+ class << self
18
+ def match?(data)
19
+ data.key?('switch')
20
+ end
21
+
22
+ def new_from_api(data)
23
+ instance = super
24
+ instance.assign_attributes(
25
+ switch_state: data.dig('switch', 'state').to_i,
26
+ switch_mode: data.dig('switch', 'mode').to_s,
27
+ switch_lock: data.dig('switch', 'lock').to_i,
28
+ switch_devicelock: data.dig('switch', 'devicelock').to_i,
29
+ simpleonoff_state: data.dig('simpleonoff', 'state').to_i,
30
+ powermeter_voltage: data.dig('powermeter', 'voltage').to_i,
31
+ powermeter_power: data.dig('powermeter', 'power').to_i,
32
+ powermeter_energy: data.dig('powermeter', 'energy').to_i,
33
+ temperature_celsius: data.dig('temperature', 'celsius').to_i,
34
+ temperature_offset: data.dig('temperature', 'offset').to_i
35
+ )
36
+ instance
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,5 +1,5 @@
1
1
  module Fritzbox
2
2
  module Smarthome
3
- VERSION = '0.1.3'.freeze
3
+ VERSION = '0.4.0'.freeze
4
4
  end
5
5
  end
@@ -4,8 +4,13 @@ require 'httparty'
4
4
  require 'nori'
5
5
 
6
6
  require 'fritzbox/smarthome/version'
7
- require "fritzbox/smarthome/resource"
7
+ require 'fritzbox/smarthome/null_logger'
8
+ require 'fritzbox/smarthome/resource'
8
9
  require 'fritzbox/smarthome/actor'
10
+ require 'fritzbox/smarthome/heater'
11
+ require 'fritzbox/smarthome/switch'
12
+ require 'fritzbox/smarthome/smoke_detector'
13
+ require 'fritzbox/smarthome/lightbulb'
9
14
 
10
15
  module Fritzbox
11
16
  module Smarthome
@@ -25,7 +30,7 @@ module Fritzbox
25
30
  defaults.username = 'smarthome'
26
31
  defaults.password = 'verysmart'
27
32
  defaults.verify_ssl = true
28
- defaults.logger = nil
33
+ defaults.logger = NullLogger.new
29
34
  end
30
35
 
31
36
  class << self
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.1.3
4
+ version: 0.4.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: 2021-01-01 00:00:00.000000000 Z
11
+ date: 2022-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -17,9 +17,9 @@ dependencies:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '5.1'
20
- - - "<"
20
+ - - "<="
21
21
  - !ruby/object:Gem::Version
22
- version: '6.2'
22
+ version: '7.1'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -27,9 +27,9 @@ dependencies:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
29
  version: '5.1'
30
- - - "<"
30
+ - - "<="
31
31
  - !ruby/object:Gem::Version
32
- version: '6.2'
32
+ version: '7.1'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: activemodel
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -37,9 +37,9 @@ dependencies:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
39
  version: '5.1'
40
- - - "<"
40
+ - - "<="
41
41
  - !ruby/object:Gem::Version
42
- version: '6.2'
42
+ version: '7.1'
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
@@ -47,23 +47,23 @@ dependencies:
47
47
  - - ">="
48
48
  - !ruby/object:Gem::Version
49
49
  version: '5.1'
50
- - - "<"
50
+ - - "<="
51
51
  - !ruby/object:Gem::Version
52
- version: '6.2'
52
+ version: '7.1'
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: httparty
55
55
  requirement: !ruby/object:Gem::Requirement
56
56
  requirements:
57
57
  - - "~>"
58
58
  - !ruby/object:Gem::Version
59
- version: '0.16'
59
+ version: '0.20'
60
60
  type: :runtime
61
61
  prerelease: false
62
62
  version_requirements: !ruby/object:Gem::Requirement
63
63
  requirements:
64
64
  - - "~>"
65
65
  - !ruby/object:Gem::Version
66
- version: '0.16'
66
+ version: '0.20'
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: nori
69
69
  requirement: !ruby/object:Gem::Requirement
@@ -84,14 +84,14 @@ dependencies:
84
84
  requirements:
85
85
  - - "~>"
86
86
  - !ruby/object:Gem::Version
87
- version: '1.8'
87
+ version: '1.13'
88
88
  type: :runtime
89
89
  prerelease: false
90
90
  version_requirements: !ruby/object:Gem::Requirement
91
91
  requirements:
92
92
  - - "~>"
93
93
  - !ruby/object:Gem::Version
94
- version: '1.8'
94
+ version: '1.13'
95
95
  - !ruby/object:Gem::Dependency
96
96
  name: rake
97
97
  requirement: !ruby/object:Gem::Requirement
@@ -112,42 +112,42 @@ dependencies:
112
112
  requirements:
113
113
  - - "~>"
114
114
  - !ruby/object:Gem::Version
115
- version: '3.0'
115
+ version: '3.11'
116
116
  type: :development
117
117
  prerelease: false
118
118
  version_requirements: !ruby/object:Gem::Requirement
119
119
  requirements:
120
120
  - - "~>"
121
121
  - !ruby/object:Gem::Version
122
- version: '3.0'
122
+ version: '3.11'
123
123
  - !ruby/object:Gem::Dependency
124
124
  name: webmock
125
125
  requirement: !ruby/object:Gem::Requirement
126
126
  requirements:
127
127
  - - "~>"
128
128
  - !ruby/object:Gem::Version
129
- version: '3.3'
129
+ version: '3.14'
130
130
  type: :development
131
131
  prerelease: false
132
132
  version_requirements: !ruby/object:Gem::Requirement
133
133
  requirements:
134
134
  - - "~>"
135
135
  - !ruby/object:Gem::Version
136
- version: '3.3'
136
+ version: '3.14'
137
137
  - !ruby/object:Gem::Dependency
138
138
  name: byebug
139
139
  requirement: !ruby/object:Gem::Requirement
140
140
  requirements:
141
141
  - - "~>"
142
142
  - !ruby/object:Gem::Version
143
- version: '11.0'
143
+ version: '11.1'
144
144
  type: :development
145
145
  prerelease: false
146
146
  version_requirements: !ruby/object:Gem::Requirement
147
147
  requirements:
148
148
  - - "~>"
149
149
  - !ruby/object:Gem::Version
150
- version: '11.0'
150
+ version: '11.1'
151
151
  description: ''
152
152
  email:
153
153
  - spam@klaus-meyer.net
@@ -170,7 +170,12 @@ files:
170
170
  - fritzbox-smarthome.gemspec
171
171
  - lib/fritzbox/smarthome.rb
172
172
  - lib/fritzbox/smarthome/actor.rb
173
+ - lib/fritzbox/smarthome/heater.rb
174
+ - lib/fritzbox/smarthome/lightbulb.rb
175
+ - lib/fritzbox/smarthome/null_logger.rb
173
176
  - lib/fritzbox/smarthome/resource.rb
177
+ - lib/fritzbox/smarthome/smoke_detector.rb
178
+ - lib/fritzbox/smarthome/switch.rb
174
179
  - lib/fritzbox/smarthome/version.rb
175
180
  homepage: https://github.com/klausmeyer/fritzbox-smarthome
176
181
  licenses:
@@ -191,7 +196,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
191
196
  - !ruby/object:Gem::Version
192
197
  version: '0'
193
198
  requirements: []
194
- rubygems_version: 3.1.2
199
+ rubygems_version: 3.3.7
195
200
  signing_key:
196
201
  specification_version: 4
197
202
  summary: Client library to interface with Smarthome features of your FritzBox