- Added ClimateApp to handle climate entity operations including turning on/off, adjusting temperature, and setting HVAC modes. - Implemented caching mechanism for climate entities to optimize state retrieval. - Created domain model for Climate with attributes such as current temperature, target temperature, and HVAC modes. - Developed unit tests for ClimateApp to ensure functionality and correctness. feat: add RemoteApp for SwitchBot commands - Introduced RemoteApp to facilitate sending commands to SwitchBot devices. - Implemented SendCommand method for executing device commands without state management. chore: update configuration for SwitchBot integration - Added SwitchBotToken and SwitchBotSecret to configuration for enabling SwitchBot Cloud commands. feat: define gRPC services for Climate and Remote operations - Created climate.proto and remote.proto files to define gRPC services for climate management and remote command execution. - Implemented corresponding request and response message structures for gRPC interactions.
112 lines
3.2 KiB
Go
112 lines
3.2 KiB
Go
package grpc
|
|
|
|
import (
|
|
hav1 "gitea.nik4nao.com/nik/home-services/gen/ha/v1"
|
|
"gitea.nik4nao.com/nik/home-services/ha-gateway/internal/core/domain"
|
|
)
|
|
|
|
// domainStateToProto normalizes domain state timestamps and attributes for gRPC.
|
|
func domainStateToProto(s *domain.EntityState) *hav1.EntityState {
|
|
return &hav1.EntityState{
|
|
EntityId: string(s.EntityID),
|
|
State: s.State,
|
|
Attributes: s.Attributes,
|
|
LastChanged: s.LastChanged.Format("2006-01-02T15:04:05Z07:00"),
|
|
LastUpdated: s.LastUpdated.Format("2006-01-02T15:04:05Z07:00"),
|
|
}
|
|
}
|
|
|
|
// protoTurnOnToParams preserves optional protobuf fields so the app layer can
|
|
// distinguish "unset" from explicit zero values.
|
|
func protoTurnOnToParams(r *hav1.TurnOnRequest) domain.TurnOnParams {
|
|
p := domain.TurnOnParams{
|
|
EntityID: domain.EntityID(r.EntityId),
|
|
}
|
|
if r.BrightnessPct != nil {
|
|
v := r.GetBrightnessPct()
|
|
p.BrightnessPct = &v
|
|
}
|
|
if r.ColorTempKelvin != nil {
|
|
v := r.GetColorTempKelvin()
|
|
p.ColorTempKelvin = &v
|
|
}
|
|
if r.RgbColor != nil {
|
|
p.RGBColor = &domain.RGBColor{
|
|
R: uint8(r.RgbColor.R),
|
|
G: uint8(r.RgbColor.G),
|
|
B: uint8(r.RgbColor.B),
|
|
}
|
|
}
|
|
if r.Transition != nil {
|
|
v := r.GetTransition()
|
|
p.Transition = &v
|
|
}
|
|
return p
|
|
}
|
|
|
|
// protoTurnOffToParams preserves optional protobuf fields for turn-off calls.
|
|
func protoTurnOffToParams(r *hav1.TurnOffRequest) domain.TurnOffParams {
|
|
p := domain.TurnOffParams{
|
|
EntityID: domain.EntityID(r.EntityId),
|
|
}
|
|
if r.Transition != nil {
|
|
v := r.GetTransition()
|
|
p.Transition = &v
|
|
}
|
|
return p
|
|
}
|
|
|
|
// domainLightToProto exposes discovery metadata without leaking domain enums.
|
|
func domainLightToProto(l domain.Light) *hav1.LightEntity {
|
|
modes := make([]string, len(l.SupportedColorModes))
|
|
for i, m := range l.SupportedColorModes {
|
|
modes[i] = string(m)
|
|
}
|
|
return &hav1.LightEntity{
|
|
EntityId: string(l.EntityID),
|
|
FriendlyName: l.FriendlyName,
|
|
State: l.State,
|
|
SupportedColorModes: modes,
|
|
MinColorTempKelvin: l.MinColorTempKelvin,
|
|
MaxColorTempKelvin: l.MaxColorTempKelvin,
|
|
IsHueGroup: l.IsHueGroup,
|
|
EffectList: l.EffectList,
|
|
}
|
|
}
|
|
|
|
// domainSwitchToProto exposes switch discovery metadata over gRPC.
|
|
func domainSwitchToProto(s domain.Switch) *hav1.SwitchEntity {
|
|
return &hav1.SwitchEntity{
|
|
EntityId: string(s.EntityID),
|
|
FriendlyName: s.FriendlyName,
|
|
State: s.State,
|
|
DeviceClass: s.DeviceClass,
|
|
}
|
|
}
|
|
|
|
// domainClimateToProto exposes climate discovery metadata over gRPC, keeping
|
|
// current/target temperature as optional so clients can distinguish "no
|
|
// sensor reading" from 0°.
|
|
func domainClimateToProto(c domain.Climate) *hav1.ClimateEntity {
|
|
e := &hav1.ClimateEntity{
|
|
EntityId: string(c.EntityID),
|
|
FriendlyName: c.FriendlyName,
|
|
State: c.State,
|
|
HvacModes: c.HVACModes,
|
|
FanMode: c.FanMode,
|
|
FanModes: c.FanModes,
|
|
TargetTempStep: float32(c.TargetTempStep),
|
|
MinTemp: float32(c.MinTemp),
|
|
MaxTemp: float32(c.MaxTemp),
|
|
}
|
|
if c.CurrentTemperature != nil {
|
|
v := float32(*c.CurrentTemperature)
|
|
e.CurrentTemperature = &v
|
|
}
|
|
if c.TargetTemperature != nil {
|
|
v := float32(*c.TargetTemperature)
|
|
e.TargetTemperature = &v
|
|
}
|
|
return e
|
|
}
|