/// <summary>
        /// Heartbeat ping. Failure will result in the heartbeat being stopped, which will
        /// make any future calls throw an exception as the heartbeat indicator will be disabled.
        /// </summary>
        /// <returns>>A <see cref="Task"/> representing the asynchronous operation.</returns>
        private async Task RefreshAsync()
        {
            // Get current revision status
            var summaryRequest = new ThermostatSummaryRequest {
                Selection = new Selection {
                    SelectionType = "registered"
                }
            };
            var status = await _client.GetAsync <ThermostatSummaryRequest, ThermostatSummaryResponse>(summaryRequest);

            foreach (var thermostatRevision in status.RevisionList)
            {
                _log.LogInformation($"Got revision: {thermostatRevision}");
                var revisionStatus = new RevisionStatus(thermostatRevision);

                if (_revisionStatusCache[revisionStatus.ThermostatIdentifier].ThermostatRevision != revisionStatus.ThermostatRevision ||
                    _revisionStatusCache[revisionStatus.ThermostatIdentifier].RuntimeRevision != revisionStatus.RuntimeRevision)
                {
                    await RefreshThermostatAsync(revisionStatus);
                }

                // Cache last run values
                _revisionStatusCache[revisionStatus.ThermostatIdentifier] = revisionStatus;
            }
        }
Example #2
0
        /// <summary>
        /// Get, cache, and publish initial states.
        /// </summary>
        /// <returns>An awaitable <see cref="Task"/>.</returns>
        private async Task GetInitialStatusAsync()
        {
            var summaryRequest = new ThermostatSummaryRequest {
                Selection = new Selection {
                    SelectionType = "registered"
                }
            };
            var summary = await _client.GetAsync <ThermostatSummaryRequest, ThermostatSummaryResponse>(summaryRequest)
                          .ConfigureAwait(false);

            // Set initial revision cache
            _revisionStatusCache.Clear();
            _thermostatStatus.Clear();
            foreach (var revision in summary.RevisionList)
            {
                _log.LogInformation($"Got revision: {revision}");
                var revisionStatus = new RevisionStatus(revision);

                RefreshThermostatAsync(revisionStatus);
                _revisionStatusCache.Add(revisionStatus.ThermostatIdentifier, revisionStatus);
            }
        }
        /// <summary>
        /// Handles updating status for a thermostat and publishing changes.
        /// </summary>
        /// <param name="revisionStatus">Revision status that triggered the update.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        private async Task RefreshThermostatAsync(RevisionStatus revisionStatus)
        {
            // Build single thermostat request
            var thermostatRequest = new ThermostatRequest
            {
                Selection = new Selection
                {
                    SelectionType          = "thermostats",
                    SelectionMatch         = revisionStatus.ThermostatIdentifier,
                    IncludeEquipmentStatus = true,
                    IncludeEvents          = true,
                    IncludeRuntime         = true,
                    IncludeSensors         = true,
                    IncludeSettings        = true,
                    IncludeWeather         = true
                }
            };

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

            // Publish updates and cache new values
            await UpdateState(thermostatUpdate?.ThermostatList?.FirstOrDefault());
        }
Example #4
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;
            }
        }