protected ClientPropertyBase(ClientPropertyMetadata creationOptions)
        {
            Name        = creationOptions.Name;
            Type        = creationOptions.PropertyType;
            _propertyId = creationOptions.NodeId + "/" + creationOptions.PropertyId;
            DataType    = creationOptions.DataType;
            Format      = creationOptions.Format;
            Unit        = creationOptions.Unit;
            _rawValue   = creationOptions.InitialValue;

            foreach (var key in creationOptions.Tags.Keys)
            {
                Tags.Add(key, creationOptions.Tags[key]);
            }
        }
예제 #2
0
        private void CheckForValidityAndThrowIfSomethingIsWrong(ClientPropertyMetadata creationOptions)
        {
            var errorList   = new ArrayList();
            var warningList = new ArrayList();

            var isMetadataOk = creationOptions.ValidateAndFix(ref errorList, ref warningList);

            if (isMetadataOk == false)
            {
                var errorMessage = $"Provided metadata is incorrect. Errors: ";
                foreach (var problem in errorList)
                {
                    errorMessage += problem + " ";
                }
                throw new ArgumentException(errorMessage);
            }
        }
예제 #3
0
        /// <summary>
        /// Creates a client number property.
        /// </summary>
        public ClientNumberProperty CreateClientNumberProperty(ClientPropertyMetadata creationOptions)
        {
            if (creationOptions.DataType == DataType.Blank)
            {
                creationOptions.DataType = DataType.Float;
            }
            if (creationOptions.DataType != DataType.Float)
            {
                throw new ArgumentException($"You're creating a {nameof(CreateClientNumberProperty)} property, but type specified is {creationOptions.DataType}. Either set it correctly, or leave a default value (that is is, don't set it at all).");
            }

            CheckForValidityAndThrowIfSomethingIsWrong(creationOptions);

            var createdProperty = new ClientNumberProperty(creationOptions);

            _properties.Add(createdProperty);

            return(createdProperty);
        }
예제 #4
0
 internal ClientDateTimeProperty(ClientPropertyMetadata creationOptions) : base(creationOptions)
 {
 }
예제 #5
0
 internal ClientColorProperty(ClientPropertyMetadata creationProperties) : base(creationProperties)
 {
 }
 internal ClientChoiceProperty(ClientPropertyMetadata creationProperties) : base(creationProperties)
 {
     _possibleValues = creationProperties.Format.Split(',');
 }
예제 #7
0
 internal ClientNumberProperty(ClientPropertyMetadata creationOptions) : base(creationOptions)
 {
 }
예제 #8
0
        private static bool TryParseProperties(ref ArrayList unparsedTopicList, ref ArrayList parsedTopicList, string baseTopic, ref ClientDeviceMetadata candidateDevice, ref ArrayList errorList, ref ArrayList warningList)
        {
            var isParseSuccessful = false;

            for (var n = 0; n < candidateDevice.Nodes.Length; n++)
            {
                var candidatePropertyIds = ((string)candidateDevice.Nodes[n].AllAttributes["$properties"]).Split(',');
                var goodProperties       = new ArrayList();

                for (var p = 0; p < candidatePropertyIds.Length; p++)
                {
                    var candidateProperty = new ClientPropertyMetadata()
                    {
                        NodeId = candidateDevice.Nodes[n].Id, PropertyId = candidatePropertyIds[p]
                    };

                    // Parsing property attributes and value.
                    var attributeRegex = new Regex($@"^({baseTopic})\/({candidateDevice.Id})\/({candidateDevice.Nodes[n].Id})\/({candidateProperty.PropertyId})(\/\$[a-z0-9][a-z0-9-]+)?(:)(.+)$");
                    var setRegex       = new Regex($@"^({baseTopic})\/({candidateDevice.Id})\/({candidateDevice.Nodes[n].Id})\/({candidateProperty.PropertyId})(\/set)(:)(.+)$");
                    var valueRegex     = new Regex($@"^({baseTopic})\/({candidateDevice.Id})\/({candidateDevice.Nodes[n].Id})\/({candidateProperty.PropertyId})()(:)(.+)$");

                    var isSettable         = false;
                    var isRetained         = false;
                    var isNameReceived     = false;
                    var isDataTypeReceived = false;
                    var isSettableReceived = false;
                    var isRetainedReceived = false;

                    foreach (string inputString in unparsedTopicList)
                    {
                        var attributeMatch = attributeRegex.Match(inputString);
                        if (attributeMatch.Success)
                        {
                            var key   = attributeMatch.Groups[5].Value;
                            var value = attributeMatch.Groups[7].Value;

                            if (key == "/$name")
                            {
                                candidateProperty.Name = value;
                                isNameReceived         = true;
                            }
                            if (key == "/$datatype")
                            {
                                if (Helpers.TryParseHomieDataType(value, out var parsedType))
                                {
                                    candidateProperty.DataType = parsedType;
                                    isDataTypeReceived         = true;
                                }
                                ;
                            }
                            if (key == "/$format")
                            {
                                candidateProperty.Format = value;
                            }
                            if (key == "/$settable")
                            {
                                if (Helpers.TryParseBool(value, out isSettable))
                                {
                                    isSettableReceived = true;
                                }
                            }
                            if (key == "/$retained")
                            {
                                if (Helpers.TryParseBool(value, out isRetained))
                                {
                                    isRetainedReceived = true;
                                }
                                ;
                            }

                            if (key == "/$unit")
                            {
                                candidateProperty.Unit = value;
                            }

                            parsedTopicList.Add(inputString);
                        }

                        var setMatch = setRegex.Match(inputString);
                        if (setMatch.Success)
                        {
                            // Discarding this one. This is a historically cached command, and these should not execute during initialization. Besides, it shouldn't be retained,
                            // so the fact that we're here means something is wrong with the host side.
                            warningList.Add($"{candidateDevice.Id}/{candidateProperty} has /set topic assigned, which means /set message is published as retained. This is against Homie convention.");
                        }

                        var valueMatch = valueRegex.Match(inputString);
                        if (valueMatch.Success)
                        {
                            var value = attributeMatch.Groups[7].Value;
                            candidateProperty.InitialValue = value;
                        }
                    }

                    foreach (var parsedTopic in parsedTopicList)
                    {
                        unparsedTopicList.Remove(parsedTopic);
                    }

                    // Basic data extraction is done. Now we'll validate if values of the fields are compatible with each other and also YAHI itself.
                    var isOk = isNameReceived & isDataTypeReceived & isSettableReceived & isRetainedReceived;
                    if (isOk)
                    {
                        // Setting property type.
                        if ((isSettable == false) && (isRetained == true))
                        {
                            candidateProperty.PropertyType = PropertyType.State;
                        }
                        if ((isSettable == true) && (isRetained == false))
                        {
                            candidateProperty.PropertyType = PropertyType.Command;
                        }
                        if ((isSettable == true) && (isRetained == true))
                        {
                            candidateProperty.PropertyType = PropertyType.Parameter;
                        }
                        if ((isSettable == false) && (isRetained == false))
                        {
                            errorList.Add($"{candidateDevice.Id}/{candidateProperty.NodeId}/{candidateProperty.PropertyId} has all mandatory fields set, but this retainability and settability configuration is not supported by YAHI. Skipping this property entirely.");
                            isOk = false;
                        }
                    }
                    else
                    {
                        // Some of the mandatory topic were not received. Can't let this property through.
                        errorList.Add($"{candidateDevice.Id}/{candidateProperty.NodeId}/{candidateProperty.PropertyId} is defined, but mandatory attributes are missing. Skipping this property entirely.");
                        isOk = false;
                    }

                    // Validating by property data type, because rules are very different for each of those.
                    if (isOk)
                    {
                        var tempErrorList   = new ArrayList();
                        var tempWarningList = new ArrayList();

                        // ValidateAndFix method does not know anythin about device it is parsing, thus doing some wrapping around error and warning lists (because I want device info in those).
                        isOk = candidateProperty.ValidateAndFix(ref tempErrorList, ref tempWarningList);

                        foreach (var error in tempErrorList)
                        {
                            errorList.Add($"{candidateDevice.Id}/{error}");
                        }
                        foreach (var warning in tempWarningList)
                        {
                            warningList.Add($"{candidateDevice.Id}/{warning}");
                        }
                    }

                    if (isOk)
                    {
                        goodProperties.Add(candidateProperty);
                    }
                }

                // Converting local temporary property lists to final arrays.
                candidateDevice.Nodes[n].Properties = new ClientPropertyMetadata[goodProperties.Count];
                for (var i = 0; i < goodProperties.Count; i++)
                {
                    candidateDevice.Nodes[n].Properties[i] = (ClientPropertyMetadata)goodProperties[i];
                }
            }

            // Converting local temporary node lists to final arrays and returning.
            foreach (var node in candidateDevice.Nodes)
            {
                if (node.Properties.Length > 0)
                {
                    isParseSuccessful = true;
                }
            }

            return(isParseSuccessful);
        }
 public ClientTextProperty(ClientPropertyMetadata creationOptions) : base(creationOptions)
 {
 }