Top Level Namespace

Defined Under Namespace

Modules: Hodmin Classes: Array, FirmwareHomie, HomieDevice, HomiePair, Log, SelectObject, String

Instance Method Summary collapse

Instance Method Details

#check_config_ok?(config, configfile) ⇒ Boolean

Checks Hash with config-data

Returns:

  • (Boolean)


391
392
393
394
395
396
397
398
399
400
401
402
403
# File 'lib/hodmin/hodmin_tools.rb', line 391

def check_config_ok?(config, configfile)
  status_ok = true
  if config['mqtt']['host'] == 'mqtt.example.com'
    puts "ERR: No valid config-file found.\nPlease edit config file: #{configfile}."
    status_ok = false
  end

  if !config['mqtt']['base_topic'].empty? && config['mqtt']['base_topic'].split(//).last != '/'
    puts "ERR: mqtt: base_topic MUST end with '/'. Base_topic given: #{config['mqtt']['base_topic']}"
    status_ok = false
  end
  status_ok
end

#cleanup_instance_var_name(inputstring) ⇒ Object

Helper to remove some special chars from string to avoid problems in instance_variable_set:



430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
# File 'lib/hodmin/hodmin_tools.rb', line 430

def cleanup_instance_var_name(inputstring)
	# translate some signs into char-representation:
	str = inputstring
	to_be_replaced = [['%','PCT'],[':','CLN'],['?','QMARK'],['&','AMPS'],['!','EXCLM'],['.','DOT']]
	to_be_replaced.each{|org,rpl| str = str.gsub(org,'_' + rpl + '_')}
	# translate some signs into '_':
	str.tr!('/-','__')
	# remove '$':
	str.delete!('$')
	# remove all other nonnumerical chars:
	str.gsub!(/[^0-9a-zA-Z_]/i, '')
	# sometimes remaining string may be empty:
	str = 'GENERIC_TOPIC' if str.empty?
	# return string as new instance variable name:
	str
end

#default_configObject

Returns Hash with default config-data for Hodmin. File MUST be edited after creation by user.



406
407
408
409
410
411
412
413
414
415
416
# File 'lib/hodmin/hodmin_tools.rb', line 406

def default_config
  config = {}
  config['mqtt'] = Hash['protocol' => 'mqtt://', 'host' => 'mqtt.example.com', 'port' => '1883',\
                        'user' => 'username', 'password' => 'password', 'base_topic' => 'devices/homie/',\
                        'auth' => true, 'timeout' => 0.3]
  config['firmware'] = Hash['dir' => '/home/user/sketchbook/', 'filepattern' => '*.bin']
  config['logging'] = Hash['logdestination' => 'nil']
  config['output'] = Hash['list' => 'HD.mac HD.online HD.localip HD.name FW.checksum'\
                   + 'FW.fw_name FW.fw_version HD.upgradable', 'nil' => '']
  config
end

#default_config_initializeObject

Returns Hash with default config-data to initialize a Homie-device. File MUST be edited after creation by user.



419
420
421
422
423
424
425
426
427
# File 'lib/hodmin/hodmin_tools.rb', line 419

def default_config_initialize
  config = {}
  config['name'] = 'Homie1234'
  config['wifi'] = Hash['ssid' => 'myWifi', 'password'=>'password']
  config['mqtt'] = Hash['host' => 'myhost.mydomain.local', 'port' => 1883, 'base_topic'=>'devices/homie/'\
           , 'auth'=>true, 'username'=>'user1', 'password' => 'mqttpassword']
  config['ota'] = Hash['enabled' => true]
  config
end

#ensure_individual_instance_name(name, list) ⇒ Object



447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'lib/hodmin/hodmin_tools.rb', line 447

def ensure_individual_instance_name(name,list)
	instance_variable_names = list.map do |i|
		i = i.to_s
		i[0] = '' if i[0] == '@'
		i
	end
	n = 1
	org_name = name
	while instance_variable_names.include?(name)
		# name already used, so change it a little bit:
		name = org_name + '_' + n.to_s.rjust(3, "0")
		n += 1
		raise "ERR: Too many topics with special chars: #{instance_variable_names.join(', ')}" if n > 999
	end
	name
end

#fetch_homie_dev_list(*fw_list) ⇒ Object

Return a list of Homie-Devices controlled by given broker.



240
241
242
243
244
245
246
247
# File 'lib/hodmin/hodmin_tools.rb', line 240

def fetch_homie_dev_list(*fw_list)
  client = mqtt_connect
  base_topic = configatron.mqtt.base_topic + '#'
  client.subscribe(base_topic)
  list = get_homies(client, fw_list)
  client.disconnect
  list
end

#fetch_homie_fw_listObject

Return a list of Homie-firmwares found in given diretory-tree. Firmwares are identfied by Magic-byte (see homie-esp8266.readme.io/v2.0.0/docs/magic-bytes). Filenames are ignored, you can specify a pattern in hodmin-config to speed up searching. Default filename-pattern is ‘*.bin’



253
254
255
256
257
258
259
260
261
262
# File 'lib/hodmin/hodmin_tools.rb', line 253

def fetch_homie_fw_list
  directory = configatron.firmware.dir + '**/' + configatron.firmware.filepattern
  Log.log.info "Scanning dir: #{directory}"
  binlist = Dir[directory]
  fw_list = []
  binlist.each do |fw|
    fw_list << FirmwareHomie.new(fw) if homie_firmware?(fw)
  end
  fw_list
end

#get_config_from_option(cline) ⇒ Object



62
63
64
65
66
# File 'lib/hodmin/hodmin_push_config.rb', line 62

def get_config_from_option(cline)
  # Example: cline = '{"ota":{"enabled":"true"}, "wifi":{"ssid":"abc", "password":"secret"}}'
  return '' if cline.to_s.strip.empty?
  JSON.parse(cline)
end

#get_homies(client, *fw_list) ⇒ Object

Reads all Homie-Devices from given broker. To be called with connected MQTT-client. Topic has to be set in calling program. Variable timeout_seconds defines, after what time our client.get will be cancelled. Choose a value high enough for your data, but fast enough for quick response. default is 0.7 sec, which should be enough for a lot of Homies controlled by a broker running on a Raspberry-PI.



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/hodmin/hodmin_tools.rb', line 219

def get_homies(client, *fw_list)
  allmqtt = []
  begin
    Timeout.timeout(configatron.mqtt.timeout.to_f) do
      client.get { |topic, message| allmqtt << [topic, message] }
    end
  # we want to read all published messages right now and then leave (otherwise we are blocked)
  rescue Timeout::Error
  end
  # find all homie-IDs (MAC-addresses)
  macs = allmqtt.select { |t, _m| t.include?('/$homie') }.map { |t, _m| t.split('/$').first.split('/').last }
  # create a array of homie-devices for macs in our list:
  homies = []
  macs.each do |mac|
    mqtt = allmqtt.select { |t, _m| t.include?(mac) }
    homies << HomieDevice.new(mqtt, fw_list)
  end
  homies
end

#hodmin_initialize(gopts, copts) ⇒ Object

Initiate a Homie-Device with first config from a YAML-file. Uses bash CLI calling Curl-binary. Will not work, if curl is not available. Perhaps a better solution should use http-requests => to be done Status: experimental



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/hodmin/hodmin_initialize.rb', line 5

def hodmin_initialize(gopts, copts)
  c1 = 'command -v curl >/dev/null 2>&1 || { echo  "curl required but it is not installed."; }'
  ip = '192.168.123.1'
  ans = `#{c1}`.to_s.strip
  if ans.empty?
    default_filename = 'homie-initialize.yaml'
    filename = copts[:configfile_given] ? copts[:configfile] : default_filename

    unless File.exists?(filename)
      puts "ERR: Configfile with initializing data not found: #{filename}"
      exit if filename != default_filename
      # create example config-file:
      File.open(filename, 'w') { |f| f.puts default_config_initialize.to_yaml }
      puts "WARN: Default initializing data written to: #{filename}. Please edit this file!"
      exit
    end
    
    # write config in JSON-Format to tempfile:
    tempfile = 'configHOMIEjson.tmp'
    File.open(tempfile,'w'){|f| f.puts YAML.load_file(filename).to_json}
    
    # upload to device:
    puts "trying to connect to #{ip} ..."
    c2 = "curl -X PUT http://#{ip}/config -d @#{tempfile} --header 'Content-Type: application/json'"
    ans = `#{c2}`.to_s
    json = JSON.parse(ans)
    if json['success']
      puts "\nDevice is initialized now."
    else
      puts "\nOops. Something went wrong: curl answered: #{ans}"
    end
    File.delete(tempfile)
  else
    # curl not installed
    puts 'ERR: curl required, but it is not installed. Aborting.'
    exit
  end
end

#hodmin_list(gopts, copts) ⇒ Object

Homie-Admin LIST Print list of Homie-Decices with installed firmware and available firmware in our repo



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/hodmin/hodmin_list.rb', line 3

def hodmin_list(gopts, copts)
  all_fws = fetch_homie_fw_list # we need it for checking upgrade-availability
  my_fws = all_fws.select_by_opts(copts)\
                  .sort do |a, b|
                    [a.fw_brand, a.fw_name, b.fw_version] <=> \
                      [b.fw_brand, b.fw_name, a.fw_version]
                  end
                  
  # fetch all devices, set upgradable-attribute based on my_fws:
  my_devs = fetch_homie_dev_list(my_fws).select_by_opts(gopts)
  my_list = []
  already_listed = []

  my_devs.each do |d|
    firmware = my_fws.select { |f| f.checksum == d.fw_checksum }
    if firmware.count > 0
      # found installed firmware
      my_list << HomiePair.new(d, firmware)
      already_listed << firmware.first.checksum # remember this firmware as already listed
    else
      # did not find firmware-file installed on this device
      my_list << HomiePair.new(d, nil) unless gopts[:upgradable_given]
    end
  end

  # now append remaining firmwares (for which we did not find any Homie running this) to my_list:
  already_listed.uniq!
  my_fws.select { |f| !already_listed.include?(f.checksum) }.each { |f| my_list << HomiePair.new(nil, f) }

  # attributes of my_list we want to see in output:
  # HD: attributes coming from HomieDevice
  # FW: attributes coming from firmware-file
  # AD: additional attributes in HomiePair-class for special purposes
  # Read our format for table from config-file:
  attribs = configatron.output.list.strip.split(/ /) unless configatron.output.nil?
  
  # create output-table
  rows = my_list.create_output_table(attribs, copts[:style])
  # define a header for our output-table
  # header = attribs.map { |a| a.gsub(/HD./, '').gsub(/FW./, '').gsub(/AD./, '') }
  header = attribs.map(&:setup_header)
  # build table object:
  output = TTY::Table.new header, rows
  table_style = copts[:style_given] ? copts[:style].to_sym : :unicode # :ascii :basic
  
  # show our table:
  puts output.render(table_style, alignment: copts[:style] == 'basic' ? [:left] : [:center])
end

#hodmin_pull_config(gopts, copts) ⇒ Object

pullCF reads Homie-device config-data via mqtt-protocol. Output-format is YAML



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/hodmin/hodmin_pull_config.rb', line 3

def hodmin_pull_config(gopts, copts)
  my_devs = fetch_homie_dev_list.select_by_opts(gopts)

  my_devs.each do |pull_dev|
    if copts[:outputfile_given]
      filename = pull_dev.config_yaml_filename_homie(copts[:outputfile])
      File.open(filename, 'w') do |f|
        f.puts "# YAML Configfile written by hodmin Version #{configatron.VERSION}"
        f.puts "# MAC: #{pull_dev.mac}"
        f.puts "# Status during pullCF: #{pull_dev.online_status}"
        f.puts "# #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
        f.puts pull_dev.implementation_config.config_from_string
      end
    else
      puts "Config of device #{pull_dev.name} (#{pull_dev.mac}):"
      puts pull_dev.implementation_config.config_from_string
    end
  end
end

#hodmin_push_config(gopts, copts) ⇒ Object

Pushes a config to Homie-device via MQTT-Broker.



2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/hodmin/hodmin_push_config.rb', line 2

def hodmin_push_config(gopts, copts)
  conf_cmd  = copts[:jsonconfig] || ''
  conf_file = copts[:inputfile] || ''
  conf_short = copts[:shortconfig] || ''

  number_of_options = [:jsonconfig_given, :inputfile_given, :shortconfig_given]\
                      .count { |e| copts.keys.include?(e) }

  unless number_of_options == 1
    puts 'ERR: please specify exactly ONE option of: -s, -j, -i'
    return
  end

  if copts[:inputfile_given] && !File.exist?(conf_file)
    puts "ERR: File not found: #{conf_file}"
    return
  end

  conf_file = YAML.load_file(conf_file).to_json if copts[:inputfile_given]

  conf_new = conf_cmd if copts[:jsonconfig_given]
  conf_new = conf_file if copts[:inputfile_given]
  conf_new = options_long(conf_short) if copts[:shortconfig_given]

  if conf_new.empty?
    puts 'ERR: No valid config-options found.'
    return
  end

  my_devs = fetch_homie_dev_list.select_by_opts(gopts)

  my_devs.each do |up_dev|
    copts = get_config_from_option(conf_new)
    puts "Device #{up_dev.mac} is #{up_dev.online_status}"
    next unless up_dev.online?
    print 'Start updating? <Yn>:'
    answer = STDIN.gets.chomp.downcase
    next unless up_dev.online && 'y' == answer
    client = mqtt_connect
    base_topic = configatron.mqtt.base_topic + up_dev.mac + '/'
    client.subscribe(base_topic + '$implementation/config')
    conf_old = ''
    client.get do |_topic, message|
      # wait for next message in our queue:
      if conf_old == ''
        # first loop, store existing config to compare after update:
        conf_old = message # we do need message only
        client.publish(base_topic + '$implementation/config/set', copts.to_json, retain: false)
        puts 'done, device reboots, waiting for ACK...'
      else
        # we received a new config
        new_conf = message
        break if JSON.parse(new_conf).values_at(*copts.keys) == copts.values
      end
      puts "ACK received, device #{up_dev.mac} rebooted with new config."
    end
    client.disconnect
  end
end

#hodmin_push_firmware(gopts, copts) ⇒ Object

Uploads firmware to Homie-Device(s)



2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/hodmin/hodmin_push_firmware.rb', line 2

def hodmin_push_firmware(gopts, copts)
  fw_checksum = copts[:checksum] || ''
  fw_name = copts[:fw_name] || ''
  batchmode = copts[:auto] || false
  mac = gopts[:mac] || ''
  hd_upgrade = gopts[:upgradable_given] && gopts[:upgradable]
  fw_upgrade = copts[:upgrade_given] && copts[:upgrade]

  gopts[:mac] = mac = '*' if hd_upgrade && gopts[:mac_given]

  if fw_checksum.empty? && fw_name.empty?
    puts "ERR: No valid firmware-referrer found. (Chksum:#{fw_checksum}, Name: #{fw_name})"
    return
  end
  unless (!fw_checksum.empty? && fw_name.empty?) || (fw_checksum.empty? && !fw_name.empty?)
    puts 'ERR: Please specify firmware either by checksum or by name (for newest of this name).'
    return
  end

  unless !mac.empty? || !fw_name.empty?
    puts 'ERR: No valid device specified.'
    return
  end

  # first find our firmware:
  my_fws = fetch_homie_fw_list.select_by_opts(copts)
                              .sort { |a, b| [a.fw_name, b.fw_version] <=> [b.fw_name, a.fw_version] }

  if my_fws.empty?
    puts 'ERR: None of available firmwares does match this pattern'
    return
  else
    if my_fws.size > 1 && !fw_upgrade
      puts 'ERR: Firmware specification is ambigous'
      return
    end
  end

  # only first firmware selected for pushing:
  my_fw = my_fws.first

  # now find our device(s)
  my_devs = fetch_homie_dev_list(my_fws).select_by_opts(gopts)

  return if my_devs.empty?

  my_devs.each do |up_dev|
    next if hd_upgrade && !up_dev.upgradable
    my_fw = my_fws.select { |f| f.fw_name == up_dev.fw_name }.sort_by(&:fw_version).last if hd_upgrade
    puts "Device #{up_dev.mac} is #{up_dev.online_status}. (installed FW-Checksum: #{up_dev.fw_checksum})"
    next unless up_dev.online? && up_dev.fw_checksum != my_fw.checksum
    if batchmode
      answer = 'y'
    else
      print "New firmware: #{my_fw.checksum}. Start pushing? <Yn>:"
      answer = STDIN.gets.chomp.downcase
    end
    Log.log.info "Dev. #{up_dev.mac} (running #{up_dev.fw_version}) upgrading to #{my_fw.fw_version}"
    up_dev.push_firmware_to_dev(my_fw) if 'y' == answer
  end
end

#hodmin_remove(copts) ⇒ Object



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/hodmin/hodmin_remove.rb', line 1

def hodmin_remove(copts)
  fw_checksum = copts[:checksum] || ''
  fw_name = copts[:fw_name] || ''

  if fw_checksum.empty? && fw_name.empty?
    puts "ERR: No valid firmware-referrer found. (Chksum:#{fw_checksum}, Name: #{fw_name})"
    return
  end
  unless (!fw_checksum.empty? && fw_name.empty?) || (fw_checksum.empty? && !fw_name.empty?)
    puts 'ERR: Please specify firmware either by checksum or by name (for newest of this name).'
    return
  end

  # first find our firmware-files:
  my_fws = fetch_homie_fw_list.select_by_opts(copts)
                              .sort { |a, b| [a.fw_name, b.fw_version] <=> [b.fw_name, a.fw_version] }

  if my_fws.empty?
    puts 'ERR: None of available firmwares does match this pattern'
    return
  else
    my_fws.each do |f|
      puts "found Fw: Name: #{f.fw_name}, Version: #{f.fw_version}, MD5: #{f.checksum}"
    end
  end

  my_fws.each do |my_fw|
    print "Remove firmware: #{my_fw.fw_name}, #{my_fw.fw_version}, #{my_fw.checksum}. Remove now? <Yn>:"
    answer = STDIN.gets.chomp.downcase
    File.delete(my_fw.file_path) if 'y' == answer
  end
end

#hodmin_rename(_gopts, copts) ⇒ Object



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/hodmin/hodmin_rename.rb', line 1

def hodmin_rename(_gopts, copts)
  fw_checksum = copts[:checksum] || ''
  fw_name = copts[:fw_name] || ''

  if fw_checksum.empty? && fw_name.empty?
    puts "ERR: No valid firmware-referrer found. (Chksum:#{fw_checksum}, Name: #{fw_name})"
    return
  end
  unless (!fw_checksum.empty? && fw_name.empty?) || (fw_checksum.empty? && !fw_name.empty?)
    puts 'ERR: Please specify firmware either by checksum or by name (for newest of this name).'
    return
  end

  # first find our firmware-files:
  my_fws = fetch_homie_fw_list.select_by_opts(copts)
                              .sort { |a, b| [a.fw_name, b.fw_version] <=> [b.fw_name, a.fw_version] }

  if my_fws.empty?
    puts 'ERR: None of available firmware does match this pattern'
    return
  else
    my_fws.each do |f|
      puts "found Fw: Name: #{f.fw_name}, Version: #{f.fw_version}, MD5: #{f.checksum}"
    end
  end

  my_fws.each do |my_fw|
    bin_pattern = "Homie_#{my_fw.fw_name}_#{my_fw.fw_version}_#{my_fw.checksum}.bin"
    fileobj = Pathname.new(my_fw.file_path)
    next if bin_pattern == Pathname.new(my_fw.file_path).basename.to_s
    puts "Rename firmware: #{my_fw.fw_name}, #{my_fw.fw_version}, #{my_fw.checksum}."
    print "Rename to #{bin_pattern}? <Yn>:"
    answer = STDIN.gets.chomp.downcase
    fileobj.rename(fileobj.dirname + bin_pattern) if 'y' == answer
  end
end

#homie_firmware?(filename) ⇒ Boolean

Searches within a binaryfile for so called Homie-magic-bytes to detect a Homie-firmware. See homie-esp8266.readme.io/v2.0.0/docs/magic-bytes

Returns:

  • (Boolean)


73
74
75
76
77
78
# File 'lib/hodmin/hodmin_tools.rb', line 73

def homie_firmware?(filename)
  # returns TRUE, if Homiepattern is found inside binary
  binfile = IO.binread(filename).unpack('H*').first
  homie_pattern = "\x25\x48\x4f\x4d\x49\x45\x5f\x45\x53\x50\x38\x32\x36\x36\x5f\x46\x57\x25".unpack('H*').first
  binfile.include?(homie_pattern)
end

#mqtt_connectObject

Methods connects to a MQTT-broker and returns an object linking to this connection.



81
82
83
84
85
86
87
88
89
90
91
# File 'lib/hodmin/hodmin_tools.rb', line 81

def mqtt_connect
  # establish connection for publishing, return client-object
  credentials = configatron.mqtt.auth ? configatron.mqtt.user + ':' + configatron.mqtt.password + '@' : ''
  connection = configatron.mqtt.protocol + credentials + configatron.mqtt.host
  begin
    MQTT::Client.connect(connection, configatron.mqtt.port)
  rescue MQTT::ProtocolException
    puts "ERR: Username and / or password wrong?\n#{connection} at port #{configatron.mqtt.port}"
    exit
  end
end

#options_long(short) ⇒ Object

Returns JSON-String with key-value pairs depending on input-string. Example: hodmin pushCF -s “name:test-esp8266 ota:on ssid:xy wifipw:xy host:xy port:xy base_topic:xy auth:off user:xy mqttpw:xy” Enclose multiple options in “”, separate options with a blank



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/hodmin/hodmin_push_config.rb', line 72

def options_long(short)
  list = short.split(/ /)
  cfg = { 'wifi' => {}, 'mqtt' => {} }
  list.each do |o|
    key, value = o.split(/:/).map(&:strip)
    case key.downcase
    when 'name' then cfg['name'] = value
    when 'ssid' then cfg['wifi'] = Hash['ssid' => value]
    when 'wifipw' then cfg['wifi'] = Hash['password' => value]
    when 'host' then cfg['mqtt'] << Hash['host' => value]
    when 'port' then cfg['mqtt'] << Hash['port' => value]
    when 'base_topic' then cfg['mqtt'] = Hash['base_topic' => value]
    when 'auth' then cfg['mqtt'] = cfg['mqtt'].merge(Hash['auth' => value == 'on' ? true : false])
    when 'user' then cfg['mqtt'] << Hash['username' => value]
    when 'mqttpw' then cfg['mqtt'] = cfg['mqtt'].merge(Hash['password' => value])
    when 'ota' then cfg['ota'] = Hash['enabled' => value == 'on' ? true : false]
#   to be done:
#    when 'settings' then 
#           puts "to be done: settings not implemented in short-config right now"
#           puts "key=#{key}, value=#{value}"
    else
      puts "ERR: illegal option: #{key.downcase}"
      exit
    end
  end

  cfg = cfg.delete_if { |_k, v| v.nil? || v.empty? }
  puts "\nNew config will be: #{cfg.inspect}"
  cfg.to_json
end

#upgradable?(fw_name, fw_version, fw_list) ⇒ Boolean

Check a firmware against available bin-files of Homie-firmwares. Returns true, if there is a higher Version than installed. Returns false, if there is no suitable firmware-file found or installed version is the highest version found.

Returns:

  • (Boolean)


351
352
353
354
355
356
357
358
# File 'lib/hodmin/hodmin_tools.rb', line 351

def upgradable?(fw_name, fw_version, fw_list)
  fw_list.flatten!
  # select highest Version of fw_name from given firmware_list:
  return false if fw_list.empty? # No entries in Softwarelist
  best_version = fw_list.select { |h| h.fw_name == fw_name }\
                        .sort_by(&:fw_version).last
  best_version.nil? ? false : fw_version < best_version.fw_version
end