# frozen_string_literal: true require "dry-struct" require "dry-transformer" require "json" module DjiMqttConnect module Thing::Product class EventsMarshal < MessageMarshal include Utils::MessageParsing # Rename pesky `method` argument to `_method` and makes a copy of the raw data class AttributeTransformer < Dry::Transformer::Pipe import Dry::Transformer::Conditional import Dry::Transformer::ArrayTransformations import Dry::Transformer::HashTransformations define! do # Keep a copy of the original data hash copy_keys "data" => "_data" # Fix the legacy hms message guard ->(message) { message.dig("data", "method") == "hms" } do map_value "data" do reject_keys ["event"] map_value "output" do map_value "codes" do map_array do nest "args", ["component_index", "sensor_index"] map_value "id", ->(id) { "0x#{id}" } map_value "level", ->(level) { [level - 1, 0].max } rename_keys "id" => "code" deep_merge({"module" => 3}) # hardcoded to hms module end end end unwrap "output", ["codes"] rename_keys "codes" => "list" end unwrap "data", ["method"] end # Fix the legacy status code message guard ->(message) { message.dig("data", "method") == "status_code" } do map_value "data" do unwrap "output", ["codes"] end unwrap "data", ["method"] end rename_keys "method" => "_method" end end # Attempts to look a the method attribute, and builds a specific Message class for the message def load(raw_message) # Parse the message from JSON parsed_message = JSON.parse(raw_message) # Transform the message transformed_message = attribute_transformer.call(parsed_message) message_method = transformed_message["_method"] # Build an instance of the class, or a generic message from the current class message_class = message_class_from_message_method(message_method, EventsMessage) message_class.new transformed_message rescue JSON::ParserError => e raise ParseError.new(e, "Unable to parse message as JSON") rescue Dry::Struct::Error => e raise ParseError.new(e, "Unexpected #{message_class} payload") rescue TypeError => e # FIXME: airsense messages have data as an array, this breaks a lot of code... raise ParseError.new(e, "Unable to parse message") end private def attribute_transformer @attribute_transformer ||= AttributeTransformer.new end end end end