Add weather service
This commit is contained in:
parent
522c10a9c0
commit
a2ff29ce84
1
go.mod
1
go.mod
@ -3,6 +3,7 @@ module go.arsenm.dev/infinitime
|
|||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/fxamacker/cbor/v2 v2.3.0
|
||||||
github.com/godbus/dbus/v5 v5.0.3
|
github.com/godbus/dbus/v5 v5.0.3
|
||||||
github.com/muka/go-bluetooth v0.0.0-20211122080231-b99792bbe62a
|
github.com/muka/go-bluetooth v0.0.0-20211122080231-b99792bbe62a
|
||||||
)
|
)
|
||||||
|
6
go.sum
6
go.sum
@ -3,6 +3,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||||
|
github.com/fxamacker/cbor v1.5.1 h1:XjQWBgdmQyqimslUh5r4tUGmoqzHmBFQOImkWGi2awg=
|
||||||
|
github.com/fxamacker/cbor v1.5.1/go.mod h1:3aPGItF174ni7dDzd6JZ206H8cmr4GDNBGpPa971zsU=
|
||||||
|
github.com/fxamacker/cbor/v2 v2.3.0 h1:aM45YGMctNakddNNAezPxDUpv38j44Abh+hifNuqXik=
|
||||||
|
github.com/fxamacker/cbor/v2 v2.3.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||||
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
|
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
|
||||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
@ -24,6 +28,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/suapapa/go_eddystone v1.3.1/go.mod h1:bXC11TfJOS+3g3q/Uzd7FKd5g62STQEfeEIhcKe4Qy8=
|
github.com/suapapa/go_eddystone v1.3.1/go.mod h1:bXC11TfJOS+3g3q/Uzd7FKd5g62STQEfeEIhcKe4Qy8=
|
||||||
|
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
|
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
@ -4,9 +4,11 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fxamacker/cbor/v2"
|
||||||
bt "github.com/muka/go-bluetooth/api"
|
bt "github.com/muka/go-bluetooth/api"
|
||||||
"github.com/muka/go-bluetooth/bluez/profile/adapter"
|
"github.com/muka/go-bluetooth/bluez/profile/adapter"
|
||||||
"github.com/muka/go-bluetooth/bluez/profile/device"
|
"github.com/muka/go-bluetooth/bluez/profile/device"
|
||||||
@ -27,6 +29,7 @@ const (
|
|||||||
HeartRateChar = "00002a37-0000-1000-8000-00805f9b34fb"
|
HeartRateChar = "00002a37-0000-1000-8000-00805f9b34fb"
|
||||||
FSTransferChar = "adaf0200-4669-6c65-5472-616e73666572"
|
FSTransferChar = "adaf0200-4669-6c65-5472-616e73666572"
|
||||||
FSVersionChar = "adaf0100-4669-6c65-5472-616e73666572"
|
FSVersionChar = "adaf0100-4669-6c65-5472-616e73666572"
|
||||||
|
WeatherDataChar = "00040001-78fc-48fe-8e23-433b3a1942d0"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
@ -42,6 +45,7 @@ type Device struct {
|
|||||||
heartRateChar *gatt.GattCharacteristic1
|
heartRateChar *gatt.GattCharacteristic1
|
||||||
fsVersionChar *gatt.GattCharacteristic1
|
fsVersionChar *gatt.GattCharacteristic1
|
||||||
fsTransferChar *gatt.GattCharacteristic1
|
fsTransferChar *gatt.GattCharacteristic1
|
||||||
|
weatherDataChar *gatt.GattCharacteristic1
|
||||||
notifEventCh chan uint8
|
notifEventCh chan uint8
|
||||||
notifEventDone bool
|
notifEventDone bool
|
||||||
onReconnect func()
|
onReconnect func()
|
||||||
@ -50,10 +54,11 @@ type Device struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrNoDevices = errors.New("no InfiniTime devices found")
|
ErrNoDevices = errors.New("no InfiniTime devices found")
|
||||||
ErrNotFound = errors.New("could not find any advertising InfiniTime devices")
|
ErrNotFound = errors.New("could not find any advertising InfiniTime devices")
|
||||||
ErrNotConnected = errors.New("not connected")
|
ErrNotConnected = errors.New("not connected")
|
||||||
ErrCharNotAvail = errors.New("required characteristic is not available")
|
ErrCharNotAvail = errors.New("required characteristic is not available")
|
||||||
|
ErrNoTimelineHeader = errors.New("events must contain the timeline header")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
@ -333,6 +338,8 @@ func (i *Device) resolveChars() error {
|
|||||||
i.fsTransferChar = char
|
i.fsTransferChar = char
|
||||||
case FSVersionChar:
|
case FSVersionChar:
|
||||||
i.fsVersionChar = char
|
i.fsVersionChar = char
|
||||||
|
case WeatherDataChar:
|
||||||
|
i.weatherDataChar = char
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -685,6 +692,32 @@ func (i *Device) FS() (*blefs.FS, error) {
|
|||||||
return blefs.New(i.fsTransferChar)
|
return blefs.New(i.fsTransferChar)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddWeatherEvent adds one of the event structs from
|
||||||
|
// the weather package to the timeline. Input must be
|
||||||
|
// a struct containing TimelineHeader.
|
||||||
|
func (i *Device) AddWeatherEvent(event interface{}) error {
|
||||||
|
if err := i.checkStatus(i.weatherDataChar); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Get type of input
|
||||||
|
inputType := reflect.TypeOf(event)
|
||||||
|
// Check if input contains TimelineHeader
|
||||||
|
_, hdrExists := inputType.FieldByName("TimelineHeader")
|
||||||
|
// If header does not exist or input is not struct
|
||||||
|
if !hdrExists || inputType.Kind() != reflect.Struct {
|
||||||
|
return ErrNoTimelineHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode event as CBOR
|
||||||
|
data, err := cbor.Marshal(event)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write data to weather data characteristic
|
||||||
|
return i.weatherDataChar.WriteValue(data, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Device) checkStatus(char *gatt.GattCharacteristic1) error {
|
func (i *Device) checkStatus(char *gatt.GattCharacteristic1) error {
|
||||||
if !i.device.Properties.Connected {
|
if !i.device.Properties.Connected {
|
||||||
return ErrNotConnected
|
return ErrNotConnected
|
||||||
|
193
weather/weather.go
Normal file
193
weather/weather.go
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
package weather
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Type of weather event
|
||||||
|
type EventType uint8
|
||||||
|
|
||||||
|
// These event types correspond to structs
|
||||||
|
// that can be used to add an event to the
|
||||||
|
// weather timeline
|
||||||
|
const (
|
||||||
|
EventTypeObscuration EventType = iota
|
||||||
|
EventTypePrecipitation
|
||||||
|
EventTypeWind
|
||||||
|
EventTypeTemperature
|
||||||
|
EventTypeAirQuality
|
||||||
|
EventTypeSpecial
|
||||||
|
EventTypePressure
|
||||||
|
EventTypeLocation
|
||||||
|
EventTypeClouds
|
||||||
|
EventTypeHumidity
|
||||||
|
)
|
||||||
|
|
||||||
|
// Special event type
|
||||||
|
type SpecialType uint8
|
||||||
|
|
||||||
|
// See https://git.io/JM7Oe for the meaning of each type
|
||||||
|
const (
|
||||||
|
SpecialTypeSquall SpecialType = iota
|
||||||
|
SpecialTypeTsunami
|
||||||
|
SpecialTypeTornado
|
||||||
|
SpecialTypeFire
|
||||||
|
SpecialTypeThunder
|
||||||
|
)
|
||||||
|
|
||||||
|
// Precipitation type
|
||||||
|
type PrecipitationType uint8
|
||||||
|
|
||||||
|
// See https://git.io/JM7YM for the meaning of each type
|
||||||
|
const (
|
||||||
|
PrecipitationTypeNone PrecipitationType = iota
|
||||||
|
PrecipitationTypeRain
|
||||||
|
PrecipitationTypeDrizzle
|
||||||
|
PrecipitationTypeFreezingRain
|
||||||
|
PrecipitationTypeSleet
|
||||||
|
PrecipitationTypeHail
|
||||||
|
PrecipitationTypeSmallHail
|
||||||
|
PrecipitationTypeSnow
|
||||||
|
PrecipitationTypeSnowGrains
|
||||||
|
PrecipitationTypeIceCrystals
|
||||||
|
PrecipitationTypeAsh
|
||||||
|
)
|
||||||
|
|
||||||
|
// Visibility obscuration type
|
||||||
|
type ObscurationType uint8
|
||||||
|
|
||||||
|
// See https://git.io/JM7Yd for the meaning of each type
|
||||||
|
const (
|
||||||
|
ObscurationTypeNone ObscurationType = iota
|
||||||
|
ObscurationTypeFog
|
||||||
|
ObscurationTypeHaze
|
||||||
|
ObscurationTypeSmoke
|
||||||
|
ObscurationTypeAsh
|
||||||
|
ObscurationTypeDust
|
||||||
|
ObscurationTypeSand
|
||||||
|
ObscurationTypeMist
|
||||||
|
ObscurationTypePrecipitation
|
||||||
|
)
|
||||||
|
|
||||||
|
// TimelineHeader contains the header for a timeline envent
|
||||||
|
type TimelineHeader struct {
|
||||||
|
// UNIX timestamp with timezone offset
|
||||||
|
Timestamp uint64
|
||||||
|
// Seconds until the event expires
|
||||||
|
Expires uint32
|
||||||
|
// Type of weather event
|
||||||
|
EventType EventType
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHeader creates and populates a new timeline header
|
||||||
|
// and returns it
|
||||||
|
func NewHeader(evtType EventType, expires time.Duration) TimelineHeader {
|
||||||
|
return TimelineHeader{
|
||||||
|
Timestamp: uint64(time.Now().Unix()),
|
||||||
|
Expires: uint32(expires.Seconds()),
|
||||||
|
EventType: evtType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloudsEvent corresponds to EventTypeClouds
|
||||||
|
type CloudsEvent struct {
|
||||||
|
TimelineHeader
|
||||||
|
// Cloud coverage percentage
|
||||||
|
Amount uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObscurationEvent corresponds to EventTypeObscuration
|
||||||
|
type ObscurationEvent struct {
|
||||||
|
TimelineHeader
|
||||||
|
// Type of obscuration
|
||||||
|
Type ObscurationType
|
||||||
|
// Visibility in meters. 65535 is unspecified.
|
||||||
|
Amount uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrecipitationEvent corresponds to EventTypePrecipitation
|
||||||
|
type PrecipitationEvent struct {
|
||||||
|
TimelineHeader
|
||||||
|
// Type of precipitation
|
||||||
|
Type PrecipitationType
|
||||||
|
// Amount of rain in millimeters. 255 is unspecified.
|
||||||
|
Amount uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// WindEvent corresponds to EventTypeWind
|
||||||
|
type WindEvent struct {
|
||||||
|
TimelineHeader
|
||||||
|
// Minimum speed in meters per second
|
||||||
|
SpeedMin uint8
|
||||||
|
// Maximum speed in meters per second
|
||||||
|
SpeedMax uint8
|
||||||
|
// Unitless direction, about 1 unit per 0.71 degrees.
|
||||||
|
DirectionMin uint8
|
||||||
|
// Unitless direction, about 1 unit per 0.71 degrees
|
||||||
|
DirectionMax uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// TemperatureEvent corresponds to EventTypeTemperature
|
||||||
|
type TemperatureEvent struct {
|
||||||
|
TimelineHeader
|
||||||
|
// Temperature in celcius multiplied by 100.
|
||||||
|
// -32768 is "no data"
|
||||||
|
Temperature int16
|
||||||
|
// Dew point in celcius multiplied by 100.
|
||||||
|
// -32768 is "no data"
|
||||||
|
DewPoint int16
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocationEvent corresponds to EventTypeLocation
|
||||||
|
type LocationEvent struct {
|
||||||
|
TimelineHeader
|
||||||
|
// Location name
|
||||||
|
Location string
|
||||||
|
// Altitude from sea level in meters
|
||||||
|
Altitude int16
|
||||||
|
// EPSG:3857 latitude (Google Maps, Openstreetmaps)
|
||||||
|
Latitude int32
|
||||||
|
// EPSG:3857 longitude (Google Maps, Openstreetmaps)
|
||||||
|
Longitude int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// HumidityEvent corresponds to EventTypeHumidity
|
||||||
|
type HumidityEvent struct {
|
||||||
|
TimelineHeader
|
||||||
|
// Relative humidity percentage
|
||||||
|
Humidity uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// PressureEvent corresponds to EventTypePressure
|
||||||
|
type PressureEvent struct {
|
||||||
|
TimelineHeader
|
||||||
|
// Air pressure in hectopascals (hPa)
|
||||||
|
Pressure int16
|
||||||
|
}
|
||||||
|
|
||||||
|
// SpecialEvent corresponds to EventTypeSpecial
|
||||||
|
type SpecialEvent struct {
|
||||||
|
TimelineHeader
|
||||||
|
// Type of special event
|
||||||
|
Type SpecialType
|
||||||
|
}
|
||||||
|
|
||||||
|
// AirQualityEvent corresponds to EventTypeAirQuality
|
||||||
|
type AirQualityEvent struct {
|
||||||
|
TimelineHeader
|
||||||
|
// Name of the polluting item
|
||||||
|
//
|
||||||
|
// Do not localize the name. That should be handled by the watch.
|
||||||
|
//
|
||||||
|
// For particulate matter, use "PM0.1"`, "PM5", or "PM10".
|
||||||
|
//
|
||||||
|
// For chemicals, use the molecular formula ("NO2", "CO2", or "O3").
|
||||||
|
//
|
||||||
|
// For pollen, use the genus of the plant.
|
||||||
|
Polluter string
|
||||||
|
// Amount of pollution in SI units
|
||||||
|
//
|
||||||
|
// https://ec.europa.eu/environment/air/quality/standards.htm
|
||||||
|
// http://www.ourair.org/wp-content/uploads/2012-aaqs2.pdf
|
||||||
|
Amount uint32
|
||||||
|
}
|
Reference in New Issue
Block a user