lifx-lan 0.8.2 → 0.8.3
raw patch · 9 files changed
+3226/−2183 lines, 9 filesdep ~ansi-terminaldep ~basedep ~containersPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: ansi-terminal, base, containers, network, text, time
API changes (from Hackage documentation)
- Lifx.Lan.Internal: instance Control.Monad.Error.Class.MonadError e m => Control.Monad.Error.Class.MonadError e (Lifx.Lan.Internal.LifxT m)
+ Lifx.Lan: StateGroup :: ByteString -> Text -> POSIXTime -> StateGroup
+ Lifx.Lan: [$sel:group:StateGroup] :: StateGroup -> ByteString
+ Lifx.Lan: [$sel:label:StateGroup] :: StateGroup -> Text
+ Lifx.Lan: [$sel:updatedAt:StateGroup] :: StateGroup -> POSIXTime
+ Lifx.Lan: [GetGroup] :: Message StateGroup
+ Lifx.Lan: [SetLabel] :: Text -> Message ()
+ Lifx.Lan: data StateGroup
+ Lifx.Lan: instance GHC.Classes.Eq Lifx.Lan.StateGroup
+ Lifx.Lan: instance GHC.Classes.Ord Lifx.Lan.StateGroup
+ Lifx.Lan: instance GHC.Generics.Generic Lifx.Lan.StateGroup
+ Lifx.Lan: instance GHC.Show.Show Lifx.Lan.StateGroup
+ Lifx.Lan: instance Lifx.Lan.MessageResult Lifx.Lan.StateGroup
+ Lifx.Lan: instance Lifx.Lan.Response Lifx.Lan.StateGroup
+ Lifx.Lan.Internal: instance Control.Monad.Error.Class.MonadError e m => Control.Monad.Error.Class.MonadError (Data.Either.Either e Lifx.Lan.Internal.LifxError) (Lifx.Lan.Internal.LifxT m)
- Lifx.Lan: class Monad m => MonadLifx m where {
+ Lifx.Lan: class (Monad m) => MonadLifx m where {
- Lifx.Lan.Internal: class MonadIO m => MonadLifxIO m
+ Lifx.Lan.Internal: class (MonadIO m) => MonadLifxIO m
- Lifx.Lan.Mock.Terminal: MockState :: LightState -> Maybe StateService -> Maybe StateHostFirmware -> Maybe StateVersion -> MockState
+ Lifx.Lan.Mock.Terminal: MockState :: LightState -> StateGroup -> Maybe StateService -> Maybe StateHostFirmware -> Maybe StateVersion -> MockState
- Lifx.Lan.Mock.Terminal: runMock :: [(Device, Text)] -> Mock a -> IO (Either MockError a)
+ Lifx.Lan.Mock.Terminal: runMock :: [(Device, (Text, Text))] -> Mock a -> IO (Either MockError a)
Files
- CHANGELOG.md +8/−5
- README.md +5/−1
- lifx-lan.cabal +7/−7
- src/Lifx/Internal/Colour.hs +3/−3
- src/Lifx/Internal/ProductInfo.hs +3084/−2128
- src/Lifx/Internal/ProductInfoMap.hs +3/−3
- src/Lifx/Lan.hs +79/−25
- src/Lifx/Lan/Internal.hs +9/−8
- src/Lifx/Lan/Mock/Terminal.hs +28/−3
CHANGELOG.md view
@@ -1,10 +1,13 @@ # Revision history for lifx-lan -## 0.8 -- 25-02-2023+## 0.8.3 -- 2024-09-03+- Update to latest products list.++## 0.8 -- 2023-02-25 - Various minor improvements to MTL interfaces.-- Update to latest products list+- Update to latest products list. -## 0.7.1 -- 21-10-2022+## 0.7.1 -- 2022-10-21 - Handle invalid UTF-8 in light label. - Ensure discovery exits successfully when zero devices are wanted. @@ -39,8 +42,8 @@ ## 0.4.0 -- 2021-07-30 - Use abstract Device type. - Use more types from standard libraries where appropriate:- - PortNumber- - NominalDiffTime+ - `PortNumber`+ - `NominalDiffTime` - General cleanup and documentation improvements. ## 0.3.0 -- 2021-06-19
README.md view
@@ -1,4 +1,8 @@-Haskell bindings to the [LIFX LAN API](https://lan.developer.lifx.com/docs).+# ⚠️ Looking for maintainers! ⚠️++Now that LIFX have given up on Europe, I'm less inclined to work on this project. Let me know if you're interested.++## Haskell bindings to the [LIFX LAN API](https://lan.developer.lifx.com/docs). This library provides a reasonably high-level interface, but doesn't try to be *too* clever. For example, it doesn't check message delivery, and throws an error if a light takes too long to respond.
lifx-lan.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: lifx-lan-version: 0.8.2+version: 0.8.3 license: BSD-3-Clause license-file: LICENSE author: George Thomas@@ -27,21 +27,21 @@ ghc-options: -Wall build-depends:- base ^>= {4.16, 4.17, 4.18},- ansi-terminal ^>= {0.11.1, 1.0},+ base ^>= {4.16, 4.17, 4.18, 4.19},+ ansi-terminal ^>= {0.11.1, 1.0, 1.1}, binary ^>= 0.8.8, bytestring ^>= {0.10.8, 0.11, 0.12}, colour ^>= 2.3.6, composition ^>= 1.0.2.1,- containers ^>= 0.6.2.1,+ containers ^>= {0.6.2.1, 0.7}, extra ^>= 1.7.1, monad-loops ^>= 0.4.3, mtl ^>= {2.2.2, 2.3},- network ^>= 3.1.1.1,+ network ^>= {3.1.1.1, 3.2}, random ^>= 1.2.0, safe ^>= 0.3.19,- text ^>= {1.2.3, 2.0},- time ^>= {1.9.3, 1.10, 1.11, 1.12, 1.13},+ text ^>= {1.2.3, 2.0, 2.1},+ time ^>= {1.9.3, 1.10, 1.11, 1.12, 1.13, 1.14}, transformers ^>= {0.5.6, 0.6}, default-language: Haskell2010 default-extensions:
src/Lifx/Internal/Colour.hs view
@@ -48,14 +48,14 @@ , kelvin = 0 } -interpolateColour :: Num a => a -> RGB a -> RGB a -> RGB a+interpolateColour :: (Num a) => a -> RGB a -> RGB a -> RGB a interpolateColour r = liftA2 (\a b -> a * (r + b * (1 - r))) maxWord16 :: Float maxWord16 = fromIntegral $ maxBound @Word16 -minKelvin :: Num a => a+minKelvin :: (Num a) => a minKelvin = 1500 -maxKelvin :: Num a => a+maxKelvin :: (Num a) => a maxKelvin = 9000
src/Lifx/Internal/ProductInfo.hs view
@@ -8,2134 +8,3090 @@ [ VendorInfo { vid = 1 , name = "LIFX"- , defaults = Features- { hev = False- , color = False- , chain = False- , matrix = False- , relays = False- , buttons = False- , infrared = False- , multizone = False- , temperatureRange = Nothing- , extendedMultizone = False- }- , products =- [ ProductInfo- { pid = 1- , name = "LIFX Original 1000"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 3- , name = "LIFX Color 650"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 10- , name = "LIFX White 800 (Low Voltage)"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2700- , 6500- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 11- , name = "LIFX White 800 (High Voltage)"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2700- , 6500- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 15- , name = "LIFX Color 1000"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 18- , name = "LIFX White 900 BR30 (Low Voltage)"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 19- , name = "LIFX White 900 BR30 (High Voltage)"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 20- , name = "LIFX Color 1000 BR30"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 22- , name = "LIFX Color 1000"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 27- , name = "LIFX A19"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 2- , minor = 80- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 28- , name = "LIFX BR30"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 2- , minor = 80- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 29- , name = "LIFX A19 Night Vision"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just True- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 2- , minor = 80- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 30- , name = "LIFX BR30 Night Vision"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just True- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 2- , minor = 80- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 31- , name = "LIFX Z"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just True- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 32- , name = "LIFX Z"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just True- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 2- , minor = 77- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Nothing- , extendedMultizone = Just True- }- }- , Upgrade- { major = 2- , minor = 80- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 36- , name = "LIFX Downlight"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 2- , minor = 80- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 37- , name = "LIFX Downlight"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 2- , minor = 80- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 38- , name = "LIFX Beam"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just True- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 2- , minor = 77- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Nothing- , extendedMultizone = Just True- }- }- , Upgrade- { major = 2- , minor = 80- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 39- , name = "LIFX Downlight White to Warm"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 2- , minor = 80- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 40- , name = "LIFX Downlight"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 2- , minor = 80- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 43- , name = "LIFX A19"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 2- , minor = 80- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 44- , name = "LIFX BR30"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 2- , minor = 80- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 45- , name = "LIFX A19 Night Vision"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just True- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 2- , minor = 80- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 46- , name = "LIFX BR30 Night Vision"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just True- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 2- , minor = 80- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 49- , name = "LIFX Mini Color"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 50- , name = "LIFX Mini White to Warm"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 6500- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 3- , minor = 70- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 51- , name = "LIFX Mini White"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2700- , 2700- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 52- , name = "LIFX GU10"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 53- , name = "LIFX GU10"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 55- , name = "LIFX Tile"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just True- , matrix = Just True- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 57- , name = "LIFX Candle"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just True- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 59- , name = "LIFX Mini Color"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 60- , name = "LIFX Mini White to Warm"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 6500- )- , extendedMultizone = Nothing- }- , upgrades =- [ Upgrade- { major = 3- , minor = 70- , features = PartialFeatures- { hev = Nothing- , color = Nothing- , chain = Nothing- , matrix = Nothing- , relays = Nothing- , buttons = Nothing- , infrared = Nothing- , multizone = Nothing- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- }- ]- }- , ProductInfo- { pid = 61- , name = "LIFX Mini White"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2700- , 2700- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 62- , name = "LIFX A19"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 63- , name = "LIFX BR30"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 64- , name = "LIFX A19 Night Vision"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just True- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 65- , name = "LIFX BR30 Night Vision"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just True- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 66- , name = "LIFX Mini White"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2700- , 2700- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 68- , name = "LIFX Candle"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just True- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 70- , name = "LIFX Switch"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Just True- , buttons = Just True- , infrared = Just False- , multizone = Just False- , temperatureRange = Nothing- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 71- , name = "LIFX Switch"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Just True- , buttons = Just True- , infrared = Just False- , multizone = Just False- , temperatureRange = Nothing- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 81- , name = "LIFX Candle White to Warm"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2200- , 6500- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 82- , name = "LIFX Filament Clear"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2100- , 2100- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 85- , name = "LIFX Filament Amber"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2000- , 2000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 87- , name = "LIFX Mini White"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2700- , 2700- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 88- , name = "LIFX Mini White"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2700- , 2700- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 89- , name = "LIFX Switch"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Just True- , buttons = Just True- , infrared = Just False- , multizone = Just False- , temperatureRange = Nothing- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 90- , name = "LIFX Clean"- , features = PartialFeatures- { hev = Just True- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 91- , name = "LIFX Color"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 92- , name = "LIFX Color"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 93- , name = "LIFX A19 US"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 94- , name = "LIFX BR30"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 96- , name = "LIFX Candle White to Warm"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2200- , 6500- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 97- , name = "LIFX A19"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 98- , name = "LIFX BR30"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 99- , name = "LIFX Clean"- , features = PartialFeatures- { hev = Just True- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 100- , name = "LIFX Filament Clear"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2100- , 2100- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 101- , name = "LIFX Filament Amber"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2000- , 2000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 109- , name = "LIFX A19 Night Vision"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just True- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 110- , name = "LIFX BR30 Night Vision"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just True- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 111- , name = "LIFX A19 Night Vision"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just True- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 112- , name = "LIFX BR30 Night Vision Intl"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just True- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 113- , name = "LIFX Mini WW US"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 114- , name = "LIFX Mini WW Intl"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 115- , name = "LIFX Switch"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Just True- , buttons = Just True- , infrared = Just False- , multizone = Just False- , temperatureRange = Nothing- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 116- , name = "LIFX Switch"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Just True- , buttons = Just True- , infrared = Just False- , multizone = Just False- , temperatureRange = Nothing- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 117- , name = "LIFX Z US"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just True- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Just True- }- , upgrades = []- }- , ProductInfo- { pid = 118- , name = "LIFX Z Intl"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just True- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Just True- }- , upgrades = []- }- , ProductInfo- { pid = 119- , name = "LIFX Beam US"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just True- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Just True- }- , upgrades = []- }- , ProductInfo- { pid = 120- , name = "LIFX Beam Intl"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just True- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Just True- }- , upgrades = []- }- , ProductInfo- { pid = 121- , name = "LIFX Downlight Intl"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 122- , name = "LIFX Downlight US"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 123- , name = "LIFX Color US"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 124- , name = "LIFX Color Intl"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 125- , name = "LIFX White to Warm US"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 126- , name = "LIFX White to Warm Intl"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 127- , name = "LIFX White US"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2700- , 2700- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 128- , name = "LIFX White Intl"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2700- , 2700- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 129- , name = "LIFX Color US"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 130- , name = "LIFX Color Intl"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 131- , name = "LIFX White To Warm US"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 132- , name = "LIFX White To Warm Intl"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 133- , name = "LIFX White US"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2700- , 2700- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 134- , name = "LIFX White Intl"- , features = PartialFeatures- { hev = Nothing- , color = Just False- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 2700- , 2700- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 135- , name = "LIFX GU10 Color US"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 136- , name = "LIFX GU10 Color Intl"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just False- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 137- , name = "LIFX Candle Color US"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just True- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }- , upgrades = []- }- , ProductInfo- { pid = 138- , name = "LIFX Candle Color Intl"- , features = PartialFeatures- { hev = Nothing- , color = Just True- , chain = Just False- , matrix = Just True- , relays = Nothing- , buttons = Nothing- , infrared = Just False- , multizone = Just False- , temperatureRange = Just- ( 1500- , 9000- )- , extendedMultizone = Nothing- }+ , defaults =+ Features+ { hev = False+ , color = False+ , chain = False+ , matrix = False+ , relays = False+ , buttons = False+ , infrared = False+ , multizone = False+ , temperatureRange = Nothing+ , extendedMultizone = False+ }+ , products =+ [ ProductInfo+ { pid = 1+ , name = "LIFX Original 1000"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 3+ , name = "LIFX Color 650"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 10+ , name = "LIFX White 800 (Low Voltage)"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2700+ , 6500+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 11+ , name = "LIFX White 800 (High Voltage)"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2700+ , 6500+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 15+ , name = "LIFX Color 1000"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 18+ , name = "LIFX White 900 BR30 (Low Voltage)"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 19+ , name = "LIFX White 900 BR30 (High Voltage)"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 20+ , name = "LIFX Color 1000 BR30"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 22+ , name = "LIFX Color 1000"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 27+ , name = "LIFX A19"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 2+ , minor = 80+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 28+ , name = "LIFX BR30"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 2+ , minor = 80+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 29+ , name = "LIFX A19 Night Vision"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just True+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 2+ , minor = 80+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 30+ , name = "LIFX BR30 Night Vision"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just True+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 2+ , minor = 80+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 31+ , name = "LIFX Z"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 32+ , name = "LIFX Z"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 2+ , minor = 77+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange = Nothing+ , extendedMultizone = Just True+ }+ }+ , Upgrade+ { major = 2+ , minor = 80+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 36+ , name = "LIFX Downlight"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 2+ , minor = 80+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 37+ , name = "LIFX Downlight"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 2+ , minor = 80+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 38+ , name = "LIFX Beam"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 2+ , minor = 77+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange = Nothing+ , extendedMultizone = Just True+ }+ }+ , Upgrade+ { major = 2+ , minor = 80+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 39+ , name = "LIFX Downlight White to Warm"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 2+ , minor = 80+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 40+ , name = "LIFX Downlight"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 2+ , minor = 80+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 43+ , name = "LIFX A19"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 2+ , minor = 80+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 44+ , name = "LIFX BR30"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 2+ , minor = 80+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 45+ , name = "LIFX A19 Night Vision"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just True+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 2+ , minor = 80+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 46+ , name = "LIFX BR30 Night Vision"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just True+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 2+ , minor = 80+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 49+ , name = "LIFX Mini Color"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 50+ , name = "LIFX Mini White to Warm"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 6500+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 3+ , minor = 70+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 51+ , name = "LIFX Mini White"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2700+ , 2700+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 52+ , name = "LIFX GU10"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 53+ , name = "LIFX GU10"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 55+ , name = "LIFX Tile"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just True+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 57+ , name = "LIFX Candle"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 59+ , name = "LIFX Mini Color"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 60+ , name = "LIFX Mini White to Warm"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 6500+ )+ , extendedMultizone = Nothing+ }+ , upgrades =+ [ Upgrade+ { major = 3+ , minor = 70+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Nothing+ , chain = Nothing+ , matrix = Nothing+ , relays = Nothing+ , buttons = Nothing+ , infrared = Nothing+ , multizone = Nothing+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ }+ ]+ }+ , ProductInfo+ { pid = 61+ , name = "LIFX Mini White"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2700+ , 2700+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 62+ , name = "LIFX A19"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 63+ , name = "LIFX BR30"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 64+ , name = "LIFX A19 Night Vision"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just True+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 65+ , name = "LIFX BR30 Night Vision"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just True+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 66+ , name = "LIFX Mini White"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2700+ , 2700+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 68+ , name = "LIFX Candle"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 70+ , name = "LIFX Switch"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Just True+ , buttons = Just True+ , infrared = Just False+ , multizone = Just False+ , temperatureRange = Nothing+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 71+ , name = "LIFX Switch"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Just True+ , buttons = Just True+ , infrared = Just False+ , multizone = Just False+ , temperatureRange = Nothing+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 81+ , name = "LIFX Candle White to Warm"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2200+ , 6500+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 82+ , name = "LIFX Filament Clear"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2100+ , 2100+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 85+ , name = "LIFX Filament Amber"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2000+ , 2000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 87+ , name = "LIFX Mini White"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2700+ , 2700+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 88+ , name = "LIFX Mini White"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2700+ , 2700+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 89+ , name = "LIFX Switch"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Just True+ , buttons = Just True+ , infrared = Just False+ , multizone = Just False+ , temperatureRange = Nothing+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 90+ , name = "LIFX Clean"+ , features =+ PartialFeatures+ { hev = Just True+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 91+ , name = "LIFX Color"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 92+ , name = "LIFX Color"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 93+ , name = "LIFX A19 US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 94+ , name = "LIFX BR30"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 96+ , name = "LIFX Candle White to Warm"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2200+ , 6500+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 97+ , name = "LIFX A19"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 98+ , name = "LIFX BR30"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 99+ , name = "LIFX Clean"+ , features =+ PartialFeatures+ { hev = Just True+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 100+ , name = "LIFX Filament Clear"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2100+ , 2100+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 101+ , name = "LIFX Filament Amber"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2000+ , 2000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 109+ , name = "LIFX A19 Night Vision"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just True+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 110+ , name = "LIFX BR30 Night Vision"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just True+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 111+ , name = "LIFX A19 Night Vision"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just True+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 112+ , name = "LIFX BR30 Night Vision Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just True+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 113+ , name = "LIFX Mini WW US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 114+ , name = "LIFX Mini WW Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 115+ , name = "LIFX Switch"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Just True+ , buttons = Just True+ , infrared = Just False+ , multizone = Just False+ , temperatureRange = Nothing+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 116+ , name = "LIFX Switch"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Just True+ , buttons = Just True+ , infrared = Just False+ , multizone = Just False+ , temperatureRange = Nothing+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 117+ , name = "LIFX Z US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Just True+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 118+ , name = "LIFX Z Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Just True+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 119+ , name = "LIFX Beam US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Just True+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 120+ , name = "LIFX Beam Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Just True+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 121+ , name = "LIFX Downlight Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 122+ , name = "LIFX Downlight US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 123+ , name = "LIFX Color US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 124+ , name = "LIFX Colour Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 125+ , name = "LIFX White to Warm US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 126+ , name = "LIFX White to Warm Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 127+ , name = "LIFX White US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2700+ , 2700+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 128+ , name = "LIFX White Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2700+ , 2700+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 129+ , name = "LIFX Color US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 130+ , name = "LIFX Colour Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 131+ , name = "LIFX White To Warm US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 132+ , name = "LIFX White To Warm Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 133+ , name = "LIFX White US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2700+ , 2700+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 134+ , name = "LIFX White Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just False+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 2700+ , 2700+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 135+ , name = "LIFX GU10 Color US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 136+ , name = "LIFX GU10 Colour Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 137+ , name = "LIFX Candle Color US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 138+ , name = "LIFX Candle Colour Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 141+ , name = "LIFX Neon US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Just True+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 142+ , name = "LIFX Neon Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Just True+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 143+ , name = "LIFX String US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Just True+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 144+ , name = "LIFX String Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Just True+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 161+ , name = "LIFX Outdoor Neon US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Just True+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 162+ , name = "LIFX Outdoor Neon Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Just True+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 163+ , name = "LIFX A19 US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 164+ , name = "LIFX BR30 US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 165+ , name = "LIFX A19 Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 166+ , name = "LIFX BR30 Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 167+ , name = "LIFX Downlight"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 168+ , name = "LIFX Downlight"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 169+ , name = "LIFX A21 1600lm US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 170+ , name = "LIFX A21 1600lm Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 171+ , name = "LIFX Round Spot US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 173+ , name = "LIFX Round Path US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 174+ , name = "LIFX Square Path US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 175+ , name = "LIFX PAR38 US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 176+ , name = "LIFX Ceiling US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 177+ , name = "LIFX Ceiling Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 181+ , name = "LIFX Color US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 182+ , name = "LIFX Colour Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 185+ , name = "LIFX Candle Color US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 186+ , name = "LIFX Candle Colour Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 187+ , name = "LIFX Candle Color US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 188+ , name = "LIFX Candle Colour Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 203+ , name = "LIFX String US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Just True+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 204+ , name = "LIFX String Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Just True+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 205+ , name = "LIFX Indoor Neon US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Just True+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 206+ , name = "LIFX Indoor Neon Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just False+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just True+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Just True+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 215+ , name = "LIFX Candle Color US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 216+ , name = "LIFX Candle Colour Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 217+ , name = "LIFX Tube US"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ }+ , upgrades = []+ }+ , ProductInfo+ { pid = 218+ , name = "LIFX Tube Intl"+ , features =+ PartialFeatures+ { hev = Nothing+ , color = Just True+ , chain = Just False+ , matrix = Just True+ , relays = Nothing+ , buttons = Nothing+ , infrared = Just False+ , multizone = Just False+ , temperatureRange =+ Just+ ( 1500+ , 9000+ )+ , extendedMultizone = Nothing+ } , upgrades = [] } ]
src/Lifx/Internal/ProductInfoMap.hs view
@@ -20,9 +20,9 @@ productInfo <&> \VendorInfo{..} -> ( vid ,- ( defaults- , Map.fromList $ ((.pid) &&& id) <$> products- )+ ( defaults+ , Map.fromList $ ((.pid) &&& id) <$> products+ ) ) -- | Information about a particular LIFX product.
src/Lifx/Lan.hs view
@@ -41,6 +41,7 @@ StateHostFirmware (..), StatePower (..), StateVersion (..),+ StateGroup (..), LightState (..), -- ** Product info@@ -63,7 +64,7 @@ import Control.Monad.Reader import Control.Monad.State import Control.Monad.Trans.Maybe-import Control.Monad.Writer hiding (Product)+import Control.Monad.Writer import Data.Composition import Data.Either.Extra import Data.Fixed@@ -71,6 +72,7 @@ import Data.Functor import Data.Maybe import Data.Time+import Data.Time.Clock.POSIX import Data.Word import Network.Socket import System.IO.Error@@ -90,6 +92,7 @@ ) import Data.Binary.Put ( Put,+ putByteString, putWord16le, putWord32le, putWord64be,@@ -103,7 +106,7 @@ import Data.Map (Map) import Data.Map.Strict qualified as Map import Data.Text (Text)-import Data.Text.Encoding (decodeUtf8')+import Data.Text.Encoding (decodeUtf8', encodeUtf8) import Data.Text.Encoding.Error (UnicodeException (DecodeError)) import GHC.Generics (Generic) import Network.Socket.ByteString (recvFrom, sendTo)@@ -144,8 +147,12 @@ GetPower :: Message StatePower -- | https://lan.developer.lifx.com/docs/changing-a-device#setpower---packet-21 SetPower :: Bool -> Message ()+ -- | https://lan.developer.lifx.com/docs/changing-a-device#setlabel---packet-24+ SetLabel :: Text -> Message () -- | https://lan.developer.lifx.com/docs/querying-the-device-for-data#getversion---packet-32 GetVersion :: Message StateVersion+ -- | https://lan.developer.lifx.com/docs/querying-the-device-for-data#getgroup---packet-51+ GetGroup :: Message StateGroup -- | https://lan.developer.lifx.com/docs/querying-the-device-for-data#getcolor---packet-101 GetColor :: Message LightState -- | https://lan.developer.lifx.com/docs/changing-a-device#setcolor---packet-102@@ -199,6 +206,17 @@ } deriving (Eq, Ord, Show, Generic) +-- | https://lan.developer.lifx.com/docs/information-messages#stategroup---packet-53+data StateGroup = StateGroup+ { group :: BS.ByteString+ -- ^ The unique identifier of this group as a UUID.+ , label :: Text+ -- ^ The name assigned to this group.+ , updatedAt :: POSIXTime+ -- ^ When this group was set on the device.+ }+ deriving (Eq, Ord, Show, Generic)+ -- | https://lan.developer.lifx.com/docs/information-messages#lightstate---packet-107 data LightState = LightState { hsbk :: HSBK@@ -210,7 +228,7 @@ {- Message responses -} class MessageResult a where- getSendResult :: MonadLifxIO m => Device -> m a+ getSendResult :: (MonadLifxIO m) => Device -> m a default getSendResult :: (MonadLifxIO m, Response a) => Device -> m a getSendResult receiver = untilJustM do timeoutDuration <- getTimeout@@ -226,7 +244,7 @@ Right r -> pure r broadcastAndGetResult ::- MonadLifxIO m =>+ (MonadLifxIO m) => -- | Transform output and discard messages which return 'Nothing'. (HostAddress -> a -> m (Maybe b)) -> -- | Return once this predicate over received messages passes. Otherwise just keep waiting until timeout.@@ -318,6 +336,15 @@ skip 4 pure StateVersion{product = p, ..} instance MessageResult StateVersion+instance Response StateGroup where+ expectedPacketType = 53+ messageSize = 56+ getBody = do+ group <- getByteString 16+ label <- either (fail . showDecodeError) pure . decodeUtf8' . BS.takeWhile (/= 0) =<< getByteString 32+ updatedAt <- secondsToNominalDiffTime . MkFixed . (* 1000) . fromIntegral <$> getWord64le+ pure StateGroup{..}+instance MessageResult StateGroup instance Response LightState where expectedPacketType = 107 messageSize = 52@@ -328,19 +355,17 @@ label <- either (fail . showDecodeError) pure . decodeUtf8' . BS.takeWhile (/= 0) =<< getByteString 32 skip 8 pure LightState{..}- where- showDecodeError = \case- DecodeError s _ -> s- _ -> "impossible" instance MessageResult LightState -msgResWitness :: (MessageResult r => Message r -> a) -> (Message r -> a)+msgResWitness :: ((MessageResult r) => Message r -> a) -> (Message r -> a) msgResWitness f m = case m of GetService{} -> f m GetHostFirmware{} -> f m GetPower{} -> f m SetPower{} -> f m+ SetLabel{} -> f m GetVersion{} -> f m+ GetGroup{} -> f m GetColor{} -> f m SetColor{} -> f m SetLightPower{} -> f m@@ -360,7 +385,7 @@ Right x -> pure x runLifxT ::- MonadIO m =>+ (MonadIO m) => -- | Timeout for waiting for message responses, in microseconds. Int -> LifxT m a ->@@ -369,10 +394,15 @@ sock <- liftIO $ socket AF_INET Datagram defaultProtocol liftIO $ setSocketOption sock Broadcast 1 liftIO . bind sock $ SockAddrInet defaultPort 0- source <- randomIO+ source <-+ untilJustM $+ randomIO <&> \case+ -- 0 and 1 cause problems on old firmware: https://lan.developer.lifx.com/docs/packet-contents#frame-header+ n | n > 1 -> Just n+ _ -> Nothing runExceptT $ runReaderT (evalStateT x 0) (sock, source, timeoutDuration) -class Monad m => MonadLifx m where+class (Monad m) => MonadLifx m where -- | The type of errors associated with 'm'. type MonadLifxError m @@ -390,7 +420,7 @@ -- otherwise just keep waiting until timeout. discoverDevices :: Maybe Int -> m [Device] -instance MonadIO m => MonadLifx (LifxT m) where+instance (MonadIO m) => MonadLifx (LifxT m) where type MonadLifxError (LifxT m) = LifxError lifxThrow = lifxThrowIO liftProductLookupError = ProductLookupError@@ -411,21 +441,21 @@ checkPort port pure . guard $ service == ServiceUDP p = nDevices <&> \n -> (>= n) . length-instance MonadLifx m => MonadLifx (MaybeT m) where+instance (MonadLifx m) => MonadLifx (MaybeT m) where type MonadLifxError (MaybeT m) = MonadLifxError m liftProductLookupError = liftProductLookupError @m sendMessage = lift .: sendMessage broadcastMessage = lift . broadcastMessage discoverDevices = lift . discoverDevices lifxThrow = lift . lifxThrow-instance MonadLifx m => MonadLifx (ExceptT e m) where+instance (MonadLifx m) => MonadLifx (ExceptT e m) where type MonadLifxError (ExceptT e m) = MonadLifxError m liftProductLookupError = liftProductLookupError @m sendMessage = lift .: sendMessage broadcastMessage = lift . broadcastMessage discoverDevices = lift . discoverDevices lifxThrow = lift . lifxThrow-instance MonadLifx m => MonadLifx (StateT s m) where+instance (MonadLifx m) => MonadLifx (StateT s m) where type MonadLifxError (StateT s m) = MonadLifxError m liftProductLookupError = liftProductLookupError @m sendMessage = lift .: sendMessage@@ -439,7 +469,7 @@ broadcastMessage = lift . broadcastMessage discoverDevices = lift . discoverDevices lifxThrow = lift . lifxThrow-instance MonadLifx m => MonadLifx (ReaderT e m) where+instance (MonadLifx m) => MonadLifx (ReaderT e m) where type MonadLifxError (ReaderT e m) = MonadLifxError m liftProductLookupError = liftProductLookupError @m sendMessage = lift .: sendMessage@@ -545,12 +575,24 @@ , packetType = 21 , .. }+ SetLabel{} ->+ Header+ { size = headerSize + 32+ , packetType = 24+ , ..+ } GetVersion{} -> Header { size = headerSize , packetType = 32 , .. }+ GetGroup{} ->+ Header+ { size = headerSize+ , packetType = 51+ , ..+ } GetColor{} -> Header { size = headerSize@@ -583,7 +625,13 @@ GetPower -> mempty SetPower b -> putWord16le if b then maxBound else minBound+ SetLabel t -> do+ putByteString b+ replicateM_ (BS.length b) $ putWord8 0+ where+ b = encodeUtf8 t GetVersion -> mempty+ GetGroup -> mempty GetColor -> mempty SetColor HSBK{..} d -> do putWord8 0@@ -597,7 +645,7 @@ putWord32le $ nominalDiffTimeToInt @Milli d -- | Ask a device for its vendor and product ID, and look up info on it from the official database.-getProductInfo :: forall m. MonadLifx m => Device -> m Product+getProductInfo :: forall m. (MonadLifx m) => Device -> m Product getProductInfo dev = do StateHostFirmware{..} <- sendMessage dev GetHostFirmware v <- sendMessage dev GetVersion@@ -615,6 +663,7 @@ where mt = case m of SetPower{} -> Nothing+ SetLabel{} -> Nothing SetColor _ t -> Just t SetLightPower _ t -> Just t timeMicros t = round $ t * 1_000_000@@ -629,7 +678,7 @@ ) $> fromIntegral x -headerSize :: Num a => a+headerSize :: (Num a) => a headerSize = 36 nominalDiffTimeToInt :: forall f a r. (HasResolution r, f ~ Fixed r, Integral a) => NominalDiffTime -> a@@ -638,10 +687,10 @@ MkFixed n = realToFrac @Pico @f $ nominalDiffTimeToSeconds t -- | Inverted 'whileM'.-untilM :: Monad m => m Bool -> m ()+untilM :: (Monad m) => m Bool -> m () untilM = whileM . fmap not -checkPort :: MonadLifxIO f => PortNumber -> f ()+checkPort :: (MonadLifxIO f) => PortNumber -> f () checkPort port = when (port /= lifxPort) . lifxThrowIO $ UnexpectedPort port -- these helpers are all used by 'sendMessage' and 'broadcastMessage'@@ -661,7 +710,7 @@ Right (_, _, res) -> pure $ Just res where throwDecodeFailure (bs', bo, e) = lifxThrowIO $ DecodeFailure (BL.toStrict bs') bo e-sendMessage' :: MonadLifxIO m => Bool -> HostAddress -> Message r -> m ()+sendMessage' :: (MonadLifxIO m) => Bool -> HostAddress -> Message r -> m () sendMessage' tagged receiver msg = do sock <- getSocket counter <- getCounter@@ -671,11 +720,11 @@ sock (BL.toStrict $ encodeMessage tagged False counter source msg) (SockAddrInet lifxPort receiver)-hostAddressFromSock :: MonadLifxIO m => SockAddr -> m HostAddress+hostAddressFromSock :: (MonadLifxIO m) => SockAddr -> m HostAddress hostAddressFromSock = \case SockAddrInet port ha -> checkPort port >> pure ha addr -> lifxThrowIO $ UnexpectedSockAddrType addr-receiveMessage :: MonadLifxIO m => Int -> Int -> m (Maybe (BS.ByteString, SockAddr))+receiveMessage :: (MonadLifxIO m) => Int -> Int -> m (Maybe (BS.ByteString, SockAddr)) receiveMessage t size = do sock <- getSocket liftIO@@ -683,7 +732,12 @@ . recvFrom sock $ headerSize + size -broadcast :: MonadLifxIO m => Message r -> m ()+broadcast :: (MonadLifxIO m) => Message r -> m () broadcast msg = do incrementCounter sendMessage' False (tupleToHostAddress (255, 255, 255, 255)) msg++showDecodeError :: UnicodeException -> String+showDecodeError = \case+ DecodeError s _ -> s+ _ -> "impossible"
src/Lifx/Lan/Internal.hs view
@@ -46,7 +46,7 @@ deriving (Eq, Ord, Show, Generic) -- | A monad for sending and receiving LIFX messages.-class MonadIO m => MonadLifxIO m where+class (MonadIO m) => MonadLifxIO m where getSocket :: m Socket getSource :: m Word32 getTimeout :: m Int@@ -65,7 +65,7 @@ m () handleOldMessage _ _ _ _ = pure () -instance MonadIO m => MonadLifxIO (LifxT m) where+instance (MonadIO m) => MonadLifxIO (LifxT m) where getSocket = LifxT $ asks fst3 getSource = LifxT $ asks snd3 getTimeout = LifxT $ asks thd3@@ -95,16 +95,17 @@ instance MonadTrans LifxT where lift = LifxT . lift . lift . lift-instance MonadReader s m => MonadReader s (LifxT m) where+instance (MonadReader s m) => MonadReader s (LifxT m) where ask = lift ask local f m = LifxT $ StateT \s -> ReaderT \e -> ExceptT $ local f $ unLifx e s m-instance MonadState s m => MonadState s (LifxT m) where+instance (MonadState s m) => MonadState s (LifxT m) where state = lift . state-instance MonadError e m => MonadError e (LifxT m) where- throwError = lift . throwError @e @m- catchError m h = LifxT $ StateT \s -> ReaderT \e ->- ExceptT $ catchError @e @m (unLifx e s m) (unLifx e s . h)+instance (MonadError e m) => MonadError (Either e LifxError) (LifxT m) where+ throwError = either (lift . throwError @e @m) (LifxT . throwError)+ catchError m h = LifxT $ StateT \s -> ReaderT \e -> ExceptT do+ (m', s'') <- either ((,s) . h . Right) (first pure) <$> unLifx e s m+ catchError @e @m (unLifx e s'' m') (unLifx e s'' . h . Left) unLifx :: (Socket, Word32, Int) -> Word8 -> LifxT m a -> m (Either LifxError (a, Word8)) unLifx e s = runExceptT . flip runReaderT e . flip runStateT s . (.unwrap)
src/Lifx/Lan/Mock/Terminal.hs view
@@ -8,15 +8,22 @@ import Control.Monad.Except import Control.Monad.Reader import Control.Monad.State-import Data.Bifunctor import Data.Colour.RGBSpace import Data.Colour.SRGB import Data.Foldable+import Data.Function+import Data.Functor+import Data.Traversable+import Data.Tuple.Extra+import Numeric.Natural +import Data.ByteString qualified as BS+import Data.List (unfoldr) import Data.Map (Map) import Data.Map.Strict qualified as Map import Data.Text (Text) import Data.Text.IO qualified as T+import Data.Time.Clock.POSIX (getPOSIXTime) import System.Console.ANSI hiding (SetColor) import Lifx.Internal.Colour@@ -32,6 +39,7 @@ data MockState = MockState { light :: LightState+ , group :: StateGroup , service :: Maybe StateService , hostFirmware :: Maybe StateHostFirmware , version :: Maybe StateVersion@@ -46,8 +54,23 @@ Note that sending some messages (e.g. 'GetVersion') will throw exceptions, since the necessary state isn't specified. See `runMockFull` for more control. -}-runMock :: [(Device, Text)] -> Mock a -> IO (Either MockError a)-runMock = runMockFull . fmap (second \t -> MockState (LightState (HSBK 0 0 0 0) 1 t) Nothing Nothing Nothing)+runMock :: [(Device, (Text, Text))] -> Mock a -> IO (Either MockError a)+runMock ds m = do+ t0 <- getPOSIXTime+ flip runMockFull m . flip evalState 0 $ for ds \d ->+ state ((BS.pack . unfoldNat) &&& succ) <&> \uuid ->+ d & second \(label, group) ->+ MockState+ { light = LightState (HSBK 0 0 0 0) 1 label+ , group = StateGroup uuid group t0+ , service = Nothing+ , hostFirmware = Nothing+ , version = Nothing+ }+ where+ -- represent input as base-(maxBound @a)+ unfoldNat :: forall a. (Integral a, Bounded a) => Natural -> [a]+ unfoldNat = unfoldr \n -> guard (n /= 0) $> swap (second fromIntegral (n `quotRem` fromIntegral (maxBound @a))) -- | More general version of `runMock`, which allows specifying extra information about devices. runMockFull :: [(Device, MockState)] -> Mock a -> IO (Either MockError a)@@ -79,7 +102,9 @@ GetHostFirmware -> whenProvided s.hostFirmware GetPower -> pure $ StatePower s.light.power SetPower (convertPower -> power) -> modify $ Map.insert d s{light = s.light{power}}+ SetLabel label -> modify $ Map.insert d s{light = s.light{label}} GetVersion -> whenProvided s.version+ GetGroup -> pure s.group GetColor -> pure s.light SetColor hsbk _t -> modify $ Map.insert d s{light = s.light{hsbk}} SetLightPower (convertPower -> power) _t -> modify $ Map.insert d s{light = s.light{power}}