/// <summary>
        /// Update state and publish new statuses.
        /// </summary>
        /// <param name="thermostat">Thermostat status.</param>
        /// <returns>>A <see cref="Task"/> representing the asynchronous operation.</returns>
        private async Task UpdateState(Thermostat thermostat)
        {
            if (thermostat == null)
            {
                return;
            }

            var thermostatStatus = new ThermostatStatus();

            // Equipment status
            foreach (var device in thermostat.EquipmentStatus.Split(',').Where(x => !string.IsNullOrEmpty(x)))
            {
                thermostatStatus.EquipmentStatus[device] = "on";
            }

            // Status
            thermostatStatus.Status["hvacMode"]          = thermostat.Settings.HvacMode;
            thermostatStatus.Status["humidifierMode"]    = thermostat.Settings.HumidifierMode;
            thermostatStatus.Status["dehumidifierMode"]  = thermostat.Settings.DehumidifierMode;
            thermostatStatus.Status["autoAway"]          = thermostat.Settings.AutoAway.HasValue && thermostat.Settings.AutoAway.Value ? "true" : "false";
            thermostatStatus.Status["vent"]              = thermostat.Settings.Vent;
            thermostatStatus.Status["actualTemperature"] = thermostat.Runtime.ActualTemperature.HasValue ? (thermostat.Runtime.ActualTemperature.Value / 10m).ToString() : null;
            thermostatStatus.Status["actualHumidity"]    = thermostat.Runtime.ActualHumidity.HasValue ? thermostat.Runtime.ActualHumidity.Value.ToString() : null;
            thermostatStatus.Status["desiredHeat"]       = thermostat.Runtime.DesiredHeat.HasValue ? (thermostat.Runtime.DesiredHeat.Value / 10m).ToString() : null;
            thermostatStatus.Status["desiredCool"]       = thermostat.Runtime.DesiredCool.HasValue ? (thermostat.Runtime.DesiredCool.Value / 10m).ToString() : null;
            thermostatStatus.Status["desiredHumidity"]   = thermostat.Runtime.DesiredHumidity.HasValue ? thermostat.Runtime.DesiredHumidity.Value.ToString() : null;
            thermostatStatus.Status["desiredDehumidity"] = thermostat.Runtime.DesiredDehumidity.HasValue ? thermostat.Runtime.DesiredDehumidity.Value.ToString() : null;
            thermostatStatus.Status["desiredFanMode"]    = thermostat.Runtime.DesiredFanMode;

            // Weather forcasts
            var forecast = thermostat.Weather.Forecasts?.FirstOrDefault();

            if (forecast != null)
            {
                thermostatStatus.Status["weatherDewPoint"]            = forecast.Dewpoint.HasValue ? (forecast.Dewpoint.Value / 10m).ToString() : null;
                thermostatStatus.Status["weatherPrecipitationChance"] = forecast.Pop.HasValue ? forecast.Pop.Value.ToString() : null;
                thermostatStatus.Status["weatherPressure"]            = forecast.Pressure.HasValue ? (forecast.Pressure.Value / 100m).ToString() : null;
                thermostatStatus.Status["weatherRelativeHumidity"]    = forecast.RelativeHumidity.HasValue ? forecast.RelativeHumidity.Value.ToString() : null;
                thermostatStatus.Status["weatherTemperature"]         = forecast.Temperature.HasValue ? (forecast.Temperature.Value / 10m).ToString() : null;
                thermostatStatus.Status["weatherTempLow"]             = forecast.TempLow.HasValue ? (forecast.TempLow.Value / 10m).ToString() : null;
                thermostatStatus.Status["weatherTempHigh"]            = forecast.TempHigh.HasValue ? (forecast.TempHigh.Value / 10m).ToString() : null;
                thermostatStatus.Status["weatherVisibility"]          = forecast.Visibility.HasValue ? forecast.Visibility.Value.ToString() : null;
                thermostatStatus.Status["weatherWindBearing"]         = forecast.WindBearing.HasValue ? forecast.WindBearing.Value.ToString() : null;
                thermostatStatus.Status["weatherWindDirection"]       = forecast.WindDirection;
                thermostatStatus.Status["weatherWindGust"]            = forecast.WindGust.HasValue ? forecast.WindGust.Value.ToString() : null;
                thermostatStatus.Status["weatherWindSpeed"]           = forecast.WindSpeed.HasValue ? forecast.WindSpeed.Value.ToString() : null;
            }

            // Sensors
            if (thermostat.RemoteSensors != null && thermostat.RemoteSensors.Count > 0)
            {
                foreach (var sensor in thermostat.RemoteSensors)
                {
                    thermostatStatus.Sensors[sensor.Name] = sensor.Capability.ToDictionary(
                        s => s.Type,
                        s =>
                    {
                        // Convert temperature values to human readable
                        if (string.Equals(s.Type, "temperature", System.StringComparison.OrdinalIgnoreCase))
                        {
                            if (decimal.TryParse(s.Value, out var decimalValue))
                            {
                                return((decimalValue / 10m).ToString());
                            }
                        }

                        return(s.Value);
                    });
                }
            }

            // Hold
            var holdEvent = thermostat.Events.FirstOrDefault(x => x.Type == "hold");

            if (holdEvent != null && holdEvent.Running.HasValue && holdEvent.Running.Value)
            {
                thermostatStatus.ActiveHold["running"]             = holdEvent.Running.HasValue && holdEvent.Running.Value ? "true" : "false";
                thermostatStatus.ActiveHold["startTime"]           = DateTime.TryParse($"{holdEvent.StartDate} {holdEvent.StartTime}", out var startTimeResult) ? startTimeResult.ToString() : null;
                thermostatStatus.ActiveHold["endTime"]             = DateTime.TryParse($"{holdEvent.EndDate} {holdEvent.EndTime}", out var endTimeResult) ? endTimeResult.ToString() : null;
                thermostatStatus.ActiveHold["coldHoldTemp"]        = holdEvent.CoolHoldTemp.HasValue ? (holdEvent.CoolHoldTemp.Value / 10m).ToString() : null;
                thermostatStatus.ActiveHold["heatHoldTemp"]        = holdEvent.HeatHoldTemp.HasValue ? (holdEvent.HeatHoldTemp.Value / 10m).ToString() : null;
                thermostatStatus.ActiveHold["fan"]                 = holdEvent.Fan;
                thermostatStatus.ActiveHold["fanMinOnTime"]        = holdEvent.FanMinOnTime.HasValue ? holdEvent.FanMinOnTime.Value.ToString() : null;
                thermostatStatus.ActiveHold["vent"]                = holdEvent.Vent;
                thermostatStatus.ActiveHold["ventilatorMinOnTime"] = holdEvent.VentilatorMinOnTime.HasValue ? holdEvent.VentilatorMinOnTime.Value.ToString() : null;
            }

            if (_thermostatStatus.ContainsKey(thermostat.Identifier))
            {
                // Publish updates
                foreach (var device in thermostatStatus.EquipmentStatus)
                {
                    if (device.Value != _thermostatStatus[thermostat.Identifier].EquipmentStatus[device.Key] &&
                        device.Value != null)
                    {
                        await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                      .WithTopic($"{TopicRoot}/{thermostat.Identifier}/{device.Key}")
                                                      .WithPayload(device.Value)
                                                      .WithAtLeastOnceQoS()
                                                      .WithRetainFlag()
                                                      .Build())
                        .ConfigureAwait(false);
                    }
                }

                foreach (var status in thermostatStatus.Status)
                {
                    if (status.Value != _thermostatStatus[thermostat.Identifier].Status[status.Key] &&
                        status.Value != null)
                    {
                        await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                      .WithTopic($"{TopicRoot}/{thermostat.Identifier}/{status.Key}")
                                                      .WithPayload(status.Value)
                                                      .WithAtLeastOnceQoS()
                                                      .WithRetainFlag()
                                                      .Build())
                        .ConfigureAwait(false);
                    }
                }

                // Hold status
                foreach (var holdStatus in thermostatStatus.ActiveHold)
                {
                    if (holdStatus.Value != _thermostatStatus[thermostat.Identifier].ActiveHold[holdStatus.Key] &&
                        holdStatus.Value != null)
                    {
                        await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                      .WithTopic($"{TopicRoot}/{thermostat.Identifier}/hold/{holdStatus.Key}")
                                                      .WithPayload(holdStatus.Value)
                                                      .WithAtLeastOnceQoS()
                                                      .WithRetainFlag()
                                                      .Build())
                        .ConfigureAwait(false);
                    }
                }

                // Publish everything for new sensors, new capabilities, and changes in existing ability values
                foreach (var sensor in thermostatStatus.Sensors)
                {
                    foreach (var sensorCapability in sensor.Value)
                    {
                        if (!_thermostatStatus[thermostat.Identifier].Sensors.ContainsKey(sensor.Key) &&
                            sensorCapability.Value != null)
                        {
                            await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                          .WithTopic($"{TopicRoot}/{thermostat.Identifier}/sensor/{sensor.Key.Sluggify()}/{sensorCapability.Key}")
                                                          .WithPayload(sensorCapability.Value)
                                                          .WithAtLeastOnceQoS()
                                                          .WithRetainFlag()
                                                          .Build())
                            .ConfigureAwait(false);
                        }
                        else if (!_thermostatStatus[thermostat.Identifier].Sensors[sensor.Key].ContainsKey(sensorCapability.Key) &&
                                 sensorCapability.Value != null)
                        {
                            await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                          .WithTopic($"{TopicRoot}/{thermostat.Identifier}/sensor/{sensor.Key.Sluggify()}/{sensorCapability.Key}")
                                                          .WithPayload(sensorCapability.Value)
                                                          .WithAtLeastOnceQoS()
                                                          .WithRetainFlag()
                                                          .Build())
                            .ConfigureAwait(false);
                        }
                        else if (sensorCapability.Value != _thermostatStatus[thermostat.Identifier].Sensors[sensor.Key][sensorCapability.Key] &&
                                 sensorCapability.Value != null)
                        {
                            await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                          .WithTopic($"{TopicRoot}/{thermostat.Identifier}/sensor/{sensor.Key.Sluggify()}/{sensorCapability.Key}")
                                                          .WithPayload(sensorCapability.Value)
                                                          .WithAtLeastOnceQoS()
                                                          .WithRetainFlag()
                                                          .Build())
                            .ConfigureAwait(false);
                        }
                    }
                }
            }
            else
            {
                // Publish initial state
                foreach (var device in thermostatStatus.EquipmentStatus.Where(x => x.Value != null))
                {
                    await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                  .WithTopic($"{TopicRoot}/{thermostat.Identifier}/{device.Key}")
                                                  .WithPayload(device.Value)
                                                  .WithAtLeastOnceQoS()
                                                  .WithRetainFlag()
                                                  .Build())
                    .ConfigureAwait(false);
                }

                foreach (var status in thermostatStatus.Status.Where(x => x.Value != null))
                {
                    await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                  .WithTopic($"{TopicRoot}/{thermostat.Identifier}/{status.Key}")
                                                  .WithPayload(status.Value)
                                                  .WithAtLeastOnceQoS()
                                                  .WithRetainFlag()
                                                  .Build())
                    .ConfigureAwait(false);
                }

                // Hold status
                foreach (var holdStatus in thermostatStatus.ActiveHold.Where(x => x.Value != null))
                {
                    await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                  .WithTopic($"{TopicRoot}/{thermostat.Identifier}/hold/{holdStatus.Key}")
                                                  .WithPayload(holdStatus.Value)
                                                  .WithAtLeastOnceQoS()
                                                  .WithRetainFlag()
                                                  .Build())
                    .ConfigureAwait(false);
                }

                foreach (var sensor in thermostatStatus.Sensors)
                {
                    foreach (var sensorCapability in sensor.Value.Where(x => x.Value != null))
                    {
                        await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                      .WithTopic($"{TopicRoot}/{thermostat.Identifier}/sensor/{sensor.Key.Sluggify()}/{sensorCapability.Key}")
                                                      .WithPayload(sensorCapability.Value)
                                                      .WithAtLeastOnceQoS()
                                                      .WithRetainFlag()
                                                      .Build())
                        .ConfigureAwait(false);
                    }
                }
            }

            _thermostatStatus[thermostat.Identifier] = thermostatStatus;
        }
Beispiel #2
0
        /// <summary>
        /// Handles updating status for a thermostat and publishing changes.
        /// </summary>
        /// <param name="revisionStatus">Revision status that triggered the update.</param>
        private async void RefreshThermostatAsync(RevisionStatus revisionStatus)
        {
            // Build single thermostat request
            var thermostatRequest = new ThermostatRequest
            {
                Selection = new Selection
                {
                    SelectionType          = "thermostats",
                    SelectionMatch         = revisionStatus.ThermostatIdentifier,
                    IncludeEquipmentStatus = true,
                    IncludeDevice          = true,
                    IncludeLocation        = true,
                    IncludeSettings        = true,
                    IncludeRuntime         = true,
                    IncludeSensors         = true,
                    IncludeWeather         = true
                }
            };

            var thermostatUpdate = await _client.GetAsync <ThermostatRequest, ThermostatResponse>(thermostatRequest)
                                   .ConfigureAwait(false);

            // Publish updates and cache new values
            var thermostat = thermostatUpdate.ThermostatList.FirstOrDefault();

            if (thermostat != null)
            {
                var thermostatStatus = new ThermostatStatus();

                // Equipment status
                foreach (var device in thermostat.EquipmentStatus.Split(',').Where(x => !string.IsNullOrEmpty(x)))
                {
                    thermostatStatus.EquipmentStatus[device] = "on";
                }

                // ID
                thermostatStatus.Status["name"] = thermostat.Name;
                thermostatStatus.Status["city"] = thermostat.Location.City;

                // Status
                thermostatStatus.Status["hvacMode"]          = thermostat.Settings.HvacMode;
                thermostatStatus.Status["humidifierMode"]    = thermostat.Settings.HumidifierMode;
                thermostatStatus.Status["dehumidifierMode"]  = thermostat.Settings.DehumidifierMode;
                thermostatStatus.Status["autoAway"]          = thermostat.Settings.AutoAway ? "true" : "false";
                thermostatStatus.Status["vent"]              = thermostat.Settings.Vent;
                thermostatStatus.Status["actualTemperature"] = (thermostat.Runtime.ActualTemperature / 10m).ToString();
                thermostatStatus.Status["actualHumidity"]    = thermostat.Runtime.ActualHumidity.ToString();
                thermostatStatus.Status["desiredHeat"]       = (thermostat.Runtime.DesiredHeat / 10m).ToString();
                thermostatStatus.Status["desiredCool"]       = (thermostat.Runtime.DesiredCool / 10m).ToString();
                thermostatStatus.Status["desiredHumidity"]   = thermostat.Runtime.DesiredHumidity.ToString();
                thermostatStatus.Status["desiredDehumidity"] = thermostat.Runtime.DesiredDehumidity.ToString();
                thermostatStatus.Status["desiredFanMode"]    = thermostat.Runtime.DesiredFanMode;

                // Weather forcasts
                var forecast = thermostat.Weather.Forecasts?.FirstOrDefault();
                if (forecast != null)
                {
                    thermostatStatus.Status["weatherDewPoint"]            = (forecast.Dewpoint / 10m).ToString();
                    thermostatStatus.Status["weatherPrecipitationChance"] = forecast.Pop.ToString();
                    thermostatStatus.Status["weatherPressure"]            = (forecast.Pressure / 100m).ToString();
                    thermostatStatus.Status["weatherRelativeHumidity"]    = forecast.RelativeHumidity.ToString();
                    thermostatStatus.Status["weatherTemperature"]         = (forecast.Temperature / 10m).ToString();
                    thermostatStatus.Status["weatherTempLow"]             = (forecast.TempLow / 10m).ToString();
                    thermostatStatus.Status["weatherTempHigh"]            = (forecast.TempHigh / 10m).ToString();
                    thermostatStatus.Status["weatherVisibility"]          = forecast.Visibility.ToString();
                    thermostatStatus.Status["weatherWindBearing"]         = forecast.WindBearing.ToString();
                    thermostatStatus.Status["weatherWindDirection"]       = forecast.WindDirection.ToString();
                    thermostatStatus.Status["weatherWindGust"]            = forecast.WindGust.ToString();
                    thermostatStatus.Status["weatherWindSpeed"]           = forecast.WindSpeed.ToString();
                }

                // Sensors
                if (thermostat.RemoteSensors != null && thermostat.RemoteSensors.Count > 0)
                {
                    foreach (var sensor in thermostat.RemoteSensors)
                    {
                        thermostatStatus.Sensors[sensor.Name] = sensor.Capability.ToDictionary(
                            s => s.Type,
                            s =>
                        {
                            // Convert temperature values to human readable
                            if (string.Equals(s.Type, "temperature", System.StringComparison.OrdinalIgnoreCase))
                            {
                                if (decimal.TryParse(s.Value, out var decimalValue))
                                {
                                    return((decimalValue / 10m).ToString());
                                }
                            }

                            return(s.Value);
                        });
                    }
                }

                if (_thermostatStatus.ContainsKey(revisionStatus.ThermostatIdentifier))
                {
                    // Publish updates
                    foreach (var device in thermostatStatus.EquipmentStatus)
                    {
                        if (device.Value != _thermostatStatus[revisionStatus.ThermostatIdentifier].EquipmentStatus[device.Key])
                        {
                            await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                          .WithTopic($"{TopicRoot}/{revisionStatus.ThermostatIdentifier}/{device.Key}")
                                                          .WithPayload(device.Value.ToString())
                                                          .WithAtLeastOnceQoS()
                                                          .WithRetainFlag()
                                                          .Build())
                            .ConfigureAwait(false);
                        }
                    }

                    foreach (var status in thermostatStatus.Status)
                    {
                        if (status.Value != _thermostatStatus[revisionStatus.ThermostatIdentifier].Status[status.Key])
                        {
                            await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                          .WithTopic($"{TopicRoot}/{revisionStatus.ThermostatIdentifier}/{status.Key}")
                                                          .WithPayload(status.Value)
                                                          .WithAtLeastOnceQoS()
                                                          .WithRetainFlag()
                                                          .Build())
                            .ConfigureAwait(false);
                        }
                    }

                    // Publish everything for new sensors, new capabilities, and changes in existing ability values
                    foreach (var sensor in thermostatStatus.Sensors)
                    {
                        foreach (var sensorCapability in sensor.Value)
                        {
                            if (!_thermostatStatus[revisionStatus.ThermostatIdentifier].Sensors.ContainsKey(sensor.Key))
                            {
                                await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                              .WithTopic($"{TopicRoot}/{revisionStatus.ThermostatIdentifier}/sensor/{sensor.Key.Sluggify()}/{sensorCapability.Key}")
                                                              .WithPayload(sensorCapability.Value)
                                                              .WithAtLeastOnceQoS()
                                                              .WithRetainFlag()
                                                              .Build())
                                .ConfigureAwait(false);
                            }
                            else if (!_thermostatStatus[revisionStatus.ThermostatIdentifier].Sensors[sensor.Key].ContainsKey(sensorCapability.Key))
                            {
                                await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                              .WithTopic($"{TopicRoot}/{revisionStatus.ThermostatIdentifier}/sensor/{sensor.Key.Sluggify()}/{sensorCapability.Key}")
                                                              .WithPayload(sensorCapability.Value)
                                                              .WithAtLeastOnceQoS()
                                                              .WithRetainFlag()
                                                              .Build())
                                .ConfigureAwait(false);
                            }
                            else if (sensorCapability.Value != _thermostatStatus[revisionStatus.ThermostatIdentifier].Sensors[sensor.Key][sensorCapability.Key])
                            {
                                await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                              .WithTopic($"{TopicRoot}/{revisionStatus.ThermostatIdentifier}/sensor/{sensor.Key.Sluggify()}/{sensorCapability.Key}")
                                                              .WithPayload(sensorCapability.Value)
                                                              .WithAtLeastOnceQoS()
                                                              .WithRetainFlag()
                                                              .Build())
                                .ConfigureAwait(false);
                            }
                        }
                    }
                }
                else
                {
                    // Publish initial state
                    foreach (var device in thermostatStatus.EquipmentStatus)
                    {
                        await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                      .WithTopic($"{TopicRoot}/{revisionStatus.ThermostatIdentifier}/{device.Key}")
                                                      .WithPayload(device.Value.ToString())
                                                      .WithAtLeastOnceQoS()
                                                      .WithRetainFlag()
                                                      .Build())
                        .ConfigureAwait(false);
                    }

                    foreach (var status in thermostatStatus.Status)
                    {
                        await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                      .WithTopic($"{TopicRoot}/{revisionStatus.ThermostatIdentifier}/{status.Key}")
                                                      .WithPayload(status.Value)
                                                      .WithAtLeastOnceQoS()
                                                      .WithRetainFlag()
                                                      .Build())
                        .ConfigureAwait(false);
                    }

                    foreach (var sensor in thermostatStatus.Sensors)
                    {
                        foreach (var sensorCapability in sensor.Value)
                        {
                            await MqttClient.PublishAsync(new MqttApplicationMessageBuilder()
                                                          .WithTopic($"{TopicRoot}/{revisionStatus.ThermostatIdentifier}/sensor/{sensor.Key.Sluggify()}/{sensorCapability.Key}")
                                                          .WithPayload(sensorCapability.Value)
                                                          .WithAtLeastOnceQoS()
                                                          .WithRetainFlag()
                                                          .Build())
                            .ConfigureAwait(false);
                        }
                    }
                }

                _thermostatStatus[revisionStatus.ThermostatIdentifier] = thermostatStatus;
            }
        }