mqtt_spec.rb 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. require 'api_client'
  2. require 'mqtt_client'
  3. RSpec.describe 'State' do
  4. before(:all) do
  5. @client = ApiClient.new(ENV.fetch('ESPMH_HOSTNAME'), ENV.fetch('ESPMH_TEST_DEVICE_ID_BASE'))
  6. @client.upload_json('/settings', 'settings.json')
  7. @topic_prefix = ENV.fetch('ESPMH_MQTT_TOPIC_PREFIX')
  8. @client.put(
  9. '/settings',
  10. mqtt_server: ENV.fetch('ESPMH_MQTT_SERVER'),
  11. mqtt_username: ENV.fetch('ESPMH_MQTT_USERNAME'),
  12. mqtt_password: ENV.fetch('ESPMH_MQTT_PASSWORD'),
  13. mqtt_topic_pattern: "#{@topic_prefix}commands/:device_id/:device_type/:group_id",
  14. mqtt_state_topic_pattern: "#{@topic_prefix}state/:device_id/:device_type/:group_id",
  15. mqtt_update_topic_pattern: "#{@topic_prefix}updates/:device_id/:device_type/:group_id",
  16. )
  17. @mqtt_client = MqttClient.new(
  18. *%w(SERVER USERNAME PASSWORD).map { |x| ENV.fetch("ESPMH_MQTT_#{x}") } << @topic_prefix
  19. )
  20. end
  21. after(:all) do
  22. @mqtt_client.disconnect
  23. end
  24. before(:each) do
  25. @id_params = {
  26. id: @client.generate_id,
  27. type: 'rgb_cct',
  28. group_id: 1
  29. }
  30. end
  31. context 'birth and LWT' do
  32. # Unfortunately, no way to easily simulate an unclean disconnect, so only test birth
  33. it 'should send birth message when configured' do
  34. birth_topic = "#{@topic_prefix}birth"
  35. @client.put(
  36. '/settings',
  37. mqtt_birth_topic: birth_topic
  38. )
  39. seen_birth = false
  40. @mqtt_client.on_message(birth_topic) do |topic, message|
  41. seen_birth = true
  42. end
  43. # Force MQTT reconnect by updating settings
  44. @client.put('/settings', fakekey: 'fakevalue')
  45. @mqtt_client.wait_for_listeners
  46. expect(seen_birth).to be(true)
  47. end
  48. end
  49. context 'commands and state' do
  50. # Check state using HTTP
  51. it 'should affect state' do
  52. @client.patch_state({level: 50, status: 'off'}, @id_params)
  53. @mqtt_client.patch_state(@id_params, status: 'on', level: 70)
  54. state = @client.get_state(@id_params)
  55. expect(state.keys).to include(*%w(level status))
  56. expect(state['status']).to eq('ON')
  57. expect(state['level']).to eq(70)
  58. end
  59. it 'should publish to state topics' do
  60. desired_state = {'status' => 'ON', 'level' => 80}
  61. seen_state = false
  62. @client.patch_state({status: 'off'}, @id_params)
  63. @mqtt_client.on_state(@id_params) do |id, message|
  64. seen_state = (id == @id_params && desired_state.all? { |k,v| v == message[k] })
  65. end
  66. @mqtt_client.patch_state(@id_params, desired_state)
  67. @mqtt_client.wait_for_listeners
  68. expect(seen_state).to be(true)
  69. end
  70. it 'should publish an update message for each new command' do
  71. tweak_params = {'hue' => 49, 'brightness' => 128, 'saturation' => 50}
  72. desired_state = {'state' => 'ON'}.merge(tweak_params)
  73. init_state = desired_state.merge(Hash[
  74. tweak_params.map do |k, v|
  75. [k, v + 10]
  76. end
  77. ])
  78. @client.patch_state(@id_params, init_state)
  79. accumulated_state = {}
  80. @mqtt_client.on_update(@id_params) do |id, message|
  81. desired_state == accumulated_state.merge!(message)
  82. end
  83. @mqtt_client.patch_state(@id_params, desired_state)
  84. @mqtt_client.wait_for_listeners
  85. expect(accumulated_state).to eq(desired_state)
  86. end
  87. end
  88. end