somfy_sdn 2.1.5 → 2.2.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/exe/somfy_sdn +69 -0
- data/lib/sdn/cli/mqtt/group.rb +12 -10
- data/lib/sdn/cli/mqtt/motor.rb +19 -14
- data/lib/sdn/cli/mqtt/p_queue.rb +18 -0
- data/lib/sdn/cli/mqtt/read.rb +125 -126
- data/lib/sdn/cli/mqtt/subscriptions.rb +186 -140
- data/lib/sdn/cli/mqtt/write.rb +39 -34
- data/lib/sdn/cli/mqtt.rb +84 -53
- data/lib/sdn/cli/provisioner.rb +56 -33
- data/lib/sdn/cli/simulator.rb +99 -65
- data/lib/sdn/client.rb +38 -24
- data/lib/sdn/message/control.rb +60 -30
- data/lib/sdn/message/get.rb +6 -2
- data/lib/sdn/message/helpers.rb +23 -22
- data/lib/sdn/message/ilt2/get.rb +6 -3
- data/lib/sdn/message/ilt2/master_control.rb +10 -7
- data/lib/sdn/message/ilt2/post.rb +7 -5
- data/lib/sdn/message/ilt2/set.rb +28 -19
- data/lib/sdn/message/post.rb +3 -5
- data/lib/sdn/message/set.rb +48 -22
- data/lib/sdn/message.rb +50 -34
- data/lib/sdn/version.rb +3 -1
- data/lib/sdn.rb +18 -12
- data/lib/somfy_sdn.rb +3 -1
- metadata +43 -13
- data/bin/somfy_sdn +0 -60
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module SDN
         | 
| 2 4 | 
             
              class Message
         | 
| 3 5 | 
             
                module ILT2
         | 
| @@ -10,12 +12,14 @@ module SDN | |
| 10 12 | 
             
                        return unless checksum(data[0..2]) == data[3..4]
         | 
| 11 13 | 
             
                        # no clue what's special about these
         | 
| 12 14 | 
             
                        return unless data[0..1] == [0xfa, 0x7a]
         | 
| 15 | 
            +
             | 
| 13 16 | 
             
                        klass = case data[2]
         | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 17 | 
            +
                                when 0x00 then Down
         | 
| 18 | 
            +
                                when 0xfa then Up
         | 
| 19 | 
            +
                                when 0xff then Stop
         | 
| 20 | 
            +
                                end
         | 
| 18 21 | 
             
                        return unless klass
         | 
| 22 | 
            +
             | 
| 19 23 | 
             
                        [klass.new, 5]
         | 
| 20 24 | 
             
                      end
         | 
| 21 25 | 
             
                    end
         | 
| @@ -25,11 +29,10 @@ module SDN | |
| 25 29 |  | 
| 26 30 | 
             
                    class Stop < MasterControl
         | 
| 27 31 | 
             
                    end
         | 
| 28 | 
            -
             | 
| 32 | 
            +
             | 
| 29 33 | 
             
                    class Up < MasterControl
         | 
| 30 34 | 
             
                    end
         | 
| 31 | 
            -
             | 
| 32 35 | 
             
                  end
         | 
| 33 36 | 
             
                end
         | 
| 34 37 | 
             
              end
         | 
| 35 | 
            -
            end
         | 
| 38 | 
            +
            end
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module SDN
         | 
| 2 4 | 
             
              class Message
         | 
| 3 5 | 
             
                module ILT2
         | 
| @@ -13,7 +15,7 @@ module SDN | |
| 13 15 | 
             
                    end
         | 
| 14 16 |  | 
| 15 17 | 
             
                    def channels=(value)
         | 
| 16 | 
            -
                      @channels = value  | 
| 18 | 
            +
                      @channels = value&.& 0xff
         | 
| 17 19 | 
             
                    end
         | 
| 18 20 |  | 
| 19 21 | 
             
                    def parse(params)
         | 
| @@ -26,7 +28,7 @@ module SDN | |
| 26 28 | 
             
                    end
         | 
| 27 29 |  | 
| 28 30 | 
             
                    def class_inspect
         | 
| 29 | 
            -
                      ", @channels=#{channels.chr. | 
| 31 | 
            +
                      ", @channels=#{channels.chr.unpack1("b8")}"
         | 
| 30 32 | 
             
                    end
         | 
| 31 33 | 
             
                  end
         | 
| 32 34 |  | 
| @@ -77,9 +79,9 @@ module SDN | |
| 77 79 | 
             
                  class PostMotorPosition < Message
         | 
| 78 80 | 
             
                    MSG = 0x64
         | 
| 79 81 | 
             
                    PARAMS_LENGTH = 3
         | 
| 80 | 
            -
             | 
| 82 | 
            +
             | 
| 81 83 | 
             
                    attr_accessor :position_pulses, :position_percent
         | 
| 82 | 
            -
             | 
| 84 | 
            +
             | 
| 83 85 | 
             
                    def initialize(position_pulses = nil, position_percent = nil, **kwargs)
         | 
| 84 86 | 
             
                      super(**kwargs)
         | 
| 85 87 | 
             
                      self.position_pulses = position_pulses
         | 
| @@ -94,7 +96,7 @@ module SDN | |
| 94 96 |  | 
| 95 97 | 
             
                    def params
         | 
| 96 98 | 
             
                      from_number(position_pulses, 2) +
         | 
| 97 | 
            -
                        from_number(position_percent && position_percent * 255 / 100)
         | 
| 99 | 
            +
                        from_number(position_percent && (position_percent * 255 / 100))
         | 
| 98 100 | 
             
                    end
         | 
| 99 101 | 
             
                  end
         | 
| 100 102 |  | 
    
        data/lib/sdn/message/ilt2/set.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module SDN
         | 
| 2 4 | 
             
              class Message
         | 
| 3 5 | 
             
                module ILT2
         | 
| @@ -19,7 +21,7 @@ module SDN | |
| 19 21 | 
             
                      down_limit: 2,
         | 
| 20 22 | 
             
                      ip: 4,
         | 
| 21 23 | 
             
                      unlock: 5
         | 
| 22 | 
            -
                    }
         | 
| 24 | 
            +
                    }.freeze
         | 
| 23 25 |  | 
| 24 26 | 
             
                    # when target_type is down_limit, target is number of 10ms intervals it's still allowed to roll up
         | 
| 25 27 | 
             
                    attr_reader :target_type, :target, :priority
         | 
| @@ -40,16 +42,21 @@ module SDN | |
| 40 42 | 
             
                    end
         | 
| 41 43 |  | 
| 42 44 | 
             
                    def target_type=(value)
         | 
| 43 | 
            -
                       | 
| 45 | 
            +
                      unless TARGET_TYPE.key?(value)
         | 
| 46 | 
            +
                        raise ArgumentError,
         | 
| 47 | 
            +
                              "target_type must be one of :current, :up_limit, :down_limit, :ip, or :unlock"
         | 
| 48 | 
            +
                      end
         | 
| 49 | 
            +
             | 
| 44 50 | 
             
                      @target_type = value
         | 
| 45 51 | 
             
                    end
         | 
| 46 52 |  | 
| 47 53 | 
             
                    def target=(value)
         | 
| 48 | 
            -
                      @target = value | 
| 54 | 
            +
                      @target = value&.& 0xff
         | 
| 49 55 | 
             
                    end
         | 
| 50 56 |  | 
| 51 57 | 
             
                    def priority=(value)
         | 
| 52 | 
            -
                      raise ArgumentError, "priority must be between 1 and 100" unless (1..100). | 
| 58 | 
            +
                      raise ArgumentError, "priority must be between 1 and 100" unless (1..100).cover?(value)
         | 
| 59 | 
            +
             | 
| 53 60 | 
             
                      @priority = value
         | 
| 54 61 | 
             
                    end
         | 
| 55 62 |  | 
| @@ -78,12 +85,13 @@ module SDN | |
| 78 85 | 
             
                    end
         | 
| 79 86 |  | 
| 80 87 | 
             
                    def ip=(value)
         | 
| 81 | 
            -
                      raise ArgumentError, "ip must be in range 1..16 or nil" unless ip.nil? || (1..16). | 
| 88 | 
            +
                      raise ArgumentError, "ip must be in range 1..16 or nil" unless ip.nil? || (1..16).cover?(ip)
         | 
| 89 | 
            +
             | 
| 82 90 | 
             
                      @ip = value
         | 
| 83 91 | 
             
                    end
         | 
| 84 92 |  | 
| 85 93 | 
             
                    def value=(value)
         | 
| 86 | 
            -
                      @value = value  | 
| 94 | 
            +
                      @value = value&.& 0xffff
         | 
| 87 95 | 
             
                    end
         | 
| 88 96 |  | 
| 89 97 | 
             
                    def params
         | 
| @@ -110,7 +118,7 @@ module SDN | |
| 110 118 | 
             
                      jog_down_ms: 11,
         | 
| 111 119 | 
             
                      jog_up_pulses: 12,
         | 
| 112 120 | 
             
                      jog_down_pulses: 13,
         | 
| 113 | 
            -
                      position_percent: 16 | 
| 121 | 
            +
                      position_percent: 16
         | 
| 114 122 | 
             
                    }.freeze
         | 
| 115 123 |  | 
| 116 124 | 
             
                    attr_reader :target_type, :target
         | 
| @@ -126,26 +134,27 @@ module SDN | |
| 126 134 | 
             
                      super
         | 
| 127 135 | 
             
                      self.target_type = TARGET_TYPE.invert[to_number(params[0])]
         | 
| 128 136 | 
             
                      target = to_number(params[1..2])
         | 
| 129 | 
            -
                      if target_type == :position_percent
         | 
| 130 | 
            -
             | 
| 131 | 
            -
                      end
         | 
| 132 | 
            -
                      if target_type == :ip
         | 
| 133 | 
            -
                        target += 1
         | 
| 134 | 
            -
                      end
         | 
| 137 | 
            +
                      target = target.to_f / 255 * 100 if target_type == :position_percent
         | 
| 138 | 
            +
                      target += 1 if target_type == :ip
         | 
| 135 139 | 
             
                      self.target = target
         | 
| 136 140 | 
             
                    end
         | 
| 137 141 |  | 
| 138 142 | 
             
                    def target_type=(value)
         | 
| 139 | 
            -
                       | 
| 143 | 
            +
                      unless TARGET_TYPE.key?(value)
         | 
| 144 | 
            +
                        raise ArgumentError, "target_type must be one of :up_limit, :down_limit, " \
         | 
| 145 | 
            +
                                             ":stop, :ip, :next_ip_up, :next_ip_down, :jog_up, " \
         | 
| 146 | 
            +
                                             ":jog_down, or :position_percent"
         | 
| 147 | 
            +
                      end
         | 
| 148 | 
            +
             | 
| 140 149 | 
             
                      @target_type = value
         | 
| 141 150 | 
             
                    end
         | 
| 142 151 |  | 
| 143 152 | 
             
                    def target=(value)
         | 
| 144 | 
            -
                      if target_type == :position_percent && value
         | 
| 145 | 
            -
             | 
| 146 | 
            -
             | 
| 147 | 
            -
             | 
| 148 | 
            -
             | 
| 153 | 
            +
                      @target = if target_type == :position_percent && value
         | 
| 154 | 
            +
                                  value.clamp(0, 100)
         | 
| 155 | 
            +
                                else
         | 
| 156 | 
            +
                                  value&.& 0xffff
         | 
| 157 | 
            +
                                end
         | 
| 149 158 | 
             
                    end
         | 
| 150 159 |  | 
| 151 160 | 
             
                    def params
         | 
    
        data/lib/sdn/message/post.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module SDN
         | 
| 2 4 | 
             
              class Message
         | 
| 3 5 | 
             
                class PostGroupAddr < Message
         | 
| @@ -24,7 +26,7 @@ module SDN | |
| 24 26 | 
             
                  end
         | 
| 25 27 |  | 
| 26 28 | 
             
                  def class_inspect
         | 
| 27 | 
            -
                    ", group_index=#{group_index.inspect}, group_address=#{group_address ? print_address(group_address) :  | 
| 29 | 
            +
                    ", group_index=#{group_index.inspect}, group_address=#{group_address ? print_address(group_address) : "nil"}"
         | 
| 28 30 | 
             
                  end
         | 
| 29 31 | 
             
                end
         | 
| 30 32 |  | 
| @@ -160,10 +162,6 @@ module SDN | |
| 160 162 | 
             
                class PostNetworkLock < UnknownMessage
         | 
| 161 163 | 
             
                  MSG = 0x36
         | 
| 162 164 | 
             
                  PARAMS_LENGTH = 5
         | 
| 163 | 
            -
             | 
| 164 | 
            -
                  def parse(params)
         | 
| 165 | 
            -
                    super
         | 
| 166 | 
            -
                  end
         | 
| 167 165 | 
             
                end
         | 
| 168 166 |  | 
| 169 167 | 
             
                class PostNodeAddr < Message
         | 
    
        data/lib/sdn/message/set.rb
    CHANGED
    
    | @@ -1,9 +1,17 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module SDN
         | 
| 2 4 | 
             
              class Message
         | 
| 3 5 | 
             
                class SetFactoryDefault < Message
         | 
| 4 6 | 
             
                  MSG = 0x1f
         | 
| 5 7 | 
             
                  PARAMS_LENGTH = 1
         | 
| 6 | 
            -
                  RESET = { all_settings: 0x00, | 
| 8 | 
            +
                  RESET = { all_settings: 0x00,
         | 
| 9 | 
            +
                            group_addresses: 0x01,
         | 
| 10 | 
            +
                            limits: 0x11,
         | 
| 11 | 
            +
                            rotation: 0x12,
         | 
| 12 | 
            +
                            rolling_speed: 0x13,
         | 
| 13 | 
            +
                            ips: 0x15,
         | 
| 14 | 
            +
                            locks: 0x17 }.freeze
         | 
| 7 15 |  | 
| 8 16 | 
             
                  attr_reader :reset
         | 
| 9 17 |  | 
| @@ -19,7 +27,11 @@ module SDN | |
| 19 27 | 
             
                  end
         | 
| 20 28 |  | 
| 21 29 | 
             
                  def reset=(value)
         | 
| 22 | 
            -
                     | 
| 30 | 
            +
                    unless RESET.key?(value)
         | 
| 31 | 
            +
                      raise ArgumentError,
         | 
| 32 | 
            +
                            "reset must be one of :all_settings, :group_addresses, :limits, :rotation, :rolling_speed, :ips, :locks"
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
             | 
| 23 35 | 
             
                    @reset = value
         | 
| 24 36 | 
             
                  end
         | 
| 25 37 |  | 
| @@ -32,7 +44,8 @@ module SDN | |
| 32 44 | 
             
                  MSG = 0x51
         | 
| 33 45 | 
             
                  PARAMS_LENGTH = 4
         | 
| 34 46 |  | 
| 35 | 
            -
                   | 
| 47 | 
            +
                  attr_accessor :group_address
         | 
| 48 | 
            +
                  attr_reader :group_index
         | 
| 36 49 |  | 
| 37 50 | 
             
                  def initialize(dest = nil, group_index = 1, group_address = nil, **kwargs)
         | 
| 38 51 | 
             
                    kwargs[:dest] ||= dest
         | 
| @@ -48,12 +61,9 @@ module SDN | |
| 48 61 | 
             
                  end
         | 
| 49 62 |  | 
| 50 63 | 
             
                  def group_index=(value)
         | 
| 51 | 
            -
                    raise ArgumentError, "group_index is out of range" unless (1..16). | 
| 52 | 
            -
                    @group_index = value
         | 
| 53 | 
            -
                  end
         | 
| 64 | 
            +
                    raise ArgumentError, "group_index is out of range" unless (1..16).cover?(value)
         | 
| 54 65 |  | 
| 55 | 
            -
             | 
| 56 | 
            -
                    @group_address = value
         | 
| 66 | 
            +
                    @group_index = value
         | 
| 57 67 | 
             
                  end
         | 
| 58 68 |  | 
| 59 69 | 
             
                  def params
         | 
| @@ -61,7 +71,7 @@ module SDN | |
| 61 71 | 
             
                  end
         | 
| 62 72 |  | 
| 63 73 | 
             
                  def class_inspect
         | 
| 64 | 
            -
                    ", group_index=#{group_index.inspect}, group_address=#{group_address ? print_address(group_address) :  | 
| 74 | 
            +
                    ", group_index=#{group_index.inspect}, group_address=#{group_address ? print_address(group_address) : "nil"}"
         | 
| 65 75 | 
             
                  end
         | 
| 66 76 | 
             
                end
         | 
| 67 77 |  | 
| @@ -84,7 +94,8 @@ module SDN | |
| 84 94 | 
             
                  end
         | 
| 85 95 |  | 
| 86 96 | 
             
                  def direction=(value)
         | 
| 87 | 
            -
                    raise ArgumentError, "direction must be one of :standard, :reversed" unless DIRECTION. | 
| 97 | 
            +
                    raise ArgumentError, "direction must be one of :standard, :reversed" unless DIRECTION.key?(value)
         | 
| 98 | 
            +
             | 
| 88 99 | 
             
                    @direction = value
         | 
| 89 100 | 
             
                  end
         | 
| 90 101 |  | 
| @@ -97,7 +108,11 @@ module SDN | |
| 97 108 | 
             
                  MSG = 0x15
         | 
| 98 109 | 
             
                  PARAMS_LENGTH = 4
         | 
| 99 110 | 
             
                  # for distribute, value is how many IPs to distribute over
         | 
| 100 | 
            -
                  TYPE = { delete: 0x00, | 
| 111 | 
            +
                  TYPE = { delete: 0x00,
         | 
| 112 | 
            +
                           current_position: 0x01,
         | 
| 113 | 
            +
                           position_pulses: 0x02,
         | 
| 114 | 
            +
                           position_percent: 0x03,
         | 
| 115 | 
            +
                           distribute: 0x04 }.freeze
         | 
| 101 116 |  | 
| 102 117 | 
             
                  attr_reader :type, :ip, :value
         | 
| 103 118 |  | 
| @@ -113,23 +128,28 @@ module SDN | |
| 113 128 | 
             
                    super
         | 
| 114 129 | 
             
                    self.type = TYPE.invert[to_number(params[0])]
         | 
| 115 130 | 
             
                    ip = to_number(params[1])
         | 
| 116 | 
            -
                    ip = nil if ip | 
| 131 | 
            +
                    ip = nil if ip.zero?
         | 
| 117 132 | 
             
                    self.ip = ip
         | 
| 118 133 | 
             
                    self.value = to_number(params[2..3])
         | 
| 119 134 | 
             
                  end
         | 
| 120 135 |  | 
| 121 136 | 
             
                  def type=(value)
         | 
| 122 | 
            -
                     | 
| 137 | 
            +
                    unless TYPE.key?(value)
         | 
| 138 | 
            +
                      raise ArgumentError,
         | 
| 139 | 
            +
                            "type must be one of :delete, :current_position, :position_pulses, :position_percent, :distribute"
         | 
| 140 | 
            +
                    end
         | 
| 141 | 
            +
             | 
| 123 142 | 
             
                    @type = value
         | 
| 124 143 | 
             
                  end
         | 
| 125 144 |  | 
| 126 145 | 
             
                  def ip=(value)
         | 
| 127 | 
            -
                    raise ArgumentError, "ip must be in range 1..16 or nil" unless ip.nil? || (1..16). | 
| 146 | 
            +
                    raise ArgumentError, "ip must be in range 1..16 or nil" unless ip.nil? || (1..16).cover?(ip)
         | 
| 147 | 
            +
             | 
| 128 148 | 
             
                    @ip = value
         | 
| 129 149 | 
             
                  end
         | 
| 130 150 |  | 
| 131 151 | 
             
                  def value=(value)
         | 
| 132 | 
            -
                    @value = value  | 
| 152 | 
            +
                    @value = value&.& 0xffff
         | 
| 133 153 | 
             
                  end
         | 
| 134 154 |  | 
| 135 155 | 
             
                  def params
         | 
| @@ -140,8 +160,8 @@ module SDN | |
| 140 160 | 
             
                class SetMotorLimits < Message
         | 
| 141 161 | 
             
                  MSG = 0x11
         | 
| 142 162 | 
             
                  PARAMS_LENGTH = 4
         | 
| 143 | 
            -
                  TYPE = { delete: 0x00, current_position: 0x01, specified_position: 0x02, jog_ms: 0x04, jog_pulses: 0x05 }
         | 
| 144 | 
            -
                  TARGET = { down: 0x00, up: 0x01 }
         | 
| 163 | 
            +
                  TYPE = { delete: 0x00, current_position: 0x01, specified_position: 0x02, jog_ms: 0x04, jog_pulses: 0x05 }.freeze
         | 
| 164 | 
            +
                  TARGET = { down: 0x00, up: 0x01 }.freeze
         | 
| 145 165 |  | 
| 146 166 | 
             
                  attr_reader :type, :target, :value
         | 
| 147 167 |  | 
| @@ -161,17 +181,22 @@ module SDN | |
| 161 181 | 
             
                  end
         | 
| 162 182 |  | 
| 163 183 | 
             
                  def type=(value)
         | 
| 164 | 
            -
                     | 
| 184 | 
            +
                    unless TYPE.key?(value)
         | 
| 185 | 
            +
                      raise ArgumentError,
         | 
| 186 | 
            +
                            "type must be one of :delete, :current_position, :specified_position, :jog_ms, :jog_pulses"
         | 
| 187 | 
            +
                    end
         | 
| 188 | 
            +
             | 
| 165 189 | 
             
                    @type = value
         | 
| 166 190 | 
             
                  end
         | 
| 167 191 |  | 
| 168 192 | 
             
                  def target=(value)
         | 
| 169 | 
            -
                    raise ArgumentError, "target must be one of :up, :down" unless TARGET. | 
| 193 | 
            +
                    raise ArgumentError, "target must be one of :up, :down" unless TARGET.key?(value)
         | 
| 194 | 
            +
             | 
| 170 195 | 
             
                    @target = value
         | 
| 171 196 | 
             
                  end
         | 
| 172 197 |  | 
| 173 198 | 
             
                  def value=(value)
         | 
| 174 | 
            -
                    @value = value | 
| 199 | 
            +
                    @value = value&.& 0xffff
         | 
| 175 200 | 
             
                  end
         | 
| 176 201 |  | 
| 177 202 | 
             
                  def params
         | 
| @@ -186,6 +211,7 @@ module SDN | |
| 186 211 | 
             
                  PARAMS_LENGTH = 3
         | 
| 187 212 |  | 
| 188 213 | 
             
                  attr_accessor :up_speed, :down_speed, :slow_speed
         | 
| 214 | 
            +
             | 
| 189 215 | 
             
                  def initialize(dest = nil, up_speed: nil, down_speed: nil, slow_speed: nil, **kwargs)
         | 
| 190 216 | 
             
                    kwargs[:dest] ||= dest
         | 
| 191 217 | 
             
                    super(**kwargs)
         | 
| @@ -212,7 +238,7 @@ module SDN | |
| 212 238 | 
             
                  attr_accessor :locked, :priority
         | 
| 213 239 |  | 
| 214 240 | 
             
                  def parse(params)
         | 
| 215 | 
            -
                    self.locked = to_number(params[0]) == 1 | 
| 241 | 
            +
                    self.locked = to_number(params[0]) == 1
         | 
| 216 242 | 
             
                    self.priority = to_number(params[1])
         | 
| 217 243 | 
             
                  end
         | 
| 218 244 |  | 
| @@ -227,7 +253,7 @@ module SDN | |
| 227 253 |  | 
| 228 254 | 
             
                  attr_accessor :label
         | 
| 229 255 |  | 
| 230 | 
            -
                  def initialize(dest = nil, label =  | 
| 256 | 
            +
                  def initialize(dest = nil, label = "", **kwargs)
         | 
| 231 257 | 
             
                    kwargs[:dest] ||= dest
         | 
| 232 258 | 
             
                    super(**kwargs)
         | 
| 233 259 | 
             
                    self.label = label
         | 
    
        data/lib/sdn/message.rb
    CHANGED
    
    | @@ -1,4 +1,6 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "sdn/message/helpers"
         | 
| 2 4 |  | 
| 3 5 | 
             
            module SDN
         | 
| 4 6 | 
             
              class MalformedMessage < RuntimeError; end
         | 
| @@ -6,14 +8,16 @@ module SDN | |
| 6 8 | 
             
              class Message
         | 
| 7 9 | 
             
                class << self
         | 
| 8 10 | 
             
                  def inherited(klass)
         | 
| 11 | 
            +
                    super
         | 
| 9 12 | 
             
                    return Message.inherited(klass) unless self == Message
         | 
| 13 | 
            +
             | 
| 10 14 | 
             
                    @message_map = nil
         | 
| 11 15 | 
             
                    (@subclasses ||= []) << klass
         | 
| 12 16 | 
             
                  end
         | 
| 13 17 |  | 
| 14 18 | 
             
                  def expected_response?(message)
         | 
| 15 | 
            -
                    if  | 
| 16 | 
            -
                      message.class.name == name.sub("::Get", "::Post")
         | 
| 19 | 
            +
                    if /::Get([A-Za-z]+)/.match?(name)
         | 
| 20 | 
            +
                      message.class.name == name.sub("::Get", "::Post") # rubocop:disable Style/ClassEqualityComparison
         | 
| 17 21 | 
             
                    else
         | 
| 18 22 | 
             
                      message.is_a?(Ack) || message.is_a?(Nack)
         | 
| 19 23 | 
             
                    end
         | 
| @@ -30,6 +34,7 @@ module SDN | |
| 30 34 | 
             
                      return result if result
         | 
| 31 35 |  | 
| 32 36 | 
             
                      return [nil, 0] if data.length - offset < 11
         | 
| 37 | 
            +
             | 
| 33 38 | 
             
                      msg = to_number(data[offset])
         | 
| 34 39 | 
             
                      length = to_number(data[offset + 1])
         | 
| 35 40 | 
             
                      ack_requested = length & 0x80 == 0x80
         | 
| @@ -57,7 +62,7 @@ module SDN | |
| 57 62 | 
             
                      result.parse(data.slice(offset + 9, length - 11))
         | 
| 58 63 | 
             
                      result.msg = msg if message_class == UnknownMessage
         | 
| 59 64 | 
             
                    rescue ArgumentError => e
         | 
| 60 | 
            -
                      SDN.logger.warn " | 
| 65 | 
            +
                      SDN.logger.warn "Discarding illegal message of type #{message_class.name}: #{e}"
         | 
| 61 66 | 
             
                      result = nil
         | 
| 62 67 | 
             
                    end
         | 
| 63 68 | 
             
                    [result, offset + length]
         | 
| @@ -67,10 +72,10 @@ module SDN | |
| 67 72 |  | 
| 68 73 | 
             
                  def message_map
         | 
| 69 74 | 
             
                    @message_map ||=
         | 
| 70 | 
            -
                      @subclasses. | 
| 75 | 
            +
                      @subclasses.each_with_object({}) do |klass, memo|
         | 
| 71 76 | 
             
                        next memo unless klass.constants(false).include?(:MSG)
         | 
| 77 | 
            +
             | 
| 72 78 | 
             
                        memo[klass.const_get(:MSG, false)] = klass
         | 
| 73 | 
            -
                        memo
         | 
| 74 79 | 
             
                      end
         | 
| 75 80 | 
             
                  end
         | 
| 76 81 | 
             
                end
         | 
| @@ -83,7 +88,7 @@ module SDN | |
| 83 88 | 
             
                def initialize(node_type: nil, ack_requested: false, src: nil, dest: nil)
         | 
| 84 89 | 
             
                  @node_type = node_type || 0
         | 
| 85 90 | 
             
                  @ack_requested = ack_requested
         | 
| 86 | 
            -
                  if src.nil? && !dest.nil? &&  | 
| 91 | 
            +
                  if src.nil? && !dest.nil? && group_address?(dest)
         | 
| 87 92 | 
             
                    src = dest
         | 
| 88 93 | 
             
                    dest = nil
         | 
| 89 94 | 
             
                  end
         | 
| @@ -92,7 +97,11 @@ module SDN | |
| 92 97 | 
             
                end
         | 
| 93 98 |  | 
| 94 99 | 
             
                def parse(params)
         | 
| 95 | 
            -
                   | 
| 100 | 
            +
                  return unless self.class.const_defined?(:PARAMS_LENGTH) && params.length != self.class.const_get(:PARAMS_LENGTH)
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                  raise MalformedMessage, "unrecognized params for #{self.class.name}: #{params.map do |b|
         | 
| 103 | 
            +
                                                                                           format("%02x", b)
         | 
| 104 | 
            +
                                                                                         end}"
         | 
| 96 105 | 
             
                end
         | 
| 97 106 |  | 
| 98 107 | 
             
                def serialize
         | 
| @@ -105,25 +114,32 @@ module SDN | |
| 105 114 | 
             
                end
         | 
| 106 115 |  | 
| 107 116 | 
             
                def ==(other)
         | 
| 108 | 
            -
                   | 
| 117 | 
            +
                  serialize == other.serialize
         | 
| 109 118 | 
             
                end
         | 
| 110 119 |  | 
| 111 120 | 
             
                def inspect
         | 
| 112 | 
            -
                  "#<%s @node_type=%s, @ack_requested=%s, @src=%s, @dest=%s%s>" | 
| 121 | 
            +
                  format("#<%s @node_type=%s, @ack_requested=%s, @src=%s, @dest=%s%s>",
         | 
| 122 | 
            +
                         self.class.name,
         | 
| 123 | 
            +
                         node_type_to_string(node_type),
         | 
| 124 | 
            +
                         ack_requested,
         | 
| 125 | 
            +
                         print_address(src),
         | 
| 126 | 
            +
                         print_address(dest),
         | 
| 127 | 
            +
                         class_inspect)
         | 
| 113 128 | 
             
                end
         | 
| 114 129 | 
             
                alias_method :to_s, :inspect
         | 
| 115 130 |  | 
| 116 131 | 
             
                def class_inspect
         | 
| 117 | 
            -
                  ivars = instance_variables - [ | 
| 132 | 
            +
                  ivars = instance_variables - %i[@node_type @ack_requested @src @dest @params]
         | 
| 118 133 | 
             
                  return if ivars.empty?
         | 
| 134 | 
            +
             | 
| 119 135 | 
             
                  ivars.map { |iv| ", #{iv}=#{instance_variable_get(iv).inspect}" }.join
         | 
| 120 136 | 
             
                end
         | 
| 121 137 |  | 
| 122 138 | 
             
                protected
         | 
| 123 139 |  | 
| 124 | 
            -
                def params | 
| 125 | 
            -
             | 
| 126 | 
            -
                 | 
| 140 | 
            +
                def params
         | 
| 141 | 
            +
                  []
         | 
| 142 | 
            +
                end
         | 
| 127 143 |  | 
| 128 144 | 
             
                class SimpleRequest < Message
         | 
| 129 145 | 
             
                  PARAMS_LENGTH = 0
         | 
| @@ -144,11 +160,10 @@ module SDN | |
| 144 160 | 
             
                             limits_not_set: 0x22,
         | 
| 145 161 | 
             
                             ip_not_set: 0x23,
         | 
| 146 162 | 
             
                             out_of_range: 0x24,
         | 
| 147 | 
            -
                             busy: 0xff }
         | 
| 148 | 
            -
             | 
| 149 | 
            -
             | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 163 | 
            +
                             busy: 0xff }.freeze
         | 
| 164 | 
            +
                  # 17 limits not set?
         | 
| 165 | 
            +
                  # 37 not implemented? (get motor rolling speed)
         | 
| 166 | 
            +
                  # 39 at limit? blocked?
         | 
| 152 167 |  | 
| 153 168 | 
             
                  # presumed
         | 
| 154 169 | 
             
                  attr_accessor :error_code
         | 
| @@ -180,33 +195,34 @@ module SDN | |
| 180 195 | 
             
                    self.params = params
         | 
| 181 196 | 
             
                  end
         | 
| 182 197 |  | 
| 183 | 
            -
                   | 
| 198 | 
            +
                  alias_method :parse, :params=
         | 
| 184 199 |  | 
| 185 200 | 
             
                  def serialize
         | 
| 186 201 | 
             
                    # prevent serializing something we don't know
         | 
| 187 202 | 
             
                    raise NotImplementedError unless params
         | 
| 203 | 
            +
             | 
| 188 204 | 
             
                    super
         | 
| 189 205 | 
             
                  end
         | 
| 190 206 |  | 
| 191 207 | 
             
                  def class_inspect
         | 
| 192 | 
            -
                    result = if  | 
| 193 | 
            -
             | 
| 194 | 
            -
             | 
| 195 | 
            -
             | 
| 196 | 
            -
             | 
| 208 | 
            +
                    result = if instance_of?(UnknownMessage)
         | 
| 209 | 
            +
                               format(", @msg=%02xh", msg)
         | 
| 210 | 
            +
                             else
         | 
| 211 | 
            +
                               super || ""
         | 
| 212 | 
            +
                             end
         | 
| 197 213 | 
             
                    return result if params.empty?
         | 
| 198 214 |  | 
| 199 | 
            -
                    result << ", @params=#{params.map { |b| "%02x"  | 
| 215 | 
            +
                    result << ", @params=#{params.map { |b| format("%02x", b) }.join(" ")}"
         | 
| 200 216 | 
             
                  end
         | 
| 201 217 | 
             
                end
         | 
| 202 218 | 
             
              end
         | 
| 203 219 | 
             
            end
         | 
| 204 220 |  | 
| 205 | 
            -
            require  | 
| 206 | 
            -
            require  | 
| 207 | 
            -
            require  | 
| 208 | 
            -
            require  | 
| 209 | 
            -
            require  | 
| 210 | 
            -
            require  | 
| 211 | 
            -
            require  | 
| 212 | 
            -
            require  | 
| 221 | 
            +
            require "sdn/message/control"
         | 
| 222 | 
            +
            require "sdn/message/get"
         | 
| 223 | 
            +
            require "sdn/message/post"
         | 
| 224 | 
            +
            require "sdn/message/set"
         | 
| 225 | 
            +
            require "sdn/message/ilt2/get"
         | 
| 226 | 
            +
            require "sdn/message/ilt2/master_control"
         | 
| 227 | 
            +
            require "sdn/message/ilt2/post"
         | 
| 228 | 
            +
            require "sdn/message/ilt2/set"
         | 
    
        data/lib/sdn/version.rb
    CHANGED
    
    
    
        data/lib/sdn.rb
    CHANGED
    
    | @@ -1,21 +1,27 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require  | 
| 4 | 
            -
             | 
| 3 | 
            +
            require "logger"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require "sdn/client"
         | 
| 6 | 
            +
            require "sdn/message"
         | 
| 5 7 |  | 
| 6 8 | 
             
            module SDN
         | 
| 7 | 
            -
              BROADCAST_ADDRESS = [0xff, 0xff, 0xff]
         | 
| 9 | 
            +
              BROADCAST_ADDRESS = [0xff, 0xff, 0xff].freeze
         | 
| 8 10 |  | 
| 9 11 | 
             
              class << self
         | 
| 10 | 
            -
                def logger
         | 
| 11 | 
            -
                   | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
                       | 
| 15 | 
            -
             | 
| 16 | 
            -
                      end
         | 
| 17 | 
            -
                    end
         | 
| 12 | 
            +
                def logger=(logger)
         | 
| 13 | 
            +
                  logger.datetime_format = "%Y-%m-%d %H:%M:%S.%L"
         | 
| 14 | 
            +
                  logger.formatter = proc do |severity, datetime, _progname, msg|
         | 
| 15 | 
            +
                    "#{datetime.strftime(logger.datetime_format)} " \
         | 
| 16 | 
            +
                      "[#{Process.pid}/#{Thread.current.object_id}] " \
         | 
| 17 | 
            +
                      "#{severity}: #{msg}\n"
         | 
| 18 18 | 
             
                  end
         | 
| 19 | 
            +
                  @logger = logger
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def logger
         | 
| 23 | 
            +
                  self.logger = Logger.new($stdout, :info) unless @logger
         | 
| 24 | 
            +
                  @logger
         | 
| 19 25 | 
             
                end
         | 
| 20 26 | 
             
              end
         | 
| 21 27 | 
             
            end
         | 
    
        data/lib/somfy_sdn.rb
    CHANGED